@seedcord/services 0.8.1 → 0.8.2-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +4 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
package/LICENSE
CHANGED
|
@@ -175,7 +175,7 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
|
175
175
|
|
|
176
176
|
END OF TERMS AND CONDITIONS
|
|
177
177
|
|
|
178
|
-
Copyright
|
|
178
|
+
Copyright 2026 Dhruv Jain (materwelonDhruv)
|
|
179
179
|
|
|
180
180
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
181
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
_This repository is a work in progress._
|
|
8
8
|
|
|
9
9
|
- There are no stable releases yet but changes are being made actively.
|
|
10
|
-
-
|
|
10
|
+
- Until a major v1.0.0 release for seedcord, expect breaking changes in minor versions.
|
|
11
11
|
- Documentation will come soon as well!
|
|
12
12
|
|
|
13
13
|
If you'd like to try it out, you can check out the code in `mock`
|
package/dist/index.cjs
CHANGED
|
@@ -1285,6 +1285,7 @@ const DEFAULT_HEALTH_CHECK_PATH = "/healthcheck";
|
|
|
1285
1285
|
* information, useful for container orchestration and monitoring.
|
|
1286
1286
|
*/
|
|
1287
1287
|
var HealthCheck = class {
|
|
1288
|
+
/** @internal */
|
|
1288
1289
|
logger = new Logger("HealthCheck");
|
|
1289
1290
|
port;
|
|
1290
1291
|
path;
|
|
@@ -1299,6 +1300,7 @@ var HealthCheck = class {
|
|
|
1299
1300
|
/**
|
|
1300
1301
|
* Starts the health check server.
|
|
1301
1302
|
* @returns Promise that resolves when the server is listening
|
|
1303
|
+
* @internal
|
|
1302
1304
|
*/
|
|
1303
1305
|
async init() {
|
|
1304
1306
|
return new Promise((resolve, reject) => {
|
|
@@ -1337,6 +1339,7 @@ var HealthCheck = class {
|
|
|
1337
1339
|
* Stops the health check server.
|
|
1338
1340
|
*
|
|
1339
1341
|
* @returns Promise that resolves when the server is closed
|
|
1342
|
+
* @internal
|
|
1340
1343
|
*/
|
|
1341
1344
|
stop() {
|
|
1342
1345
|
const server = this.server;
|
|
@@ -1522,7 +1525,7 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
1522
1525
|
//#endregion
|
|
1523
1526
|
//#region src/index.ts
|
|
1524
1527
|
/** Package version */
|
|
1525
|
-
const version = "0.8.1";
|
|
1528
|
+
const version = "0.8.2-next.1";
|
|
1526
1529
|
|
|
1527
1530
|
//#endregion
|
|
1528
1531
|
exports.CoordinatedLifecycle = CoordinatedLifecycle;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["format","TransportStream","path","fs","format","Envapter","transports","Envapter","path","EventEmitter","SeedcordError","SeedcordErrorCode","SeedcordError","SeedcordErrorCode","PHASE_ORDER","SeedcordError","SeedcordErrorCode"],"sources":["../src/RateLimiter.ts","../src/Logger/LogFormatter.ts","../src/Logger/Transports/SinkTransport.ts","../src/Logger/TransportFactory.ts","../src/Logger/LoggerChannelRegistry.ts","../src/Logger/LoggerUtilities.ts","../src/Logger/Logger.ts","../src/StrictEventEmitter.ts","../src/Lifecycle/CoordinatedLifecycle.ts","../src/Lifecycle/CoordinatedShutdown.ts","../src/HealthCheck.ts","../src/Lifecycle/CoordinatedStartup.ts","../src/index.ts"],"sourcesContent":["import type { EpochMs } from '@seedcord/types';\n\nconst SWEEP_INTERVAL_MS = 60_000;\n\n/**\n * A usage window for a {@link RateLimiter} key.\n */\nexport interface RateLimitWindow {\n /** Window length in milliseconds. */\n delay: number;\n /**\n * Hits allowed inside the window before the key is limited.\n *\n * @defaultValue `1`\n */\n limit?: number;\n}\n\n/**\n * The outcome of a {@link RateLimiter.hit}.\n */\nexport interface RateLimitResult {\n /** Whether the key is at or over its limit for the current window. */\n limited: boolean;\n /** Absolute epoch milliseconds when the key next frees up. Convert to seconds for Discord timestamp markup. */\n expires: EpochMs;\n}\n\n/**\n * Tracks per-key usage windows and reports when a key is limited and when it frees up.\n *\n * Each key holds a sliding window of hit expiry times, and is limited once its live-hit count\n * reaches the window's `limit`. Expired hits are dropped on the next read, and a background sweep\n * drops fully-expired keys so the map does not grow without bound.\n */\nexport class RateLimiter {\n private readonly map = new Map<string, number[]>();\n\n constructor() {\n // keep the sweep from holding the process open\n setInterval(() => {\n this.sweep();\n }, SWEEP_INTERVAL_MS).unref();\n }\n\n /** Number of keys currently tracked. */\n get size(): number {\n return this.map.size;\n }\n\n /**\n * Records a hit for `key` and reports whether the key is now limited.\n *\n * @param key - The bucket to record against. The caller builds it from its own scope.\n * @param window - The usage window to apply for this hit.\n */\n hit(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n // a window must allow at least one hit, so 0 or a negative never means \"block everything\"\n const limit = Math.max(1, window.limit ?? 1);\n\n const live = this.live(key, now);\n\n if (live.length >= limit) {\n this.map.set(key, live);\n // soonest a slot frees is the earliest expiry\n return { limited: true, expires: Math.min(...live) as EpochMs };\n }\n\n const expires: EpochMs = (now + window.delay) as EpochMs;\n live.push(expires);\n this.map.set(key, live);\n\n return { limited: false, expires };\n }\n\n /**\n * Reports whether `key` is limited right now without recording a hit.\n *\n * The read half of a peek-then-commit. A gate calls `peek` to decide whether to refuse, and\n * `hit` to charge the slot only once it is the gate that let the request through.\n */\n peek(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n const limit = Math.max(1, window.limit ?? 1);\n // a read, so it never writes the filtered hits back, hit owns the only mutation\n const live = this.live(key, now);\n\n if (live.length >= limit) return { limited: true, expires: Math.min(...live) as EpochMs };\n return { limited: false, expires: (now + window.delay) as EpochMs };\n }\n\n private live(key: string, now: number): number[] {\n return (this.map.get(key) ?? []).filter((exp) => exp > now);\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, live] of this.map) {\n const kept = live.filter((exp) => exp > now);\n if (kept.length === 0) this.map.delete(key);\n else this.map.set(key, kept);\n }\n }\n}\n","import stripAnsi from 'strip-ansi';\nimport { format } from 'winston';\n\nimport type { Logform } from 'winston';\n\n/** An Error temporarily annotated with its ANSI-formatted name while pretty-printing stacks. */\ntype FormattedError = Error & { __formattedName?: string; __plainName?: string };\n\n/**\n * Options for pretty log formatting.\n */\nexport interface PrettyFormatOptions {\n /**\n * Number of spaces to pad the log level to.\n *\n * @defaultValue `7`\n */\n padding?: number;\n /**\n * Whether to strip ANSI codes from extra log data.\n *\n * @defaultValue `false`\n */\n stripExtras?: boolean;\n}\n\n/**\n * Options for JSON log formatting.\n * @internal\n */\nexport interface JsonFormatOptions {\n /**\n * Whether to strip ANSI codes from log messages and extra data.\n *\n * @defaultValue `false`\n */\n stripAnsi?: boolean;\n /**\n * Whether to produce minimal JSON output without extra fields.\n *\n * @defaultValue `false`\n */\n minimal?: boolean;\n}\n\n/**\n * Formats log records for console and file outputs.\n *\n * Supports pretty-printed colored logs for development\n * and JSON logs for production with optional ANSI stripping.\n * @internal\n */\nexport class LogFormatter {\n private readonly DEFAULT_PADDING = 7;\n private readonly SPLAT: symbol = Symbol.for('splat');\n\n private safeString(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === undefined || value === null) return '';\n if (typeof (value as { toString?: () => string }).toString === 'function') {\n return String((value as { toString: () => string }).toString());\n }\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n }\n return '';\n }\n\n private sanitizeAnsi(value: unknown): unknown {\n if (typeof value === 'string') return stripAnsi(value);\n if (value instanceof Error) {\n const error = value;\n const sanitized = new Error(stripAnsi(error.message));\n sanitized.name = error.name;\n if (typeof error.stack === 'string') sanitized.stack = stripAnsi(error.stack);\n return sanitized;\n }\n return value;\n }\n\n private getExtras(info: Logform.TransformableInfo): unknown[] {\n const raw = info[this.SPLAT];\n return Array.isArray(raw) ? raw : [];\n }\n\n private readonly FORMAT_SPECIFIERS = /%[sdifjoO]/gu;\n private readonly HAD_FORMAT_KEY = Symbol.for('hadFormatSpecifiers');\n private readonly SAVED_SPLAT_KEY = Symbol.for('savedSplat');\n\n private markFormatSpecifiers(): Logform.Format {\n return format((info) => {\n const msg = typeof info.message === 'string' ? info.message : '';\n const extras = this.getExtras(info);\n const matches = msg.match(this.FORMAT_SPECIFIERS);\n const formatCount = matches ? matches.length : 0;\n info[this.HAD_FORMAT_KEY] = formatCount;\n info[this.SAVED_SPLAT_KEY] = [...extras];\n return info;\n })();\n }\n\n public createPreFormat(): Logform.Format {\n return this.markFormatSpecifiers();\n }\n\n private preserveErrorFormatting(): Logform.Format {\n return format((info) => {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error && /\\u001b/.test(item.name)) {\n const originalName = item.name;\n const plainName = stripAnsi(item.name);\n\n const formatted = item as FormattedError;\n formatted.__formattedName = originalName;\n formatted.__plainName = plainName;\n }\n }\n\n return info;\n })();\n }\n\n private restoreErrorFormatting(): Logform.Format {\n return format((info) => {\n if (typeof info.stack === 'string') {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error) {\n const { __formattedName: formattedName, __plainName: plainName } = item as FormattedError;\n\n if (typeof formattedName === 'string' && typeof plainName === 'string') {\n info.stack = (info.stack as string).replace(\n new RegExp(`^${this.escapeRegex(plainName)}`, 'm'),\n formattedName\n );\n }\n }\n }\n }\n\n return info;\n })();\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Creates pretty-printed format with colors and timestamps.\n *\n * Ideal for development environments where human readability matters.\n *\n * @param options - Formatting options including padding and ANSI stripping\n * @returns Array of Winston format transformers\n */\n public pretty(options: PrettyFormatOptions = {}): Logform.Format[] {\n const padding = options.padding ?? this.DEFAULT_PADDING;\n return [\n this.preserveErrorFormatting(),\n format.errors({ stack: true }),\n this.restoreErrorFormatting(),\n format.splat(),\n format.colorize({ level: true }),\n format.timestamp({ format: 'D MMM, hh:mm:ss a' }),\n // eslint-disable-next-line max-statements\n format.printf((info: Logform.TransformableInfo) => {\n let ts = this.safeString(info.timestamp);\n let lvl = this.safeString(info.level).padEnd(padding);\n let lbl = this.safeString(info.label);\n let msg = this.safeString(info.message);\n\n if (options.stripExtras) {\n ts = stripAnsi(ts);\n lvl = stripAnsi(lvl);\n lbl = stripAnsi(lbl);\n msg = stripAnsi(msg);\n }\n\n const base = `${ts} [${lvl}]: ${lbl} - ${msg}`;\n const savedExtras = info[this.SAVED_SPLAT_KEY];\n // Array.isArray narrows to any[]; keep the elements as unknown so the filter below stays safe.\n const extras: unknown[] = Array.isArray(savedExtras) ? savedExtras : this.getExtras(info);\n\n let rendered = base;\n\n if (typeof info.stack === 'string') {\n let stack = this.safeString(info.stack);\n if (options.stripExtras) stack = stripAnsi(stack);\n rendered += `\\n${stack}`;\n }\n\n const cleaned = options.stripExtras ? extras.map((entry) => this.sanitizeAnsi(entry)) : extras;\n const rawFormatCount = info[this.HAD_FORMAT_KEY];\n const formatSpecifierCount = typeof rawFormatCount === 'number' ? rawFormatCount : 0;\n const filtered = cleaned.filter((x, index) => {\n if (x === null || x === undefined) return false;\n if (x instanceof Error && typeof info.stack === 'string') return false;\n if (typeof x !== 'object') {\n return index >= formatSpecifierCount;\n }\n return Object.keys(x).length > 0;\n });\n\n if (filtered.length) {\n const primitives: string[] = [];\n const objects: string[] = [];\n\n for (const x of filtered) {\n if (typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean') {\n primitives.push(String(x));\n } else {\n try {\n objects.push(JSON.stringify(x, null, 2));\n } catch {\n objects.push(String(x));\n }\n }\n }\n\n if (primitives.length) {\n rendered += ` ${primitives.join(' ')}`;\n }\n if (objects.length) {\n rendered += `\\n${objects.join('\\n')}`;\n }\n }\n\n return rendered;\n })\n ];\n }\n\n /**\n * Creates JSON format for structured logging.\n *\n * Best for production environments where logs are parsed by tools.\n *\n * @param options - JSON formatting options including ANSI stripping and minimal mode\n * @returns Array of Winston format transformers\n */\n public json(options: JsonFormatOptions = {}): Logform.Format[] {\n const base = [format.timestamp(), format.errors({ stack: true })];\n\n if (options.stripAnsi) {\n base.push(\n format((info) => {\n info.message = typeof info.message === 'string' ? stripAnsi(info.message) : info.message;\n if (typeof info.stack === 'string') info.stack = stripAnsi(info.stack);\n const extras = this.getExtras(info);\n if (extras.length) info.extras = extras.map((entry) => this.sanitizeAnsi(entry));\n return info;\n })()\n );\n }\n\n base.push(options.minimal ? format.json({}) : format.json({ bigint: true, space: 0 }));\n\n return base;\n }\n}\n","import TransportStream from 'winston-transport';\n\nimport type { Logform } from 'winston';\nimport type { TransportStreamOptions } from 'winston-transport';\n\n/**\n * Options for creating a SinkTransport.\n * @internal\n */\nexport interface SinkTransportOptions extends TransportStreamOptions {\n readonly channel: string;\n readonly sink: ILoggerSink;\n}\n\n/**\n * A log entry passed to custom sinks.\n */\nexport interface LoggerSinkLogEntry {\n /** Channel the log originated from */\n readonly channel: string;\n /** Pre-formatted log message ready for display */\n readonly rendered: string;\n /** Raw Winston log info object */\n readonly info: Logform.TransformableInfo;\n}\n\n/**\n * Custom sink interface for capturing logger output.\n *\n * Implement this to route logs to custom destinations.\n */\nexport interface ILoggerSink {\n /**\n * Called when a log entry is emitted.\n *\n * @param entry - The log entry with channel, rendered message, and raw info\n */\n onLog(entry: LoggerSinkLogEntry): void;\n}\n\n/**\n * Handle for managing a sink's lifecycle.\n */\nexport interface ILoggerSinkHandle {\n /**\n * Removes the sink and restores console output if muted.\n */\n dispose(): void;\n}\n\n/**\n * Winston transport that forwards logs to custom sinks.\n * @internal\n */\nexport class SinkTransport extends TransportStream {\n private readonly channelName: string;\n private readonly sink: ILoggerSink;\n\n public constructor(options: SinkTransportOptions) {\n super(options);\n this.channelName = options.channel;\n this.sink = options.sink;\n }\n\n public override log(info: Logform.TransformableInfo, callback: () => void): void {\n setImmediate(() => this.emit('logged', info));\n\n const rendered = this.resolveRendered(info);\n const channel = this.resolveChannel(info);\n\n this.sink.onLog({ channel, rendered, info });\n\n callback();\n }\n\n private resolveChannel(info: Logform.TransformableInfo): string {\n const channel = info.channel;\n return typeof channel === 'string' ? channel : this.channelName;\n }\n\n private resolveRendered(info: Logform.TransformableInfo): string {\n const msg = info[Symbol.for('message')];\n\n if (typeof msg === 'string') return msg;\n\n const fallback = info.message;\n if (typeof fallback === 'string') return fallback;\n\n if (fallback instanceof Error) return fallback.stack ?? fallback.message;\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- last-resort stringify for a non-string, non-Error message value\n return String(fallback ?? '');\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport { format, transports } from 'winston';\n\nimport { LogFormatter } from './LogFormatter';\nimport { SinkTransport } from './Transports/SinkTransport';\n\nimport type { ILoggerSink } from './Transports/SinkTransport';\nimport type { LoggerFormatMode, LoggerLevel, TransportConfig, WinstonTransport } from './Types';\n\n/**\n * Input parameters for building a Winston transport.\n * @internal\n */\nexport interface TransportBuildInput {\n channel: string;\n label: string;\n level: LoggerLevel;\n config: TransportConfig;\n defaultFormat: LoggerFormatMode;\n stripAnsi: boolean;\n}\n\n/**\n * Input parameters for building a sink transport.\n * @internal\n */\ninterface BuildInput {\n readonly channel: string;\n readonly label: string;\n readonly level: LoggerLevel;\n}\n\n/**\n * Creates Winston transports with proper formatting and file path resolution.\n *\n * Builds console and file transports with environment-aware defaults,\n * filename template expansion, and format selection.\n * @internal\n */\nexport class TransportFactory {\n private readonly formatter: LogFormatter;\n private readonly MILLISECOND_PAD = 3;\n private cachedSessionTimestamp: { date: string; timestamp: string } | null = null;\n\n constructor() {\n this.formatter = new LogFormatter();\n }\n\n private ensureDir(filepath: string): void {\n const dir = path.dirname(filepath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n }\n\n private withDefaultLabel(label: string): ReturnType<typeof format.combine> {\n return format.combine(\n format((info) => {\n info.label ??= label;\n return info;\n })()\n );\n }\n\n public buildPreFormat(): ReturnType<typeof format.combine> {\n return format.combine(this.formatter.createPreFormat());\n }\n\n private buildConsoleFormat(label: string): ReturnType<typeof format.combine> {\n // Use JSON format in production, pretty format in development\n if (Envapter.isProduction) {\n return format.combine(this.withDefaultLabel(label), ...this.formatter.json({ stripAnsi: true }));\n }\n return format.combine(this.withDefaultLabel(label), ...this.formatter.pretty());\n }\n\n private buildFileFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private buildStreamFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n // Stream transport uses similar formatting to file transport\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private pad(value: number): string {\n return value.toString().padStart(2, '0');\n }\n\n private buildTimestamp(): { date: string; timestamp: string } {\n if (this.cachedSessionTimestamp) {\n return this.cachedSessionTimestamp;\n }\n\n const now = new Date();\n const yyyy = now.getFullYear();\n const mm = this.pad(now.getMonth() + 1);\n const dd = this.pad(now.getDate());\n const hh = this.pad(now.getHours());\n const min = this.pad(now.getMinutes());\n const ss = this.pad(now.getSeconds());\n const ms = now.getMilliseconds().toString().padStart(this.MILLISECOND_PAD, '0');\n\n const date = `${yyyy}-${mm}-${dd}`;\n const timestamp = `${date}-${hh}${min}${ss}-${ms}`;\n\n this.cachedSessionTimestamp = { date, timestamp };\n return this.cachedSessionTimestamp;\n }\n\n private resolveFilename(template: string, channel: string): string {\n const { date, timestamp } = this.buildTimestamp();\n return template\n .replaceAll('{channel}', channel)\n .replaceAll('{date}', date)\n .replaceAll('{timestamp}', timestamp);\n }\n\n /**\n * Builds a Winston transport from configuration.\n *\n * Creates console, file, or stream transport with proper formatting,\n * level filtering, and file rotation settings.\n *\n * @param input - Transport configuration parameters\n * @returns Configured Winston transport instance\n */\n public build(input: TransportBuildInput): WinstonTransport {\n const effectiveFormat = input.config.format ?? input.defaultFormat;\n const shouldStripAnsi = input.config.stripAnsi ?? input.stripAnsi;\n const level = input.config.level ?? input.level;\n\n if (input.config.type === 'console') {\n return new transports.Console({\n level,\n format: this.buildConsoleFormat(input.label)\n });\n }\n\n if (input.config.type === 'stream') {\n return new transports.Stream({\n level,\n stream: input.config.stream,\n format: this.buildStreamFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n const filenameTemplate = input.config.filename ?? 'logs/application-{timestamp}.log';\n const resolvedFilename = this.resolveFilename(filenameTemplate, input.channel);\n this.ensureDir(resolvedFilename);\n\n return new transports.File({\n level,\n filename: resolvedFilename,\n ...(input.config.maxSize !== undefined ? { maxsize: input.config.maxSize } : {}),\n ...(input.config.maxFiles !== undefined ? { maxFiles: input.config.maxFiles } : {}),\n tailable: true,\n format: this.buildFileFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n /**\n * Builds a sink transport for routing logs to custom destinations.\n *\n * @param input - Transport configuration with channel and level info\n * @param sink - Custom sink to receive log entries\n * @returns Configured sink transport instance\n */\n public buildSinkTransport(input: BuildInput, sink: ILoggerSink): WinstonTransport {\n const format = this.buildConsoleFormat(input.label);\n\n return new SinkTransport({\n level: input.level,\n channel: input.channel,\n sink,\n format\n });\n }\n}\n","import path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport winston, { createLogger } from 'winston';\n\nimport { TransportFactory } from './TransportFactory';\n\nimport type { ILoggerSink, ILoggerSinkHandle } from './Transports/SinkTransport';\nimport type {\n ChannelConfig,\n LoggerConfiguration,\n LoggerLevel,\n TransportConfig,\n WinstonInstance,\n WinstonTransport\n} from './Types';\n\n/**\n * Manages Winston logger instances per channel with caching.\n *\n * Stores channel configuration and creates transports for each channel\n * with environment-aware defaults.\n */\nexport class LoggerChannelRegistry {\n private static _instance: LoggerChannelRegistry | null = null;\n\n private nextSinkId = 1;\n private readonly sinks = new Map<\n number,\n {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n >();\n\n private readonly DEFAULT_LEVEL: LoggerLevel = Envapter.isDevelopment\n ? 'silly'\n : Envapter.isStaging\n ? 'debug'\n : 'info';\n\n private config: LoggerConfiguration = {\n defaultChannel: 'default',\n channels: {},\n files: {\n maxSizeMB: 10,\n maxFiles: 5,\n patterns: {\n dev: 'logs/{channel}-{timestamp}.log',\n staging: 'logs/staging-{date}-{timestamp}.jsonl',\n prod: 'logs/production-{date}.jsonl'\n }\n }\n };\n\n private readonly FORMAT = Envapter.isDevelopment ? 'pretty' : 'json';\n\n private readonly cache = new Map<string, WinstonInstance>();\n private readonly transportFactory: TransportFactory;\n\n private constructor() {\n this.transportFactory = new TransportFactory();\n }\n\n /**\n * Gets the singleton instance of the registry.\n */\n public static get instance(): LoggerChannelRegistry {\n return (this._instance ??= new LoggerChannelRegistry());\n }\n\n private getDefaultChannelConfig(name: string): ChannelConfig {\n return {\n name,\n level: this.DEFAULT_LEVEL,\n transports: [\n { type: 'console', level: this.DEFAULT_LEVEL, format: this.FORMAT, stripAnsi: !Envapter.isDevelopment },\n {\n type: 'file',\n level: this.DEFAULT_LEVEL,\n filename: Envapter.isDevelopment\n ? this.config.files.patterns.dev\n : Envapter.isStaging\n ? this.config.files.patterns.staging\n : this.config.files.patterns.prod,\n format: this.FORMAT,\n stripAnsi: true,\n maxSize: this.config.files.maxSizeMB * 1024 * 1024,\n maxFiles: this.config.files.maxFiles\n }\n ]\n };\n }\n\n private mergeChannelConfig(base: ChannelConfig, override?: ChannelConfig): ChannelConfig {\n if (!override) return base;\n\n const level = override.level ?? base.level;\n const stripAnsi = override.stripAnsi ?? base.stripAnsi;\n const format = override.format ?? base.format;\n const transports = override.transports ?? base.transports;\n const name = override.name || base.name;\n\n return {\n name,\n ...(level !== undefined ? { level } : {}),\n ...(stripAnsi !== undefined ? { stripAnsi } : {}),\n ...(format !== undefined ? { format } : {}),\n ...(transports !== undefined ? { transports } : {})\n };\n }\n\n /**\n * Updates global logger configuration and clears cache.\n *\n * @param config - Partial configuration to merge with existing settings\n */\n public configure(config: Partial<LoggerConfiguration>): void {\n this.disposeCachedLoggers();\n this.config = { ...this.config, ...config, channels: { ...this.config.channels, ...(config.channels ?? {}) } };\n this.cache.clear();\n }\n\n // configure() rebuilds every logger; detach + close the old transports and reset sink\n // bookkeeping first, or the discarded loggers leak transports (and their file handles /\n // sink wrappers) across reconfigures, e.g. on every dev HMR cycle.\n private disposeCachedLoggers(): void {\n for (const logger of this.cache.values()) {\n for (const transport of [...logger.transports]) {\n logger.remove(transport);\n transport.close?.();\n }\n }\n for (const record of this.sinks.values()) {\n record.transportsByChannel.clear();\n record.removedConsoleByChannel.clear();\n }\n }\n\n /**\n * Returns the name of the default channel.\n */\n public getDefaultChannel(): string {\n return this.config.defaultChannel;\n }\n\n /**\n * Returns a list of all known channels (configured or cached).\n */\n public getChannels(): string[] {\n const configuredChannels = Object.keys(this.config.channels);\n const cachedChannels = Array.from(this.cache.keys());\n const allChannels = new Set([...configuredChannels, ...cachedChannels, this.config.defaultChannel]);\n return Array.from(allChannels).sort();\n }\n\n /**\n * Gets the log file path for a specific channel, if one exists.\n *\n * @param channel - Channel name to get log file path for\n * @returns The log file path, or null if no file transport exists\n */\n public getLogFilePath(channel: string): string | null {\n const logger = this.cache.get(channel);\n if (!logger) return null;\n\n for (const transport of logger.transports) {\n if (transport instanceof winston.transports.File) {\n return path.resolve(transport.dirname, transport.filename);\n }\n }\n\n return null;\n }\n\n /**\n * Gets or creates a Winston logger instance for the given channel.\n *\n * @param channel - Channel name to get logger for\n * @returns Configured Winston logger instance\n */\n public get(channel: string): WinstonInstance {\n const cached = this.cache.get(channel);\n if (cached) return cached;\n\n const channelConfig = this.mergeChannelConfig(\n this.getDefaultChannelConfig(channel),\n this.config.channels[channel]\n );\n\n const effectiveLevel = channelConfig.level ?? this.DEFAULT_LEVEL;\n\n const consoleMuted = this.isConsoleMuted();\n\n const transports = (channelConfig.transports ?? [])\n .filter((transportConfig: TransportConfig) => !(consoleMuted && transportConfig.type === 'console'))\n .map((transportConfig: TransportConfig) =>\n this.transportFactory.build({\n channel,\n label: channel,\n level: effectiveLevel,\n config: transportConfig,\n defaultFormat: channelConfig.format ?? 'pretty',\n stripAnsi: channelConfig.stripAnsi ?? true\n })\n );\n\n const logger = createLogger({\n level: effectiveLevel,\n format: this.transportFactory.buildPreFormat(),\n transports\n });\n\n for (const entry of this.sinks.values()) {\n this.applySinkToCachedLogger(channel, logger, entry);\n }\n\n this.cache.set(channel, logger);\n return logger;\n }\n\n private isConsoleMuted(): boolean {\n for (const entry of this.sinks.values()) {\n if (entry.muteConsole) return true;\n }\n return false;\n }\n\n /**\n * Installs a custom sink to capture log output across all channels.\n *\n * Useful for routing logs to custom destinations like TUIs or remote services.\n *\n * @param sink - Custom sink implementation to receive log entries\n * @param options - Optional configuration for console muting behavior\n * @returns Handle to dispose the sink when no longer needed\n */\n public installSink(sink: ILoggerSink, options?: { muteConsole?: boolean }): ILoggerSinkHandle {\n const id = this.nextSinkId;\n this.nextSinkId += 1;\n\n const record = {\n sink,\n muteConsole: options?.muteConsole ?? true,\n transportsByChannel: new Map<string, WinstonTransport>(),\n removedConsoleByChannel: new Map<string, WinstonTransport[]>()\n };\n\n this.sinks.set(id, record);\n\n for (const [channel, logger] of this.cache.entries()) {\n this.applySinkToCachedLogger(channel, logger, record);\n }\n\n return new (class implements ILoggerSinkHandle {\n public constructor(\n private readonly registry: LoggerChannelRegistry,\n private readonly key: number\n ) {}\n public dispose(): void {\n this.registry.uninstallSink(this.key);\n }\n })(this, id);\n }\n\n private uninstallSink(id: number): void {\n const record = this.sinks.get(id);\n if (!record) return;\n\n for (const [channel, logger] of this.cache.entries()) {\n const sinkTransport = record.transportsByChannel.get(channel);\n if (sinkTransport) logger.remove(sinkTransport);\n\n const removed = record.removedConsoleByChannel.get(channel);\n if (removed?.length) {\n for (const t of removed) logger.add(t);\n }\n }\n\n this.sinks.delete(id);\n }\n\n private applySinkToCachedLogger(\n channel: string,\n logger: WinstonInstance,\n record: {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n ): void {\n if (record.muteConsole) {\n const removed: WinstonTransport[] = [];\n for (const t of logger.transports) {\n if (t instanceof winston.transports.Console) {\n removed.push(t);\n }\n }\n if (removed.length) {\n for (const t of removed) logger.remove(t);\n record.removedConsoleByChannel.set(channel, removed);\n }\n }\n\n const sinkTransport = this.transportFactory.buildSinkTransport(\n { channel, label: channel, level: logger.level as unknown as LoggerLevel },\n record.sink\n );\n\n logger.add(sinkTransport);\n record.transportsByChannel.set(channel, sinkTransport);\n }\n}\n","import { formatFilePath } from '@seedcord/utils';\nimport chalk from 'chalk';\n\nimport type { LoggerLevel } from './Types';\nimport type { ILogger } from '@seedcord/types';\n\n/**\n * Provides access to common logging utilities.\n */\nexport class LoggerUtilities {\n constructor(private readonly logger: ILogger) {}\n\n private arrow(text: string): string {\n return `${chalk.gray('→')} ${text}`;\n }\n\n /**\n * Logs a single item with an arrow prefix.\n * @param text - The text to log\n */\n public item(text: string, level: LoggerLevel = 'info'): void {\n this.logger[level](this.arrow(text));\n }\n\n /**\n * Logs a list of items with optional heading.\n *\n * @param items - Array of items to log as a list\n * @param heading - Optional heading to display above the list\n */\n public list(items: string[], heading?: string, level: LoggerLevel = 'info'): void {\n if (heading) this.logger[level](heading);\n for (const item of items) {\n this.logger[level](this.arrow(item));\n }\n }\n\n /**\n * Logs a summary with title and key-value pairs.\n * Example: \"Loaded: 5 handlers, 3 commands\"\n *\n * @param title - The title of the summary\n * @param items - Object with counts/values to display\n */\n public summary(title: string, items: Record<string, number | string>, level: LoggerLevel = 'info'): void {\n const entries = Object.entries(items).map(([key, value]) => `${chalk.magenta.bold(String(value))} ${key}`);\n this.logger[level](`${chalk.bold.green(title)}: ${entries.join(', ')}`);\n }\n\n /**\n * Logs a component registration message.\n *\n * @param name - Name of the component being registered\n * @param from - File path the component is from\n * @param type - Optional type label (e.g., 'middleware', 'handler')\n */\n public registration(name: string, from: string, type?: string, level: LoggerLevel = 'info'): void {\n const scope = type ? `${type} ` : '';\n this.logger[level](\n `${chalk.italic('Registered')} ${chalk.bold.yellow(scope)}${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(from))}`\n );\n }\n\n /**\n * Logs component initialization start/end.\n *\n * @param component - Name of the component\n * @param action - 'start' or 'end' to indicate initialization phase\n */\n public initialization(component: string, action: 'start' | 'end', level: LoggerLevel = 'info'): void {\n const verb = action === 'start' ? 'Initializing' : 'Initialized';\n this.logger[level](chalk.bold(`${verb} ${component}`));\n }\n\n /**\n * Logs progress as \"[current/total]\" with optional item label.\n *\n * @param current - Current progress count\n * @param total - Total count\n * @param item - Optional item label to append\n */\n public progress(current: number, total: number, item?: string, level: LoggerLevel = 'info'): void {\n const base = `[${current}/${total}]`;\n const suffix = item ? ` ${item}` : '';\n this.logger[level](`${chalk.cyan(base)}${suffix}`);\n }\n\n /**\n * Logs content in a decorative box.\n *\n * @param title - Title to display in the box\n * @param content - Lines of content to display in the box\n */\n public box(title: string, content: string[], level: LoggerLevel = 'info'): void {\n const width = Math.max(title.length, ...content.map((line) => line.length)) + 2;\n const horizontal = '─'.repeat(width);\n this.logger[level](`╭${horizontal}╮`);\n this.logger[level](`│ ${title.padEnd(width - 1, ' ')}│`);\n for (const line of content) {\n this.logger[level](`│ ${line.padEnd(width - 1, ' ')}│`);\n }\n this.logger[level](`╰${horizontal}╯`);\n }\n}\n","import { LoggerChannelRegistry } from './LoggerChannelRegistry';\nimport { LoggerUtilities } from './LoggerUtilities';\n\nimport type { LoggerConfiguration, LoggerOptions } from './Types';\nimport type { ILogger } from '@seedcord/types';\nimport type { Logger as Winston } from 'winston';\n\n/**\n * Public logging service with channel-aware transports and per-run file output.\n *\n * - Channel separation (e.g., bot, cli, hmr)\n * - Production-safe JSON logs with ANSI stripping\n * - Unique log files per run via filename templates\n */\nexport class Logger implements ILogger {\n declare private logger: Winston;\n private readonly label: string;\n private channel: string;\n private readonly registry = LoggerChannelRegistry.instance;\n\n public readonly utils: LoggerUtilities;\n\n /**\n * Configures global logger settings, applied to all channels.\n *\n * @param config - Partial configuration to merge with defaults\n */\n public static configure(config: Partial<LoggerConfiguration>): void {\n LoggerChannelRegistry.instance.configure(config);\n }\n\n /**\n * Creates a new Logger instance.\n *\n * @param label - Prefix/label for all log entries from this logger\n * @param options - Optional configuration for channel, format, and ANSI handling\n */\n constructor(label: string, options?: LoggerOptions) {\n this.label = label;\n this.channel = options?.channel ?? this.registry.getDefaultChannel();\n this.logger = this.registry.get(this.channel).child({ label: this.label, channel: this.channel });\n\n this.utils = new LoggerUtilities(this);\n }\n\n /**\n * Switches this logger to a different channel.\n *\n * @param channel - Channel name to switch to\n */\n public setChannel(channel: string): void {\n this.channel = channel;\n this.logger = this.registry.get(channel).child({ label: this.label, channel });\n }\n\n /**\n * Returns a new Logger for this label on the specified channel.\n *\n * @param channel - Channel name to use\n */\n public inChannel(channel: string): Logger {\n return new Logger(this.label, { channel });\n }\n\n /**\n * Logs an error message with optional additional data.\n *\n * @param msg - The error message to log\n * @param args - Additional data to include in the log entry\n */\n public error(msg: string, ...args: unknown[]): void {\n this.logger.error(msg, ...args);\n }\n\n /**\n * Logs a warning message with optional additional data.\n *\n * @param msg - The warning message to log\n * @param args - Additional data to include in the log entry\n */\n public warn(msg: string, ...args: unknown[]): void {\n this.logger.warn(msg, ...args);\n }\n\n /**\n * Logs an informational message with optional additional data.\n *\n * @param msg - The informational message to log\n * @param args - Additional data to include in the log entry\n */\n public info(msg: string, ...args: unknown[]): void {\n this.logger.info(msg, ...args);\n }\n\n /**\n * Logs an HTTP-related message with optional additional data.\n *\n * @param msg - The HTTP message to log\n * @param args - Additional data to include in the log entry\n */\n public http(msg: string, ...args: unknown[]): void {\n this.logger.http(msg, ...args);\n }\n\n /**\n * Logs a verbose message with optional additional data.\n *\n * @param msg - The verbose message to log\n * @param args - Additional data to include in the log entry\n */\n public verbose(msg: string, ...args: unknown[]): void {\n this.logger.verbose(msg, ...args);\n }\n\n /**\n * Logs a debug message with optional additional data.\n *\n * @param msg - The debug message to log\n * @param args - Additional data to include in the log entry\n */\n public debug(msg: string, ...args: unknown[]): void {\n this.logger.debug(msg, ...args);\n }\n\n /**\n * Logs a silly/trace level message with optional additional data.\n *\n * @param msg - The silly message to log\n * @param args - Additional data to include in the log entry\n */\n public silly(msg: string, ...args: unknown[]): void {\n this.logger.silly(msg, ...args);\n }\n}\n","import { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n/** Tuple type used for all event payloads. */\nexport type SEArgsTuple = readonly unknown[];\n\n/** Convenience map for emitters that intentionally expose no events. */\nexport type SENoEvents = Record<never, SEArgsTuple>;\n\n/**\n * Accepts any object type and constrains every value to be a tuple.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport type SEEventMapLike<TEvents extends object> = { [K in keyof TEvents]: SEArgsTuple };\n\n/**\n * Narrows a provided event map to the keys that can be emitted or listened for.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n * @internal\n */\ntype SEEventKey<TEvents extends object> = Extract<keyof TEvents, string | symbol>;\n\n/**\n * Typed wrapper around Node.js {@link EventEmitter} enforcing tuple payloads per event name.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport class StrictEventEmitter<TEvents extends SEEventMapLike<TEvents>> extends EventEmitter {\n /**\n * Registers a persistent listener with tuple-safe arguments for the given event.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override on<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.on(event, listener);\n }\n\n /**\n * Registers a one time listener that is removed after the first invocation.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override once<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.once(event, listener);\n }\n\n /**\n * Removes a previously registered listener for the given event.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override off<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.off(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.on} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override addListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return this.on(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.off} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override removeListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.removeListener(event, listener);\n }\n\n /**\n * Emits an event with the strictly typed argument tuple for the event name.\n *\n * @param event - The event name to emit\n * @param args - Tuple payload for the event\n * @returns True when the event had listeners, false otherwise\n */\n override emit<TEventKey extends SEEventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n // justified: widen the per-event tuple to its base array type so it spreads into Node's\n // `emit(event, ...args: any[])`. There's no declaration fix for Node's base signature.\n return super.emit(event, ...(args as readonly unknown[]));\n }\n\n /**\n * Retrieves the listener list for a given event with the correct tuple signature.\n *\n * @param event - The event name to inspect\n * @returns Array of listeners registered for the event\n */\n override listeners<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey\n ): ((...args: TEvents[TEventKey]) => void)[] {\n return super.listeners(event) as ((...args: TEvents[TEventKey]) => void)[];\n }\n\n /**\n * Counts listeners for an event without widening the return type of {@link EventEmitter.listenerCount}.\n *\n * @param event - The event name to inspect\n * @returns The total number of listeners registered for the event\n */\n listenerCountTyped<TEventKey extends SEEventKey<TEvents>>(event: TEventKey): number {\n return super.listenerCount(event);\n }\n\n /**\n * Returns the list of event names known to the emitter with the mapped key type.\n *\n * @returns Array of event keys supported by the emitter\n */\n eventNamesTyped(): SEEventKey<TEvents>[] {\n return super.eventNames() as SEEventKey<TEvents>[];\n }\n\n /**\n * Waits for an event to be emitted, resolving with the listener arguments tuple once triggered.\n * Supports optional abort signals and timeouts for cancellation semantics.\n *\n * @param event - The event name to wait for\n * @param opts - Optional abort signal or timeout in milliseconds\n * @returns Promise resolving with the emitted argument tuple; rejects when aborted or timed out\n */\n waitFor<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n opts?: { signal?: AbortSignal; timeoutMs?: number }\n ): Promise<TEvents[TEventKey]> {\n return new Promise<TEvents[TEventKey]>((resolve, reject) => {\n const onEvent = (...args: TEvents[TEventKey]): void => {\n cleanup();\n resolve(args);\n };\n\n const onAbort = (): void => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForAborted));\n };\n\n let timeoutId: NodeJS.Timeout | null = null;\n\n const cleanup = (): void => {\n this.off(event, onEvent);\n opts?.signal?.removeEventListener('abort', onAbort);\n if (timeoutId) clearTimeout(timeoutId);\n };\n\n this.once(event, onEvent);\n\n if (opts?.signal) {\n if (opts.signal.aborted) return onAbort();\n opts.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n if (opts?.timeoutMs !== undefined) {\n const timeoutMs = opts.timeoutMs;\n timeoutId = setTimeout(() => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForTimeout, [timeoutMs]));\n }, timeoutMs);\n }\n });\n }\n}\n","/*\n * Inspired by Akka Coordinated Shutdown: https://doc.akka.io/libraries/akka-core/current/coordinated-shutdown.html\n * and Lewis's implementation in a private repo elsewhere (https://github.com/Yomanz)\n */\n\nimport { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { Logger } from '../Logger';\nimport { StrictEventEmitter } from '../StrictEventEmitter';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { SEEventMapLike } from '../StrictEventEmitter';\n\n/**\n * Abstract base class for coordinated lifecycle management (startup/shutdown)\n */\nexport abstract class CoordinatedLifecycle<\n TPhase extends number,\n TEvents extends SEEventMapLike<TEvents>\n> extends StrictEventEmitter<TEvents> {\n protected readonly logger: Logger;\n protected readonly tasksMap = new Map<TPhase, LifecycleTask[]>();\n\n protected constructor(\n loggerName: string,\n protected readonly phaseOrder: TPhase[],\n protected readonly phaseEnum: Record<number, string>\n ) {\n super();\n this.logger = new Logger(loggerName);\n this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));\n }\n\n /**\n * Adds a lifecycle task to a specific phase.\n *\n * Tasks are executed in phase order during lifecycle operations.\n * Each task has a timeout to prevent hanging operations.\n *\n * @param phase - The lifecycle phase to add the task to\n * @param taskName - Unique name for the task (used for logging and removal)\n * @param task - Async function to execute during the phase\n * @param timeoutMs - Maximum time allowed for task execution in milliseconds\n * @example\n * ```typescript\n * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {\n * await database.connect();\n * }, 10000);\n * ```\n */\n public addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void {\n if (!this.canAddTask()) return;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);\n }\n\n tasks.push({ name: taskName, task, timeout: timeoutMs });\n this.logger.debug(\n `${chalk.italic('Added')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} to phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n /**\n * Removes a lifecycle task from a specific phase.\n *\n * @param phase - The lifecycle phase to remove the task from\n * @param taskName - Name of the task to remove\n * @returns True if the task was found and removed, false otherwise\n */\n public removeTask(phase: TPhase, taskName: string): boolean {\n if (!this.canRemoveTask()) return false;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) return false;\n\n const initialLength = tasks.length;\n const filteredTasks = tasks.filter((task) => task.name !== taskName);\n this.tasksMap.set(phase, filteredTasks);\n\n const removed = initialLength !== filteredTasks.length;\n if (removed) {\n this.logger.debug(\n `${chalk.italic('Removed')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} from phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n return removed;\n }\n\n /**\n * Run all tasks in a specific phase\n */\n protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.warn(`No tasks to run in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`);\n return;\n }\n\n this.logger.info(\n `${chalk.bold.yellow('Running')} ${this.getTaskType()} phase ${chalk.bold.magenta(this.phaseEnum[phase])} with ${chalk.bold.cyan(tasks.length)} tasks`\n );\n this.emitPhase(phase, 'start');\n\n const results: PromiseSettledResult<void>[] = await this.executeTasksInPhase(phase, tasks);\n\n const failures = results.filter((r) => r.status === 'rejected').length;\n if (failures > 0) {\n // Pass the raw phase name; chalk's ANSI codes would otherwise leak into the serialized\n // error message (e.g. the unknown-exception webhook payload).\n throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);\n } else {\n this.logger.info(\n `Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green('completed successfully')}`\n );\n }\n\n this.emitPhase(phase, 'complete');\n }\n\n /**\n * Run a single task with timeout\n */\n protected async runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n } catch (error) {\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // The phase event key is interpolated from phaseOrder at runtime; the subclass event map derives\n // its phase keys from the same range, so the key is always valid, but TS can't correlate a\n // template-literal key with the generic TEvents. Emit the no-payload event via the base emitter.\n private emitPhase(phase: TPhase, action: 'start' | 'complete'): void {\n EventEmitter.prototype.emit.call(this, `phase:${phase}:${action}`);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract canAddTask(): boolean;\n protected abstract canRemoveTask(): boolean;\n protected abstract getTaskType(): string;\n protected abstract executeTasksInPhase(\n phase: TPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]>;\n}\n","import chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Shutdown phases for coordinated application shutdown.\n */\nexport enum ShutdownPhase {\n /** Stop accepting new requests/interactions */\n StopAcceptingRequests = 1,\n /** Stop background services (health checks, etc.) */\n StopServices,\n /** Disconnect from external resources (database, APIs) */\n ExternalResources,\n /** Disconnect from Discord */\n DiscordCleanup,\n /** Final cleanup tasks */\n FinalCleanup\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: ShutdownPhase[] = [\n ShutdownPhase.StopAcceptingRequests,\n ShutdownPhase.StopServices,\n ShutdownPhase.ExternalResources,\n ShutdownPhase.DiscordCleanup,\n ShutdownPhase.FinalCleanup\n];\n\n/**\n * Strict-event-emitter payload map for coordinated shutdown phases.\n */\nexport type CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;\n\n// Delay process.exit so winston has a window to flush buffered log lines before the event loop dies.\nconst LOG_FLUSH_DELAY_MS = 500;\n\n/**\n * CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.\n *\n * It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.\n * Tasks can be added or removed dynamically, and each task has an associated timeout.\n */\nexport class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {\n private readonly isShutdownEnabled: boolean;\n\n private isShuttingDown = false;\n private exitCode = 0;\n private onSigTerm: (() => void) | null = null;\n private onSigInt: (() => void) | null = null;\n\n public constructor(enabled = true) {\n super('CoordinatedShutdown', PHASE_ORDER, ShutdownPhase);\n\n this.isShutdownEnabled = enabled;\n this.registerSignalHandlers();\n }\n\n protected canAddTask(): boolean {\n return this.isShutdownEnabled;\n }\n\n protected canRemoveTask(): boolean {\n return true;\n }\n\n protected getTaskType(): string {\n return 'shutdown';\n }\n\n protected async executeTasksInPhase(\n phase: ShutdownPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n private registerSignalHandlers(): void {\n if (!this.isShutdownEnabled) return;\n\n this.onSigTerm = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGTERM')} signal`);\n void this.run(0);\n };\n\n this.onSigInt = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGINT')} signal`);\n void this.run(0);\n };\n\n process.on('SIGTERM', this.onSigTerm);\n process.on('SIGINT', this.onSigInt);\n }\n\n private removeSignalHandlers(): void {\n if (this.onSigTerm) {\n process.off('SIGTERM', this.onSigTerm);\n this.onSigTerm = null;\n }\n if (this.onSigInt) {\n process.off('SIGINT', this.onSigInt);\n this.onSigInt = null;\n }\n }\n\n /**\n * Adds a task to a specific shutdown phase with timeout.\n *\n * @param phase - The shutdown phase from {@link ShutdownPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}\n */\n public override addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs = 5000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n /**\n * Removes a task from a specific shutdown phase.\n *\n * @param phase - The shutdown phase to remove from\n * @param taskName - Name of the task to remove\n * @returns True if task was found and removed\n */\n public override removeTask(phase: ShutdownPhase, taskName: string): boolean {\n return super.removeTask(phase, taskName);\n }\n\n /**\n * Executes the coordinated shutdown sequence.\n *\n * Runs all registered tasks across shutdown phases in reverse order.\n * Tasks within each phase are executed in parallel for faster shutdown.\n * Process exits with the specified code when complete.\n *\n * @param exitCode - Process exit code. {@default `0`}\n * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}\n * @returns Promise that resolves when shutdown is complete\n * @example\n * ```typescript\n * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);\n * await shutdown.run(0); // Graceful shutdown\n * ```\n */\n public async run(exitCode = 0, exitProcess = true): Promise<void> {\n this.removeSignalHandlers();\n\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown sequence already in progress');\n return;\n }\n\n this.isShuttingDown = true;\n this.exitCode = exitCode;\n this.logger.info(\n `${chalk.bold.yellow('Starting')} coordinated shutdown with exit code ${chalk.bold.cyan(exitCode)}`\n );\n this.emit('shutdown:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n await this.runPhase(phase);\n }\n\n this.logger.info(`${chalk.bold.green('Coordinated shutdown completed')} successfully`);\n this.emit('shutdown:complete');\n } catch (error) {\n this.logger.error(`${chalk.bold.red('Coordinated shutdown failed')}`);\n this.emit('shutdown:error', error);\n } finally {\n if (exitProcess) {\n this.logger.info(`${chalk.bold.red('Exiting')} process with code ${chalk.bold.cyan(this.exitCode)}`);\n setTimeout(() => {\n process.exit(this.exitCode);\n }, LOG_FLUSH_DELAY_MS);\n } else {\n this.logger.info(`${chalk.bold.yellow('Skipping')} process exit (dev mode)`);\n this.isShuttingDown = false;\n }\n }\n }\n}\n","import { createServer } from 'http';\n\nimport chalk from 'chalk';\n\nimport { ShutdownPhase } from './Lifecycle/CoordinatedShutdown';\nimport { Logger } from './Logger';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'http';\n\nconst HTTP_OK = 200;\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\nconst DEFAULT_HEALTH_CHECK_PATH = '/healthcheck';\n\n/**\n * HTTP health check service for monitoring bot status.\n *\n * Provides a simple HTTP endpoint that responds with JSON status\n * information, useful for container orchestration and monitoring.\n */\nexport class HealthCheck {\n public readonly logger = new Logger('HealthCheck');\n\n public readonly port: number;\n public readonly path: string;\n public readonly host: string | undefined;\n\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;\n this.host = options?.host;\n\n shutdown.addTask(ShutdownPhase.StopServices, 'stop-healthcheck-server', async () => await this.stop());\n }\n\n /**\n * Starts the health check server.\n * @returns Promise that resolves when the server is listening\n */\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (req.method === 'GET' && req.url === this.path) {\n res.writeHead(HTTP_OK, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));\n } else {\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n }\n });\n this.server = server;\n\n const onListenError = (err: Error): void => reject(err);\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // Swap the listen-time reject handler for a logging one: keeping it would reject an\n // already-settled promise on a late error, and removing it without a replacement\n // would crash the process on an unhandled 'error' event.\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n const address = this.host ?? 'localhost';\n this.logger.info(\n `${chalk.green.bold('✓')} Health check server listening on ${chalk.cyan(`http://${address}:${this.port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this.host) {\n this.logger.debug(`Binding health check server to ${this.host}`);\n server.listen(this.port, this.host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this.port);\n }\n });\n }\n\n /**\n * Stops the health check server.\n *\n * @returns Promise that resolves when the server is closed\n */\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n server.once('close', () => resolve());\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(chalk.bold.red('Health check server stopped'));\n });\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Startup phases for coordinated initialization\n *\n * Defines the order in which different components are initialized during bot startup.\n */\nexport enum StartupPhase {\n /** Validate environment variables and config files */\n Validation = 1,\n /** Discover plugin constructors via decorators or registry */\n Discovery,\n /** Register plugin metadata and declared dependencies */\n Registration,\n /** Inject and validate plugin-specific configuration */\n Configuration,\n /** Instantiate plugin classes with Core and arguments */\n Instantiation,\n /** Activate plugins by calling their init/setup methods */\n Activation,\n /** Mark seedcord as ready and start handling interactions */\n Ready\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: StartupPhase[] = [\n StartupPhase.Validation,\n StartupPhase.Discovery,\n StartupPhase.Registration,\n StartupPhase.Configuration,\n StartupPhase.Instantiation,\n StartupPhase.Activation,\n StartupPhase.Ready\n];\n\n/**\n * Strict-event-emitter payload map for coordinated startup phases.\n */\nexport type CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;\n\n/**\n * Manages bot startup lifecycle with ordered phases\n *\n * Coordinates initialization of all bot components in a predictable sequence.\n * Tasks are executed within their designated phases to ensure proper dependency order.\n */\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {\n private isStartingUp = false;\n private hasStarted = false;\n\n public constructor() {\n super('CoordinatedStartup', PHASE_ORDER, StartupPhase);\n }\n\n /**\n * Adds a task to a specific startup phase with timeout.\n *\n * @param phase - The startup phase from {@link StartupPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}\n */\n public override addTask(phase: StartupPhase, taskName: string, task: () => Promise<void>, timeoutMs = 10000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n protected canAddTask(): boolean {\n if (this.hasStarted) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);\n }\n\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);\n }\n\n return true;\n }\n\n protected canRemoveTask(): boolean {\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);\n }\n\n return true;\n }\n\n protected getTaskType(): string {\n return 'startup';\n }\n\n protected async executeTasksInPhase(\n phase: StartupPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n /**\n * Executes the coordinated startup sequence.\n *\n * Runs all registered tasks across startup phases in the correct order.\n * Each phase completes before the next phase begins. Tasks within a phase\n * are executed sequentially to maintain predictable initialization.\n *\n * @returns Promise that resolves when startup is complete\n * @throws An {@link Error} If startup fails or is called multiple times\n * @example\n * ```typescript\n * const startup = new CoordinatedStartup();\n * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);\n * await startup.run();\n * ```\n */\n public async run(): Promise<void> {\n if (this.hasStarted) {\n this.logger.warn('Startup sequence has already completed');\n return;\n }\n\n if (this.isStartingUp) {\n this.logger.warn('Startup sequence already in progress');\n return;\n }\n\n this.isStartingUp = true;\n this.logger.info(`${chalk.bold.green('Starting')} coordinated startup sequence`);\n this.emit('startup:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false mid-loop from the cli\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted');\n return;\n }\n await this.runPhase(phase);\n }\n\n this.hasStarted = true;\n this.logger.info(`${chalk.bold.green('Coordinated startup completed')} successfully`);\n this.emit('startup:complete');\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false before this catch runs\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted during error handling');\n return;\n }\n this.logger.error(`${chalk.bold.red('Coordinated startup failed')}`);\n this.emit('startup:error', error);\n throw error;\n } finally {\n this.isStartingUp = false;\n }\n }\n\n protected override async runTaskWithTimeout(phase: StartupPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n } catch (error) {\n if (!this.isStartingUp) {\n return;\n }\n\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Aborts the startup sequence if it is currently running.\n */\n public abort(): void {\n if (this.isStartingUp) {\n this.isStartingUp = false;\n this.logger.warn('Aborting coordinated startup sequence');\n }\n }\n\n /**\n * Check if startup has completed\n */\n public get isReady(): boolean {\n return this.hasStarted;\n }\n\n /**\n * Check if startup is currently running\n */\n public get isRunning(): boolean {\n return this.isStartingUp;\n }\n}\n","export * from './RateLimiter';\nexport * from './HealthCheck';\nexport * from './Lifecycle';\nexport * from './Logger';\nexport * from './StrictEventEmitter';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,MAAM,oBAAoB;;;;;;;;AAiC1B,IAAa,cAAb,MAAyB;CACrB,AAAiB,sBAAM,IAAI,IAAsB;CAEjD,cAAc;EAEV,kBAAkB;GACd,KAAK,MAAM;EACf,GAAG,iBAAiB,CAAC,CAAC,MAAM;CAChC;;CAGA,IAAI,OAAe;EACf,OAAO,KAAK,IAAI;CACpB;;;;;;;CAQA,IAAI,KAAa,QAA0C;EACvD,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;EAE3C,MAAM,OAAO,KAAK,KAAK,KAAK,GAAG;EAE/B,IAAI,KAAK,UAAU,OAAO;GACtB,KAAK,IAAI,IAAI,KAAK,IAAI;GAEtB,OAAO;IAAE,SAAS;IAAM,SAAS,KAAK,IAAI,GAAG,IAAI;GAAa;EAClE;EAEA,MAAM,UAAoB,MAAM,OAAO;EACvC,KAAK,KAAK,OAAO;EACjB,KAAK,IAAI,IAAI,KAAK,IAAI;EAEtB,OAAO;GAAE,SAAS;GAAO;EAAQ;CACrC;;;;;;;CAQA,KAAK,KAAa,QAA0C;EACxD,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;EAE3C,MAAM,OAAO,KAAK,KAAK,KAAK,GAAG;EAE/B,IAAI,KAAK,UAAU,OAAO,OAAO;GAAE,SAAS;GAAM,SAAS,KAAK,IAAI,GAAG,IAAI;EAAa;EACxF,OAAO;GAAE,SAAS;GAAO,SAAU,MAAM,OAAO;EAAkB;CACtE;CAEA,AAAQ,KAAK,KAAa,KAAuB;EAC7C,QAAQ,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC,EAAC,CAAE,QAAQ,QAAQ,MAAM,GAAG;CAC9D;CAEA,AAAQ,QAAc;EAClB,MAAM,MAAM,KAAK,IAAI;EACrB,KAAK,MAAM,CAAC,KAAK,SAAS,KAAK,KAAK;GAChC,MAAM,OAAO,KAAK,QAAQ,QAAQ,MAAM,GAAG;GAC3C,IAAI,KAAK,WAAW,GAAG,KAAK,IAAI,OAAO,GAAG;QACrC,KAAK,IAAI,IAAI,KAAK,IAAI;EAC/B;CACJ;AACJ;;;;;;;;;;;ACpDA,IAAa,eAAb,MAA0B;CACtB,AAAiB,kBAAkB;CACnC,AAAiB,QAAgB,OAAO,IAAI,OAAO;CAEnD,AAAQ,WAAW,OAAwB;EACvC,IAAI,OAAO,UAAU,UAAU,OAAO;EACtC,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;EAClD,IAAI,OAAQ,MAAsC,aAAa,YAC3D,OAAO,OAAQ,MAAqC,SAAS,CAAC;EAElE,IAAI,OAAO,UAAU,UACjB,IAAI;GACA,OAAO,KAAK,UAAU,KAAK;EAC/B,QAAQ;GACJ,OAAO;EACX;EAEJ,OAAO;CACX;CAEA,AAAQ,aAAa,OAAyB;EAC1C,IAAI,OAAO,UAAU,UAAU,+BAAiB,KAAK;EACrD,IAAI,iBAAiB,OAAO;GACxB,MAAM,QAAQ;GACd,MAAM,YAAY,IAAI,8BAAgB,MAAM,OAAO,CAAC;GACpD,UAAU,OAAO,MAAM;GACvB,IAAI,OAAO,MAAM,UAAU,UAAU,UAAU,gCAAkB,MAAM,KAAK;GAC5E,OAAO;EACX;EACA,OAAO;CACX;CAEA,AAAQ,UAAU,MAA4C;EAC1D,MAAM,MAAM,KAAK,KAAK;EACtB,OAAO,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC;CACvC;CAEA,AAAiB,oBAAoB;CACrC,AAAiB,iBAAiB,OAAO,IAAI,qBAAqB;CAClE,AAAiB,kBAAkB,OAAO,IAAI,YAAY;CAE1D,AAAQ,uBAAuC;EAC3C,4BAAe,SAAS;GACpB,MAAM,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;GAC9D,MAAM,SAAS,KAAK,UAAU,IAAI;GAClC,MAAM,UAAU,IAAI,MAAM,KAAK,iBAAiB;GAChD,MAAM,cAAc,UAAU,QAAQ,SAAS;GAC/C,KAAK,KAAK,kBAAkB;GAC5B,KAAK,KAAK,mBAAmB,CAAC,GAAG,MAAM;GACvC,OAAO;EACX,CAAC,CAAC,CAAC;CACP;CAEA,AAAO,kBAAkC;EACrC,OAAO,KAAK,qBAAqB;CACrC;CAEA,AAAQ,0BAA0C;EAC9C,4BAAe,SAAS;GACpB,MAAM,SAAS,KAAK,UAAU,IAAI;GAElC,KAAK,MAAM,QAAQ,QACf,IAAI,gBAAgB,SAAS,SAAS,KAAK,KAAK,IAAI,GAAG;IACnD,MAAM,eAAe,KAAK;IAC1B,MAAM,oCAAsB,KAAK,IAAI;IAErC,MAAM,YAAY;IAClB,UAAU,kBAAkB;IAC5B,UAAU,cAAc;GAC5B;GAGJ,OAAO;EACX,CAAC,CAAC,CAAC;CACP;CAEA,AAAQ,yBAAyC;EAC7C,4BAAe,SAAS;GACpB,IAAI,OAAO,KAAK,UAAU,UAAU;IAChC,MAAM,SAAS,KAAK,UAAU,IAAI;IAElC,KAAK,MAAM,QAAQ,QACf,IAAI,gBAAgB,OAAO;KACvB,MAAM,EAAE,iBAAiB,eAAe,aAAa,cAAc;KAEnE,IAAI,OAAO,kBAAkB,YAAY,OAAO,cAAc,UAC1D,KAAK,QAAS,KAAK,MAAiB,QAChC,IAAI,OAAO,IAAI,KAAK,YAAY,SAAS,KAAK,GAAG,GACjD,aACJ;IAER;GAER;GAEA,OAAO;EACX,CAAC,CAAC,CAAC;CACP;CAEA,AAAQ,YAAY,KAAqB;EACrC,OAAO,IAAI,QAAQ,uBAAuB,MAAM;CACpD;;;;;;;;;CAUA,AAAO,OAAO,UAA+B,CAAC,GAAqB;EAC/D,MAAM,UAAU,QAAQ,WAAW,KAAK;EACxC,OAAO;GACH,KAAK,wBAAwB;GAC7BA,eAAO,OAAO,EAAE,OAAO,KAAK,CAAC;GAC7B,KAAK,uBAAuB;GAC5BA,eAAO,MAAM;GACbA,eAAO,SAAS,EAAE,OAAO,KAAK,CAAC;GAC/BA,eAAO,UAAU,EAAE,QAAQ,oBAAoB,CAAC;GAEhDA,eAAO,QAAQ,SAAoC;IAC/C,IAAI,KAAK,KAAK,WAAW,KAAK,SAAS;IACvC,IAAI,MAAM,KAAK,WAAW,KAAK,KAAK,CAAC,CAAC,OAAO,OAAO;IACpD,IAAI,MAAM,KAAK,WAAW,KAAK,KAAK;IACpC,IAAI,MAAM,KAAK,WAAW,KAAK,OAAO;IAEtC,IAAI,QAAQ,aAAa;KACrB,6BAAe,EAAE;KACjB,8BAAgB,GAAG;KACnB,8BAAgB,GAAG;KACnB,8BAAgB,GAAG;IACvB;IAEA,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,KAAK,IAAI,KAAK;IACzC,MAAM,cAAc,KAAK,KAAK;IAE9B,MAAM,SAAoB,MAAM,QAAQ,WAAW,IAAI,cAAc,KAAK,UAAU,IAAI;IAExF,IAAI,WAAW;IAEf,IAAI,OAAO,KAAK,UAAU,UAAU;KAChC,IAAI,QAAQ,KAAK,WAAW,KAAK,KAAK;KACtC,IAAI,QAAQ,aAAa,gCAAkB,KAAK;KAChD,YAAY,KAAK;IACrB;IAEA,MAAM,UAAU,QAAQ,cAAc,OAAO,KAAK,UAAU,KAAK,aAAa,KAAK,CAAC,IAAI;IACxF,MAAM,iBAAiB,KAAK,KAAK;IACjC,MAAM,uBAAuB,OAAO,mBAAmB,WAAW,iBAAiB;IACnF,MAAM,WAAW,QAAQ,QAAQ,GAAG,UAAU;KAC1C,IAAI,MAAM,QAAQ,MAAM,QAAW,OAAO;KAC1C,IAAI,aAAa,SAAS,OAAO,KAAK,UAAU,UAAU,OAAO;KACjE,IAAI,OAAO,MAAM,UACb,OAAO,SAAS;KAEpB,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,SAAS;IACnC,CAAC;IAED,IAAI,SAAS,QAAQ;KACjB,MAAM,aAAuB,CAAC;KAC9B,MAAM,UAAoB,CAAC;KAE3B,KAAK,MAAM,KAAK,UACZ,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WAC/D,WAAW,KAAK,OAAO,CAAC,CAAC;UAEzB,IAAI;MACA,QAAQ,KAAK,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;KAC3C,QAAQ;MACJ,QAAQ,KAAK,OAAO,CAAC,CAAC;KAC1B;KAIR,IAAI,WAAW,QACX,YAAY,IAAI,WAAW,KAAK,GAAG;KAEvC,IAAI,QAAQ,QACR,YAAY,KAAK,QAAQ,KAAK,IAAI;IAE1C;IAEA,OAAO;GACX,CAAC;EACL;CACJ;;;;;;;;;CAUA,AAAO,KAAK,UAA6B,CAAC,GAAqB;EAC3D,MAAM,OAAO,CAACA,eAAO,UAAU,GAAGA,eAAO,OAAO,EAAE,OAAO,KAAK,CAAC,CAAC;EAEhE,IAAI,QAAQ,WACR,KAAK,0BACO,SAAS;GACb,KAAK,UAAU,OAAO,KAAK,YAAY,mCAAqB,KAAK,OAAO,IAAI,KAAK;GACjF,IAAI,OAAO,KAAK,UAAU,UAAU,KAAK,gCAAkB,KAAK,KAAK;GACrE,MAAM,SAAS,KAAK,UAAU,IAAI;GAClC,IAAI,OAAO,QAAQ,KAAK,SAAS,OAAO,KAAK,UAAU,KAAK,aAAa,KAAK,CAAC;GAC/E,OAAO;EACX,CAAC,CAAC,CAAC,CACP;EAGJ,KAAK,KAAK,QAAQ,UAAUA,eAAO,KAAK,CAAC,CAAC,IAAIA,eAAO,KAAK;GAAE,QAAQ;GAAM,OAAO;EAAE,CAAC,CAAC;EAErF,OAAO;CACX;AACJ;;;;;;;;ACrNA,IAAa,gBAAb,cAAmCC,0BAAgB;CAC/C,AAAiB;CACjB,AAAiB;CAEjB,AAAO,YAAY,SAA+B;EAC9C,MAAM,OAAO;EACb,KAAK,cAAc,QAAQ;EAC3B,KAAK,OAAO,QAAQ;CACxB;CAEA,AAAgB,IAAI,MAAiC,UAA4B;EAC7E,mBAAmB,KAAK,KAAK,UAAU,IAAI,CAAC;EAE5C,MAAM,WAAW,KAAK,gBAAgB,IAAI;EAC1C,MAAM,UAAU,KAAK,eAAe,IAAI;EAExC,KAAK,KAAK,MAAM;GAAE;GAAS;GAAU;EAAK,CAAC;EAE3C,SAAS;CACb;CAEA,AAAQ,eAAe,MAAyC;EAC5D,MAAM,UAAU,KAAK;EACrB,OAAO,OAAO,YAAY,WAAW,UAAU,KAAK;CACxD;CAEA,AAAQ,gBAAgB,MAAyC;EAC7D,MAAM,MAAM,KAAK,OAAO,IAAI,SAAS;EAErC,IAAI,OAAO,QAAQ,UAAU,OAAO;EAEpC,MAAM,WAAW,KAAK;EACtB,IAAI,OAAO,aAAa,UAAU,OAAO;EAEzC,IAAI,oBAAoB,OAAO,OAAO,SAAS,SAAS,SAAS;EAGjE,OAAO,OAAO,YAAY,EAAE;CAChC;AACJ;;;;;;;;;;;ACnDA,IAAa,mBAAb,MAA8B;CAC1B,AAAiB;CACjB,AAAiB,kBAAkB;CACnC,AAAQ,yBAAqE;CAE7E,cAAc;EACV,KAAK,YAAY,IAAI,aAAa;CACtC;CAEA,AAAQ,UAAU,UAAwB;EACtC,MAAM,MAAMC,kBAAK,QAAQ,QAAQ;EACjC,IAAI,CAACC,gBAAG,WAAW,GAAG,GAAG,gBAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;CAClE;CAEA,AAAQ,iBAAiB,OAAkD;EACvE,OAAOC,eAAO,6BACF,SAAS;GACb,KAAK,UAAU;GACf,OAAO;EACX,CAAC,CAAC,CAAC,CACP;CACJ;CAEA,AAAO,iBAAoD;EACvD,OAAOA,eAAO,QAAQ,KAAK,UAAU,gBAAgB,CAAC;CAC1D;CAEA,AAAQ,mBAAmB,OAAkD;EAEzE,IAAIC,gBAAS,cACT,OAAOD,eAAO,QAAQ,KAAK,iBAAiB,KAAK,GAAG,GAAG,KAAK,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC,CAAC;EAEnG,OAAOA,eAAO,QAAQ,KAAK,iBAAiB,KAAK,GAAG,GAAG,KAAK,UAAU,OAAO,CAAC;CAClF;CAEA,AAAQ,gBACJ,OACA,MACA,WACiC;EACjC,MAAM,UACF,SAAS,WAAW,KAAK,UAAU,OAAO,EAAE,aAAa,UAAU,CAAC,IAAI,KAAK,UAAU,KAAK,EAAE,UAAU,CAAC;EAC7G,OAAOA,eAAO,QAAQ,KAAK,iBAAiB,KAAK,GAAG,GAAG,OAAO;CAClE;CAEA,AAAQ,kBACJ,OACA,MACA,WACiC;EAEjC,MAAM,UACF,SAAS,WAAW,KAAK,UAAU,OAAO,EAAE,aAAa,UAAU,CAAC,IAAI,KAAK,UAAU,KAAK,EAAE,UAAU,CAAC;EAC7G,OAAOA,eAAO,QAAQ,KAAK,iBAAiB,KAAK,GAAG,GAAG,OAAO;CAClE;CAEA,AAAQ,IAAI,OAAuB;EAC/B,OAAO,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG;CAC3C;CAEA,AAAQ,iBAAsD;EAC1D,IAAI,KAAK,wBACL,OAAO,KAAK;EAGhB,MAAM,sBAAM,IAAI,KAAK;EACrB,MAAM,OAAO,IAAI,YAAY;EAC7B,MAAM,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC;EACtC,MAAM,KAAK,KAAK,IAAI,IAAI,QAAQ,CAAC;EACjC,MAAM,KAAK,KAAK,IAAI,IAAI,SAAS,CAAC;EAClC,MAAM,MAAM,KAAK,IAAI,IAAI,WAAW,CAAC;EACrC,MAAM,KAAK,KAAK,IAAI,IAAI,WAAW,CAAC;EACpC,MAAM,KAAK,IAAI,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,KAAK,iBAAiB,GAAG;EAE9E,MAAM,OAAO,GAAG,KAAK,GAAG,GAAG,GAAG;EAC9B,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG;EAE9C,KAAK,yBAAyB;GAAE;GAAM;EAAU;EAChD,OAAO,KAAK;CAChB;CAEA,AAAQ,gBAAgB,UAAkB,SAAyB;EAC/D,MAAM,EAAE,MAAM,cAAc,KAAK,eAAe;EAChD,OAAO,SACF,WAAW,aAAa,OAAO,CAAC,CAChC,WAAW,UAAU,IAAI,CAAC,CAC1B,WAAW,eAAe,SAAS;CAC5C;;;;;;;;;;CAWA,AAAO,MAAM,OAA8C;EACvD,MAAM,kBAAkB,MAAM,OAAO,UAAU,MAAM;EACrD,MAAM,kBAAkB,MAAM,OAAO,aAAa,MAAM;EACxD,MAAM,QAAQ,MAAM,OAAO,SAAS,MAAM;EAE1C,IAAI,MAAM,OAAO,SAAS,WACtB,OAAO,IAAIE,mBAAW,QAAQ;GAC1B;GACA,QAAQ,KAAK,mBAAmB,MAAM,KAAK;EAC/C,CAAC;EAGL,IAAI,MAAM,OAAO,SAAS,UACtB,OAAO,IAAIA,mBAAW,OAAO;GACzB;GACA,QAAQ,MAAM,OAAO;GACrB,QAAQ,KAAK,kBAAkB,MAAM,OAAO,iBAAiB,eAAe;EAChF,CAAC;EAGL,MAAM,mBAAmB,MAAM,OAAO,YAAY;EAClD,MAAM,mBAAmB,KAAK,gBAAgB,kBAAkB,MAAM,OAAO;EAC7E,KAAK,UAAU,gBAAgB;EAE/B,OAAO,IAAIA,mBAAW,KAAK;GACvB;GACA,UAAU;GACV,GAAI,MAAM,OAAO,YAAY,SAAY,EAAE,SAAS,MAAM,OAAO,QAAQ,IAAI,CAAC;GAC9E,GAAI,MAAM,OAAO,aAAa,SAAY,EAAE,UAAU,MAAM,OAAO,SAAS,IAAI,CAAC;GACjF,UAAU;GACV,QAAQ,KAAK,gBAAgB,MAAM,OAAO,iBAAiB,eAAe;EAC9E,CAAC;CACL;;;;;;;;CASA,AAAO,mBAAmB,OAAmB,MAAqC;EAC9E,MAAM,SAAS,KAAK,mBAAmB,MAAM,KAAK;EAElD,OAAO,IAAI,cAAc;GACrB,OAAO,MAAM;GACb,SAAS,MAAM;GACf;GACA;EACJ,CAAC;CACL;AACJ;;;;;;;;;;ACxKA,IAAa,wBAAb,MAAa,sBAAsB;CAC/B,OAAe,YAA0C;CAEzD,AAAQ,aAAa;CACrB,AAAiB,wBAAQ,IAAI,IAQ3B;CAEF,AAAiB,gBAA6BC,gBAAS,gBACjD,UACAA,gBAAS,YACP,UACA;CAER,AAAQ,SAA8B;EAClC,gBAAgB;EAChB,UAAU,CAAC;EACX,OAAO;GACH,WAAW;GACX,UAAU;GACV,UAAU;IACN,KAAK;IACL,SAAS;IACT,MAAM;GACV;EACJ;CACJ;CAEA,AAAiB,SAASA,gBAAS,gBAAgB,WAAW;CAE9D,AAAiB,wBAAQ,IAAI,IAA6B;CAC1D,AAAiB;CAEjB,AAAQ,cAAc;EAClB,KAAK,mBAAmB,IAAI,iBAAiB;CACjD;;;;CAKA,WAAkB,WAAkC;EAChD,OAAQ,KAAK,cAAc,IAAI,sBAAsB;CACzD;CAEA,AAAQ,wBAAwB,MAA6B;EACzD,OAAO;GACH;GACA,OAAO,KAAK;GACZ,YAAY,CACR;IAAE,MAAM;IAAW,OAAO,KAAK;IAAe,QAAQ,KAAK;IAAQ,WAAW,CAACA,gBAAS;GAAc,GACtG;IACI,MAAM;IACN,OAAO,KAAK;IACZ,UAAUA,gBAAS,gBACb,KAAK,OAAO,MAAM,SAAS,MAC3BA,gBAAS,YACP,KAAK,OAAO,MAAM,SAAS,UAC3B,KAAK,OAAO,MAAM,SAAS;IACnC,QAAQ,KAAK;IACb,WAAW;IACX,SAAS,KAAK,OAAO,MAAM,YAAY,OAAO;IAC9C,UAAU,KAAK,OAAO,MAAM;GAChC,CACJ;EACJ;CACJ;CAEA,AAAQ,mBAAmB,MAAqB,UAAyC;EACrF,IAAI,CAAC,UAAU,OAAO;EAEtB,MAAM,QAAQ,SAAS,SAAS,KAAK;EACrC,MAAM,YAAY,SAAS,aAAa,KAAK;EAC7C,MAAM,SAAS,SAAS,UAAU,KAAK;EACvC,MAAM,aAAa,SAAS,cAAc,KAAK;EAG/C,OAAO;GACH,MAHS,SAAS,QAAQ,KAAK;GAI/B,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;GACvC,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;GAC/C,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;GACzC,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;EACrD;CACJ;;;;;;CAOA,AAAO,UAAU,QAA4C;EACzD,KAAK,qBAAqB;EAC1B,KAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;GAAQ,UAAU;IAAE,GAAG,KAAK,OAAO;IAAU,GAAI,OAAO,YAAY,CAAC;GAAG;EAAE;EAC7G,KAAK,MAAM,MAAM;CACrB;CAKA,AAAQ,uBAA6B;EACjC,KAAK,MAAM,UAAU,KAAK,MAAM,OAAO,GACnC,KAAK,MAAM,aAAa,CAAC,GAAG,OAAO,UAAU,GAAG;GAC5C,OAAO,OAAO,SAAS;GACvB,UAAU,QAAQ;EACtB;EAEJ,KAAK,MAAM,UAAU,KAAK,MAAM,OAAO,GAAG;GACtC,OAAO,oBAAoB,MAAM;GACjC,OAAO,wBAAwB,MAAM;EACzC;CACJ;;;;CAKA,AAAO,oBAA4B;EAC/B,OAAO,KAAK,OAAO;CACvB;;;;CAKA,AAAO,cAAwB;EAC3B,MAAM,qBAAqB,OAAO,KAAK,KAAK,OAAO,QAAQ;EAC3D,MAAM,iBAAiB,MAAM,KAAK,KAAK,MAAM,KAAK,CAAC;EACnD,MAAM,cAAc,IAAI,IAAI;GAAC,GAAG;GAAoB,GAAG;GAAgB,KAAK,OAAO;EAAc,CAAC;EAClG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,KAAK;CACxC;;;;;;;CAQA,AAAO,eAAe,SAAgC;EAClD,MAAM,SAAS,KAAK,MAAM,IAAI,OAAO;EACrC,IAAI,CAAC,QAAQ,OAAO;EAEpB,KAAK,MAAM,aAAa,OAAO,YAC3B,IAAI,qBAAqB,gBAAQ,WAAW,MACxC,OAAOC,kBAAK,QAAQ,UAAU,SAAS,UAAU,QAAQ;EAIjE,OAAO;CACX;;;;;;;CAQA,AAAO,IAAI,SAAkC;EACzC,MAAM,SAAS,KAAK,MAAM,IAAI,OAAO;EACrC,IAAI,QAAQ,OAAO;EAEnB,MAAM,gBAAgB,KAAK,mBACvB,KAAK,wBAAwB,OAAO,GACpC,KAAK,OAAO,SAAS,QACzB;EAEA,MAAM,iBAAiB,cAAc,SAAS,KAAK;EAEnD,MAAM,eAAe,KAAK,eAAe;EAEzC,MAAM,cAAc,cAAc,cAAc,CAAC,EAAC,CAC7C,QAAQ,oBAAqC,EAAE,gBAAgB,gBAAgB,SAAS,UAAU,CAAC,CACnG,KAAK,oBACF,KAAK,iBAAiB,MAAM;GACxB;GACA,OAAO;GACP,OAAO;GACP,QAAQ;GACR,eAAe,cAAc,UAAU;GACvC,WAAW,cAAc,aAAa;EAC1C,CAAC,CACL;EAEJ,MAAM,mCAAsB;GACxB,OAAO;GACP,QAAQ,KAAK,iBAAiB,eAAe;GAC7C;EACJ,CAAC;EAED,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,GAClC,KAAK,wBAAwB,SAAS,QAAQ,KAAK;EAGvD,KAAK,MAAM,IAAI,SAAS,MAAM;EAC9B,OAAO;CACX;CAEA,AAAQ,iBAA0B;EAC9B,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,GAClC,IAAI,MAAM,aAAa,OAAO;EAElC,OAAO;CACX;;;;;;;;;;CAWA,AAAO,YAAY,MAAmB,SAAwD;EAC1F,MAAM,KAAK,KAAK;EAChB,KAAK,cAAc;EAEnB,MAAM,SAAS;GACX;GACA,aAAa,SAAS,eAAe;GACrC,qCAAqB,IAAI,IAA8B;GACvD,yCAAyB,IAAI,IAAgC;EACjE;EAEA,KAAK,MAAM,IAAI,IAAI,MAAM;EAEzB,KAAK,MAAM,CAAC,SAAS,WAAW,KAAK,MAAM,QAAQ,GAC/C,KAAK,wBAAwB,SAAS,QAAQ,MAAM;EAGxD,OAAO,IAAK,MAAmC;GAEtB;GACA;GAFrB,AAAO,YACH,AAAiB,UACjB,AAAiB,KACnB;IAFmB;IACA;GAClB;GACH,AAAO,UAAgB;IACnB,KAAK,SAAS,cAAc,KAAK,GAAG;GACxC;EACJ,EAAG,MAAM,EAAE;CACf;CAEA,AAAQ,cAAc,IAAkB;EACpC,MAAM,SAAS,KAAK,MAAM,IAAI,EAAE;EAChC,IAAI,CAAC,QAAQ;EAEb,KAAK,MAAM,CAAC,SAAS,WAAW,KAAK,MAAM,QAAQ,GAAG;GAClD,MAAM,gBAAgB,OAAO,oBAAoB,IAAI,OAAO;GAC5D,IAAI,eAAe,OAAO,OAAO,aAAa;GAE9C,MAAM,UAAU,OAAO,wBAAwB,IAAI,OAAO;GAC1D,IAAI,SAAS,QACT,KAAK,MAAM,KAAK,SAAS,OAAO,IAAI,CAAC;EAE7C;EAEA,KAAK,MAAM,OAAO,EAAE;CACxB;CAEA,AAAQ,wBACJ,SACA,QACA,QAMI;EACJ,IAAI,OAAO,aAAa;GACpB,MAAM,UAA8B,CAAC;GACrC,KAAK,MAAM,KAAK,OAAO,YACnB,IAAI,aAAa,gBAAQ,WAAW,SAChC,QAAQ,KAAK,CAAC;GAGtB,IAAI,QAAQ,QAAQ;IAChB,KAAK,MAAM,KAAK,SAAS,OAAO,OAAO,CAAC;IACxC,OAAO,wBAAwB,IAAI,SAAS,OAAO;GACvD;EACJ;EAEA,MAAM,gBAAgB,KAAK,iBAAiB,mBACxC;GAAE;GAAS,OAAO;GAAS,OAAO,OAAO;EAAgC,GACzE,OAAO,IACX;EAEA,OAAO,IAAI,aAAa;EACxB,OAAO,oBAAoB,IAAI,SAAS,aAAa;CACzD;AACJ;;;;;;;AClTA,IAAa,kBAAb,MAA6B;CACI;CAA7B,YAAY,AAAiB,QAAiB;EAAjB;CAAkB;CAE/C,AAAQ,MAAM,MAAsB;EAChC,OAAO,GAAG,cAAM,KAAK,GAAG,EAAE,GAAG;CACjC;;;;;CAMA,AAAO,KAAK,MAAc,QAAqB,QAAc;EACzD,KAAK,OAAO,MAAM,CAAC,KAAK,MAAM,IAAI,CAAC;CACvC;;;;;;;CAQA,AAAO,KAAK,OAAiB,SAAkB,QAAqB,QAAc;EAC9E,IAAI,SAAS,KAAK,OAAO,MAAM,CAAC,OAAO;EACvC,KAAK,MAAM,QAAQ,OACf,KAAK,OAAO,MAAM,CAAC,KAAK,MAAM,IAAI,CAAC;CAE3C;;;;;;;;CASA,AAAO,QAAQ,OAAe,OAAwC,QAAqB,QAAc;EACrG,MAAM,UAAU,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,GAAG,cAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,EAAE,GAAG,KAAK;EACzG,KAAK,OAAO,MAAM,CAAC,GAAG,cAAM,KAAK,MAAM,KAAK,EAAE,IAAI,QAAQ,KAAK,IAAI,GAAG;CAC1E;;;;;;;;CASA,AAAO,aAAa,MAAc,MAAc,MAAe,QAAqB,QAAc;EAC9F,MAAM,QAAQ,OAAO,GAAG,KAAK,KAAK;EAClC,KAAK,OAAO,MAAM,CACd,GAAG,cAAM,OAAO,YAAY,EAAE,GAAG,cAAM,KAAK,OAAO,KAAK,IAAI,cAAM,KAAK,KAAK,IAAI,EAAE,QAAQ,cAAM,yCAAoB,IAAI,CAAC,GAC7H;CACJ;;;;;;;CAQA,AAAO,eAAe,WAAmB,QAAyB,QAAqB,QAAc;EACjG,MAAM,OAAO,WAAW,UAAU,iBAAiB;EACnD,KAAK,OAAO,MAAM,CAAC,cAAM,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;CACzD;;;;;;;;CASA,AAAO,SAAS,SAAiB,OAAe,MAAe,QAAqB,QAAc;EAC9F,MAAM,OAAO,IAAI,QAAQ,GAAG,MAAM;EAClC,MAAM,SAAS,OAAO,IAAI,SAAS;EACnC,KAAK,OAAO,MAAM,CAAC,GAAG,cAAM,KAAK,IAAI,IAAI,QAAQ;CACrD;;;;;;;CAQA,AAAO,IAAI,OAAe,SAAmB,QAAqB,QAAc;EAC5E,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,GAAG,QAAQ,KAAK,SAAS,KAAK,MAAM,CAAC,IAAI;EAC9E,MAAM,aAAa,IAAI,OAAO,KAAK;EACnC,KAAK,OAAO,MAAM,CAAC,IAAI,WAAW,EAAE;EACpC,KAAK,OAAO,MAAM,CAAC,KAAK,MAAM,OAAO,QAAQ,GAAG,GAAG,EAAE,EAAE;EACvD,KAAK,MAAM,QAAQ,SACf,KAAK,OAAO,MAAM,CAAC,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG,EAAE,EAAE;EAE1D,KAAK,OAAO,MAAM,CAAC,IAAI,WAAW,EAAE;CACxC;AACJ;;;;;;;;;;;ACzFA,IAAa,SAAb,MAAa,OAA0B;CAEnC,AAAiB;CACjB,AAAQ;CACR,AAAiB,WAAW,sBAAsB;CAElD,AAAgB;;;;;;CAOhB,OAAc,UAAU,QAA4C;EAChE,sBAAsB,SAAS,UAAU,MAAM;CACnD;;;;;;;CAQA,YAAY,OAAe,SAAyB;EAChD,KAAK,QAAQ;EACb,KAAK,UAAU,SAAS,WAAW,KAAK,SAAS,kBAAkB;EACnE,KAAK,SAAS,KAAK,SAAS,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM;GAAE,OAAO,KAAK;GAAO,SAAS,KAAK;EAAQ,CAAC;EAEhG,KAAK,QAAQ,IAAI,gBAAgB,IAAI;CACzC;;;;;;CAOA,AAAO,WAAW,SAAuB;EACrC,KAAK,UAAU;EACf,KAAK,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,MAAM;GAAE,OAAO,KAAK;GAAO;EAAQ,CAAC;CACjF;;;;;;CAOA,AAAO,UAAU,SAAyB;EACtC,OAAO,IAAI,OAAO,KAAK,OAAO,EAAE,QAAQ,CAAC;CAC7C;;;;;;;CAQA,AAAO,MAAM,KAAa,GAAG,MAAuB;EAChD,KAAK,OAAO,MAAM,KAAK,GAAG,IAAI;CAClC;;;;;;;CAQA,AAAO,KAAK,KAAa,GAAG,MAAuB;EAC/C,KAAK,OAAO,KAAK,KAAK,GAAG,IAAI;CACjC;;;;;;;CAQA,AAAO,KAAK,KAAa,GAAG,MAAuB;EAC/C,KAAK,OAAO,KAAK,KAAK,GAAG,IAAI;CACjC;;;;;;;CAQA,AAAO,KAAK,KAAa,GAAG,MAAuB;EAC/C,KAAK,OAAO,KAAK,KAAK,GAAG,IAAI;CACjC;;;;;;;CAQA,AAAO,QAAQ,KAAa,GAAG,MAAuB;EAClD,KAAK,OAAO,QAAQ,KAAK,GAAG,IAAI;CACpC;;;;;;;CAQA,AAAO,MAAM,KAAa,GAAG,MAAuB;EAChD,KAAK,OAAO,MAAM,KAAK,GAAG,IAAI;CAClC;;;;;;;CAQA,AAAO,MAAM,KAAa,GAAG,MAAuB;EAChD,KAAK,OAAO,MAAM,KAAK,GAAG,IAAI;CAClC;AACJ;;;;;;;;;ACtGA,IAAa,qBAAb,cAAiFC,yBAAa;;;;;;;;CAQ1F,AAAS,GACL,OACA,UACI;EACJ,OAAO,MAAM,GAAG,OAAO,QAAQ;CACnC;;;;;;;;CASA,AAAS,KACL,OACA,UACI;EACJ,OAAO,MAAM,KAAK,OAAO,QAAQ;CACrC;;;;;;;;CASA,AAAS,IACL,OACA,UACI;EACJ,OAAO,MAAM,IAAI,OAAO,QAAQ;CACpC;;;;;;;;CASA,AAAS,YACL,OACA,UACI;EACJ,OAAO,KAAK,GAAG,OAAO,QAAQ;CAClC;;;;;;;;CASA,AAAS,eACL,OACA,UACI;EACJ,OAAO,MAAM,eAAe,OAAO,QAAQ;CAC/C;;;;;;;;CASA,AAAS,KAA4C,OAAkB,GAAG,MAAmC;EAGzG,OAAO,MAAM,KAAK,OAAO,GAAI,IAA2B;CAC5D;;;;;;;CAQA,AAAS,UACL,OACyC;EACzC,OAAO,MAAM,UAAU,KAAK;CAChC;;;;;;;CAQA,mBAA0D,OAA0B;EAChF,OAAO,MAAM,cAAc,KAAK;CACpC;;;;;;CAOA,kBAAyC;EACrC,OAAO,MAAM,WAAW;CAC5B;;;;;;;;;CAUA,QACI,OACA,MAC2B;EAC3B,OAAO,IAAI,SAA6B,SAAS,WAAW;GACxD,MAAM,WAAW,GAAG,SAAmC;IACnD,QAAQ;IACR,QAAQ,IAAI;GAChB;GAEA,MAAM,gBAAsB;IACxB,QAAQ;IACR,OAAO,IAAIC,wCAAcC,mCAAkB,0BAA0B,CAAC;GAC1E;GAEA,IAAI,YAAmC;GAEvC,MAAM,gBAAsB;IACxB,KAAK,IAAI,OAAO,OAAO;IACvB,MAAM,QAAQ,oBAAoB,SAAS,OAAO;IAClD,IAAI,WAAW,aAAa,SAAS;GACzC;GAEA,KAAK,KAAK,OAAO,OAAO;GAExB,IAAI,MAAM,QAAQ;IACd,IAAI,KAAK,OAAO,SAAS,OAAO,QAAQ;IACxC,KAAK,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;GACjE;GAEA,IAAI,MAAM,cAAc,QAAW;IAC/B,MAAM,YAAY,KAAK;IACvB,YAAY,iBAAiB;KACzB,QAAQ;KACR,OAAO,IAAID,wCAAcC,mCAAkB,4BAA4B,CAAC,SAAS,CAAC,CAAC;IACvF,GAAG,SAAS;GAChB;EACJ,CAAC;CACL;AACJ;;;;;;;AC7KA,IAAsB,uBAAtB,cAGU,mBAA4B;CAMX;CACA;CANvB,AAAmB;CACnB,AAAmB,2BAAW,IAAI,IAA6B;CAE/D,AAAU,YACN,YACA,AAAmB,YACnB,AAAmB,WACrB;EACE,MAAM;EAHa;EACA;EAGnB,KAAK,SAAS,IAAI,OAAO,UAAU;EACnC,KAAK,WAAW,SAAS,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC;CACnE;;;;;;;;;;;;;;;;;;CAmBA,AAAO,QAAQ,OAAe,UAAkB,MAA2B,WAAyB;EAChG,IAAI,CAAC,KAAK,WAAW,GAAG;EAExB,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK;EACrC,IAAI,CAAC,OACD,MAAM,IAAIC,wCAAcC,mCAAkB,uBAAuB,CAAC,KAAK,CAAC;EAG5E,MAAM,KAAK;GAAE,MAAM;GAAU;GAAM,SAAS;EAAU,CAAC;EACvD,KAAK,OAAO,MACR,GAAG,cAAM,OAAO,OAAO,EAAE,GAAG,KAAK,YAAY,EAAE,QAAQ,cAAM,KAAK,KAAK,QAAQ,EAAE,YAAY,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,GACzI;CACJ;;;;;;;;CASA,AAAO,WAAW,OAAe,UAA2B;EACxD,IAAI,CAAC,KAAK,cAAc,GAAG,OAAO;EAElC,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK;EACrC,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,gBAAgB,MAAM;EAC5B,MAAM,gBAAgB,MAAM,QAAQ,SAAS,KAAK,SAAS,QAAQ;EACnE,KAAK,SAAS,IAAI,OAAO,aAAa;EAEtC,MAAM,UAAU,kBAAkB,cAAc;EAChD,IAAI,SACA,KAAK,OAAO,MACR,GAAG,cAAM,OAAO,SAAS,EAAE,GAAG,KAAK,YAAY,EAAE,QAAQ,cAAM,KAAK,KAAK,QAAQ,EAAE,cAAc,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,GAC7I;EAGJ,OAAO;CACX;;;;CAKA,MAAgB,SAAS,OAA8B;EACnD,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,KAAK,CAAC;EAC3C,IAAI,MAAM,WAAW,GAAG;GACpB,KAAK,OAAO,KAAK,4BAA4B,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,GAAG;GACxF;EACJ;EAEA,KAAK,OAAO,KACR,GAAG,cAAM,KAAK,OAAO,SAAS,EAAE,GAAG,KAAK,YAAY,EAAE,SAAS,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,EAAE,QAAQ,cAAM,KAAK,KAAK,MAAM,MAAM,EAAE,OACnJ;EACA,KAAK,UAAU,OAAO,OAAO;EAI7B,MAAM,YAAW,MAFmC,KAAK,oBAAoB,OAAO,KAAK,EAEjE,CAAC,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC,CAAC;EAChE,IAAI,WAAW,GAGX,MAAM,IAAID,wCAAcC,mCAAkB,wBAAwB,CAAC,KAAK,UAAU,QAAQ,QAAQ,CAAC;OAEnG,KAAK,OAAO,KACR,SAAS,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,EAAE,GAAG,cAAM,KAAK,MAAM,wBAAwB,GACnG;EAGJ,KAAK,UAAU,OAAO,UAAU;CACpC;;;;CAKA,MAAgB,mBAAmB,OAAe,MAAoC;EAClF,KAAK,OAAO,KACR,GAAG,cAAM,OAAO,UAAU,EAAE,QAAQ,cAAM,KAAK,KAAK,KAAK,IAAI,EAAE,YAAY,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,GACvH;EAEA,IAAI;EAEJ,IAAI;GACA,MAAM,QAAQ,KAAK,CACf,KAAK,KAAK,GACV,IAAI,SAAe,GAAG,WAAW;IAC7B,YAAY,iBAAiB;KACzB,OAAO,IAAID,wCAAcC,mCAAkB,sBAAsB,CAAC,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC;IAC/F,GAAG,KAAK,OAAO;GACnB,CAAC,CACL,CAAC;GAED,KAAK,OAAO,KACR,GAAG,cAAM,OAAO,WAAW,EAAE,QAAQ,cAAM,KAAK,KAAK,KAAK,IAAI,EAAE,YAAY,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,GACxH;EACJ,SAAS,OAAO;GACZ,KAAK,OAAO,MACR,GAAG,cAAM,OAAO,QAAQ,EAAE,QAAQ,cAAM,KAAK,KAAK,KAAK,IAAI,EAAE,YAAY,cAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,EAAE,IACnH,KACJ;GACA,MAAM;EACV,UAAU;GACN,IAAI,WACA,aAAa,SAAS;EAE9B;CACJ;CAKA,AAAQ,UAAU,OAAe,QAAoC;EACjE,yBAAa,UAAU,KAAK,KAAK,MAAM,SAAS,MAAM,GAAG,QAAQ;CACrE;AAUJ;;;;;;;ACvKA,IAAY,gBAAL;;CAEH;;CAEA;;CAEA;;CAEA;;CAEA;;AACJ;;AAGA,MAAMC,gBAA+B;;;;;;AAMrC;AAQA,MAAM,qBAAqB;;;;;;;AAQ3B,IAAa,sBAAb,cAAyC,qBAA+D;CACpG,AAAiB;CAEjB,AAAQ,iBAAiB;CACzB,AAAQ,WAAW;CACnB,AAAQ,YAAiC;CACzC,AAAQ,WAAgC;CAExC,AAAO,YAAY,UAAU,MAAM;EAC/B,MAAM,uBAAuBA,eAAa,aAAa;EAEvD,KAAK,oBAAoB;EACzB,KAAK,uBAAuB;CAChC;CAEA,AAAU,aAAsB;EAC5B,OAAO,KAAK;CAChB;CAEA,AAAU,gBAAyB;EAC/B,OAAO;CACX;CAEA,AAAU,cAAsB;EAC5B,OAAO;CACX;CAEA,MAAgB,oBACZ,OACA,OACqC;EACrC,MAAM,WAAW,MAAM,KAAK,SAAS,KAAK,mBAAmB,OAAO,IAAI,CAAC;EACzE,OAAO,QAAQ,WAAW,QAAQ;CACtC;CAEA,AAAQ,yBAA+B;EACnC,IAAI,CAAC,KAAK,mBAAmB;EAE7B,KAAK,kBAAkB;GACnB,KAAK,OAAO,KAAK,YAAY,cAAM,OAAO,KAAK,SAAS,EAAE,QAAQ;GAClE,AAAK,KAAK,IAAI,CAAC;EACnB;EAEA,KAAK,iBAAiB;GAClB,KAAK,OAAO,KAAK,YAAY,cAAM,OAAO,KAAK,QAAQ,EAAE,QAAQ;GACjE,AAAK,KAAK,IAAI,CAAC;EACnB;EAEA,QAAQ,GAAG,WAAW,KAAK,SAAS;EACpC,QAAQ,GAAG,UAAU,KAAK,QAAQ;CACtC;CAEA,AAAQ,uBAA6B;EACjC,IAAI,KAAK,WAAW;GAChB,QAAQ,IAAI,WAAW,KAAK,SAAS;GACrC,KAAK,YAAY;EACrB;EACA,IAAI,KAAK,UAAU;GACf,QAAQ,IAAI,UAAU,KAAK,QAAQ;GACnC,KAAK,WAAW;EACpB;CACJ;;;;;;;;;CAUA,AAAgB,QAAQ,OAAsB,UAAkB,MAA2B,YAAY,KAAY;EAC/G,MAAM,QAAQ,OAAO,UAAU,MAAM,SAAS;CAClD;;;;;;;;CASA,AAAgB,WAAW,OAAsB,UAA2B;EACxE,OAAO,MAAM,WAAW,OAAO,QAAQ;CAC3C;;;;;;;;;;;;;;;;;CAkBA,MAAa,IAAI,WAAW,GAAG,cAAc,MAAqB;EAC9D,KAAK,qBAAqB;EAE1B,IAAI,KAAK,gBAAgB;GACrB,KAAK,OAAO,KAAK,uCAAuC;GACxD;EACJ;EAEA,KAAK,iBAAiB;EACtB,KAAK,WAAW;EAChB,KAAK,OAAO,KACR,GAAG,cAAM,KAAK,OAAO,UAAU,EAAE,uCAAuC,cAAM,KAAK,KAAK,QAAQ,GACpG;EACA,KAAK,KAAK,gBAAgB;EAE1B,IAAI;GACA,KAAK,MAAM,SAASA,eAChB,MAAM,KAAK,SAAS,KAAK;GAG7B,KAAK,OAAO,KAAK,GAAG,cAAM,KAAK,MAAM,gCAAgC,EAAE,cAAc;GACrF,KAAK,KAAK,mBAAmB;EACjC,SAAS,OAAO;GACZ,KAAK,OAAO,MAAM,GAAG,cAAM,KAAK,IAAI,6BAA6B,GAAG;GACpE,KAAK,KAAK,kBAAkB,KAAK;EACrC,UAAU;GACN,IAAI,aAAa;IACb,KAAK,OAAO,KAAK,GAAG,cAAM,KAAK,IAAI,SAAS,EAAE,qBAAqB,cAAM,KAAK,KAAK,KAAK,QAAQ,GAAG;IACnG,iBAAiB;KACb,QAAQ,KAAK,KAAK,QAAQ;IAC9B,GAAG,kBAAkB;GACzB,OAAO;IACH,KAAK,OAAO,KAAK,GAAG,cAAM,KAAK,OAAO,UAAU,EAAE,yBAAyB;IAC3E,KAAK,iBAAiB;GAC1B;EACJ;CACJ;AACJ;;;;AC9KA,MAAM,UAAU;AAChB,MAAM,iBAAiB;AAEvB,MAAM,4BAA4B;AAClC,MAAM,4BAA4B;;;;;;;AAQlC,IAAa,cAAb,MAAyB;CACrB,AAAgB,SAAS,IAAI,OAAO,aAAa;CAEjD,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,AAAQ;CAER,YAAY,UAA+B,SAA6B;EACpE,KAAK,OAAO,SAAS,QAAQ;EAC7B,KAAK,OAAO,SAAS,QAAQ;EAC7B,KAAK,OAAO,SAAS;EAErB,SAAS,WAAoC,2BAA2B,YAAY,MAAM,KAAK,KAAK,CAAC;CACzG;;;;;CAMA,MAAa,OAAsB;EAC/B,OAAO,IAAI,SAAe,SAAS,WAAW;GAC1C,MAAM,iCAAuB,KAAsB,QAAwB;IACvE,IAAI,IAAI,WAAW,SAAS,IAAI,QAAQ,KAAK,MAAM;KAC/C,IAAI,UAAU,SAAS,EAAE,gBAAgB,mBAAmB,CAAC;KAC7D,IAAI,IAAI,KAAK,UAAU;MAAE,QAAQ;MAAM,WAAW,KAAK,IAAI;KAAE,CAAC,CAAC;IACnE,OAAO;KACH,IAAI,UAAU,gBAAgB,EAAE,gBAAgB,mBAAmB,CAAC;KACpE,IAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,YAAY,CAAC,CAAC;IACnD;GACJ,CAAC;GACD,KAAK,SAAS;GAEd,MAAM,iBAAiB,QAAqB,OAAO,GAAG;GACtD,OAAO,GAAG,SAAS,aAAa;GAEhC,OAAO,KAAK,mBAAmB;IAI3B,OAAO,eAAe,SAAS,aAAa;IAC5C,OAAO,GAAG,UAAU,QAAQ,KAAK,OAAO,MAAM,6BAA6B,GAAG,CAAC;IAE/E,MAAM,UAAU,KAAK,QAAQ;IAC7B,KAAK,OAAO,KACR,GAAG,cAAM,MAAM,KAAK,GAAG,EAAE,oCAAoC,cAAM,KAAK,UAAU,QAAQ,GAAG,KAAK,OAAO,KAAK,MAAM,GACxH;IACA,QAAQ;GACZ,CAAC;GAED,IAAI,KAAK,MAAM;IACX,KAAK,OAAO,MAAM,kCAAkC,KAAK,MAAM;IAC/D,OAAO,OAAO,KAAK,MAAM,KAAK,IAAI;GACtC,OAAO;IACH,KAAK,OAAO,MAAM,+CAA+C;IACjE,OAAO,OAAO,KAAK,IAAI;GAC3B;EACJ,CAAC;CACL;;;;;;CAOA,AAAO,OAAsB;EACzB,MAAM,SAAS,KAAK;EAGpB,IAAI,CAAC,QAAQ,WAAW,OAAO,QAAQ,QAAQ;EAE/C,OAAO,IAAI,SAAS,SAAS,WAAW;GACpC,OAAO,KAAK,eAAe,QAAQ,CAAC;GACpC,OAAO,OAAO,QAAQ;IAClB,IAAI,KAAK;KACL,OAAO,GAAG;KACV;IACJ;IACA,KAAK,OAAO,KAAK,cAAM,KAAK,IAAI,6BAA6B,CAAC;GAClE,CAAC;EACL,CAAC;CACL;AACJ;;;;;;;;;AC5FA,IAAY,eAAL;;CAEH;;CAEA;;CAEA;;CAEA;;CAEA;;CAEA;;CAEA;;AACJ;;AAGA,MAAM,cAA8B;;;;;;;;AAQpC;;;;;;;AAaA,IAAa,qBAAb,cAAwC,qBAA6D;CACjG,AAAQ,eAAe;CACvB,AAAQ,aAAa;CAErB,AAAO,cAAc;EACjB,MAAM,sBAAsB,aAAa,YAAY;CACzD;;;;;;;;;CAUA,AAAgB,QAAQ,OAAqB,UAAkB,MAA2B,YAAY,KAAa;EAC/G,MAAM,QAAQ,OAAO,UAAU,MAAM,SAAS;CAClD;CAEA,AAAU,aAAsB;EAC5B,IAAI,KAAK,YACL,MAAM,IAAIC,wCAAcC,mCAAkB,2BAA2B;EAGzE,IAAI,KAAK,cACL,MAAM,IAAID,wCAAcC,mCAAkB,qBAAqB;EAGnE,OAAO;CACX;CAEA,AAAU,gBAAyB;EAC/B,IAAI,KAAK,cACL,MAAM,IAAID,wCAAcC,mCAAkB,wBAAwB;EAGtE,OAAO;CACX;CAEA,AAAU,cAAsB;EAC5B,OAAO;CACX;CAEA,MAAgB,oBACZ,OACA,OACqC;EACrC,MAAM,WAAW,MAAM,KAAK,SAAS,KAAK,mBAAmB,OAAO,IAAI,CAAC;EACzE,OAAO,QAAQ,WAAW,QAAQ;CACtC;;;;;;;;;;;;;;;;;CAkBA,MAAa,MAAqB;EAC9B,IAAI,KAAK,YAAY;GACjB,KAAK,OAAO,KAAK,wCAAwC;GACzD;EACJ;EAEA,IAAI,KAAK,cAAc;GACnB,KAAK,OAAO,KAAK,sCAAsC;GACvD;EACJ;EAEA,KAAK,eAAe;EACpB,KAAK,OAAO,KAAK,GAAG,cAAM,KAAK,MAAM,UAAU,EAAE,8BAA8B;EAC/E,KAAK,KAAK,eAAe;EAEzB,IAAI;GACA,KAAK,MAAM,SAAS,aAAa;IAE7B,IAAI,CAAC,KAAK,cAAc;KACpB,KAAK,OAAO,KAAK,0BAA0B;KAC3C;IACJ;IACA,MAAM,KAAK,SAAS,KAAK;GAC7B;GAEA,KAAK,aAAa;GAClB,KAAK,OAAO,KAAK,GAAG,cAAM,KAAK,MAAM,+BAA+B,EAAE,cAAc;GACpF,KAAK,KAAK,kBAAkB;EAChC,SAAS,OAAO;GAEZ,IAAI,CAAC,KAAK,cAAc;IACpB,KAAK,OAAO,KAAK,gDAAgD;IACjE;GACJ;GACA,KAAK,OAAO,MAAM,GAAG,cAAM,KAAK,IAAI,4BAA4B,GAAG;GACnE,KAAK,KAAK,iBAAiB,KAAK;GAChC,MAAM;EACV,UAAU;GACN,KAAK,eAAe;EACxB;CACJ;CAEA,MAAyB,mBAAmB,OAAqB,MAAoC;EACjG,KAAK,OAAO,KACR,GAAG,cAAM,OAAO,UAAU,EAAE,QAAQ,cAAM,KAAK,KAAK,KAAK,IAAI,EAAE,YAAY,cAAM,KAAK,QAAQ,aAAa,MAAM,GACrH;EAEA,IAAI;EAEJ,IAAI;GACA,MAAM,QAAQ,KAAK,CACf,KAAK,KAAK,GACV,IAAI,SAAe,GAAG,WAAW;IAC7B,YAAY,iBAAiB;KACzB,OAAO,IAAID,wCAAcC,mCAAkB,sBAAsB,CAAC,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC;IAC/F,GAAG,KAAK,OAAO;GACnB,CAAC,CACL,CAAC;GAED,KAAK,OAAO,KACR,GAAG,cAAM,OAAO,WAAW,EAAE,QAAQ,cAAM,KAAK,KAAK,KAAK,IAAI,EAAE,YAAY,cAAM,KAAK,QAAQ,aAAa,MAAM,GACtH;EACJ,SAAS,OAAO;GACZ,IAAI,CAAC,KAAK,cACN;GAGJ,KAAK,OAAO,MACR,GAAG,cAAM,OAAO,QAAQ,EAAE,QAAQ,cAAM,KAAK,KAAK,KAAK,IAAI,EAAE,YAAY,cAAM,KAAK,QAAQ,aAAa,MAAM,EAAE,IACjH,KACJ;GACA,MAAM;EACV,UAAU;GACN,IAAI,WACA,aAAa,SAAS;EAE9B;CACJ;;;;CAKA,AAAO,QAAc;EACjB,IAAI,KAAK,cAAc;GACnB,KAAK,eAAe;GACpB,KAAK,OAAO,KAAK,uCAAuC;EAC5D;CACJ;;;;CAKA,IAAW,UAAmB;EAC1B,OAAO,KAAK;CAChB;;;;CAKA,IAAW,YAAqB;EAC5B,OAAO,KAAK;CAChB;AACJ;;;;;ACxNA,MAAa"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["format","TransportStream","path","fs","format","Envapter","transports","Envapter","path","EventEmitter","SeedcordError","SeedcordErrorCode","SeedcordError","SeedcordErrorCode","PHASE_ORDER","SeedcordError","SeedcordErrorCode"],"sources":["../src/RateLimiter.ts","../src/Logger/LogFormatter.ts","../src/Logger/Transports/SinkTransport.ts","../src/Logger/TransportFactory.ts","../src/Logger/LoggerChannelRegistry.ts","../src/Logger/LoggerUtilities.ts","../src/Logger/Logger.ts","../src/StrictEventEmitter.ts","../src/Lifecycle/CoordinatedLifecycle.ts","../src/Lifecycle/CoordinatedShutdown.ts","../src/HealthCheck.ts","../src/Lifecycle/CoordinatedStartup.ts","../src/index.ts"],"sourcesContent":["import type { EpochMs } from '@seedcord/types';\n\nconst SWEEP_INTERVAL_MS = 60_000;\n\n/**\n * A usage window for a {@link RateLimiter} key.\n */\nexport interface RateLimitWindow {\n /** Window length in milliseconds. */\n delay: number;\n /**\n * Hits allowed inside the window before the key is limited.\n *\n * @defaultValue `1`\n */\n limit?: number;\n}\n\n/**\n * The outcome of a {@link RateLimiter.hit}.\n */\nexport interface RateLimitResult {\n /** Whether the key is at or over its limit for the current window. */\n limited: boolean;\n /** Absolute epoch milliseconds when the key next frees up. Convert to seconds for Discord timestamp markup. */\n expires: EpochMs;\n}\n\n/**\n * Tracks per-key usage windows and reports when a key is limited and when it frees up.\n *\n * Each key holds a sliding window of hit expiry times, and is limited once its live-hit count\n * reaches the window's `limit`. Expired hits are dropped on the next read, and a background sweep\n * drops fully-expired keys so the map does not grow without bound.\n */\nexport class RateLimiter {\n private readonly map = new Map<string, number[]>();\n\n constructor() {\n // keep the sweep from holding the process open\n setInterval(() => {\n this.sweep();\n }, SWEEP_INTERVAL_MS).unref();\n }\n\n /** Number of keys currently tracked. */\n get size(): number {\n return this.map.size;\n }\n\n /**\n * Records a hit for `key` and reports whether the key is now limited.\n *\n * @param key - The bucket to record against. The caller builds it from its own scope.\n * @param window - The usage window to apply for this hit.\n */\n hit(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n // a window must allow at least one hit, so 0 or a negative never means \"block everything\"\n const limit = Math.max(1, window.limit ?? 1);\n\n const live = this.live(key, now);\n\n if (live.length >= limit) {\n this.map.set(key, live);\n // soonest a slot frees is the earliest expiry\n return { limited: true, expires: Math.min(...live) as EpochMs };\n }\n\n const expires: EpochMs = (now + window.delay) as EpochMs;\n live.push(expires);\n this.map.set(key, live);\n\n return { limited: false, expires };\n }\n\n /**\n * Reports whether `key` is limited right now without recording a hit.\n *\n * The read half of a peek-then-commit. A gate calls `peek` to decide whether to refuse, and\n * `hit` to charge the slot only once it is the gate that let the request through.\n */\n peek(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n const limit = Math.max(1, window.limit ?? 1);\n // a read, so it never writes the filtered hits back, hit owns the only mutation\n const live = this.live(key, now);\n\n if (live.length >= limit) return { limited: true, expires: Math.min(...live) as EpochMs };\n return { limited: false, expires: (now + window.delay) as EpochMs };\n }\n\n private live(key: string, now: number): number[] {\n return (this.map.get(key) ?? []).filter((exp) => exp > now);\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, live] of this.map) {\n const kept = live.filter((exp) => exp > now);\n if (kept.length === 0) this.map.delete(key);\n else this.map.set(key, kept);\n }\n }\n}\n","import stripAnsi from 'strip-ansi';\nimport { format } from 'winston';\n\nimport type { Logform } from 'winston';\n\n/** An Error temporarily annotated with its ANSI-formatted name while pretty-printing stacks. */\ntype FormattedError = Error & { __formattedName?: string; __plainName?: string };\n\n/**\n * Options for pretty log formatting.\n */\nexport interface PrettyFormatOptions {\n /**\n * Number of spaces to pad the log level to.\n *\n * @defaultValue `7`\n */\n padding?: number;\n /**\n * Whether to strip ANSI codes from extra log data.\n *\n * @defaultValue `false`\n */\n stripExtras?: boolean;\n}\n\n/**\n * Options for JSON log formatting.\n * @internal\n */\nexport interface JsonFormatOptions {\n /**\n * Whether to strip ANSI codes from log messages and extra data.\n *\n * @defaultValue `false`\n */\n stripAnsi?: boolean;\n /**\n * Whether to produce minimal JSON output without extra fields.\n *\n * @defaultValue `false`\n */\n minimal?: boolean;\n}\n\n/**\n * Formats log records for console and file outputs.\n *\n * Supports pretty-printed colored logs for development\n * and JSON logs for production with optional ANSI stripping.\n * @internal\n */\nexport class LogFormatter {\n private readonly DEFAULT_PADDING = 7;\n private readonly SPLAT: symbol = Symbol.for('splat');\n\n private safeString(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === undefined || value === null) return '';\n if (typeof (value as { toString?: () => string }).toString === 'function') {\n return String((value as { toString: () => string }).toString());\n }\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n }\n return '';\n }\n\n private sanitizeAnsi(value: unknown): unknown {\n if (typeof value === 'string') return stripAnsi(value);\n if (value instanceof Error) {\n const error = value;\n const sanitized = new Error(stripAnsi(error.message));\n sanitized.name = error.name;\n if (typeof error.stack === 'string') sanitized.stack = stripAnsi(error.stack);\n return sanitized;\n }\n return value;\n }\n\n private getExtras(info: Logform.TransformableInfo): unknown[] {\n const raw = info[this.SPLAT];\n return Array.isArray(raw) ? raw : [];\n }\n\n private readonly FORMAT_SPECIFIERS = /%[sdifjoO]/gu;\n private readonly HAD_FORMAT_KEY = Symbol.for('hadFormatSpecifiers');\n private readonly SAVED_SPLAT_KEY = Symbol.for('savedSplat');\n\n private markFormatSpecifiers(): Logform.Format {\n return format((info) => {\n const msg = typeof info.message === 'string' ? info.message : '';\n const extras = this.getExtras(info);\n const matches = msg.match(this.FORMAT_SPECIFIERS);\n const formatCount = matches ? matches.length : 0;\n info[this.HAD_FORMAT_KEY] = formatCount;\n info[this.SAVED_SPLAT_KEY] = [...extras];\n return info;\n })();\n }\n\n public createPreFormat(): Logform.Format {\n return this.markFormatSpecifiers();\n }\n\n private preserveErrorFormatting(): Logform.Format {\n return format((info) => {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error && /\\u001b/.test(item.name)) {\n const originalName = item.name;\n const plainName = stripAnsi(item.name);\n\n const formatted = item as FormattedError;\n formatted.__formattedName = originalName;\n formatted.__plainName = plainName;\n }\n }\n\n return info;\n })();\n }\n\n private restoreErrorFormatting(): Logform.Format {\n return format((info) => {\n if (typeof info.stack === 'string') {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error) {\n const { __formattedName: formattedName, __plainName: plainName } = item as FormattedError;\n\n if (typeof formattedName === 'string' && typeof plainName === 'string') {\n info.stack = (info.stack as string).replace(\n new RegExp(`^${this.escapeRegex(plainName)}`, 'm'),\n formattedName\n );\n }\n }\n }\n }\n\n return info;\n })();\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Creates pretty-printed format with colors and timestamps.\n *\n * Ideal for development environments where human readability matters.\n *\n * @param options - Formatting options including padding and ANSI stripping\n * @returns Array of Winston format transformers\n */\n public pretty(options: PrettyFormatOptions = {}): Logform.Format[] {\n const padding = options.padding ?? this.DEFAULT_PADDING;\n return [\n this.preserveErrorFormatting(),\n format.errors({ stack: true }),\n this.restoreErrorFormatting(),\n format.splat(),\n format.colorize({ level: true }),\n format.timestamp({ format: 'D MMM, hh:mm:ss a' }),\n // eslint-disable-next-line max-statements\n format.printf((info: Logform.TransformableInfo) => {\n let ts = this.safeString(info.timestamp);\n let lvl = this.safeString(info.level).padEnd(padding);\n let lbl = this.safeString(info.label);\n let msg = this.safeString(info.message);\n\n if (options.stripExtras) {\n ts = stripAnsi(ts);\n lvl = stripAnsi(lvl);\n lbl = stripAnsi(lbl);\n msg = stripAnsi(msg);\n }\n\n const base = `${ts} [${lvl}]: ${lbl} - ${msg}`;\n const savedExtras = info[this.SAVED_SPLAT_KEY];\n // Array.isArray narrows to any[]; keep the elements as unknown so the filter below stays safe.\n const extras: unknown[] = Array.isArray(savedExtras) ? savedExtras : this.getExtras(info);\n\n let rendered = base;\n\n if (typeof info.stack === 'string') {\n let stack = this.safeString(info.stack);\n if (options.stripExtras) stack = stripAnsi(stack);\n rendered += `\\n${stack}`;\n }\n\n const cleaned = options.stripExtras ? extras.map((entry) => this.sanitizeAnsi(entry)) : extras;\n const rawFormatCount = info[this.HAD_FORMAT_KEY];\n const formatSpecifierCount = typeof rawFormatCount === 'number' ? rawFormatCount : 0;\n const filtered = cleaned.filter((x, index) => {\n if (x === null || x === undefined) return false;\n if (x instanceof Error && typeof info.stack === 'string') return false;\n if (typeof x !== 'object') {\n return index >= formatSpecifierCount;\n }\n return Object.keys(x).length > 0;\n });\n\n if (filtered.length) {\n const primitives: string[] = [];\n const objects: string[] = [];\n\n for (const x of filtered) {\n if (typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean') {\n primitives.push(String(x));\n } else {\n try {\n objects.push(JSON.stringify(x, null, 2));\n } catch {\n objects.push(String(x));\n }\n }\n }\n\n if (primitives.length) {\n rendered += ` ${primitives.join(' ')}`;\n }\n if (objects.length) {\n rendered += `\\n${objects.join('\\n')}`;\n }\n }\n\n return rendered;\n })\n ];\n }\n\n /**\n * Creates JSON format for structured logging.\n *\n * Best for production environments where logs are parsed by tools.\n *\n * @param options - JSON formatting options including ANSI stripping and minimal mode\n * @returns Array of Winston format transformers\n */\n public json(options: JsonFormatOptions = {}): Logform.Format[] {\n const base = [format.timestamp(), format.errors({ stack: true })];\n\n if (options.stripAnsi) {\n base.push(\n format((info) => {\n info.message = typeof info.message === 'string' ? stripAnsi(info.message) : info.message;\n if (typeof info.stack === 'string') info.stack = stripAnsi(info.stack);\n const extras = this.getExtras(info);\n if (extras.length) info.extras = extras.map((entry) => this.sanitizeAnsi(entry));\n return info;\n })()\n );\n }\n\n base.push(options.minimal ? format.json({}) : format.json({ bigint: true, space: 0 }));\n\n return base;\n }\n}\n","import TransportStream from 'winston-transport';\n\nimport type { Logform } from 'winston';\nimport type { TransportStreamOptions } from 'winston-transport';\n\n/**\n * Options for creating a SinkTransport.\n * @internal\n */\nexport interface SinkTransportOptions extends TransportStreamOptions {\n readonly channel: string;\n readonly sink: ILoggerSink;\n}\n\n/**\n * A log entry passed to custom sinks.\n */\nexport interface LoggerSinkLogEntry {\n /** Channel the log originated from */\n readonly channel: string;\n /** Pre-formatted log message ready for display */\n readonly rendered: string;\n /** Raw Winston log info object */\n readonly info: Logform.TransformableInfo;\n}\n\n/**\n * Custom sink interface for capturing logger output.\n *\n * Implement this to route logs to custom destinations.\n */\nexport interface ILoggerSink {\n /**\n * Called when a log entry is emitted.\n *\n * @param entry - The log entry with channel, rendered message, and raw info\n */\n onLog(entry: LoggerSinkLogEntry): void;\n}\n\n/**\n * Handle for managing a sink's lifecycle.\n */\nexport interface ILoggerSinkHandle {\n /**\n * Removes the sink and restores console output if muted.\n */\n dispose(): void;\n}\n\n/**\n * Winston transport that forwards logs to custom sinks.\n * @internal\n */\nexport class SinkTransport extends TransportStream {\n private readonly channelName: string;\n private readonly sink: ILoggerSink;\n\n public constructor(options: SinkTransportOptions) {\n super(options);\n this.channelName = options.channel;\n this.sink = options.sink;\n }\n\n public override log(info: Logform.TransformableInfo, callback: () => void): void {\n setImmediate(() => this.emit('logged', info));\n\n const rendered = this.resolveRendered(info);\n const channel = this.resolveChannel(info);\n\n this.sink.onLog({ channel, rendered, info });\n\n callback();\n }\n\n private resolveChannel(info: Logform.TransformableInfo): string {\n const channel = info.channel;\n return typeof channel === 'string' ? channel : this.channelName;\n }\n\n private resolveRendered(info: Logform.TransformableInfo): string {\n const msg = info[Symbol.for('message')];\n\n if (typeof msg === 'string') return msg;\n\n const fallback = info.message;\n if (typeof fallback === 'string') return fallback;\n\n if (fallback instanceof Error) return fallback.stack ?? fallback.message;\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- last-resort stringify for a non-string, non-Error message value\n return String(fallback ?? '');\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport { format, transports } from 'winston';\n\nimport { LogFormatter } from './LogFormatter';\nimport { SinkTransport } from './Transports/SinkTransport';\n\nimport type { ILoggerSink } from './Transports/SinkTransport';\nimport type { LoggerFormatMode, LoggerLevel, TransportConfig, WinstonTransport } from './Types';\n\n/**\n * Input parameters for building a Winston transport.\n * @internal\n */\nexport interface TransportBuildInput {\n channel: string;\n label: string;\n level: LoggerLevel;\n config: TransportConfig;\n defaultFormat: LoggerFormatMode;\n stripAnsi: boolean;\n}\n\n/**\n * Input parameters for building a sink transport.\n * @internal\n */\ninterface BuildInput {\n readonly channel: string;\n readonly label: string;\n readonly level: LoggerLevel;\n}\n\n/**\n * Creates Winston transports with proper formatting and file path resolution.\n *\n * Builds console and file transports with environment-aware defaults,\n * filename template expansion, and format selection.\n * @internal\n */\nexport class TransportFactory {\n private readonly formatter: LogFormatter;\n private readonly MILLISECOND_PAD = 3;\n private cachedSessionTimestamp: { date: string; timestamp: string } | null = null;\n\n constructor() {\n this.formatter = new LogFormatter();\n }\n\n private ensureDir(filepath: string): void {\n const dir = path.dirname(filepath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n }\n\n private withDefaultLabel(label: string): ReturnType<typeof format.combine> {\n return format.combine(\n format((info) => {\n info.label ??= label;\n return info;\n })()\n );\n }\n\n public buildPreFormat(): ReturnType<typeof format.combine> {\n return format.combine(this.formatter.createPreFormat());\n }\n\n private buildConsoleFormat(label: string): ReturnType<typeof format.combine> {\n // Use JSON format in production, pretty format in development\n if (Envapter.isProduction) {\n return format.combine(this.withDefaultLabel(label), ...this.formatter.json({ stripAnsi: true }));\n }\n return format.combine(this.withDefaultLabel(label), ...this.formatter.pretty());\n }\n\n private buildFileFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private buildStreamFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n // Stream transport uses similar formatting to file transport\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private pad(value: number): string {\n return value.toString().padStart(2, '0');\n }\n\n private buildTimestamp(): { date: string; timestamp: string } {\n if (this.cachedSessionTimestamp) {\n return this.cachedSessionTimestamp;\n }\n\n const now = new Date();\n const yyyy = now.getFullYear();\n const mm = this.pad(now.getMonth() + 1);\n const dd = this.pad(now.getDate());\n const hh = this.pad(now.getHours());\n const min = this.pad(now.getMinutes());\n const ss = this.pad(now.getSeconds());\n const ms = now.getMilliseconds().toString().padStart(this.MILLISECOND_PAD, '0');\n\n const date = `${yyyy}-${mm}-${dd}`;\n const timestamp = `${date}-${hh}${min}${ss}-${ms}`;\n\n this.cachedSessionTimestamp = { date, timestamp };\n return this.cachedSessionTimestamp;\n }\n\n private resolveFilename(template: string, channel: string): string {\n const { date, timestamp } = this.buildTimestamp();\n return template\n .replaceAll('{channel}', channel)\n .replaceAll('{date}', date)\n .replaceAll('{timestamp}', timestamp);\n }\n\n /**\n * Builds a Winston transport from configuration.\n *\n * Creates console, file, or stream transport with proper formatting,\n * level filtering, and file rotation settings.\n *\n * @param input - Transport configuration parameters\n * @returns Configured Winston transport instance\n */\n public build(input: TransportBuildInput): WinstonTransport {\n const effectiveFormat = input.config.format ?? input.defaultFormat;\n const shouldStripAnsi = input.config.stripAnsi ?? input.stripAnsi;\n const level = input.config.level ?? input.level;\n\n if (input.config.type === 'console') {\n return new transports.Console({\n level,\n format: this.buildConsoleFormat(input.label)\n });\n }\n\n if (input.config.type === 'stream') {\n return new transports.Stream({\n level,\n stream: input.config.stream,\n format: this.buildStreamFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n const filenameTemplate = input.config.filename ?? 'logs/application-{timestamp}.log';\n const resolvedFilename = this.resolveFilename(filenameTemplate, input.channel);\n this.ensureDir(resolvedFilename);\n\n return new transports.File({\n level,\n filename: resolvedFilename,\n ...(input.config.maxSize !== undefined ? { maxsize: input.config.maxSize } : {}),\n ...(input.config.maxFiles !== undefined ? { maxFiles: input.config.maxFiles } : {}),\n tailable: true,\n format: this.buildFileFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n /**\n * Builds a sink transport for routing logs to custom destinations.\n *\n * @param input - Transport configuration with channel and level info\n * @param sink - Custom sink to receive log entries\n * @returns Configured sink transport instance\n */\n public buildSinkTransport(input: BuildInput, sink: ILoggerSink): WinstonTransport {\n const format = this.buildConsoleFormat(input.label);\n\n return new SinkTransport({\n level: input.level,\n channel: input.channel,\n sink,\n format\n });\n }\n}\n","import path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport winston, { createLogger } from 'winston';\n\nimport { TransportFactory } from './TransportFactory';\n\nimport type { ILoggerSink, ILoggerSinkHandle } from './Transports/SinkTransport';\nimport type {\n ChannelConfig,\n LoggerConfiguration,\n LoggerLevel,\n TransportConfig,\n WinstonInstance,\n WinstonTransport\n} from './Types';\n\n/**\n * Manages Winston logger instances per channel with caching.\n *\n * Stores channel configuration and creates transports for each channel\n * with environment-aware defaults.\n */\nexport class LoggerChannelRegistry {\n private static _instance: LoggerChannelRegistry | null = null;\n\n private nextSinkId = 1;\n private readonly sinks = new Map<\n number,\n {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n >();\n\n private readonly DEFAULT_LEVEL: LoggerLevel = Envapter.isDevelopment\n ? 'silly'\n : Envapter.isStaging\n ? 'debug'\n : 'info';\n\n private config: LoggerConfiguration = {\n defaultChannel: 'default',\n channels: {},\n files: {\n maxSizeMB: 10,\n maxFiles: 5,\n patterns: {\n dev: 'logs/{channel}-{timestamp}.log',\n staging: 'logs/staging-{date}-{timestamp}.jsonl',\n prod: 'logs/production-{date}.jsonl'\n }\n }\n };\n\n private readonly FORMAT = Envapter.isDevelopment ? 'pretty' : 'json';\n\n private readonly cache = new Map<string, WinstonInstance>();\n private readonly transportFactory: TransportFactory;\n\n private constructor() {\n this.transportFactory = new TransportFactory();\n }\n\n /**\n * Gets the singleton instance of the registry.\n */\n public static get instance(): LoggerChannelRegistry {\n return (this._instance ??= new LoggerChannelRegistry());\n }\n\n private getDefaultChannelConfig(name: string): ChannelConfig {\n return {\n name,\n level: this.DEFAULT_LEVEL,\n transports: [\n { type: 'console', level: this.DEFAULT_LEVEL, format: this.FORMAT, stripAnsi: !Envapter.isDevelopment },\n {\n type: 'file',\n level: this.DEFAULT_LEVEL,\n filename: Envapter.isDevelopment\n ? this.config.files.patterns.dev\n : Envapter.isStaging\n ? this.config.files.patterns.staging\n : this.config.files.patterns.prod,\n format: this.FORMAT,\n stripAnsi: true,\n maxSize: this.config.files.maxSizeMB * 1024 * 1024,\n maxFiles: this.config.files.maxFiles\n }\n ]\n };\n }\n\n private mergeChannelConfig(base: ChannelConfig, override?: ChannelConfig): ChannelConfig {\n if (!override) return base;\n\n const level = override.level ?? base.level;\n const stripAnsi = override.stripAnsi ?? base.stripAnsi;\n const format = override.format ?? base.format;\n const transports = override.transports ?? base.transports;\n const name = override.name || base.name;\n\n return {\n name,\n ...(level !== undefined ? { level } : {}),\n ...(stripAnsi !== undefined ? { stripAnsi } : {}),\n ...(format !== undefined ? { format } : {}),\n ...(transports !== undefined ? { transports } : {})\n };\n }\n\n /**\n * Updates global logger configuration and clears cache.\n *\n * @param config - Partial configuration to merge with existing settings\n */\n public configure(config: Partial<LoggerConfiguration>): void {\n this.disposeCachedLoggers();\n this.config = { ...this.config, ...config, channels: { ...this.config.channels, ...(config.channels ?? {}) } };\n this.cache.clear();\n }\n\n // configure() rebuilds every logger; detach + close the old transports and reset sink\n // bookkeeping first, or the discarded loggers leak transports (and their file handles /\n // sink wrappers) across reconfigures, e.g. on every dev HMR cycle.\n private disposeCachedLoggers(): void {\n for (const logger of this.cache.values()) {\n for (const transport of [...logger.transports]) {\n logger.remove(transport);\n transport.close?.();\n }\n }\n for (const record of this.sinks.values()) {\n record.transportsByChannel.clear();\n record.removedConsoleByChannel.clear();\n }\n }\n\n /**\n * Returns the name of the default channel.\n */\n public getDefaultChannel(): string {\n return this.config.defaultChannel;\n }\n\n /**\n * Returns a list of all known channels (configured or cached).\n */\n public getChannels(): string[] {\n const configuredChannels = Object.keys(this.config.channels);\n const cachedChannels = Array.from(this.cache.keys());\n const allChannels = new Set([...configuredChannels, ...cachedChannels, this.config.defaultChannel]);\n return Array.from(allChannels).sort();\n }\n\n /**\n * Gets the log file path for a specific channel, if one exists.\n *\n * @param channel - Channel name to get log file path for\n * @returns The log file path, or null if no file transport exists\n */\n public getLogFilePath(channel: string): string | null {\n const logger = this.cache.get(channel);\n if (!logger) return null;\n\n for (const transport of logger.transports) {\n if (transport instanceof winston.transports.File) {\n return path.resolve(transport.dirname, transport.filename);\n }\n }\n\n return null;\n }\n\n /**\n * Gets or creates a Winston logger instance for the given channel.\n *\n * @param channel - Channel name to get logger for\n * @returns Configured Winston logger instance\n */\n public get(channel: string): WinstonInstance {\n const cached = this.cache.get(channel);\n if (cached) return cached;\n\n const channelConfig = this.mergeChannelConfig(\n this.getDefaultChannelConfig(channel),\n this.config.channels[channel]\n );\n\n const effectiveLevel = channelConfig.level ?? this.DEFAULT_LEVEL;\n\n const consoleMuted = this.isConsoleMuted();\n\n const transports = (channelConfig.transports ?? [])\n .filter((transportConfig: TransportConfig) => !(consoleMuted && transportConfig.type === 'console'))\n .map((transportConfig: TransportConfig) =>\n this.transportFactory.build({\n channel,\n label: channel,\n level: effectiveLevel,\n config: transportConfig,\n defaultFormat: channelConfig.format ?? 'pretty',\n stripAnsi: channelConfig.stripAnsi ?? true\n })\n );\n\n const logger = createLogger({\n level: effectiveLevel,\n format: this.transportFactory.buildPreFormat(),\n transports\n });\n\n for (const entry of this.sinks.values()) {\n this.applySinkToCachedLogger(channel, logger, entry);\n }\n\n this.cache.set(channel, logger);\n return logger;\n }\n\n private isConsoleMuted(): boolean {\n for (const entry of this.sinks.values()) {\n if (entry.muteConsole) return true;\n }\n return false;\n }\n\n /**\n * Installs a custom sink to capture log output across all channels.\n *\n * Useful for routing logs to custom destinations like TUIs or remote services.\n *\n * @param sink - Custom sink implementation to receive log entries\n * @param options - Optional configuration for console muting behavior\n * @returns Handle to dispose the sink when no longer needed\n */\n public installSink(sink: ILoggerSink, options?: { muteConsole?: boolean }): ILoggerSinkHandle {\n const id = this.nextSinkId;\n this.nextSinkId += 1;\n\n const record = {\n sink,\n muteConsole: options?.muteConsole ?? true,\n transportsByChannel: new Map<string, WinstonTransport>(),\n removedConsoleByChannel: new Map<string, WinstonTransport[]>()\n };\n\n this.sinks.set(id, record);\n\n for (const [channel, logger] of this.cache.entries()) {\n this.applySinkToCachedLogger(channel, logger, record);\n }\n\n return new (class implements ILoggerSinkHandle {\n public constructor(\n private readonly registry: LoggerChannelRegistry,\n private readonly key: number\n ) {}\n public dispose(): void {\n this.registry.uninstallSink(this.key);\n }\n })(this, id);\n }\n\n private uninstallSink(id: number): void {\n const record = this.sinks.get(id);\n if (!record) return;\n\n for (const [channel, logger] of this.cache.entries()) {\n const sinkTransport = record.transportsByChannel.get(channel);\n if (sinkTransport) logger.remove(sinkTransport);\n\n const removed = record.removedConsoleByChannel.get(channel);\n if (removed?.length) {\n for (const t of removed) logger.add(t);\n }\n }\n\n this.sinks.delete(id);\n }\n\n private applySinkToCachedLogger(\n channel: string,\n logger: WinstonInstance,\n record: {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n ): void {\n if (record.muteConsole) {\n const removed: WinstonTransport[] = [];\n for (const t of logger.transports) {\n if (t instanceof winston.transports.Console) {\n removed.push(t);\n }\n }\n if (removed.length) {\n for (const t of removed) logger.remove(t);\n record.removedConsoleByChannel.set(channel, removed);\n }\n }\n\n const sinkTransport = this.transportFactory.buildSinkTransport(\n { channel, label: channel, level: logger.level as unknown as LoggerLevel },\n record.sink\n );\n\n logger.add(sinkTransport);\n record.transportsByChannel.set(channel, sinkTransport);\n }\n}\n","import { formatFilePath } from '@seedcord/utils';\nimport chalk from 'chalk';\n\nimport type { LoggerLevel } from './Types';\nimport type { ILogger } from '@seedcord/types';\n\n/**\n * Provides access to common logging utilities.\n */\nexport class LoggerUtilities {\n constructor(private readonly logger: ILogger) {}\n\n private arrow(text: string): string {\n return `${chalk.gray('→')} ${text}`;\n }\n\n /**\n * Logs a single item with an arrow prefix.\n * @param text - The text to log\n */\n public item(text: string, level: LoggerLevel = 'info'): void {\n this.logger[level](this.arrow(text));\n }\n\n /**\n * Logs a list of items with optional heading.\n *\n * @param items - Array of items to log as a list\n * @param heading - Optional heading to display above the list\n */\n public list(items: string[], heading?: string, level: LoggerLevel = 'info'): void {\n if (heading) this.logger[level](heading);\n for (const item of items) {\n this.logger[level](this.arrow(item));\n }\n }\n\n /**\n * Logs a summary with title and key-value pairs.\n * Example: \"Loaded: 5 handlers, 3 commands\"\n *\n * @param title - The title of the summary\n * @param items - Object with counts/values to display\n */\n public summary(title: string, items: Record<string, number | string>, level: LoggerLevel = 'info'): void {\n const entries = Object.entries(items).map(([key, value]) => `${chalk.magenta.bold(String(value))} ${key}`);\n this.logger[level](`${chalk.bold.green(title)}: ${entries.join(', ')}`);\n }\n\n /**\n * Logs a component registration message.\n *\n * @param name - Name of the component being registered\n * @param from - File path the component is from\n * @param type - Optional type label (e.g., 'middleware', 'handler')\n */\n public registration(name: string, from: string, type?: string, level: LoggerLevel = 'info'): void {\n const scope = type ? `${type} ` : '';\n this.logger[level](\n `${chalk.italic('Registered')} ${chalk.bold.yellow(scope)}${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(from))}`\n );\n }\n\n /**\n * Logs component initialization start/end.\n *\n * @param component - Name of the component\n * @param action - 'start' or 'end' to indicate initialization phase\n */\n public initialization(component: string, action: 'start' | 'end', level: LoggerLevel = 'info'): void {\n const verb = action === 'start' ? 'Initializing' : 'Initialized';\n this.logger[level](chalk.bold(`${verb} ${component}`));\n }\n\n /**\n * Logs progress as \"[current/total]\" with optional item label.\n *\n * @param current - Current progress count\n * @param total - Total count\n * @param item - Optional item label to append\n */\n public progress(current: number, total: number, item?: string, level: LoggerLevel = 'info'): void {\n const base = `[${current}/${total}]`;\n const suffix = item ? ` ${item}` : '';\n this.logger[level](`${chalk.cyan(base)}${suffix}`);\n }\n\n /**\n * Logs content in a decorative box.\n *\n * @param title - Title to display in the box\n * @param content - Lines of content to display in the box\n */\n public box(title: string, content: string[], level: LoggerLevel = 'info'): void {\n const width = Math.max(title.length, ...content.map((line) => line.length)) + 2;\n const horizontal = '─'.repeat(width);\n this.logger[level](`╭${horizontal}╮`);\n this.logger[level](`│ ${title.padEnd(width - 1, ' ')}│`);\n for (const line of content) {\n this.logger[level](`│ ${line.padEnd(width - 1, ' ')}│`);\n }\n this.logger[level](`╰${horizontal}╯`);\n }\n}\n","import { LoggerChannelRegistry } from './LoggerChannelRegistry';\nimport { LoggerUtilities } from './LoggerUtilities';\n\nimport type { LoggerConfiguration, LoggerOptions } from './Types';\nimport type { ILogger } from '@seedcord/types';\nimport type { Logger as Winston } from 'winston';\n\n/**\n * Public logging service with channel-aware transports and per-run file output.\n *\n * - Channel separation (e.g., bot, cli, hmr)\n * - Production-safe JSON logs with ANSI stripping\n * - Unique log files per run via filename templates\n */\nexport class Logger implements ILogger {\n declare private logger: Winston;\n private readonly label: string;\n private channel: string;\n private readonly registry = LoggerChannelRegistry.instance;\n\n public readonly utils: LoggerUtilities;\n\n /**\n * Configures global logger settings, applied to all channels.\n *\n * @param config - Partial configuration to merge with defaults\n */\n public static configure(config: Partial<LoggerConfiguration>): void {\n LoggerChannelRegistry.instance.configure(config);\n }\n\n /**\n * Creates a new Logger instance.\n *\n * @param label - Prefix/label for all log entries from this logger\n * @param options - Optional configuration for channel, format, and ANSI handling\n */\n constructor(label: string, options?: LoggerOptions) {\n this.label = label;\n this.channel = options?.channel ?? this.registry.getDefaultChannel();\n this.logger = this.registry.get(this.channel).child({ label: this.label, channel: this.channel });\n\n this.utils = new LoggerUtilities(this);\n }\n\n /**\n * Switches this logger to a different channel.\n *\n * @param channel - Channel name to switch to\n */\n public setChannel(channel: string): void {\n this.channel = channel;\n this.logger = this.registry.get(channel).child({ label: this.label, channel });\n }\n\n /**\n * Returns a new Logger for this label on the specified channel.\n *\n * @param channel - Channel name to use\n */\n public inChannel(channel: string): Logger {\n return new Logger(this.label, { channel });\n }\n\n /**\n * Logs an error message with optional additional data.\n *\n * @param msg - The error message to log\n * @param args - Additional data to include in the log entry\n */\n public error(msg: string, ...args: unknown[]): void {\n this.logger.error(msg, ...args);\n }\n\n /**\n * Logs a warning message with optional additional data.\n *\n * @param msg - The warning message to log\n * @param args - Additional data to include in the log entry\n */\n public warn(msg: string, ...args: unknown[]): void {\n this.logger.warn(msg, ...args);\n }\n\n /**\n * Logs an informational message with optional additional data.\n *\n * @param msg - The informational message to log\n * @param args - Additional data to include in the log entry\n */\n public info(msg: string, ...args: unknown[]): void {\n this.logger.info(msg, ...args);\n }\n\n /**\n * Logs an HTTP-related message with optional additional data.\n *\n * @param msg - The HTTP message to log\n * @param args - Additional data to include in the log entry\n */\n public http(msg: string, ...args: unknown[]): void {\n this.logger.http(msg, ...args);\n }\n\n /**\n * Logs a verbose message with optional additional data.\n *\n * @param msg - The verbose message to log\n * @param args - Additional data to include in the log entry\n */\n public verbose(msg: string, ...args: unknown[]): void {\n this.logger.verbose(msg, ...args);\n }\n\n /**\n * Logs a debug message with optional additional data.\n *\n * @param msg - The debug message to log\n * @param args - Additional data to include in the log entry\n */\n public debug(msg: string, ...args: unknown[]): void {\n this.logger.debug(msg, ...args);\n }\n\n /**\n * Logs a silly/trace level message with optional additional data.\n *\n * @param msg - The silly message to log\n * @param args - Additional data to include in the log entry\n */\n public silly(msg: string, ...args: unknown[]): void {\n this.logger.silly(msg, ...args);\n }\n}\n","import { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n/** Tuple type used for all event payloads. */\nexport type SEArgsTuple = readonly unknown[];\n\n/** Convenience map for emitters that intentionally expose no events. */\nexport type SENoEvents = Record<never, SEArgsTuple>;\n\n/**\n * Accepts any object type and constrains every value to be a tuple.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport type SEEventMapLike<TEvents extends object> = { [K in keyof TEvents]: SEArgsTuple };\n\n/**\n * Narrows a provided event map to the keys that can be emitted or listened for.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n * @internal\n */\ntype SEEventKey<TEvents extends object> = Extract<keyof TEvents, string | symbol>;\n\n/**\n * Typed wrapper around Node.js {@link EventEmitter} enforcing tuple payloads per event name.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport class StrictEventEmitter<TEvents extends SEEventMapLike<TEvents>> extends EventEmitter {\n /**\n * Registers a persistent listener with tuple-safe arguments for the given event.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override on<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.on(event, listener);\n }\n\n /**\n * Registers a one time listener that is removed after the first invocation.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override once<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.once(event, listener);\n }\n\n /**\n * Removes a previously registered listener for the given event.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override off<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.off(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.on} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override addListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return this.on(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.off} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override removeListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.removeListener(event, listener);\n }\n\n /**\n * Emits an event with the strictly typed argument tuple for the event name.\n *\n * @param event - The event name to emit\n * @param args - Tuple payload for the event\n * @returns True when the event had listeners, false otherwise\n */\n override emit<TEventKey extends SEEventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n // justified: widen the per-event tuple to its base array type so it spreads into Node's\n // `emit(event, ...args: any[])`. There's no declaration fix for Node's base signature.\n return super.emit(event, ...(args as readonly unknown[]));\n }\n\n /**\n * Retrieves the listener list for a given event with the correct tuple signature.\n *\n * @param event - The event name to inspect\n * @returns Array of listeners registered for the event\n */\n override listeners<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey\n ): ((...args: TEvents[TEventKey]) => void)[] {\n return super.listeners(event) as ((...args: TEvents[TEventKey]) => void)[];\n }\n\n /**\n * Counts listeners for an event without widening the return type of {@link EventEmitter.listenerCount}.\n *\n * @param event - The event name to inspect\n * @returns The total number of listeners registered for the event\n */\n listenerCountTyped<TEventKey extends SEEventKey<TEvents>>(event: TEventKey): number {\n return super.listenerCount(event);\n }\n\n /**\n * Returns the list of event names known to the emitter with the mapped key type.\n *\n * @returns Array of event keys supported by the emitter\n */\n eventNamesTyped(): SEEventKey<TEvents>[] {\n return super.eventNames() as SEEventKey<TEvents>[];\n }\n\n /**\n * Waits for an event to be emitted, resolving with the listener arguments tuple once triggered.\n * Supports optional abort signals and timeouts for cancellation semantics.\n *\n * @param event - The event name to wait for\n * @param opts - Optional abort signal or timeout in milliseconds\n * @returns Promise resolving with the emitted argument tuple; rejects when aborted or timed out\n */\n waitFor<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n opts?: { signal?: AbortSignal; timeoutMs?: number }\n ): Promise<TEvents[TEventKey]> {\n return new Promise<TEvents[TEventKey]>((resolve, reject) => {\n const onEvent = (...args: TEvents[TEventKey]): void => {\n cleanup();\n resolve(args);\n };\n\n const onAbort = (): void => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForAborted));\n };\n\n let timeoutId: NodeJS.Timeout | null = null;\n\n const cleanup = (): void => {\n this.off(event, onEvent);\n opts?.signal?.removeEventListener('abort', onAbort);\n if (timeoutId) clearTimeout(timeoutId);\n };\n\n this.once(event, onEvent);\n\n if (opts?.signal) {\n if (opts.signal.aborted) return onAbort();\n opts.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n if (opts?.timeoutMs !== undefined) {\n const timeoutMs = opts.timeoutMs;\n timeoutId = setTimeout(() => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForTimeout, [timeoutMs]));\n }, timeoutMs);\n }\n });\n }\n}\n","/*\n * Inspired by Akka Coordinated Shutdown: https://doc.akka.io/libraries/akka-core/current/coordinated-shutdown.html\n * and Lewis's implementation in a private repo elsewhere (https://github.com/Yomanz)\n */\n\nimport { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { Logger } from '../Logger';\nimport { StrictEventEmitter } from '../StrictEventEmitter';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { SEEventMapLike } from '../StrictEventEmitter';\n\n/**\n * Abstract base class for coordinated lifecycle management (startup/shutdown)\n */\nexport abstract class CoordinatedLifecycle<\n TPhase extends number,\n TEvents extends SEEventMapLike<TEvents>\n> extends StrictEventEmitter<TEvents> {\n protected readonly logger: Logger;\n protected readonly tasksMap = new Map<TPhase, LifecycleTask[]>();\n\n protected constructor(\n loggerName: string,\n protected readonly phaseOrder: TPhase[],\n protected readonly phaseEnum: Record<number, string>\n ) {\n super();\n this.logger = new Logger(loggerName);\n this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));\n }\n\n /**\n * Adds a lifecycle task to a specific phase.\n *\n * Tasks are executed in phase order during lifecycle operations.\n * Each task has a timeout to prevent hanging operations.\n *\n * @param phase - The lifecycle phase to add the task to\n * @param taskName - Unique name for the task (used for logging and removal)\n * @param task - Async function to execute during the phase\n * @param timeoutMs - Maximum time allowed for task execution in milliseconds\n * @example\n * ```typescript\n * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {\n * await database.connect();\n * }, 10000);\n * ```\n */\n public addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void {\n if (!this.canAddTask()) return;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);\n }\n\n tasks.push({ name: taskName, task, timeout: timeoutMs });\n this.logger.debug(\n `${chalk.italic('Added')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} to phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n /**\n * Removes a lifecycle task from a specific phase.\n *\n * @param phase - The lifecycle phase to remove the task from\n * @param taskName - Name of the task to remove\n * @returns True if the task was found and removed, false otherwise\n */\n public removeTask(phase: TPhase, taskName: string): boolean {\n if (!this.canRemoveTask()) return false;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) return false;\n\n const initialLength = tasks.length;\n const filteredTasks = tasks.filter((task) => task.name !== taskName);\n this.tasksMap.set(phase, filteredTasks);\n\n const removed = initialLength !== filteredTasks.length;\n if (removed) {\n this.logger.debug(\n `${chalk.italic('Removed')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} from phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n return removed;\n }\n\n /**\n * Run all tasks in a specific phase\n */\n protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.warn(`No tasks to run in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`);\n return;\n }\n\n this.logger.info(\n `${chalk.bold.yellow('Running')} ${this.getTaskType()} phase ${chalk.bold.magenta(this.phaseEnum[phase])} with ${chalk.bold.cyan(tasks.length)} tasks`\n );\n this.emitPhase(phase, 'start');\n\n const results: PromiseSettledResult<void>[] = await this.executeTasksInPhase(phase, tasks);\n\n const failures = results.filter((r) => r.status === 'rejected').length;\n if (failures > 0) {\n // Pass the raw phase name; chalk's ANSI codes would otherwise leak into the serialized\n // error message (e.g. the unknown-exception webhook payload).\n throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);\n } else {\n this.logger.info(\n `Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green('completed successfully')}`\n );\n }\n\n this.emitPhase(phase, 'complete');\n }\n\n /**\n * Run a single task with timeout\n */\n protected async runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n } catch (error) {\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // The phase event key is interpolated from phaseOrder at runtime; the subclass event map derives\n // its phase keys from the same range, so the key is always valid, but TS can't correlate a\n // template-literal key with the generic TEvents. Emit the no-payload event via the base emitter.\n private emitPhase(phase: TPhase, action: 'start' | 'complete'): void {\n EventEmitter.prototype.emit.call(this, `phase:${phase}:${action}`);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract canAddTask(): boolean;\n protected abstract canRemoveTask(): boolean;\n protected abstract getTaskType(): string;\n protected abstract executeTasksInPhase(\n phase: TPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]>;\n}\n","import chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Shutdown phases for coordinated application shutdown.\n */\nexport enum ShutdownPhase {\n /** Stop accepting new requests/interactions */\n StopAcceptingRequests = 1,\n /** Stop background services (health checks, etc.) */\n StopServices,\n /** Disconnect from external resources (database, APIs) */\n ExternalResources,\n /** Disconnect from Discord */\n DiscordCleanup,\n /** Final cleanup tasks */\n FinalCleanup\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: ShutdownPhase[] = [\n ShutdownPhase.StopAcceptingRequests,\n ShutdownPhase.StopServices,\n ShutdownPhase.ExternalResources,\n ShutdownPhase.DiscordCleanup,\n ShutdownPhase.FinalCleanup\n];\n\n/**\n * Strict-event-emitter payload map for coordinated shutdown phases.\n */\nexport type CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;\n\n// Delay process.exit so winston has a window to flush buffered log lines before the event loop dies.\nconst LOG_FLUSH_DELAY_MS = 500;\n\n/**\n * CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.\n *\n * It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.\n * Tasks can be added or removed dynamically, and each task has an associated timeout.\n */\nexport class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {\n private readonly isShutdownEnabled: boolean;\n\n private isShuttingDown = false;\n private exitCode = 0;\n private onSigTerm: (() => void) | null = null;\n private onSigInt: (() => void) | null = null;\n\n public constructor(enabled = true) {\n super('CoordinatedShutdown', PHASE_ORDER, ShutdownPhase);\n\n this.isShutdownEnabled = enabled;\n this.registerSignalHandlers();\n }\n\n protected canAddTask(): boolean {\n return this.isShutdownEnabled;\n }\n\n protected canRemoveTask(): boolean {\n return true;\n }\n\n protected getTaskType(): string {\n return 'shutdown';\n }\n\n protected async executeTasksInPhase(\n phase: ShutdownPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n private registerSignalHandlers(): void {\n if (!this.isShutdownEnabled) return;\n\n this.onSigTerm = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGTERM')} signal`);\n void this.run(0);\n };\n\n this.onSigInt = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGINT')} signal`);\n void this.run(0);\n };\n\n process.on('SIGTERM', this.onSigTerm);\n process.on('SIGINT', this.onSigInt);\n }\n\n private removeSignalHandlers(): void {\n if (this.onSigTerm) {\n process.off('SIGTERM', this.onSigTerm);\n this.onSigTerm = null;\n }\n if (this.onSigInt) {\n process.off('SIGINT', this.onSigInt);\n this.onSigInt = null;\n }\n }\n\n /**\n * Adds a task to a specific shutdown phase with timeout.\n *\n * @param phase - The shutdown phase from {@link ShutdownPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}\n */\n public override addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs = 5000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n /**\n * Removes a task from a specific shutdown phase.\n *\n * @param phase - The shutdown phase to remove from\n * @param taskName - Name of the task to remove\n * @returns True if task was found and removed\n */\n public override removeTask(phase: ShutdownPhase, taskName: string): boolean {\n return super.removeTask(phase, taskName);\n }\n\n /**\n * Executes the coordinated shutdown sequence.\n *\n * Runs all registered tasks across shutdown phases in reverse order.\n * Tasks within each phase are executed in parallel for faster shutdown.\n * Process exits with the specified code when complete.\n *\n * @param exitCode - Process exit code. {@default `0`}\n * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}\n * @returns Promise that resolves when shutdown is complete\n * @example\n * ```typescript\n * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);\n * await shutdown.run(0); // Graceful shutdown\n * ```\n */\n public async run(exitCode = 0, exitProcess = true): Promise<void> {\n this.removeSignalHandlers();\n\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown sequence already in progress');\n return;\n }\n\n this.isShuttingDown = true;\n this.exitCode = exitCode;\n this.logger.info(\n `${chalk.bold.yellow('Starting')} coordinated shutdown with exit code ${chalk.bold.cyan(exitCode)}`\n );\n this.emit('shutdown:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n await this.runPhase(phase);\n }\n\n this.logger.info(`${chalk.bold.green('Coordinated shutdown completed')} successfully`);\n this.emit('shutdown:complete');\n } catch (error) {\n this.logger.error(`${chalk.bold.red('Coordinated shutdown failed')}`);\n this.emit('shutdown:error', error);\n } finally {\n if (exitProcess) {\n this.logger.info(`${chalk.bold.red('Exiting')} process with code ${chalk.bold.cyan(this.exitCode)}`);\n setTimeout(() => {\n process.exit(this.exitCode);\n }, LOG_FLUSH_DELAY_MS);\n } else {\n this.logger.info(`${chalk.bold.yellow('Skipping')} process exit (dev mode)`);\n this.isShuttingDown = false;\n }\n }\n }\n}\n","import { createServer } from 'http';\n\nimport chalk from 'chalk';\n\nimport { ShutdownPhase } from './Lifecycle/CoordinatedShutdown';\nimport { Logger } from './Logger';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'http';\n\nconst HTTP_OK = 200;\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\nconst DEFAULT_HEALTH_CHECK_PATH = '/healthcheck';\n\n/**\n * HTTP health check service for monitoring bot status.\n *\n * Provides a simple HTTP endpoint that responds with JSON status\n * information, useful for container orchestration and monitoring.\n */\nexport class HealthCheck {\n /** @internal */\n public readonly logger = new Logger('HealthCheck');\n\n public readonly port: number;\n public readonly path: string;\n public readonly host: string | undefined;\n\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;\n this.host = options?.host;\n\n shutdown.addTask(ShutdownPhase.StopServices, 'stop-healthcheck-server', async () => await this.stop());\n }\n\n /**\n * Starts the health check server.\n * @returns Promise that resolves when the server is listening\n * @internal\n */\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (req.method === 'GET' && req.url === this.path) {\n res.writeHead(HTTP_OK, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));\n } else {\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n }\n });\n this.server = server;\n\n const onListenError = (err: Error): void => reject(err);\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // Swap the listen-time reject handler for a logging one: keeping it would reject an\n // already-settled promise on a late error, and removing it without a replacement\n // would crash the process on an unhandled 'error' event.\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n const address = this.host ?? 'localhost';\n this.logger.info(\n `${chalk.green.bold('✓')} Health check server listening on ${chalk.cyan(`http://${address}:${this.port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this.host) {\n this.logger.debug(`Binding health check server to ${this.host}`);\n server.listen(this.port, this.host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this.port);\n }\n });\n }\n\n /**\n * Stops the health check server.\n *\n * @returns Promise that resolves when the server is closed\n * @internal\n */\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n server.once('close', () => resolve());\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(chalk.bold.red('Health check server stopped'));\n });\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Startup phases for coordinated initialization\n *\n * Defines the order in which different components are initialized during bot startup.\n */\nexport enum StartupPhase {\n /** Validate environment variables and config files */\n Validation = 1,\n /** Discover plugin constructors via decorators or registry */\n Discovery,\n /** Register plugin metadata and declared dependencies */\n Registration,\n /** Inject and validate plugin-specific configuration */\n Configuration,\n /** Instantiate plugin classes with Core and arguments */\n Instantiation,\n /** Activate plugins by calling their init/setup methods */\n Activation,\n /** Mark seedcord as ready and start handling interactions */\n Ready\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: StartupPhase[] = [\n StartupPhase.Validation,\n StartupPhase.Discovery,\n StartupPhase.Registration,\n StartupPhase.Configuration,\n StartupPhase.Instantiation,\n StartupPhase.Activation,\n StartupPhase.Ready\n];\n\n/**\n * Strict-event-emitter payload map for coordinated startup phases.\n */\nexport type CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;\n\n/**\n * Manages bot startup lifecycle with ordered phases\n *\n * Coordinates initialization of all bot components in a predictable sequence.\n * Tasks are executed within their designated phases to ensure proper dependency order.\n */\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {\n private isStartingUp = false;\n private hasStarted = false;\n\n public constructor() {\n super('CoordinatedStartup', PHASE_ORDER, StartupPhase);\n }\n\n /**\n * Adds a task to a specific startup phase with timeout.\n *\n * @param phase - The startup phase from {@link StartupPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}\n */\n public override addTask(phase: StartupPhase, taskName: string, task: () => Promise<void>, timeoutMs = 10000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n protected canAddTask(): boolean {\n if (this.hasStarted) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);\n }\n\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);\n }\n\n return true;\n }\n\n protected canRemoveTask(): boolean {\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);\n }\n\n return true;\n }\n\n protected getTaskType(): string {\n return 'startup';\n }\n\n protected async executeTasksInPhase(\n phase: StartupPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n /**\n * Executes the coordinated startup sequence.\n *\n * Runs all registered tasks across startup phases in the correct order.\n * Each phase completes before the next phase begins. Tasks within a phase\n * are executed sequentially to maintain predictable initialization.\n *\n * @returns Promise that resolves when startup is complete\n * @throws An {@link Error} If startup fails or is called multiple times\n * @example\n * ```typescript\n * const startup = new CoordinatedStartup();\n * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);\n * await startup.run();\n * ```\n */\n public async run(): Promise<void> {\n if (this.hasStarted) {\n this.logger.warn('Startup sequence has already completed');\n return;\n }\n\n if (this.isStartingUp) {\n this.logger.warn('Startup sequence already in progress');\n return;\n }\n\n this.isStartingUp = true;\n this.logger.info(`${chalk.bold.green('Starting')} coordinated startup sequence`);\n this.emit('startup:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false mid-loop from the cli\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted');\n return;\n }\n await this.runPhase(phase);\n }\n\n this.hasStarted = true;\n this.logger.info(`${chalk.bold.green('Coordinated startup completed')} successfully`);\n this.emit('startup:complete');\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false before this catch runs\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted during error handling');\n return;\n }\n this.logger.error(`${chalk.bold.red('Coordinated startup failed')}`);\n this.emit('startup:error', error);\n throw error;\n } finally {\n this.isStartingUp = false;\n }\n }\n\n protected override async runTaskWithTimeout(phase: StartupPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n } catch (error) {\n if (!this.isStartingUp) {\n return;\n }\n\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Aborts the startup sequence if it is currently running.\n */\n public abort(): void {\n if (this.isStartingUp) {\n this.isStartingUp = false;\n this.logger.warn('Aborting coordinated startup sequence');\n }\n }\n\n /**\n * Check if startup has completed\n */\n public get isReady(): boolean {\n return this.hasStarted;\n }\n\n /**\n * Check if startup is currently running\n */\n public get isRunning(): boolean {\n return this.isStartingUp;\n }\n}\n","export * from './RateLimiter';\nexport * from './HealthCheck';\nexport * from './Lifecycle';\nexport * from './Logger';\nexport * from './StrictEventEmitter';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -2873,6 +2873,7 @@ declare class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, Co
|
|
|
2873
2873
|
* information, useful for container orchestration and monitoring.
|
|
2874
2874
|
*/
|
|
2875
2875
|
declare class HealthCheck {
|
|
2876
|
+
/** @internal */
|
|
2876
2877
|
readonly logger: Logger;
|
|
2877
2878
|
readonly port: number;
|
|
2878
2879
|
readonly path: string;
|
|
@@ -2882,12 +2883,14 @@ declare class HealthCheck {
|
|
|
2882
2883
|
/**
|
|
2883
2884
|
* Starts the health check server.
|
|
2884
2885
|
* @returns Promise that resolves when the server is listening
|
|
2886
|
+
* @internal
|
|
2885
2887
|
*/
|
|
2886
2888
|
init(): Promise<void>;
|
|
2887
2889
|
/**
|
|
2888
2890
|
* Stops the health check server.
|
|
2889
2891
|
*
|
|
2890
2892
|
* @returns Promise that resolves when the server is closed
|
|
2893
|
+
* @internal
|
|
2891
2894
|
*/
|
|
2892
2895
|
stop(): Promise<void>;
|
|
2893
2896
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1251,6 +1251,7 @@ const DEFAULT_HEALTH_CHECK_PATH = "/healthcheck";
|
|
|
1251
1251
|
* information, useful for container orchestration and monitoring.
|
|
1252
1252
|
*/
|
|
1253
1253
|
var HealthCheck = class {
|
|
1254
|
+
/** @internal */
|
|
1254
1255
|
logger = new Logger("HealthCheck");
|
|
1255
1256
|
port;
|
|
1256
1257
|
path;
|
|
@@ -1265,6 +1266,7 @@ var HealthCheck = class {
|
|
|
1265
1266
|
/**
|
|
1266
1267
|
* Starts the health check server.
|
|
1267
1268
|
* @returns Promise that resolves when the server is listening
|
|
1269
|
+
* @internal
|
|
1268
1270
|
*/
|
|
1269
1271
|
async init() {
|
|
1270
1272
|
return new Promise((resolve, reject) => {
|
|
@@ -1303,6 +1305,7 @@ var HealthCheck = class {
|
|
|
1303
1305
|
* Stops the health check server.
|
|
1304
1306
|
*
|
|
1305
1307
|
* @returns Promise that resolves when the server is closed
|
|
1308
|
+
* @internal
|
|
1306
1309
|
*/
|
|
1307
1310
|
stop() {
|
|
1308
1311
|
const server = this.server;
|
|
@@ -1488,7 +1491,7 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
1488
1491
|
//#endregion
|
|
1489
1492
|
//#region src/index.ts
|
|
1490
1493
|
/** Package version */
|
|
1491
|
-
const version = "0.8.1";
|
|
1494
|
+
const version = "0.8.2-next.1";
|
|
1492
1495
|
|
|
1493
1496
|
//#endregion
|
|
1494
1497
|
export { CoordinatedLifecycle, CoordinatedShutdown, CoordinatedStartup, HealthCheck, Logger, LoggerChannelRegistry, LoggerUtilities, RateLimiter, ShutdownPhase, StartupPhase, StrictEventEmitter, version };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["PHASE_ORDER"],"sources":["../src/RateLimiter.ts","../src/Logger/LogFormatter.ts","../src/Logger/Transports/SinkTransport.ts","../src/Logger/TransportFactory.ts","../src/Logger/LoggerChannelRegistry.ts","../src/Logger/LoggerUtilities.ts","../src/Logger/Logger.ts","../src/StrictEventEmitter.ts","../src/Lifecycle/CoordinatedLifecycle.ts","../src/Lifecycle/CoordinatedShutdown.ts","../src/HealthCheck.ts","../src/Lifecycle/CoordinatedStartup.ts","../src/index.ts"],"sourcesContent":["import type { EpochMs } from '@seedcord/types';\n\nconst SWEEP_INTERVAL_MS = 60_000;\n\n/**\n * A usage window for a {@link RateLimiter} key.\n */\nexport interface RateLimitWindow {\n /** Window length in milliseconds. */\n delay: number;\n /**\n * Hits allowed inside the window before the key is limited.\n *\n * @defaultValue `1`\n */\n limit?: number;\n}\n\n/**\n * The outcome of a {@link RateLimiter.hit}.\n */\nexport interface RateLimitResult {\n /** Whether the key is at or over its limit for the current window. */\n limited: boolean;\n /** Absolute epoch milliseconds when the key next frees up. Convert to seconds for Discord timestamp markup. */\n expires: EpochMs;\n}\n\n/**\n * Tracks per-key usage windows and reports when a key is limited and when it frees up.\n *\n * Each key holds a sliding window of hit expiry times, and is limited once its live-hit count\n * reaches the window's `limit`. Expired hits are dropped on the next read, and a background sweep\n * drops fully-expired keys so the map does not grow without bound.\n */\nexport class RateLimiter {\n private readonly map = new Map<string, number[]>();\n\n constructor() {\n // keep the sweep from holding the process open\n setInterval(() => {\n this.sweep();\n }, SWEEP_INTERVAL_MS).unref();\n }\n\n /** Number of keys currently tracked. */\n get size(): number {\n return this.map.size;\n }\n\n /**\n * Records a hit for `key` and reports whether the key is now limited.\n *\n * @param key - The bucket to record against. The caller builds it from its own scope.\n * @param window - The usage window to apply for this hit.\n */\n hit(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n // a window must allow at least one hit, so 0 or a negative never means \"block everything\"\n const limit = Math.max(1, window.limit ?? 1);\n\n const live = this.live(key, now);\n\n if (live.length >= limit) {\n this.map.set(key, live);\n // soonest a slot frees is the earliest expiry\n return { limited: true, expires: Math.min(...live) as EpochMs };\n }\n\n const expires: EpochMs = (now + window.delay) as EpochMs;\n live.push(expires);\n this.map.set(key, live);\n\n return { limited: false, expires };\n }\n\n /**\n * Reports whether `key` is limited right now without recording a hit.\n *\n * The read half of a peek-then-commit. A gate calls `peek` to decide whether to refuse, and\n * `hit` to charge the slot only once it is the gate that let the request through.\n */\n peek(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n const limit = Math.max(1, window.limit ?? 1);\n // a read, so it never writes the filtered hits back, hit owns the only mutation\n const live = this.live(key, now);\n\n if (live.length >= limit) return { limited: true, expires: Math.min(...live) as EpochMs };\n return { limited: false, expires: (now + window.delay) as EpochMs };\n }\n\n private live(key: string, now: number): number[] {\n return (this.map.get(key) ?? []).filter((exp) => exp > now);\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, live] of this.map) {\n const kept = live.filter((exp) => exp > now);\n if (kept.length === 0) this.map.delete(key);\n else this.map.set(key, kept);\n }\n }\n}\n","import stripAnsi from 'strip-ansi';\nimport { format } from 'winston';\n\nimport type { Logform } from 'winston';\n\n/** An Error temporarily annotated with its ANSI-formatted name while pretty-printing stacks. */\ntype FormattedError = Error & { __formattedName?: string; __plainName?: string };\n\n/**\n * Options for pretty log formatting.\n */\nexport interface PrettyFormatOptions {\n /**\n * Number of spaces to pad the log level to.\n *\n * @defaultValue `7`\n */\n padding?: number;\n /**\n * Whether to strip ANSI codes from extra log data.\n *\n * @defaultValue `false`\n */\n stripExtras?: boolean;\n}\n\n/**\n * Options for JSON log formatting.\n * @internal\n */\nexport interface JsonFormatOptions {\n /**\n * Whether to strip ANSI codes from log messages and extra data.\n *\n * @defaultValue `false`\n */\n stripAnsi?: boolean;\n /**\n * Whether to produce minimal JSON output without extra fields.\n *\n * @defaultValue `false`\n */\n minimal?: boolean;\n}\n\n/**\n * Formats log records for console and file outputs.\n *\n * Supports pretty-printed colored logs for development\n * and JSON logs for production with optional ANSI stripping.\n * @internal\n */\nexport class LogFormatter {\n private readonly DEFAULT_PADDING = 7;\n private readonly SPLAT: symbol = Symbol.for('splat');\n\n private safeString(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === undefined || value === null) return '';\n if (typeof (value as { toString?: () => string }).toString === 'function') {\n return String((value as { toString: () => string }).toString());\n }\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n }\n return '';\n }\n\n private sanitizeAnsi(value: unknown): unknown {\n if (typeof value === 'string') return stripAnsi(value);\n if (value instanceof Error) {\n const error = value;\n const sanitized = new Error(stripAnsi(error.message));\n sanitized.name = error.name;\n if (typeof error.stack === 'string') sanitized.stack = stripAnsi(error.stack);\n return sanitized;\n }\n return value;\n }\n\n private getExtras(info: Logform.TransformableInfo): unknown[] {\n const raw = info[this.SPLAT];\n return Array.isArray(raw) ? raw : [];\n }\n\n private readonly FORMAT_SPECIFIERS = /%[sdifjoO]/gu;\n private readonly HAD_FORMAT_KEY = Symbol.for('hadFormatSpecifiers');\n private readonly SAVED_SPLAT_KEY = Symbol.for('savedSplat');\n\n private markFormatSpecifiers(): Logform.Format {\n return format((info) => {\n const msg = typeof info.message === 'string' ? info.message : '';\n const extras = this.getExtras(info);\n const matches = msg.match(this.FORMAT_SPECIFIERS);\n const formatCount = matches ? matches.length : 0;\n info[this.HAD_FORMAT_KEY] = formatCount;\n info[this.SAVED_SPLAT_KEY] = [...extras];\n return info;\n })();\n }\n\n public createPreFormat(): Logform.Format {\n return this.markFormatSpecifiers();\n }\n\n private preserveErrorFormatting(): Logform.Format {\n return format((info) => {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error && /\\u001b/.test(item.name)) {\n const originalName = item.name;\n const plainName = stripAnsi(item.name);\n\n const formatted = item as FormattedError;\n formatted.__formattedName = originalName;\n formatted.__plainName = plainName;\n }\n }\n\n return info;\n })();\n }\n\n private restoreErrorFormatting(): Logform.Format {\n return format((info) => {\n if (typeof info.stack === 'string') {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error) {\n const { __formattedName: formattedName, __plainName: plainName } = item as FormattedError;\n\n if (typeof formattedName === 'string' && typeof plainName === 'string') {\n info.stack = (info.stack as string).replace(\n new RegExp(`^${this.escapeRegex(plainName)}`, 'm'),\n formattedName\n );\n }\n }\n }\n }\n\n return info;\n })();\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Creates pretty-printed format with colors and timestamps.\n *\n * Ideal for development environments where human readability matters.\n *\n * @param options - Formatting options including padding and ANSI stripping\n * @returns Array of Winston format transformers\n */\n public pretty(options: PrettyFormatOptions = {}): Logform.Format[] {\n const padding = options.padding ?? this.DEFAULT_PADDING;\n return [\n this.preserveErrorFormatting(),\n format.errors({ stack: true }),\n this.restoreErrorFormatting(),\n format.splat(),\n format.colorize({ level: true }),\n format.timestamp({ format: 'D MMM, hh:mm:ss a' }),\n // eslint-disable-next-line max-statements\n format.printf((info: Logform.TransformableInfo) => {\n let ts = this.safeString(info.timestamp);\n let lvl = this.safeString(info.level).padEnd(padding);\n let lbl = this.safeString(info.label);\n let msg = this.safeString(info.message);\n\n if (options.stripExtras) {\n ts = stripAnsi(ts);\n lvl = stripAnsi(lvl);\n lbl = stripAnsi(lbl);\n msg = stripAnsi(msg);\n }\n\n const base = `${ts} [${lvl}]: ${lbl} - ${msg}`;\n const savedExtras = info[this.SAVED_SPLAT_KEY];\n // Array.isArray narrows to any[]; keep the elements as unknown so the filter below stays safe.\n const extras: unknown[] = Array.isArray(savedExtras) ? savedExtras : this.getExtras(info);\n\n let rendered = base;\n\n if (typeof info.stack === 'string') {\n let stack = this.safeString(info.stack);\n if (options.stripExtras) stack = stripAnsi(stack);\n rendered += `\\n${stack}`;\n }\n\n const cleaned = options.stripExtras ? extras.map((entry) => this.sanitizeAnsi(entry)) : extras;\n const rawFormatCount = info[this.HAD_FORMAT_KEY];\n const formatSpecifierCount = typeof rawFormatCount === 'number' ? rawFormatCount : 0;\n const filtered = cleaned.filter((x, index) => {\n if (x === null || x === undefined) return false;\n if (x instanceof Error && typeof info.stack === 'string') return false;\n if (typeof x !== 'object') {\n return index >= formatSpecifierCount;\n }\n return Object.keys(x).length > 0;\n });\n\n if (filtered.length) {\n const primitives: string[] = [];\n const objects: string[] = [];\n\n for (const x of filtered) {\n if (typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean') {\n primitives.push(String(x));\n } else {\n try {\n objects.push(JSON.stringify(x, null, 2));\n } catch {\n objects.push(String(x));\n }\n }\n }\n\n if (primitives.length) {\n rendered += ` ${primitives.join(' ')}`;\n }\n if (objects.length) {\n rendered += `\\n${objects.join('\\n')}`;\n }\n }\n\n return rendered;\n })\n ];\n }\n\n /**\n * Creates JSON format for structured logging.\n *\n * Best for production environments where logs are parsed by tools.\n *\n * @param options - JSON formatting options including ANSI stripping and minimal mode\n * @returns Array of Winston format transformers\n */\n public json(options: JsonFormatOptions = {}): Logform.Format[] {\n const base = [format.timestamp(), format.errors({ stack: true })];\n\n if (options.stripAnsi) {\n base.push(\n format((info) => {\n info.message = typeof info.message === 'string' ? stripAnsi(info.message) : info.message;\n if (typeof info.stack === 'string') info.stack = stripAnsi(info.stack);\n const extras = this.getExtras(info);\n if (extras.length) info.extras = extras.map((entry) => this.sanitizeAnsi(entry));\n return info;\n })()\n );\n }\n\n base.push(options.minimal ? format.json({}) : format.json({ bigint: true, space: 0 }));\n\n return base;\n }\n}\n","import TransportStream from 'winston-transport';\n\nimport type { Logform } from 'winston';\nimport type { TransportStreamOptions } from 'winston-transport';\n\n/**\n * Options for creating a SinkTransport.\n * @internal\n */\nexport interface SinkTransportOptions extends TransportStreamOptions {\n readonly channel: string;\n readonly sink: ILoggerSink;\n}\n\n/**\n * A log entry passed to custom sinks.\n */\nexport interface LoggerSinkLogEntry {\n /** Channel the log originated from */\n readonly channel: string;\n /** Pre-formatted log message ready for display */\n readonly rendered: string;\n /** Raw Winston log info object */\n readonly info: Logform.TransformableInfo;\n}\n\n/**\n * Custom sink interface for capturing logger output.\n *\n * Implement this to route logs to custom destinations.\n */\nexport interface ILoggerSink {\n /**\n * Called when a log entry is emitted.\n *\n * @param entry - The log entry with channel, rendered message, and raw info\n */\n onLog(entry: LoggerSinkLogEntry): void;\n}\n\n/**\n * Handle for managing a sink's lifecycle.\n */\nexport interface ILoggerSinkHandle {\n /**\n * Removes the sink and restores console output if muted.\n */\n dispose(): void;\n}\n\n/**\n * Winston transport that forwards logs to custom sinks.\n * @internal\n */\nexport class SinkTransport extends TransportStream {\n private readonly channelName: string;\n private readonly sink: ILoggerSink;\n\n public constructor(options: SinkTransportOptions) {\n super(options);\n this.channelName = options.channel;\n this.sink = options.sink;\n }\n\n public override log(info: Logform.TransformableInfo, callback: () => void): void {\n setImmediate(() => this.emit('logged', info));\n\n const rendered = this.resolveRendered(info);\n const channel = this.resolveChannel(info);\n\n this.sink.onLog({ channel, rendered, info });\n\n callback();\n }\n\n private resolveChannel(info: Logform.TransformableInfo): string {\n const channel = info.channel;\n return typeof channel === 'string' ? channel : this.channelName;\n }\n\n private resolveRendered(info: Logform.TransformableInfo): string {\n const msg = info[Symbol.for('message')];\n\n if (typeof msg === 'string') return msg;\n\n const fallback = info.message;\n if (typeof fallback === 'string') return fallback;\n\n if (fallback instanceof Error) return fallback.stack ?? fallback.message;\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- last-resort stringify for a non-string, non-Error message value\n return String(fallback ?? '');\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport { format, transports } from 'winston';\n\nimport { LogFormatter } from './LogFormatter';\nimport { SinkTransport } from './Transports/SinkTransport';\n\nimport type { ILoggerSink } from './Transports/SinkTransport';\nimport type { LoggerFormatMode, LoggerLevel, TransportConfig, WinstonTransport } from './Types';\n\n/**\n * Input parameters for building a Winston transport.\n * @internal\n */\nexport interface TransportBuildInput {\n channel: string;\n label: string;\n level: LoggerLevel;\n config: TransportConfig;\n defaultFormat: LoggerFormatMode;\n stripAnsi: boolean;\n}\n\n/**\n * Input parameters for building a sink transport.\n * @internal\n */\ninterface BuildInput {\n readonly channel: string;\n readonly label: string;\n readonly level: LoggerLevel;\n}\n\n/**\n * Creates Winston transports with proper formatting and file path resolution.\n *\n * Builds console and file transports with environment-aware defaults,\n * filename template expansion, and format selection.\n * @internal\n */\nexport class TransportFactory {\n private readonly formatter: LogFormatter;\n private readonly MILLISECOND_PAD = 3;\n private cachedSessionTimestamp: { date: string; timestamp: string } | null = null;\n\n constructor() {\n this.formatter = new LogFormatter();\n }\n\n private ensureDir(filepath: string): void {\n const dir = path.dirname(filepath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n }\n\n private withDefaultLabel(label: string): ReturnType<typeof format.combine> {\n return format.combine(\n format((info) => {\n info.label ??= label;\n return info;\n })()\n );\n }\n\n public buildPreFormat(): ReturnType<typeof format.combine> {\n return format.combine(this.formatter.createPreFormat());\n }\n\n private buildConsoleFormat(label: string): ReturnType<typeof format.combine> {\n // Use JSON format in production, pretty format in development\n if (Envapter.isProduction) {\n return format.combine(this.withDefaultLabel(label), ...this.formatter.json({ stripAnsi: true }));\n }\n return format.combine(this.withDefaultLabel(label), ...this.formatter.pretty());\n }\n\n private buildFileFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private buildStreamFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n // Stream transport uses similar formatting to file transport\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private pad(value: number): string {\n return value.toString().padStart(2, '0');\n }\n\n private buildTimestamp(): { date: string; timestamp: string } {\n if (this.cachedSessionTimestamp) {\n return this.cachedSessionTimestamp;\n }\n\n const now = new Date();\n const yyyy = now.getFullYear();\n const mm = this.pad(now.getMonth() + 1);\n const dd = this.pad(now.getDate());\n const hh = this.pad(now.getHours());\n const min = this.pad(now.getMinutes());\n const ss = this.pad(now.getSeconds());\n const ms = now.getMilliseconds().toString().padStart(this.MILLISECOND_PAD, '0');\n\n const date = `${yyyy}-${mm}-${dd}`;\n const timestamp = `${date}-${hh}${min}${ss}-${ms}`;\n\n this.cachedSessionTimestamp = { date, timestamp };\n return this.cachedSessionTimestamp;\n }\n\n private resolveFilename(template: string, channel: string): string {\n const { date, timestamp } = this.buildTimestamp();\n return template\n .replaceAll('{channel}', channel)\n .replaceAll('{date}', date)\n .replaceAll('{timestamp}', timestamp);\n }\n\n /**\n * Builds a Winston transport from configuration.\n *\n * Creates console, file, or stream transport with proper formatting,\n * level filtering, and file rotation settings.\n *\n * @param input - Transport configuration parameters\n * @returns Configured Winston transport instance\n */\n public build(input: TransportBuildInput): WinstonTransport {\n const effectiveFormat = input.config.format ?? input.defaultFormat;\n const shouldStripAnsi = input.config.stripAnsi ?? input.stripAnsi;\n const level = input.config.level ?? input.level;\n\n if (input.config.type === 'console') {\n return new transports.Console({\n level,\n format: this.buildConsoleFormat(input.label)\n });\n }\n\n if (input.config.type === 'stream') {\n return new transports.Stream({\n level,\n stream: input.config.stream,\n format: this.buildStreamFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n const filenameTemplate = input.config.filename ?? 'logs/application-{timestamp}.log';\n const resolvedFilename = this.resolveFilename(filenameTemplate, input.channel);\n this.ensureDir(resolvedFilename);\n\n return new transports.File({\n level,\n filename: resolvedFilename,\n ...(input.config.maxSize !== undefined ? { maxsize: input.config.maxSize } : {}),\n ...(input.config.maxFiles !== undefined ? { maxFiles: input.config.maxFiles } : {}),\n tailable: true,\n format: this.buildFileFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n /**\n * Builds a sink transport for routing logs to custom destinations.\n *\n * @param input - Transport configuration with channel and level info\n * @param sink - Custom sink to receive log entries\n * @returns Configured sink transport instance\n */\n public buildSinkTransport(input: BuildInput, sink: ILoggerSink): WinstonTransport {\n const format = this.buildConsoleFormat(input.label);\n\n return new SinkTransport({\n level: input.level,\n channel: input.channel,\n sink,\n format\n });\n }\n}\n","import path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport winston, { createLogger } from 'winston';\n\nimport { TransportFactory } from './TransportFactory';\n\nimport type { ILoggerSink, ILoggerSinkHandle } from './Transports/SinkTransport';\nimport type {\n ChannelConfig,\n LoggerConfiguration,\n LoggerLevel,\n TransportConfig,\n WinstonInstance,\n WinstonTransport\n} from './Types';\n\n/**\n * Manages Winston logger instances per channel with caching.\n *\n * Stores channel configuration and creates transports for each channel\n * with environment-aware defaults.\n */\nexport class LoggerChannelRegistry {\n private static _instance: LoggerChannelRegistry | null = null;\n\n private nextSinkId = 1;\n private readonly sinks = new Map<\n number,\n {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n >();\n\n private readonly DEFAULT_LEVEL: LoggerLevel = Envapter.isDevelopment\n ? 'silly'\n : Envapter.isStaging\n ? 'debug'\n : 'info';\n\n private config: LoggerConfiguration = {\n defaultChannel: 'default',\n channels: {},\n files: {\n maxSizeMB: 10,\n maxFiles: 5,\n patterns: {\n dev: 'logs/{channel}-{timestamp}.log',\n staging: 'logs/staging-{date}-{timestamp}.jsonl',\n prod: 'logs/production-{date}.jsonl'\n }\n }\n };\n\n private readonly FORMAT = Envapter.isDevelopment ? 'pretty' : 'json';\n\n private readonly cache = new Map<string, WinstonInstance>();\n private readonly transportFactory: TransportFactory;\n\n private constructor() {\n this.transportFactory = new TransportFactory();\n }\n\n /**\n * Gets the singleton instance of the registry.\n */\n public static get instance(): LoggerChannelRegistry {\n return (this._instance ??= new LoggerChannelRegistry());\n }\n\n private getDefaultChannelConfig(name: string): ChannelConfig {\n return {\n name,\n level: this.DEFAULT_LEVEL,\n transports: [\n { type: 'console', level: this.DEFAULT_LEVEL, format: this.FORMAT, stripAnsi: !Envapter.isDevelopment },\n {\n type: 'file',\n level: this.DEFAULT_LEVEL,\n filename: Envapter.isDevelopment\n ? this.config.files.patterns.dev\n : Envapter.isStaging\n ? this.config.files.patterns.staging\n : this.config.files.patterns.prod,\n format: this.FORMAT,\n stripAnsi: true,\n maxSize: this.config.files.maxSizeMB * 1024 * 1024,\n maxFiles: this.config.files.maxFiles\n }\n ]\n };\n }\n\n private mergeChannelConfig(base: ChannelConfig, override?: ChannelConfig): ChannelConfig {\n if (!override) return base;\n\n const level = override.level ?? base.level;\n const stripAnsi = override.stripAnsi ?? base.stripAnsi;\n const format = override.format ?? base.format;\n const transports = override.transports ?? base.transports;\n const name = override.name || base.name;\n\n return {\n name,\n ...(level !== undefined ? { level } : {}),\n ...(stripAnsi !== undefined ? { stripAnsi } : {}),\n ...(format !== undefined ? { format } : {}),\n ...(transports !== undefined ? { transports } : {})\n };\n }\n\n /**\n * Updates global logger configuration and clears cache.\n *\n * @param config - Partial configuration to merge with existing settings\n */\n public configure(config: Partial<LoggerConfiguration>): void {\n this.disposeCachedLoggers();\n this.config = { ...this.config, ...config, channels: { ...this.config.channels, ...(config.channels ?? {}) } };\n this.cache.clear();\n }\n\n // configure() rebuilds every logger; detach + close the old transports and reset sink\n // bookkeeping first, or the discarded loggers leak transports (and their file handles /\n // sink wrappers) across reconfigures, e.g. on every dev HMR cycle.\n private disposeCachedLoggers(): void {\n for (const logger of this.cache.values()) {\n for (const transport of [...logger.transports]) {\n logger.remove(transport);\n transport.close?.();\n }\n }\n for (const record of this.sinks.values()) {\n record.transportsByChannel.clear();\n record.removedConsoleByChannel.clear();\n }\n }\n\n /**\n * Returns the name of the default channel.\n */\n public getDefaultChannel(): string {\n return this.config.defaultChannel;\n }\n\n /**\n * Returns a list of all known channels (configured or cached).\n */\n public getChannels(): string[] {\n const configuredChannels = Object.keys(this.config.channels);\n const cachedChannels = Array.from(this.cache.keys());\n const allChannels = new Set([...configuredChannels, ...cachedChannels, this.config.defaultChannel]);\n return Array.from(allChannels).sort();\n }\n\n /**\n * Gets the log file path for a specific channel, if one exists.\n *\n * @param channel - Channel name to get log file path for\n * @returns The log file path, or null if no file transport exists\n */\n public getLogFilePath(channel: string): string | null {\n const logger = this.cache.get(channel);\n if (!logger) return null;\n\n for (const transport of logger.transports) {\n if (transport instanceof winston.transports.File) {\n return path.resolve(transport.dirname, transport.filename);\n }\n }\n\n return null;\n }\n\n /**\n * Gets or creates a Winston logger instance for the given channel.\n *\n * @param channel - Channel name to get logger for\n * @returns Configured Winston logger instance\n */\n public get(channel: string): WinstonInstance {\n const cached = this.cache.get(channel);\n if (cached) return cached;\n\n const channelConfig = this.mergeChannelConfig(\n this.getDefaultChannelConfig(channel),\n this.config.channels[channel]\n );\n\n const effectiveLevel = channelConfig.level ?? this.DEFAULT_LEVEL;\n\n const consoleMuted = this.isConsoleMuted();\n\n const transports = (channelConfig.transports ?? [])\n .filter((transportConfig: TransportConfig) => !(consoleMuted && transportConfig.type === 'console'))\n .map((transportConfig: TransportConfig) =>\n this.transportFactory.build({\n channel,\n label: channel,\n level: effectiveLevel,\n config: transportConfig,\n defaultFormat: channelConfig.format ?? 'pretty',\n stripAnsi: channelConfig.stripAnsi ?? true\n })\n );\n\n const logger = createLogger({\n level: effectiveLevel,\n format: this.transportFactory.buildPreFormat(),\n transports\n });\n\n for (const entry of this.sinks.values()) {\n this.applySinkToCachedLogger(channel, logger, entry);\n }\n\n this.cache.set(channel, logger);\n return logger;\n }\n\n private isConsoleMuted(): boolean {\n for (const entry of this.sinks.values()) {\n if (entry.muteConsole) return true;\n }\n return false;\n }\n\n /**\n * Installs a custom sink to capture log output across all channels.\n *\n * Useful for routing logs to custom destinations like TUIs or remote services.\n *\n * @param sink - Custom sink implementation to receive log entries\n * @param options - Optional configuration for console muting behavior\n * @returns Handle to dispose the sink when no longer needed\n */\n public installSink(sink: ILoggerSink, options?: { muteConsole?: boolean }): ILoggerSinkHandle {\n const id = this.nextSinkId;\n this.nextSinkId += 1;\n\n const record = {\n sink,\n muteConsole: options?.muteConsole ?? true,\n transportsByChannel: new Map<string, WinstonTransport>(),\n removedConsoleByChannel: new Map<string, WinstonTransport[]>()\n };\n\n this.sinks.set(id, record);\n\n for (const [channel, logger] of this.cache.entries()) {\n this.applySinkToCachedLogger(channel, logger, record);\n }\n\n return new (class implements ILoggerSinkHandle {\n public constructor(\n private readonly registry: LoggerChannelRegistry,\n private readonly key: number\n ) {}\n public dispose(): void {\n this.registry.uninstallSink(this.key);\n }\n })(this, id);\n }\n\n private uninstallSink(id: number): void {\n const record = this.sinks.get(id);\n if (!record) return;\n\n for (const [channel, logger] of this.cache.entries()) {\n const sinkTransport = record.transportsByChannel.get(channel);\n if (sinkTransport) logger.remove(sinkTransport);\n\n const removed = record.removedConsoleByChannel.get(channel);\n if (removed?.length) {\n for (const t of removed) logger.add(t);\n }\n }\n\n this.sinks.delete(id);\n }\n\n private applySinkToCachedLogger(\n channel: string,\n logger: WinstonInstance,\n record: {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n ): void {\n if (record.muteConsole) {\n const removed: WinstonTransport[] = [];\n for (const t of logger.transports) {\n if (t instanceof winston.transports.Console) {\n removed.push(t);\n }\n }\n if (removed.length) {\n for (const t of removed) logger.remove(t);\n record.removedConsoleByChannel.set(channel, removed);\n }\n }\n\n const sinkTransport = this.transportFactory.buildSinkTransport(\n { channel, label: channel, level: logger.level as unknown as LoggerLevel },\n record.sink\n );\n\n logger.add(sinkTransport);\n record.transportsByChannel.set(channel, sinkTransport);\n }\n}\n","import { formatFilePath } from '@seedcord/utils';\nimport chalk from 'chalk';\n\nimport type { LoggerLevel } from './Types';\nimport type { ILogger } from '@seedcord/types';\n\n/**\n * Provides access to common logging utilities.\n */\nexport class LoggerUtilities {\n constructor(private readonly logger: ILogger) {}\n\n private arrow(text: string): string {\n return `${chalk.gray('→')} ${text}`;\n }\n\n /**\n * Logs a single item with an arrow prefix.\n * @param text - The text to log\n */\n public item(text: string, level: LoggerLevel = 'info'): void {\n this.logger[level](this.arrow(text));\n }\n\n /**\n * Logs a list of items with optional heading.\n *\n * @param items - Array of items to log as a list\n * @param heading - Optional heading to display above the list\n */\n public list(items: string[], heading?: string, level: LoggerLevel = 'info'): void {\n if (heading) this.logger[level](heading);\n for (const item of items) {\n this.logger[level](this.arrow(item));\n }\n }\n\n /**\n * Logs a summary with title and key-value pairs.\n * Example: \"Loaded: 5 handlers, 3 commands\"\n *\n * @param title - The title of the summary\n * @param items - Object with counts/values to display\n */\n public summary(title: string, items: Record<string, number | string>, level: LoggerLevel = 'info'): void {\n const entries = Object.entries(items).map(([key, value]) => `${chalk.magenta.bold(String(value))} ${key}`);\n this.logger[level](`${chalk.bold.green(title)}: ${entries.join(', ')}`);\n }\n\n /**\n * Logs a component registration message.\n *\n * @param name - Name of the component being registered\n * @param from - File path the component is from\n * @param type - Optional type label (e.g., 'middleware', 'handler')\n */\n public registration(name: string, from: string, type?: string, level: LoggerLevel = 'info'): void {\n const scope = type ? `${type} ` : '';\n this.logger[level](\n `${chalk.italic('Registered')} ${chalk.bold.yellow(scope)}${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(from))}`\n );\n }\n\n /**\n * Logs component initialization start/end.\n *\n * @param component - Name of the component\n * @param action - 'start' or 'end' to indicate initialization phase\n */\n public initialization(component: string, action: 'start' | 'end', level: LoggerLevel = 'info'): void {\n const verb = action === 'start' ? 'Initializing' : 'Initialized';\n this.logger[level](chalk.bold(`${verb} ${component}`));\n }\n\n /**\n * Logs progress as \"[current/total]\" with optional item label.\n *\n * @param current - Current progress count\n * @param total - Total count\n * @param item - Optional item label to append\n */\n public progress(current: number, total: number, item?: string, level: LoggerLevel = 'info'): void {\n const base = `[${current}/${total}]`;\n const suffix = item ? ` ${item}` : '';\n this.logger[level](`${chalk.cyan(base)}${suffix}`);\n }\n\n /**\n * Logs content in a decorative box.\n *\n * @param title - Title to display in the box\n * @param content - Lines of content to display in the box\n */\n public box(title: string, content: string[], level: LoggerLevel = 'info'): void {\n const width = Math.max(title.length, ...content.map((line) => line.length)) + 2;\n const horizontal = '─'.repeat(width);\n this.logger[level](`╭${horizontal}╮`);\n this.logger[level](`│ ${title.padEnd(width - 1, ' ')}│`);\n for (const line of content) {\n this.logger[level](`│ ${line.padEnd(width - 1, ' ')}│`);\n }\n this.logger[level](`╰${horizontal}╯`);\n }\n}\n","import { LoggerChannelRegistry } from './LoggerChannelRegistry';\nimport { LoggerUtilities } from './LoggerUtilities';\n\nimport type { LoggerConfiguration, LoggerOptions } from './Types';\nimport type { ILogger } from '@seedcord/types';\nimport type { Logger as Winston } from 'winston';\n\n/**\n * Public logging service with channel-aware transports and per-run file output.\n *\n * - Channel separation (e.g., bot, cli, hmr)\n * - Production-safe JSON logs with ANSI stripping\n * - Unique log files per run via filename templates\n */\nexport class Logger implements ILogger {\n declare private logger: Winston;\n private readonly label: string;\n private channel: string;\n private readonly registry = LoggerChannelRegistry.instance;\n\n public readonly utils: LoggerUtilities;\n\n /**\n * Configures global logger settings, applied to all channels.\n *\n * @param config - Partial configuration to merge with defaults\n */\n public static configure(config: Partial<LoggerConfiguration>): void {\n LoggerChannelRegistry.instance.configure(config);\n }\n\n /**\n * Creates a new Logger instance.\n *\n * @param label - Prefix/label for all log entries from this logger\n * @param options - Optional configuration for channel, format, and ANSI handling\n */\n constructor(label: string, options?: LoggerOptions) {\n this.label = label;\n this.channel = options?.channel ?? this.registry.getDefaultChannel();\n this.logger = this.registry.get(this.channel).child({ label: this.label, channel: this.channel });\n\n this.utils = new LoggerUtilities(this);\n }\n\n /**\n * Switches this logger to a different channel.\n *\n * @param channel - Channel name to switch to\n */\n public setChannel(channel: string): void {\n this.channel = channel;\n this.logger = this.registry.get(channel).child({ label: this.label, channel });\n }\n\n /**\n * Returns a new Logger for this label on the specified channel.\n *\n * @param channel - Channel name to use\n */\n public inChannel(channel: string): Logger {\n return new Logger(this.label, { channel });\n }\n\n /**\n * Logs an error message with optional additional data.\n *\n * @param msg - The error message to log\n * @param args - Additional data to include in the log entry\n */\n public error(msg: string, ...args: unknown[]): void {\n this.logger.error(msg, ...args);\n }\n\n /**\n * Logs a warning message with optional additional data.\n *\n * @param msg - The warning message to log\n * @param args - Additional data to include in the log entry\n */\n public warn(msg: string, ...args: unknown[]): void {\n this.logger.warn(msg, ...args);\n }\n\n /**\n * Logs an informational message with optional additional data.\n *\n * @param msg - The informational message to log\n * @param args - Additional data to include in the log entry\n */\n public info(msg: string, ...args: unknown[]): void {\n this.logger.info(msg, ...args);\n }\n\n /**\n * Logs an HTTP-related message with optional additional data.\n *\n * @param msg - The HTTP message to log\n * @param args - Additional data to include in the log entry\n */\n public http(msg: string, ...args: unknown[]): void {\n this.logger.http(msg, ...args);\n }\n\n /**\n * Logs a verbose message with optional additional data.\n *\n * @param msg - The verbose message to log\n * @param args - Additional data to include in the log entry\n */\n public verbose(msg: string, ...args: unknown[]): void {\n this.logger.verbose(msg, ...args);\n }\n\n /**\n * Logs a debug message with optional additional data.\n *\n * @param msg - The debug message to log\n * @param args - Additional data to include in the log entry\n */\n public debug(msg: string, ...args: unknown[]): void {\n this.logger.debug(msg, ...args);\n }\n\n /**\n * Logs a silly/trace level message with optional additional data.\n *\n * @param msg - The silly message to log\n * @param args - Additional data to include in the log entry\n */\n public silly(msg: string, ...args: unknown[]): void {\n this.logger.silly(msg, ...args);\n }\n}\n","import { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n/** Tuple type used for all event payloads. */\nexport type SEArgsTuple = readonly unknown[];\n\n/** Convenience map for emitters that intentionally expose no events. */\nexport type SENoEvents = Record<never, SEArgsTuple>;\n\n/**\n * Accepts any object type and constrains every value to be a tuple.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport type SEEventMapLike<TEvents extends object> = { [K in keyof TEvents]: SEArgsTuple };\n\n/**\n * Narrows a provided event map to the keys that can be emitted or listened for.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n * @internal\n */\ntype SEEventKey<TEvents extends object> = Extract<keyof TEvents, string | symbol>;\n\n/**\n * Typed wrapper around Node.js {@link EventEmitter} enforcing tuple payloads per event name.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport class StrictEventEmitter<TEvents extends SEEventMapLike<TEvents>> extends EventEmitter {\n /**\n * Registers a persistent listener with tuple-safe arguments for the given event.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override on<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.on(event, listener);\n }\n\n /**\n * Registers a one time listener that is removed after the first invocation.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override once<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.once(event, listener);\n }\n\n /**\n * Removes a previously registered listener for the given event.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override off<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.off(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.on} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override addListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return this.on(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.off} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override removeListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.removeListener(event, listener);\n }\n\n /**\n * Emits an event with the strictly typed argument tuple for the event name.\n *\n * @param event - The event name to emit\n * @param args - Tuple payload for the event\n * @returns True when the event had listeners, false otherwise\n */\n override emit<TEventKey extends SEEventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n // justified: widen the per-event tuple to its base array type so it spreads into Node's\n // `emit(event, ...args: any[])`. There's no declaration fix for Node's base signature.\n return super.emit(event, ...(args as readonly unknown[]));\n }\n\n /**\n * Retrieves the listener list for a given event with the correct tuple signature.\n *\n * @param event - The event name to inspect\n * @returns Array of listeners registered for the event\n */\n override listeners<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey\n ): ((...args: TEvents[TEventKey]) => void)[] {\n return super.listeners(event) as ((...args: TEvents[TEventKey]) => void)[];\n }\n\n /**\n * Counts listeners for an event without widening the return type of {@link EventEmitter.listenerCount}.\n *\n * @param event - The event name to inspect\n * @returns The total number of listeners registered for the event\n */\n listenerCountTyped<TEventKey extends SEEventKey<TEvents>>(event: TEventKey): number {\n return super.listenerCount(event);\n }\n\n /**\n * Returns the list of event names known to the emitter with the mapped key type.\n *\n * @returns Array of event keys supported by the emitter\n */\n eventNamesTyped(): SEEventKey<TEvents>[] {\n return super.eventNames() as SEEventKey<TEvents>[];\n }\n\n /**\n * Waits for an event to be emitted, resolving with the listener arguments tuple once triggered.\n * Supports optional abort signals and timeouts for cancellation semantics.\n *\n * @param event - The event name to wait for\n * @param opts - Optional abort signal or timeout in milliseconds\n * @returns Promise resolving with the emitted argument tuple; rejects when aborted or timed out\n */\n waitFor<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n opts?: { signal?: AbortSignal; timeoutMs?: number }\n ): Promise<TEvents[TEventKey]> {\n return new Promise<TEvents[TEventKey]>((resolve, reject) => {\n const onEvent = (...args: TEvents[TEventKey]): void => {\n cleanup();\n resolve(args);\n };\n\n const onAbort = (): void => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForAborted));\n };\n\n let timeoutId: NodeJS.Timeout | null = null;\n\n const cleanup = (): void => {\n this.off(event, onEvent);\n opts?.signal?.removeEventListener('abort', onAbort);\n if (timeoutId) clearTimeout(timeoutId);\n };\n\n this.once(event, onEvent);\n\n if (opts?.signal) {\n if (opts.signal.aborted) return onAbort();\n opts.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n if (opts?.timeoutMs !== undefined) {\n const timeoutMs = opts.timeoutMs;\n timeoutId = setTimeout(() => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForTimeout, [timeoutMs]));\n }, timeoutMs);\n }\n });\n }\n}\n","/*\n * Inspired by Akka Coordinated Shutdown: https://doc.akka.io/libraries/akka-core/current/coordinated-shutdown.html\n * and Lewis's implementation in a private repo elsewhere (https://github.com/Yomanz)\n */\n\nimport { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { Logger } from '../Logger';\nimport { StrictEventEmitter } from '../StrictEventEmitter';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { SEEventMapLike } from '../StrictEventEmitter';\n\n/**\n * Abstract base class for coordinated lifecycle management (startup/shutdown)\n */\nexport abstract class CoordinatedLifecycle<\n TPhase extends number,\n TEvents extends SEEventMapLike<TEvents>\n> extends StrictEventEmitter<TEvents> {\n protected readonly logger: Logger;\n protected readonly tasksMap = new Map<TPhase, LifecycleTask[]>();\n\n protected constructor(\n loggerName: string,\n protected readonly phaseOrder: TPhase[],\n protected readonly phaseEnum: Record<number, string>\n ) {\n super();\n this.logger = new Logger(loggerName);\n this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));\n }\n\n /**\n * Adds a lifecycle task to a specific phase.\n *\n * Tasks are executed in phase order during lifecycle operations.\n * Each task has a timeout to prevent hanging operations.\n *\n * @param phase - The lifecycle phase to add the task to\n * @param taskName - Unique name for the task (used for logging and removal)\n * @param task - Async function to execute during the phase\n * @param timeoutMs - Maximum time allowed for task execution in milliseconds\n * @example\n * ```typescript\n * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {\n * await database.connect();\n * }, 10000);\n * ```\n */\n public addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void {\n if (!this.canAddTask()) return;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);\n }\n\n tasks.push({ name: taskName, task, timeout: timeoutMs });\n this.logger.debug(\n `${chalk.italic('Added')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} to phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n /**\n * Removes a lifecycle task from a specific phase.\n *\n * @param phase - The lifecycle phase to remove the task from\n * @param taskName - Name of the task to remove\n * @returns True if the task was found and removed, false otherwise\n */\n public removeTask(phase: TPhase, taskName: string): boolean {\n if (!this.canRemoveTask()) return false;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) return false;\n\n const initialLength = tasks.length;\n const filteredTasks = tasks.filter((task) => task.name !== taskName);\n this.tasksMap.set(phase, filteredTasks);\n\n const removed = initialLength !== filteredTasks.length;\n if (removed) {\n this.logger.debug(\n `${chalk.italic('Removed')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} from phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n return removed;\n }\n\n /**\n * Run all tasks in a specific phase\n */\n protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.warn(`No tasks to run in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`);\n return;\n }\n\n this.logger.info(\n `${chalk.bold.yellow('Running')} ${this.getTaskType()} phase ${chalk.bold.magenta(this.phaseEnum[phase])} with ${chalk.bold.cyan(tasks.length)} tasks`\n );\n this.emitPhase(phase, 'start');\n\n const results: PromiseSettledResult<void>[] = await this.executeTasksInPhase(phase, tasks);\n\n const failures = results.filter((r) => r.status === 'rejected').length;\n if (failures > 0) {\n // Pass the raw phase name; chalk's ANSI codes would otherwise leak into the serialized\n // error message (e.g. the unknown-exception webhook payload).\n throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);\n } else {\n this.logger.info(\n `Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green('completed successfully')}`\n );\n }\n\n this.emitPhase(phase, 'complete');\n }\n\n /**\n * Run a single task with timeout\n */\n protected async runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n } catch (error) {\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // The phase event key is interpolated from phaseOrder at runtime; the subclass event map derives\n // its phase keys from the same range, so the key is always valid, but TS can't correlate a\n // template-literal key with the generic TEvents. Emit the no-payload event via the base emitter.\n private emitPhase(phase: TPhase, action: 'start' | 'complete'): void {\n EventEmitter.prototype.emit.call(this, `phase:${phase}:${action}`);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract canAddTask(): boolean;\n protected abstract canRemoveTask(): boolean;\n protected abstract getTaskType(): string;\n protected abstract executeTasksInPhase(\n phase: TPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]>;\n}\n","import chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Shutdown phases for coordinated application shutdown.\n */\nexport enum ShutdownPhase {\n /** Stop accepting new requests/interactions */\n StopAcceptingRequests = 1,\n /** Stop background services (health checks, etc.) */\n StopServices,\n /** Disconnect from external resources (database, APIs) */\n ExternalResources,\n /** Disconnect from Discord */\n DiscordCleanup,\n /** Final cleanup tasks */\n FinalCleanup\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: ShutdownPhase[] = [\n ShutdownPhase.StopAcceptingRequests,\n ShutdownPhase.StopServices,\n ShutdownPhase.ExternalResources,\n ShutdownPhase.DiscordCleanup,\n ShutdownPhase.FinalCleanup\n];\n\n/**\n * Strict-event-emitter payload map for coordinated shutdown phases.\n */\nexport type CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;\n\n// Delay process.exit so winston has a window to flush buffered log lines before the event loop dies.\nconst LOG_FLUSH_DELAY_MS = 500;\n\n/**\n * CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.\n *\n * It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.\n * Tasks can be added or removed dynamically, and each task has an associated timeout.\n */\nexport class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {\n private readonly isShutdownEnabled: boolean;\n\n private isShuttingDown = false;\n private exitCode = 0;\n private onSigTerm: (() => void) | null = null;\n private onSigInt: (() => void) | null = null;\n\n public constructor(enabled = true) {\n super('CoordinatedShutdown', PHASE_ORDER, ShutdownPhase);\n\n this.isShutdownEnabled = enabled;\n this.registerSignalHandlers();\n }\n\n protected canAddTask(): boolean {\n return this.isShutdownEnabled;\n }\n\n protected canRemoveTask(): boolean {\n return true;\n }\n\n protected getTaskType(): string {\n return 'shutdown';\n }\n\n protected async executeTasksInPhase(\n phase: ShutdownPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n private registerSignalHandlers(): void {\n if (!this.isShutdownEnabled) return;\n\n this.onSigTerm = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGTERM')} signal`);\n void this.run(0);\n };\n\n this.onSigInt = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGINT')} signal`);\n void this.run(0);\n };\n\n process.on('SIGTERM', this.onSigTerm);\n process.on('SIGINT', this.onSigInt);\n }\n\n private removeSignalHandlers(): void {\n if (this.onSigTerm) {\n process.off('SIGTERM', this.onSigTerm);\n this.onSigTerm = null;\n }\n if (this.onSigInt) {\n process.off('SIGINT', this.onSigInt);\n this.onSigInt = null;\n }\n }\n\n /**\n * Adds a task to a specific shutdown phase with timeout.\n *\n * @param phase - The shutdown phase from {@link ShutdownPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}\n */\n public override addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs = 5000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n /**\n * Removes a task from a specific shutdown phase.\n *\n * @param phase - The shutdown phase to remove from\n * @param taskName - Name of the task to remove\n * @returns True if task was found and removed\n */\n public override removeTask(phase: ShutdownPhase, taskName: string): boolean {\n return super.removeTask(phase, taskName);\n }\n\n /**\n * Executes the coordinated shutdown sequence.\n *\n * Runs all registered tasks across shutdown phases in reverse order.\n * Tasks within each phase are executed in parallel for faster shutdown.\n * Process exits with the specified code when complete.\n *\n * @param exitCode - Process exit code. {@default `0`}\n * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}\n * @returns Promise that resolves when shutdown is complete\n * @example\n * ```typescript\n * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);\n * await shutdown.run(0); // Graceful shutdown\n * ```\n */\n public async run(exitCode = 0, exitProcess = true): Promise<void> {\n this.removeSignalHandlers();\n\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown sequence already in progress');\n return;\n }\n\n this.isShuttingDown = true;\n this.exitCode = exitCode;\n this.logger.info(\n `${chalk.bold.yellow('Starting')} coordinated shutdown with exit code ${chalk.bold.cyan(exitCode)}`\n );\n this.emit('shutdown:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n await this.runPhase(phase);\n }\n\n this.logger.info(`${chalk.bold.green('Coordinated shutdown completed')} successfully`);\n this.emit('shutdown:complete');\n } catch (error) {\n this.logger.error(`${chalk.bold.red('Coordinated shutdown failed')}`);\n this.emit('shutdown:error', error);\n } finally {\n if (exitProcess) {\n this.logger.info(`${chalk.bold.red('Exiting')} process with code ${chalk.bold.cyan(this.exitCode)}`);\n setTimeout(() => {\n process.exit(this.exitCode);\n }, LOG_FLUSH_DELAY_MS);\n } else {\n this.logger.info(`${chalk.bold.yellow('Skipping')} process exit (dev mode)`);\n this.isShuttingDown = false;\n }\n }\n }\n}\n","import { createServer } from 'http';\n\nimport chalk from 'chalk';\n\nimport { ShutdownPhase } from './Lifecycle/CoordinatedShutdown';\nimport { Logger } from './Logger';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'http';\n\nconst HTTP_OK = 200;\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\nconst DEFAULT_HEALTH_CHECK_PATH = '/healthcheck';\n\n/**\n * HTTP health check service for monitoring bot status.\n *\n * Provides a simple HTTP endpoint that responds with JSON status\n * information, useful for container orchestration and monitoring.\n */\nexport class HealthCheck {\n public readonly logger = new Logger('HealthCheck');\n\n public readonly port: number;\n public readonly path: string;\n public readonly host: string | undefined;\n\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;\n this.host = options?.host;\n\n shutdown.addTask(ShutdownPhase.StopServices, 'stop-healthcheck-server', async () => await this.stop());\n }\n\n /**\n * Starts the health check server.\n * @returns Promise that resolves when the server is listening\n */\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (req.method === 'GET' && req.url === this.path) {\n res.writeHead(HTTP_OK, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));\n } else {\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n }\n });\n this.server = server;\n\n const onListenError = (err: Error): void => reject(err);\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // Swap the listen-time reject handler for a logging one: keeping it would reject an\n // already-settled promise on a late error, and removing it without a replacement\n // would crash the process on an unhandled 'error' event.\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n const address = this.host ?? 'localhost';\n this.logger.info(\n `${chalk.green.bold('✓')} Health check server listening on ${chalk.cyan(`http://${address}:${this.port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this.host) {\n this.logger.debug(`Binding health check server to ${this.host}`);\n server.listen(this.port, this.host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this.port);\n }\n });\n }\n\n /**\n * Stops the health check server.\n *\n * @returns Promise that resolves when the server is closed\n */\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n server.once('close', () => resolve());\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(chalk.bold.red('Health check server stopped'));\n });\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Startup phases for coordinated initialization\n *\n * Defines the order in which different components are initialized during bot startup.\n */\nexport enum StartupPhase {\n /** Validate environment variables and config files */\n Validation = 1,\n /** Discover plugin constructors via decorators or registry */\n Discovery,\n /** Register plugin metadata and declared dependencies */\n Registration,\n /** Inject and validate plugin-specific configuration */\n Configuration,\n /** Instantiate plugin classes with Core and arguments */\n Instantiation,\n /** Activate plugins by calling their init/setup methods */\n Activation,\n /** Mark seedcord as ready and start handling interactions */\n Ready\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: StartupPhase[] = [\n StartupPhase.Validation,\n StartupPhase.Discovery,\n StartupPhase.Registration,\n StartupPhase.Configuration,\n StartupPhase.Instantiation,\n StartupPhase.Activation,\n StartupPhase.Ready\n];\n\n/**\n * Strict-event-emitter payload map for coordinated startup phases.\n */\nexport type CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;\n\n/**\n * Manages bot startup lifecycle with ordered phases\n *\n * Coordinates initialization of all bot components in a predictable sequence.\n * Tasks are executed within their designated phases to ensure proper dependency order.\n */\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {\n private isStartingUp = false;\n private hasStarted = false;\n\n public constructor() {\n super('CoordinatedStartup', PHASE_ORDER, StartupPhase);\n }\n\n /**\n * Adds a task to a specific startup phase with timeout.\n *\n * @param phase - The startup phase from {@link StartupPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}\n */\n public override addTask(phase: StartupPhase, taskName: string, task: () => Promise<void>, timeoutMs = 10000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n protected canAddTask(): boolean {\n if (this.hasStarted) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);\n }\n\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);\n }\n\n return true;\n }\n\n protected canRemoveTask(): boolean {\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);\n }\n\n return true;\n }\n\n protected getTaskType(): string {\n return 'startup';\n }\n\n protected async executeTasksInPhase(\n phase: StartupPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n /**\n * Executes the coordinated startup sequence.\n *\n * Runs all registered tasks across startup phases in the correct order.\n * Each phase completes before the next phase begins. Tasks within a phase\n * are executed sequentially to maintain predictable initialization.\n *\n * @returns Promise that resolves when startup is complete\n * @throws An {@link Error} If startup fails or is called multiple times\n * @example\n * ```typescript\n * const startup = new CoordinatedStartup();\n * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);\n * await startup.run();\n * ```\n */\n public async run(): Promise<void> {\n if (this.hasStarted) {\n this.logger.warn('Startup sequence has already completed');\n return;\n }\n\n if (this.isStartingUp) {\n this.logger.warn('Startup sequence already in progress');\n return;\n }\n\n this.isStartingUp = true;\n this.logger.info(`${chalk.bold.green('Starting')} coordinated startup sequence`);\n this.emit('startup:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false mid-loop from the cli\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted');\n return;\n }\n await this.runPhase(phase);\n }\n\n this.hasStarted = true;\n this.logger.info(`${chalk.bold.green('Coordinated startup completed')} successfully`);\n this.emit('startup:complete');\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false before this catch runs\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted during error handling');\n return;\n }\n this.logger.error(`${chalk.bold.red('Coordinated startup failed')}`);\n this.emit('startup:error', error);\n throw error;\n } finally {\n this.isStartingUp = false;\n }\n }\n\n protected override async runTaskWithTimeout(phase: StartupPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n } catch (error) {\n if (!this.isStartingUp) {\n return;\n }\n\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Aborts the startup sequence if it is currently running.\n */\n public abort(): void {\n if (this.isStartingUp) {\n this.isStartingUp = false;\n this.logger.warn('Aborting coordinated startup sequence');\n }\n }\n\n /**\n * Check if startup has completed\n */\n public get isReady(): boolean {\n return this.hasStarted;\n }\n\n /**\n * Check if startup is currently running\n */\n public get isRunning(): boolean {\n return this.isStartingUp;\n }\n}\n","export * from './RateLimiter';\nexport * from './HealthCheck';\nexport * from './Lifecycle';\nexport * from './Logger';\nexport * from './StrictEventEmitter';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["PHASE_ORDER"],"sources":["../src/RateLimiter.ts","../src/Logger/LogFormatter.ts","../src/Logger/Transports/SinkTransport.ts","../src/Logger/TransportFactory.ts","../src/Logger/LoggerChannelRegistry.ts","../src/Logger/LoggerUtilities.ts","../src/Logger/Logger.ts","../src/StrictEventEmitter.ts","../src/Lifecycle/CoordinatedLifecycle.ts","../src/Lifecycle/CoordinatedShutdown.ts","../src/HealthCheck.ts","../src/Lifecycle/CoordinatedStartup.ts","../src/index.ts"],"sourcesContent":["import type { EpochMs } from '@seedcord/types';\n\nconst SWEEP_INTERVAL_MS = 60_000;\n\n/**\n * A usage window for a {@link RateLimiter} key.\n */\nexport interface RateLimitWindow {\n /** Window length in milliseconds. */\n delay: number;\n /**\n * Hits allowed inside the window before the key is limited.\n *\n * @defaultValue `1`\n */\n limit?: number;\n}\n\n/**\n * The outcome of a {@link RateLimiter.hit}.\n */\nexport interface RateLimitResult {\n /** Whether the key is at or over its limit for the current window. */\n limited: boolean;\n /** Absolute epoch milliseconds when the key next frees up. Convert to seconds for Discord timestamp markup. */\n expires: EpochMs;\n}\n\n/**\n * Tracks per-key usage windows and reports when a key is limited and when it frees up.\n *\n * Each key holds a sliding window of hit expiry times, and is limited once its live-hit count\n * reaches the window's `limit`. Expired hits are dropped on the next read, and a background sweep\n * drops fully-expired keys so the map does not grow without bound.\n */\nexport class RateLimiter {\n private readonly map = new Map<string, number[]>();\n\n constructor() {\n // keep the sweep from holding the process open\n setInterval(() => {\n this.sweep();\n }, SWEEP_INTERVAL_MS).unref();\n }\n\n /** Number of keys currently tracked. */\n get size(): number {\n return this.map.size;\n }\n\n /**\n * Records a hit for `key` and reports whether the key is now limited.\n *\n * @param key - The bucket to record against. The caller builds it from its own scope.\n * @param window - The usage window to apply for this hit.\n */\n hit(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n // a window must allow at least one hit, so 0 or a negative never means \"block everything\"\n const limit = Math.max(1, window.limit ?? 1);\n\n const live = this.live(key, now);\n\n if (live.length >= limit) {\n this.map.set(key, live);\n // soonest a slot frees is the earliest expiry\n return { limited: true, expires: Math.min(...live) as EpochMs };\n }\n\n const expires: EpochMs = (now + window.delay) as EpochMs;\n live.push(expires);\n this.map.set(key, live);\n\n return { limited: false, expires };\n }\n\n /**\n * Reports whether `key` is limited right now without recording a hit.\n *\n * The read half of a peek-then-commit. A gate calls `peek` to decide whether to refuse, and\n * `hit` to charge the slot only once it is the gate that let the request through.\n */\n peek(key: string, window: RateLimitWindow): RateLimitResult {\n const now = Date.now();\n const limit = Math.max(1, window.limit ?? 1);\n // a read, so it never writes the filtered hits back, hit owns the only mutation\n const live = this.live(key, now);\n\n if (live.length >= limit) return { limited: true, expires: Math.min(...live) as EpochMs };\n return { limited: false, expires: (now + window.delay) as EpochMs };\n }\n\n private live(key: string, now: number): number[] {\n return (this.map.get(key) ?? []).filter((exp) => exp > now);\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, live] of this.map) {\n const kept = live.filter((exp) => exp > now);\n if (kept.length === 0) this.map.delete(key);\n else this.map.set(key, kept);\n }\n }\n}\n","import stripAnsi from 'strip-ansi';\nimport { format } from 'winston';\n\nimport type { Logform } from 'winston';\n\n/** An Error temporarily annotated with its ANSI-formatted name while pretty-printing stacks. */\ntype FormattedError = Error & { __formattedName?: string; __plainName?: string };\n\n/**\n * Options for pretty log formatting.\n */\nexport interface PrettyFormatOptions {\n /**\n * Number of spaces to pad the log level to.\n *\n * @defaultValue `7`\n */\n padding?: number;\n /**\n * Whether to strip ANSI codes from extra log data.\n *\n * @defaultValue `false`\n */\n stripExtras?: boolean;\n}\n\n/**\n * Options for JSON log formatting.\n * @internal\n */\nexport interface JsonFormatOptions {\n /**\n * Whether to strip ANSI codes from log messages and extra data.\n *\n * @defaultValue `false`\n */\n stripAnsi?: boolean;\n /**\n * Whether to produce minimal JSON output without extra fields.\n *\n * @defaultValue `false`\n */\n minimal?: boolean;\n}\n\n/**\n * Formats log records for console and file outputs.\n *\n * Supports pretty-printed colored logs for development\n * and JSON logs for production with optional ANSI stripping.\n * @internal\n */\nexport class LogFormatter {\n private readonly DEFAULT_PADDING = 7;\n private readonly SPLAT: symbol = Symbol.for('splat');\n\n private safeString(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === undefined || value === null) return '';\n if (typeof (value as { toString?: () => string }).toString === 'function') {\n return String((value as { toString: () => string }).toString());\n }\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n }\n return '';\n }\n\n private sanitizeAnsi(value: unknown): unknown {\n if (typeof value === 'string') return stripAnsi(value);\n if (value instanceof Error) {\n const error = value;\n const sanitized = new Error(stripAnsi(error.message));\n sanitized.name = error.name;\n if (typeof error.stack === 'string') sanitized.stack = stripAnsi(error.stack);\n return sanitized;\n }\n return value;\n }\n\n private getExtras(info: Logform.TransformableInfo): unknown[] {\n const raw = info[this.SPLAT];\n return Array.isArray(raw) ? raw : [];\n }\n\n private readonly FORMAT_SPECIFIERS = /%[sdifjoO]/gu;\n private readonly HAD_FORMAT_KEY = Symbol.for('hadFormatSpecifiers');\n private readonly SAVED_SPLAT_KEY = Symbol.for('savedSplat');\n\n private markFormatSpecifiers(): Logform.Format {\n return format((info) => {\n const msg = typeof info.message === 'string' ? info.message : '';\n const extras = this.getExtras(info);\n const matches = msg.match(this.FORMAT_SPECIFIERS);\n const formatCount = matches ? matches.length : 0;\n info[this.HAD_FORMAT_KEY] = formatCount;\n info[this.SAVED_SPLAT_KEY] = [...extras];\n return info;\n })();\n }\n\n public createPreFormat(): Logform.Format {\n return this.markFormatSpecifiers();\n }\n\n private preserveErrorFormatting(): Logform.Format {\n return format((info) => {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error && /\\u001b/.test(item.name)) {\n const originalName = item.name;\n const plainName = stripAnsi(item.name);\n\n const formatted = item as FormattedError;\n formatted.__formattedName = originalName;\n formatted.__plainName = plainName;\n }\n }\n\n return info;\n })();\n }\n\n private restoreErrorFormatting(): Logform.Format {\n return format((info) => {\n if (typeof info.stack === 'string') {\n const extras = this.getExtras(info);\n\n for (const item of extras) {\n if (item instanceof Error) {\n const { __formattedName: formattedName, __plainName: plainName } = item as FormattedError;\n\n if (typeof formattedName === 'string' && typeof plainName === 'string') {\n info.stack = (info.stack as string).replace(\n new RegExp(`^${this.escapeRegex(plainName)}`, 'm'),\n formattedName\n );\n }\n }\n }\n }\n\n return info;\n })();\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Creates pretty-printed format with colors and timestamps.\n *\n * Ideal for development environments where human readability matters.\n *\n * @param options - Formatting options including padding and ANSI stripping\n * @returns Array of Winston format transformers\n */\n public pretty(options: PrettyFormatOptions = {}): Logform.Format[] {\n const padding = options.padding ?? this.DEFAULT_PADDING;\n return [\n this.preserveErrorFormatting(),\n format.errors({ stack: true }),\n this.restoreErrorFormatting(),\n format.splat(),\n format.colorize({ level: true }),\n format.timestamp({ format: 'D MMM, hh:mm:ss a' }),\n // eslint-disable-next-line max-statements\n format.printf((info: Logform.TransformableInfo) => {\n let ts = this.safeString(info.timestamp);\n let lvl = this.safeString(info.level).padEnd(padding);\n let lbl = this.safeString(info.label);\n let msg = this.safeString(info.message);\n\n if (options.stripExtras) {\n ts = stripAnsi(ts);\n lvl = stripAnsi(lvl);\n lbl = stripAnsi(lbl);\n msg = stripAnsi(msg);\n }\n\n const base = `${ts} [${lvl}]: ${lbl} - ${msg}`;\n const savedExtras = info[this.SAVED_SPLAT_KEY];\n // Array.isArray narrows to any[]; keep the elements as unknown so the filter below stays safe.\n const extras: unknown[] = Array.isArray(savedExtras) ? savedExtras : this.getExtras(info);\n\n let rendered = base;\n\n if (typeof info.stack === 'string') {\n let stack = this.safeString(info.stack);\n if (options.stripExtras) stack = stripAnsi(stack);\n rendered += `\\n${stack}`;\n }\n\n const cleaned = options.stripExtras ? extras.map((entry) => this.sanitizeAnsi(entry)) : extras;\n const rawFormatCount = info[this.HAD_FORMAT_KEY];\n const formatSpecifierCount = typeof rawFormatCount === 'number' ? rawFormatCount : 0;\n const filtered = cleaned.filter((x, index) => {\n if (x === null || x === undefined) return false;\n if (x instanceof Error && typeof info.stack === 'string') return false;\n if (typeof x !== 'object') {\n return index >= formatSpecifierCount;\n }\n return Object.keys(x).length > 0;\n });\n\n if (filtered.length) {\n const primitives: string[] = [];\n const objects: string[] = [];\n\n for (const x of filtered) {\n if (typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean') {\n primitives.push(String(x));\n } else {\n try {\n objects.push(JSON.stringify(x, null, 2));\n } catch {\n objects.push(String(x));\n }\n }\n }\n\n if (primitives.length) {\n rendered += ` ${primitives.join(' ')}`;\n }\n if (objects.length) {\n rendered += `\\n${objects.join('\\n')}`;\n }\n }\n\n return rendered;\n })\n ];\n }\n\n /**\n * Creates JSON format for structured logging.\n *\n * Best for production environments where logs are parsed by tools.\n *\n * @param options - JSON formatting options including ANSI stripping and minimal mode\n * @returns Array of Winston format transformers\n */\n public json(options: JsonFormatOptions = {}): Logform.Format[] {\n const base = [format.timestamp(), format.errors({ stack: true })];\n\n if (options.stripAnsi) {\n base.push(\n format((info) => {\n info.message = typeof info.message === 'string' ? stripAnsi(info.message) : info.message;\n if (typeof info.stack === 'string') info.stack = stripAnsi(info.stack);\n const extras = this.getExtras(info);\n if (extras.length) info.extras = extras.map((entry) => this.sanitizeAnsi(entry));\n return info;\n })()\n );\n }\n\n base.push(options.minimal ? format.json({}) : format.json({ bigint: true, space: 0 }));\n\n return base;\n }\n}\n","import TransportStream from 'winston-transport';\n\nimport type { Logform } from 'winston';\nimport type { TransportStreamOptions } from 'winston-transport';\n\n/**\n * Options for creating a SinkTransport.\n * @internal\n */\nexport interface SinkTransportOptions extends TransportStreamOptions {\n readonly channel: string;\n readonly sink: ILoggerSink;\n}\n\n/**\n * A log entry passed to custom sinks.\n */\nexport interface LoggerSinkLogEntry {\n /** Channel the log originated from */\n readonly channel: string;\n /** Pre-formatted log message ready for display */\n readonly rendered: string;\n /** Raw Winston log info object */\n readonly info: Logform.TransformableInfo;\n}\n\n/**\n * Custom sink interface for capturing logger output.\n *\n * Implement this to route logs to custom destinations.\n */\nexport interface ILoggerSink {\n /**\n * Called when a log entry is emitted.\n *\n * @param entry - The log entry with channel, rendered message, and raw info\n */\n onLog(entry: LoggerSinkLogEntry): void;\n}\n\n/**\n * Handle for managing a sink's lifecycle.\n */\nexport interface ILoggerSinkHandle {\n /**\n * Removes the sink and restores console output if muted.\n */\n dispose(): void;\n}\n\n/**\n * Winston transport that forwards logs to custom sinks.\n * @internal\n */\nexport class SinkTransport extends TransportStream {\n private readonly channelName: string;\n private readonly sink: ILoggerSink;\n\n public constructor(options: SinkTransportOptions) {\n super(options);\n this.channelName = options.channel;\n this.sink = options.sink;\n }\n\n public override log(info: Logform.TransformableInfo, callback: () => void): void {\n setImmediate(() => this.emit('logged', info));\n\n const rendered = this.resolveRendered(info);\n const channel = this.resolveChannel(info);\n\n this.sink.onLog({ channel, rendered, info });\n\n callback();\n }\n\n private resolveChannel(info: Logform.TransformableInfo): string {\n const channel = info.channel;\n return typeof channel === 'string' ? channel : this.channelName;\n }\n\n private resolveRendered(info: Logform.TransformableInfo): string {\n const msg = info[Symbol.for('message')];\n\n if (typeof msg === 'string') return msg;\n\n const fallback = info.message;\n if (typeof fallback === 'string') return fallback;\n\n if (fallback instanceof Error) return fallback.stack ?? fallback.message;\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- last-resort stringify for a non-string, non-Error message value\n return String(fallback ?? '');\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport { format, transports } from 'winston';\n\nimport { LogFormatter } from './LogFormatter';\nimport { SinkTransport } from './Transports/SinkTransport';\n\nimport type { ILoggerSink } from './Transports/SinkTransport';\nimport type { LoggerFormatMode, LoggerLevel, TransportConfig, WinstonTransport } from './Types';\n\n/**\n * Input parameters for building a Winston transport.\n * @internal\n */\nexport interface TransportBuildInput {\n channel: string;\n label: string;\n level: LoggerLevel;\n config: TransportConfig;\n defaultFormat: LoggerFormatMode;\n stripAnsi: boolean;\n}\n\n/**\n * Input parameters for building a sink transport.\n * @internal\n */\ninterface BuildInput {\n readonly channel: string;\n readonly label: string;\n readonly level: LoggerLevel;\n}\n\n/**\n * Creates Winston transports with proper formatting and file path resolution.\n *\n * Builds console and file transports with environment-aware defaults,\n * filename template expansion, and format selection.\n * @internal\n */\nexport class TransportFactory {\n private readonly formatter: LogFormatter;\n private readonly MILLISECOND_PAD = 3;\n private cachedSessionTimestamp: { date: string; timestamp: string } | null = null;\n\n constructor() {\n this.formatter = new LogFormatter();\n }\n\n private ensureDir(filepath: string): void {\n const dir = path.dirname(filepath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n }\n\n private withDefaultLabel(label: string): ReturnType<typeof format.combine> {\n return format.combine(\n format((info) => {\n info.label ??= label;\n return info;\n })()\n );\n }\n\n public buildPreFormat(): ReturnType<typeof format.combine> {\n return format.combine(this.formatter.createPreFormat());\n }\n\n private buildConsoleFormat(label: string): ReturnType<typeof format.combine> {\n // Use JSON format in production, pretty format in development\n if (Envapter.isProduction) {\n return format.combine(this.withDefaultLabel(label), ...this.formatter.json({ stripAnsi: true }));\n }\n return format.combine(this.withDefaultLabel(label), ...this.formatter.pretty());\n }\n\n private buildFileFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private buildStreamFormat(\n label: string,\n mode: LoggerFormatMode,\n stripAnsi: boolean\n ): ReturnType<typeof format.combine> {\n // Stream transport uses similar formatting to file transport\n const formats =\n mode === 'pretty' ? this.formatter.pretty({ stripExtras: stripAnsi }) : this.formatter.json({ stripAnsi });\n return format.combine(this.withDefaultLabel(label), ...formats);\n }\n\n private pad(value: number): string {\n return value.toString().padStart(2, '0');\n }\n\n private buildTimestamp(): { date: string; timestamp: string } {\n if (this.cachedSessionTimestamp) {\n return this.cachedSessionTimestamp;\n }\n\n const now = new Date();\n const yyyy = now.getFullYear();\n const mm = this.pad(now.getMonth() + 1);\n const dd = this.pad(now.getDate());\n const hh = this.pad(now.getHours());\n const min = this.pad(now.getMinutes());\n const ss = this.pad(now.getSeconds());\n const ms = now.getMilliseconds().toString().padStart(this.MILLISECOND_PAD, '0');\n\n const date = `${yyyy}-${mm}-${dd}`;\n const timestamp = `${date}-${hh}${min}${ss}-${ms}`;\n\n this.cachedSessionTimestamp = { date, timestamp };\n return this.cachedSessionTimestamp;\n }\n\n private resolveFilename(template: string, channel: string): string {\n const { date, timestamp } = this.buildTimestamp();\n return template\n .replaceAll('{channel}', channel)\n .replaceAll('{date}', date)\n .replaceAll('{timestamp}', timestamp);\n }\n\n /**\n * Builds a Winston transport from configuration.\n *\n * Creates console, file, or stream transport with proper formatting,\n * level filtering, and file rotation settings.\n *\n * @param input - Transport configuration parameters\n * @returns Configured Winston transport instance\n */\n public build(input: TransportBuildInput): WinstonTransport {\n const effectiveFormat = input.config.format ?? input.defaultFormat;\n const shouldStripAnsi = input.config.stripAnsi ?? input.stripAnsi;\n const level = input.config.level ?? input.level;\n\n if (input.config.type === 'console') {\n return new transports.Console({\n level,\n format: this.buildConsoleFormat(input.label)\n });\n }\n\n if (input.config.type === 'stream') {\n return new transports.Stream({\n level,\n stream: input.config.stream,\n format: this.buildStreamFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n const filenameTemplate = input.config.filename ?? 'logs/application-{timestamp}.log';\n const resolvedFilename = this.resolveFilename(filenameTemplate, input.channel);\n this.ensureDir(resolvedFilename);\n\n return new transports.File({\n level,\n filename: resolvedFilename,\n ...(input.config.maxSize !== undefined ? { maxsize: input.config.maxSize } : {}),\n ...(input.config.maxFiles !== undefined ? { maxFiles: input.config.maxFiles } : {}),\n tailable: true,\n format: this.buildFileFormat(input.label, effectiveFormat, shouldStripAnsi)\n });\n }\n\n /**\n * Builds a sink transport for routing logs to custom destinations.\n *\n * @param input - Transport configuration with channel and level info\n * @param sink - Custom sink to receive log entries\n * @returns Configured sink transport instance\n */\n public buildSinkTransport(input: BuildInput, sink: ILoggerSink): WinstonTransport {\n const format = this.buildConsoleFormat(input.label);\n\n return new SinkTransport({\n level: input.level,\n channel: input.channel,\n sink,\n format\n });\n }\n}\n","import path from 'node:path';\n\nimport { Envapter } from 'envapt';\nimport winston, { createLogger } from 'winston';\n\nimport { TransportFactory } from './TransportFactory';\n\nimport type { ILoggerSink, ILoggerSinkHandle } from './Transports/SinkTransport';\nimport type {\n ChannelConfig,\n LoggerConfiguration,\n LoggerLevel,\n TransportConfig,\n WinstonInstance,\n WinstonTransport\n} from './Types';\n\n/**\n * Manages Winston logger instances per channel with caching.\n *\n * Stores channel configuration and creates transports for each channel\n * with environment-aware defaults.\n */\nexport class LoggerChannelRegistry {\n private static _instance: LoggerChannelRegistry | null = null;\n\n private nextSinkId = 1;\n private readonly sinks = new Map<\n number,\n {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n >();\n\n private readonly DEFAULT_LEVEL: LoggerLevel = Envapter.isDevelopment\n ? 'silly'\n : Envapter.isStaging\n ? 'debug'\n : 'info';\n\n private config: LoggerConfiguration = {\n defaultChannel: 'default',\n channels: {},\n files: {\n maxSizeMB: 10,\n maxFiles: 5,\n patterns: {\n dev: 'logs/{channel}-{timestamp}.log',\n staging: 'logs/staging-{date}-{timestamp}.jsonl',\n prod: 'logs/production-{date}.jsonl'\n }\n }\n };\n\n private readonly FORMAT = Envapter.isDevelopment ? 'pretty' : 'json';\n\n private readonly cache = new Map<string, WinstonInstance>();\n private readonly transportFactory: TransportFactory;\n\n private constructor() {\n this.transportFactory = new TransportFactory();\n }\n\n /**\n * Gets the singleton instance of the registry.\n */\n public static get instance(): LoggerChannelRegistry {\n return (this._instance ??= new LoggerChannelRegistry());\n }\n\n private getDefaultChannelConfig(name: string): ChannelConfig {\n return {\n name,\n level: this.DEFAULT_LEVEL,\n transports: [\n { type: 'console', level: this.DEFAULT_LEVEL, format: this.FORMAT, stripAnsi: !Envapter.isDevelopment },\n {\n type: 'file',\n level: this.DEFAULT_LEVEL,\n filename: Envapter.isDevelopment\n ? this.config.files.patterns.dev\n : Envapter.isStaging\n ? this.config.files.patterns.staging\n : this.config.files.patterns.prod,\n format: this.FORMAT,\n stripAnsi: true,\n maxSize: this.config.files.maxSizeMB * 1024 * 1024,\n maxFiles: this.config.files.maxFiles\n }\n ]\n };\n }\n\n private mergeChannelConfig(base: ChannelConfig, override?: ChannelConfig): ChannelConfig {\n if (!override) return base;\n\n const level = override.level ?? base.level;\n const stripAnsi = override.stripAnsi ?? base.stripAnsi;\n const format = override.format ?? base.format;\n const transports = override.transports ?? base.transports;\n const name = override.name || base.name;\n\n return {\n name,\n ...(level !== undefined ? { level } : {}),\n ...(stripAnsi !== undefined ? { stripAnsi } : {}),\n ...(format !== undefined ? { format } : {}),\n ...(transports !== undefined ? { transports } : {})\n };\n }\n\n /**\n * Updates global logger configuration and clears cache.\n *\n * @param config - Partial configuration to merge with existing settings\n */\n public configure(config: Partial<LoggerConfiguration>): void {\n this.disposeCachedLoggers();\n this.config = { ...this.config, ...config, channels: { ...this.config.channels, ...(config.channels ?? {}) } };\n this.cache.clear();\n }\n\n // configure() rebuilds every logger; detach + close the old transports and reset sink\n // bookkeeping first, or the discarded loggers leak transports (and their file handles /\n // sink wrappers) across reconfigures, e.g. on every dev HMR cycle.\n private disposeCachedLoggers(): void {\n for (const logger of this.cache.values()) {\n for (const transport of [...logger.transports]) {\n logger.remove(transport);\n transport.close?.();\n }\n }\n for (const record of this.sinks.values()) {\n record.transportsByChannel.clear();\n record.removedConsoleByChannel.clear();\n }\n }\n\n /**\n * Returns the name of the default channel.\n */\n public getDefaultChannel(): string {\n return this.config.defaultChannel;\n }\n\n /**\n * Returns a list of all known channels (configured or cached).\n */\n public getChannels(): string[] {\n const configuredChannels = Object.keys(this.config.channels);\n const cachedChannels = Array.from(this.cache.keys());\n const allChannels = new Set([...configuredChannels, ...cachedChannels, this.config.defaultChannel]);\n return Array.from(allChannels).sort();\n }\n\n /**\n * Gets the log file path for a specific channel, if one exists.\n *\n * @param channel - Channel name to get log file path for\n * @returns The log file path, or null if no file transport exists\n */\n public getLogFilePath(channel: string): string | null {\n const logger = this.cache.get(channel);\n if (!logger) return null;\n\n for (const transport of logger.transports) {\n if (transport instanceof winston.transports.File) {\n return path.resolve(transport.dirname, transport.filename);\n }\n }\n\n return null;\n }\n\n /**\n * Gets or creates a Winston logger instance for the given channel.\n *\n * @param channel - Channel name to get logger for\n * @returns Configured Winston logger instance\n */\n public get(channel: string): WinstonInstance {\n const cached = this.cache.get(channel);\n if (cached) return cached;\n\n const channelConfig = this.mergeChannelConfig(\n this.getDefaultChannelConfig(channel),\n this.config.channels[channel]\n );\n\n const effectiveLevel = channelConfig.level ?? this.DEFAULT_LEVEL;\n\n const consoleMuted = this.isConsoleMuted();\n\n const transports = (channelConfig.transports ?? [])\n .filter((transportConfig: TransportConfig) => !(consoleMuted && transportConfig.type === 'console'))\n .map((transportConfig: TransportConfig) =>\n this.transportFactory.build({\n channel,\n label: channel,\n level: effectiveLevel,\n config: transportConfig,\n defaultFormat: channelConfig.format ?? 'pretty',\n stripAnsi: channelConfig.stripAnsi ?? true\n })\n );\n\n const logger = createLogger({\n level: effectiveLevel,\n format: this.transportFactory.buildPreFormat(),\n transports\n });\n\n for (const entry of this.sinks.values()) {\n this.applySinkToCachedLogger(channel, logger, entry);\n }\n\n this.cache.set(channel, logger);\n return logger;\n }\n\n private isConsoleMuted(): boolean {\n for (const entry of this.sinks.values()) {\n if (entry.muteConsole) return true;\n }\n return false;\n }\n\n /**\n * Installs a custom sink to capture log output across all channels.\n *\n * Useful for routing logs to custom destinations like TUIs or remote services.\n *\n * @param sink - Custom sink implementation to receive log entries\n * @param options - Optional configuration for console muting behavior\n * @returns Handle to dispose the sink when no longer needed\n */\n public installSink(sink: ILoggerSink, options?: { muteConsole?: boolean }): ILoggerSinkHandle {\n const id = this.nextSinkId;\n this.nextSinkId += 1;\n\n const record = {\n sink,\n muteConsole: options?.muteConsole ?? true,\n transportsByChannel: new Map<string, WinstonTransport>(),\n removedConsoleByChannel: new Map<string, WinstonTransport[]>()\n };\n\n this.sinks.set(id, record);\n\n for (const [channel, logger] of this.cache.entries()) {\n this.applySinkToCachedLogger(channel, logger, record);\n }\n\n return new (class implements ILoggerSinkHandle {\n public constructor(\n private readonly registry: LoggerChannelRegistry,\n private readonly key: number\n ) {}\n public dispose(): void {\n this.registry.uninstallSink(this.key);\n }\n })(this, id);\n }\n\n private uninstallSink(id: number): void {\n const record = this.sinks.get(id);\n if (!record) return;\n\n for (const [channel, logger] of this.cache.entries()) {\n const sinkTransport = record.transportsByChannel.get(channel);\n if (sinkTransport) logger.remove(sinkTransport);\n\n const removed = record.removedConsoleByChannel.get(channel);\n if (removed?.length) {\n for (const t of removed) logger.add(t);\n }\n }\n\n this.sinks.delete(id);\n }\n\n private applySinkToCachedLogger(\n channel: string,\n logger: WinstonInstance,\n record: {\n readonly sink: ILoggerSink;\n readonly muteConsole: boolean;\n readonly transportsByChannel: Map<string, WinstonTransport>;\n readonly removedConsoleByChannel: Map<string, WinstonTransport[]>;\n }\n ): void {\n if (record.muteConsole) {\n const removed: WinstonTransport[] = [];\n for (const t of logger.transports) {\n if (t instanceof winston.transports.Console) {\n removed.push(t);\n }\n }\n if (removed.length) {\n for (const t of removed) logger.remove(t);\n record.removedConsoleByChannel.set(channel, removed);\n }\n }\n\n const sinkTransport = this.transportFactory.buildSinkTransport(\n { channel, label: channel, level: logger.level as unknown as LoggerLevel },\n record.sink\n );\n\n logger.add(sinkTransport);\n record.transportsByChannel.set(channel, sinkTransport);\n }\n}\n","import { formatFilePath } from '@seedcord/utils';\nimport chalk from 'chalk';\n\nimport type { LoggerLevel } from './Types';\nimport type { ILogger } from '@seedcord/types';\n\n/**\n * Provides access to common logging utilities.\n */\nexport class LoggerUtilities {\n constructor(private readonly logger: ILogger) {}\n\n private arrow(text: string): string {\n return `${chalk.gray('→')} ${text}`;\n }\n\n /**\n * Logs a single item with an arrow prefix.\n * @param text - The text to log\n */\n public item(text: string, level: LoggerLevel = 'info'): void {\n this.logger[level](this.arrow(text));\n }\n\n /**\n * Logs a list of items with optional heading.\n *\n * @param items - Array of items to log as a list\n * @param heading - Optional heading to display above the list\n */\n public list(items: string[], heading?: string, level: LoggerLevel = 'info'): void {\n if (heading) this.logger[level](heading);\n for (const item of items) {\n this.logger[level](this.arrow(item));\n }\n }\n\n /**\n * Logs a summary with title and key-value pairs.\n * Example: \"Loaded: 5 handlers, 3 commands\"\n *\n * @param title - The title of the summary\n * @param items - Object with counts/values to display\n */\n public summary(title: string, items: Record<string, number | string>, level: LoggerLevel = 'info'): void {\n const entries = Object.entries(items).map(([key, value]) => `${chalk.magenta.bold(String(value))} ${key}`);\n this.logger[level](`${chalk.bold.green(title)}: ${entries.join(', ')}`);\n }\n\n /**\n * Logs a component registration message.\n *\n * @param name - Name of the component being registered\n * @param from - File path the component is from\n * @param type - Optional type label (e.g., 'middleware', 'handler')\n */\n public registration(name: string, from: string, type?: string, level: LoggerLevel = 'info'): void {\n const scope = type ? `${type} ` : '';\n this.logger[level](\n `${chalk.italic('Registered')} ${chalk.bold.yellow(scope)}${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(from))}`\n );\n }\n\n /**\n * Logs component initialization start/end.\n *\n * @param component - Name of the component\n * @param action - 'start' or 'end' to indicate initialization phase\n */\n public initialization(component: string, action: 'start' | 'end', level: LoggerLevel = 'info'): void {\n const verb = action === 'start' ? 'Initializing' : 'Initialized';\n this.logger[level](chalk.bold(`${verb} ${component}`));\n }\n\n /**\n * Logs progress as \"[current/total]\" with optional item label.\n *\n * @param current - Current progress count\n * @param total - Total count\n * @param item - Optional item label to append\n */\n public progress(current: number, total: number, item?: string, level: LoggerLevel = 'info'): void {\n const base = `[${current}/${total}]`;\n const suffix = item ? ` ${item}` : '';\n this.logger[level](`${chalk.cyan(base)}${suffix}`);\n }\n\n /**\n * Logs content in a decorative box.\n *\n * @param title - Title to display in the box\n * @param content - Lines of content to display in the box\n */\n public box(title: string, content: string[], level: LoggerLevel = 'info'): void {\n const width = Math.max(title.length, ...content.map((line) => line.length)) + 2;\n const horizontal = '─'.repeat(width);\n this.logger[level](`╭${horizontal}╮`);\n this.logger[level](`│ ${title.padEnd(width - 1, ' ')}│`);\n for (const line of content) {\n this.logger[level](`│ ${line.padEnd(width - 1, ' ')}│`);\n }\n this.logger[level](`╰${horizontal}╯`);\n }\n}\n","import { LoggerChannelRegistry } from './LoggerChannelRegistry';\nimport { LoggerUtilities } from './LoggerUtilities';\n\nimport type { LoggerConfiguration, LoggerOptions } from './Types';\nimport type { ILogger } from '@seedcord/types';\nimport type { Logger as Winston } from 'winston';\n\n/**\n * Public logging service with channel-aware transports and per-run file output.\n *\n * - Channel separation (e.g., bot, cli, hmr)\n * - Production-safe JSON logs with ANSI stripping\n * - Unique log files per run via filename templates\n */\nexport class Logger implements ILogger {\n declare private logger: Winston;\n private readonly label: string;\n private channel: string;\n private readonly registry = LoggerChannelRegistry.instance;\n\n public readonly utils: LoggerUtilities;\n\n /**\n * Configures global logger settings, applied to all channels.\n *\n * @param config - Partial configuration to merge with defaults\n */\n public static configure(config: Partial<LoggerConfiguration>): void {\n LoggerChannelRegistry.instance.configure(config);\n }\n\n /**\n * Creates a new Logger instance.\n *\n * @param label - Prefix/label for all log entries from this logger\n * @param options - Optional configuration for channel, format, and ANSI handling\n */\n constructor(label: string, options?: LoggerOptions) {\n this.label = label;\n this.channel = options?.channel ?? this.registry.getDefaultChannel();\n this.logger = this.registry.get(this.channel).child({ label: this.label, channel: this.channel });\n\n this.utils = new LoggerUtilities(this);\n }\n\n /**\n * Switches this logger to a different channel.\n *\n * @param channel - Channel name to switch to\n */\n public setChannel(channel: string): void {\n this.channel = channel;\n this.logger = this.registry.get(channel).child({ label: this.label, channel });\n }\n\n /**\n * Returns a new Logger for this label on the specified channel.\n *\n * @param channel - Channel name to use\n */\n public inChannel(channel: string): Logger {\n return new Logger(this.label, { channel });\n }\n\n /**\n * Logs an error message with optional additional data.\n *\n * @param msg - The error message to log\n * @param args - Additional data to include in the log entry\n */\n public error(msg: string, ...args: unknown[]): void {\n this.logger.error(msg, ...args);\n }\n\n /**\n * Logs a warning message with optional additional data.\n *\n * @param msg - The warning message to log\n * @param args - Additional data to include in the log entry\n */\n public warn(msg: string, ...args: unknown[]): void {\n this.logger.warn(msg, ...args);\n }\n\n /**\n * Logs an informational message with optional additional data.\n *\n * @param msg - The informational message to log\n * @param args - Additional data to include in the log entry\n */\n public info(msg: string, ...args: unknown[]): void {\n this.logger.info(msg, ...args);\n }\n\n /**\n * Logs an HTTP-related message with optional additional data.\n *\n * @param msg - The HTTP message to log\n * @param args - Additional data to include in the log entry\n */\n public http(msg: string, ...args: unknown[]): void {\n this.logger.http(msg, ...args);\n }\n\n /**\n * Logs a verbose message with optional additional data.\n *\n * @param msg - The verbose message to log\n * @param args - Additional data to include in the log entry\n */\n public verbose(msg: string, ...args: unknown[]): void {\n this.logger.verbose(msg, ...args);\n }\n\n /**\n * Logs a debug message with optional additional data.\n *\n * @param msg - The debug message to log\n * @param args - Additional data to include in the log entry\n */\n public debug(msg: string, ...args: unknown[]): void {\n this.logger.debug(msg, ...args);\n }\n\n /**\n * Logs a silly/trace level message with optional additional data.\n *\n * @param msg - The silly message to log\n * @param args - Additional data to include in the log entry\n */\n public silly(msg: string, ...args: unknown[]): void {\n this.logger.silly(msg, ...args);\n }\n}\n","import { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n/** Tuple type used for all event payloads. */\nexport type SEArgsTuple = readonly unknown[];\n\n/** Convenience map for emitters that intentionally expose no events. */\nexport type SENoEvents = Record<never, SEArgsTuple>;\n\n/**\n * Accepts any object type and constrains every value to be a tuple.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport type SEEventMapLike<TEvents extends object> = { [K in keyof TEvents]: SEArgsTuple };\n\n/**\n * Narrows a provided event map to the keys that can be emitted or listened for.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n * @internal\n */\ntype SEEventKey<TEvents extends object> = Extract<keyof TEvents, string | symbol>;\n\n/**\n * Typed wrapper around Node.js {@link EventEmitter} enforcing tuple payloads per event name.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads\n */\nexport class StrictEventEmitter<TEvents extends SEEventMapLike<TEvents>> extends EventEmitter {\n /**\n * Registers a persistent listener with tuple-safe arguments for the given event.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override on<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.on(event, listener);\n }\n\n /**\n * Registers a one time listener that is removed after the first invocation.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override once<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.once(event, listener);\n }\n\n /**\n * Removes a previously registered listener for the given event.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override off<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.off(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.on} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name to attach to\n * @param listener - Callback operating on the typed argument tuple for the event\n * @returns This emitter instance for chaining\n */\n override addListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return this.on(event, listener);\n }\n\n /**\n * Alias of {@link StrictEventEmitter.off} for compatibility with Node.js EventEmitter APIs.\n *\n * @param event - The event name whose listener should be removed\n * @param listener - Callback originally registered for the event\n * @returns This emitter instance for chaining\n */\n override removeListener<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return super.removeListener(event, listener);\n }\n\n /**\n * Emits an event with the strictly typed argument tuple for the event name.\n *\n * @param event - The event name to emit\n * @param args - Tuple payload for the event\n * @returns True when the event had listeners, false otherwise\n */\n override emit<TEventKey extends SEEventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n // justified: widen the per-event tuple to its base array type so it spreads into Node's\n // `emit(event, ...args: any[])`. There's no declaration fix for Node's base signature.\n return super.emit(event, ...(args as readonly unknown[]));\n }\n\n /**\n * Retrieves the listener list for a given event with the correct tuple signature.\n *\n * @param event - The event name to inspect\n * @returns Array of listeners registered for the event\n */\n override listeners<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey\n ): ((...args: TEvents[TEventKey]) => void)[] {\n return super.listeners(event) as ((...args: TEvents[TEventKey]) => void)[];\n }\n\n /**\n * Counts listeners for an event without widening the return type of {@link EventEmitter.listenerCount}.\n *\n * @param event - The event name to inspect\n * @returns The total number of listeners registered for the event\n */\n listenerCountTyped<TEventKey extends SEEventKey<TEvents>>(event: TEventKey): number {\n return super.listenerCount(event);\n }\n\n /**\n * Returns the list of event names known to the emitter with the mapped key type.\n *\n * @returns Array of event keys supported by the emitter\n */\n eventNamesTyped(): SEEventKey<TEvents>[] {\n return super.eventNames() as SEEventKey<TEvents>[];\n }\n\n /**\n * Waits for an event to be emitted, resolving with the listener arguments tuple once triggered.\n * Supports optional abort signals and timeouts for cancellation semantics.\n *\n * @param event - The event name to wait for\n * @param opts - Optional abort signal or timeout in milliseconds\n * @returns Promise resolving with the emitted argument tuple; rejects when aborted or timed out\n */\n waitFor<TEventKey extends SEEventKey<TEvents>>(\n event: TEventKey,\n opts?: { signal?: AbortSignal; timeoutMs?: number }\n ): Promise<TEvents[TEventKey]> {\n return new Promise<TEvents[TEventKey]>((resolve, reject) => {\n const onEvent = (...args: TEvents[TEventKey]): void => {\n cleanup();\n resolve(args);\n };\n\n const onAbort = (): void => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForAborted));\n };\n\n let timeoutId: NodeJS.Timeout | null = null;\n\n const cleanup = (): void => {\n this.off(event, onEvent);\n opts?.signal?.removeEventListener('abort', onAbort);\n if (timeoutId) clearTimeout(timeoutId);\n };\n\n this.once(event, onEvent);\n\n if (opts?.signal) {\n if (opts.signal.aborted) return onAbort();\n opts.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n if (opts?.timeoutMs !== undefined) {\n const timeoutMs = opts.timeoutMs;\n timeoutId = setTimeout(() => {\n cleanup();\n reject(new SeedcordError(SeedcordErrorCode.EventEmitterWaitForTimeout, [timeoutMs]));\n }, timeoutMs);\n }\n });\n }\n}\n","/*\n * Inspired by Akka Coordinated Shutdown: https://doc.akka.io/libraries/akka-core/current/coordinated-shutdown.html\n * and Lewis's implementation in a private repo elsewhere (https://github.com/Yomanz)\n */\n\nimport { EventEmitter } from 'node:events';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { Logger } from '../Logger';\nimport { StrictEventEmitter } from '../StrictEventEmitter';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { SEEventMapLike } from '../StrictEventEmitter';\n\n/**\n * Abstract base class for coordinated lifecycle management (startup/shutdown)\n */\nexport abstract class CoordinatedLifecycle<\n TPhase extends number,\n TEvents extends SEEventMapLike<TEvents>\n> extends StrictEventEmitter<TEvents> {\n protected readonly logger: Logger;\n protected readonly tasksMap = new Map<TPhase, LifecycleTask[]>();\n\n protected constructor(\n loggerName: string,\n protected readonly phaseOrder: TPhase[],\n protected readonly phaseEnum: Record<number, string>\n ) {\n super();\n this.logger = new Logger(loggerName);\n this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));\n }\n\n /**\n * Adds a lifecycle task to a specific phase.\n *\n * Tasks are executed in phase order during lifecycle operations.\n * Each task has a timeout to prevent hanging operations.\n *\n * @param phase - The lifecycle phase to add the task to\n * @param taskName - Unique name for the task (used for logging and removal)\n * @param task - Async function to execute during the phase\n * @param timeoutMs - Maximum time allowed for task execution in milliseconds\n * @example\n * ```typescript\n * lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {\n * await database.connect();\n * }, 10000);\n * ```\n */\n public addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void {\n if (!this.canAddTask()) return;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);\n }\n\n tasks.push({ name: taskName, task, timeout: timeoutMs });\n this.logger.debug(\n `${chalk.italic('Added')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} to phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n /**\n * Removes a lifecycle task from a specific phase.\n *\n * @param phase - The lifecycle phase to remove the task from\n * @param taskName - Name of the task to remove\n * @returns True if the task was found and removed, false otherwise\n */\n public removeTask(phase: TPhase, taskName: string): boolean {\n if (!this.canRemoveTask()) return false;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) return false;\n\n const initialLength = tasks.length;\n const filteredTasks = tasks.filter((task) => task.name !== taskName);\n this.tasksMap.set(phase, filteredTasks);\n\n const removed = initialLength !== filteredTasks.length;\n if (removed) {\n this.logger.debug(\n `${chalk.italic('Removed')} ${this.getTaskType()} task ${chalk.bold.cyan(taskName)} from phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n }\n\n return removed;\n }\n\n /**\n * Run all tasks in a specific phase\n */\n protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.warn(`No tasks to run in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`);\n return;\n }\n\n this.logger.info(\n `${chalk.bold.yellow('Running')} ${this.getTaskType()} phase ${chalk.bold.magenta(this.phaseEnum[phase])} with ${chalk.bold.cyan(tasks.length)} tasks`\n );\n this.emitPhase(phase, 'start');\n\n const results: PromiseSettledResult<void>[] = await this.executeTasksInPhase(phase, tasks);\n\n const failures = results.filter((r) => r.status === 'rejected').length;\n if (failures > 0) {\n // Pass the raw phase name; chalk's ANSI codes would otherwise leak into the serialized\n // error message (e.g. the unknown-exception webhook payload).\n throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);\n } else {\n this.logger.info(\n `Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green('completed successfully')}`\n );\n }\n\n this.emitPhase(phase, 'complete');\n }\n\n /**\n * Run a single task with timeout\n */\n protected async runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`\n );\n } catch (error) {\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n // The phase event key is interpolated from phaseOrder at runtime; the subclass event map derives\n // its phase keys from the same range, so the key is always valid, but TS can't correlate a\n // template-literal key with the generic TEvents. Emit the no-payload event via the base emitter.\n private emitPhase(phase: TPhase, action: 'start' | 'complete'): void {\n EventEmitter.prototype.emit.call(this, `phase:${phase}:${action}`);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract canAddTask(): boolean;\n protected abstract canRemoveTask(): boolean;\n protected abstract getTaskType(): string;\n protected abstract executeTasksInPhase(\n phase: TPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]>;\n}\n","import chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Shutdown phases for coordinated application shutdown.\n */\nexport enum ShutdownPhase {\n /** Stop accepting new requests/interactions */\n StopAcceptingRequests = 1,\n /** Stop background services (health checks, etc.) */\n StopServices,\n /** Disconnect from external resources (database, APIs) */\n ExternalResources,\n /** Disconnect from Discord */\n DiscordCleanup,\n /** Final cleanup tasks */\n FinalCleanup\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: ShutdownPhase[] = [\n ShutdownPhase.StopAcceptingRequests,\n ShutdownPhase.StopServices,\n ShutdownPhase.ExternalResources,\n ShutdownPhase.DiscordCleanup,\n ShutdownPhase.FinalCleanup\n];\n\n/**\n * Strict-event-emitter payload map for coordinated shutdown phases.\n */\nexport type CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;\n\n// Delay process.exit so winston has a window to flush buffered log lines before the event loop dies.\nconst LOG_FLUSH_DELAY_MS = 500;\n\n/**\n * CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.\n *\n * It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.\n * Tasks can be added or removed dynamically, and each task has an associated timeout.\n */\nexport class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {\n private readonly isShutdownEnabled: boolean;\n\n private isShuttingDown = false;\n private exitCode = 0;\n private onSigTerm: (() => void) | null = null;\n private onSigInt: (() => void) | null = null;\n\n public constructor(enabled = true) {\n super('CoordinatedShutdown', PHASE_ORDER, ShutdownPhase);\n\n this.isShutdownEnabled = enabled;\n this.registerSignalHandlers();\n }\n\n protected canAddTask(): boolean {\n return this.isShutdownEnabled;\n }\n\n protected canRemoveTask(): boolean {\n return true;\n }\n\n protected getTaskType(): string {\n return 'shutdown';\n }\n\n protected async executeTasksInPhase(\n phase: ShutdownPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n private registerSignalHandlers(): void {\n if (!this.isShutdownEnabled) return;\n\n this.onSigTerm = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGTERM')} signal`);\n void this.run(0);\n };\n\n this.onSigInt = () => {\n this.logger.info(`Received ${chalk.yellow.bold('SIGINT')} signal`);\n void this.run(0);\n };\n\n process.on('SIGTERM', this.onSigTerm);\n process.on('SIGINT', this.onSigInt);\n }\n\n private removeSignalHandlers(): void {\n if (this.onSigTerm) {\n process.off('SIGTERM', this.onSigTerm);\n this.onSigTerm = null;\n }\n if (this.onSigInt) {\n process.off('SIGINT', this.onSigInt);\n this.onSigInt = null;\n }\n }\n\n /**\n * Adds a task to a specific shutdown phase with timeout.\n *\n * @param phase - The shutdown phase from {@link ShutdownPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `5000`}\n */\n public override addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs = 5000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n /**\n * Removes a task from a specific shutdown phase.\n *\n * @param phase - The shutdown phase to remove from\n * @param taskName - Name of the task to remove\n * @returns True if task was found and removed\n */\n public override removeTask(phase: ShutdownPhase, taskName: string): boolean {\n return super.removeTask(phase, taskName);\n }\n\n /**\n * Executes the coordinated shutdown sequence.\n *\n * Runs all registered tasks across shutdown phases in reverse order.\n * Tasks within each phase are executed in parallel for faster shutdown.\n * Process exits with the specified code when complete.\n *\n * @param exitCode - Process exit code. {@default `0`}\n * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}\n * @returns Promise that resolves when shutdown is complete\n * @example\n * ```typescript\n * shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);\n * await shutdown.run(0); // Graceful shutdown\n * ```\n */\n public async run(exitCode = 0, exitProcess = true): Promise<void> {\n this.removeSignalHandlers();\n\n if (this.isShuttingDown) {\n this.logger.warn('Shutdown sequence already in progress');\n return;\n }\n\n this.isShuttingDown = true;\n this.exitCode = exitCode;\n this.logger.info(\n `${chalk.bold.yellow('Starting')} coordinated shutdown with exit code ${chalk.bold.cyan(exitCode)}`\n );\n this.emit('shutdown:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n await this.runPhase(phase);\n }\n\n this.logger.info(`${chalk.bold.green('Coordinated shutdown completed')} successfully`);\n this.emit('shutdown:complete');\n } catch (error) {\n this.logger.error(`${chalk.bold.red('Coordinated shutdown failed')}`);\n this.emit('shutdown:error', error);\n } finally {\n if (exitProcess) {\n this.logger.info(`${chalk.bold.red('Exiting')} process with code ${chalk.bold.cyan(this.exitCode)}`);\n setTimeout(() => {\n process.exit(this.exitCode);\n }, LOG_FLUSH_DELAY_MS);\n } else {\n this.logger.info(`${chalk.bold.yellow('Skipping')} process exit (dev mode)`);\n this.isShuttingDown = false;\n }\n }\n }\n}\n","import { createServer } from 'http';\n\nimport chalk from 'chalk';\n\nimport { ShutdownPhase } from './Lifecycle/CoordinatedShutdown';\nimport { Logger } from './Logger';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'http';\n\nconst HTTP_OK = 200;\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\nconst DEFAULT_HEALTH_CHECK_PATH = '/healthcheck';\n\n/**\n * HTTP health check service for monitoring bot status.\n *\n * Provides a simple HTTP endpoint that responds with JSON status\n * information, useful for container orchestration and monitoring.\n */\nexport class HealthCheck {\n /** @internal */\n public readonly logger = new Logger('HealthCheck');\n\n public readonly port: number;\n public readonly path: string;\n public readonly host: string | undefined;\n\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;\n this.host = options?.host;\n\n shutdown.addTask(ShutdownPhase.StopServices, 'stop-healthcheck-server', async () => await this.stop());\n }\n\n /**\n * Starts the health check server.\n * @returns Promise that resolves when the server is listening\n * @internal\n */\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (req.method === 'GET' && req.url === this.path) {\n res.writeHead(HTTP_OK, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));\n } else {\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n }\n });\n this.server = server;\n\n const onListenError = (err: Error): void => reject(err);\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // Swap the listen-time reject handler for a logging one: keeping it would reject an\n // already-settled promise on a late error, and removing it without a replacement\n // would crash the process on an unhandled 'error' event.\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n const address = this.host ?? 'localhost';\n this.logger.info(\n `${chalk.green.bold('✓')} Health check server listening on ${chalk.cyan(`http://${address}:${this.port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this.host) {\n this.logger.debug(`Binding health check server to ${this.host}`);\n server.listen(this.port, this.host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this.port);\n }\n });\n }\n\n /**\n * Stops the health check server.\n *\n * @returns Promise that resolves when the server is closed\n * @internal\n */\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n server.once('close', () => resolve());\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(chalk.bold.red('Health check server stopped'));\n });\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\n/**\n * Startup phases for coordinated initialization\n *\n * Defines the order in which different components are initialized during bot startup.\n */\nexport enum StartupPhase {\n /** Validate environment variables and config files */\n Validation = 1,\n /** Discover plugin constructors via decorators or registry */\n Discovery,\n /** Register plugin metadata and declared dependencies */\n Registration,\n /** Inject and validate plugin-specific configuration */\n Configuration,\n /** Instantiate plugin classes with Core and arguments */\n Instantiation,\n /** Activate plugins by calling their init/setup methods */\n Activation,\n /** Mark seedcord as ready and start handling interactions */\n Ready\n}\n\n/** Define the order of phases */\nconst PHASE_ORDER: StartupPhase[] = [\n StartupPhase.Validation,\n StartupPhase.Discovery,\n StartupPhase.Registration,\n StartupPhase.Configuration,\n StartupPhase.Instantiation,\n StartupPhase.Activation,\n StartupPhase.Ready\n];\n\n/**\n * Strict-event-emitter payload map for coordinated startup phases.\n */\nexport type CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;\n\n/**\n * Manages bot startup lifecycle with ordered phases\n *\n * Coordinates initialization of all bot components in a predictable sequence.\n * Tasks are executed within their designated phases to ensure proper dependency order.\n */\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {\n private isStartingUp = false;\n private hasStarted = false;\n\n public constructor() {\n super('CoordinatedStartup', PHASE_ORDER, StartupPhase);\n }\n\n /**\n * Adds a task to a specific startup phase with timeout.\n *\n * @param phase - The startup phase from {@link StartupPhase}\n * @param taskName - Unique identifier for the task\n * @param task - Async function to execute\n * @param timeoutMs - Task timeout in milliseconds. {@default `10000`}\n */\n public override addTask(phase: StartupPhase, taskName: string, task: () => Promise<void>, timeoutMs = 10000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n protected canAddTask(): boolean {\n if (this.hasStarted) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);\n }\n\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);\n }\n\n return true;\n }\n\n protected canRemoveTask(): boolean {\n if (this.isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);\n }\n\n return true;\n }\n\n protected getTaskType(): string {\n return 'startup';\n }\n\n protected async executeTasksInPhase(\n phase: StartupPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n /**\n * Executes the coordinated startup sequence.\n *\n * Runs all registered tasks across startup phases in the correct order.\n * Each phase completes before the next phase begins. Tasks within a phase\n * are executed sequentially to maintain predictable initialization.\n *\n * @returns Promise that resolves when startup is complete\n * @throws An {@link Error} If startup fails or is called multiple times\n * @example\n * ```typescript\n * const startup = new CoordinatedStartup();\n * startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);\n * await startup.run();\n * ```\n */\n public async run(): Promise<void> {\n if (this.hasStarted) {\n this.logger.warn('Startup sequence has already completed');\n return;\n }\n\n if (this.isStartingUp) {\n this.logger.warn('Startup sequence already in progress');\n return;\n }\n\n this.isStartingUp = true;\n this.logger.info(`${chalk.bold.green('Starting')} coordinated startup sequence`);\n this.emit('startup:start');\n\n try {\n for (const phase of PHASE_ORDER) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false mid-loop from the cli\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted');\n return;\n }\n await this.runPhase(phase);\n }\n\n this.hasStarted = true;\n this.logger.info(`${chalk.bold.green('Coordinated startup completed')} successfully`);\n this.emit('startup:complete');\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false before this catch runs\n if (!this.isStartingUp) {\n this.logger.warn('Startup sequence aborted during error handling');\n return;\n }\n this.logger.error(`${chalk.bold.red('Coordinated startup failed')}`);\n this.emit('startup:error', error);\n throw error;\n } finally {\n this.isStartingUp = false;\n }\n }\n\n protected override async runTaskWithTimeout(phase: StartupPhase, task: LifecycleTask): Promise<void> {\n this.logger.info(\n `${chalk.italic('Starting')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n await Promise.race([\n task.task(),\n new Promise<void>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));\n }, task.timeout);\n })\n ]);\n\n this.logger.info(\n `${chalk.italic('Completed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}`\n );\n } catch (error) {\n if (!this.isStartingUp) {\n return;\n }\n\n this.logger.error(\n `${chalk.italic('Failed')} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(StartupPhase[phase])}:`,\n error\n );\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Aborts the startup sequence if it is currently running.\n */\n public abort(): void {\n if (this.isStartingUp) {\n this.isStartingUp = false;\n this.logger.warn('Aborting coordinated startup sequence');\n }\n }\n\n /**\n * Check if startup has completed\n */\n public get isReady(): boolean {\n return this.hasStarted;\n }\n\n /**\n * Check if startup is currently running\n */\n public get isRunning(): boolean {\n return this.isStartingUp;\n }\n}\n","export * from './RateLimiter';\nexport * from './HealthCheck';\nexport * from './Lifecycle';\nexport * from './Logger';\nexport * from './StrictEventEmitter';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seedcord/services",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.8.1",
|
|
4
|
+
"version": "0.8.2-next.1",
|
|
5
5
|
"description": "Services for Seedcord packages",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
"strip-ansi": "^7.2.0",
|
|
37
37
|
"winston": "^3.19.0",
|
|
38
38
|
"winston-transport": "^4.9.0",
|
|
39
|
-
"@seedcord/errors": "0.2.
|
|
40
|
-
"@seedcord/types": "0.7.0",
|
|
41
|
-
"@seedcord/utils": "0.
|
|
39
|
+
"@seedcord/errors": "0.2.1-next.1",
|
|
40
|
+
"@seedcord/types": "0.7.1-next.0",
|
|
41
|
+
"@seedcord/utils": "0.7.0-next.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"type-fest": "^5.7.0",
|
|
45
|
-
"@seedcord/eslint-config": "1.4.
|
|
46
|
-
"@seedcord/tsconfig": "2.0.0",
|
|
47
|
-
"@seedcord/tsdown-config": "2.0.0"
|
|
45
|
+
"@seedcord/eslint-config": "1.4.3-next.0",
|
|
46
|
+
"@seedcord/tsconfig": "2.0.1-next.0",
|
|
47
|
+
"@seedcord/tsdown-config": "2.0.1-next.0"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"test:watch": "vitest dev",
|
|
63
63
|
"coverage": "vitest run --coverage"
|
|
64
64
|
},
|
|
65
|
-
"readme": "<p align=\"center\">\n <img src=\"https://cdn.seedcord.org/assets/banner.webp\" alt=\"seedcord\" width=\"100%\" />\n</p>\n\n---\n\n_This repository is a work in progress._\n\n- There are no stable releases yet but changes are being made actively.\n-
|
|
65
|
+
"readme": "<p align=\"center\">\n <img src=\"https://cdn.seedcord.org/assets/banner.webp\" alt=\"seedcord\" width=\"100%\" />\n</p>\n\n---\n\n_This repository is a work in progress._\n\n- There are no stable releases yet but changes are being made actively.\n- Until a major v1.0.0 release for seedcord, expect breaking changes in minor versions.\n- Documentation will come soon as well!\n\nIf you'd like to try it out, you can check out the code in `mock`\n"
|
|
66
66
|
}
|