@seedcord/core 0.1.0-next.5 → 0.1.0-next.6
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/dist/{AutocompleteOptions-eB87SZ4a.d.mts → AutocompleteOptions-eKhIe6Qt.d.mts} +9 -16
- package/dist/Plugin-C2-VHrsX.cjs +114 -0
- package/dist/Plugin-C2-VHrsX.cjs.map +1 -0
- package/dist/Plugin-CNyRKDHT.d.mts +226 -0
- package/dist/Plugin-PcdPjAzc.mjs +97 -0
- package/dist/Plugin-PcdPjAzc.mjs.map +1 -0
- package/dist/devChannel-CQU7hq7Q.mjs +14 -0
- package/dist/devChannel-CQU7hq7Q.mjs.map +1 -0
- package/dist/devChannel-CoCmAVDx.cjs +26 -0
- package/dist/devChannel-CoCmAVDx.cjs.map +1 -0
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.cjs +7 -17
- package/dist/internal.index.cjs.map +1 -1
- package/dist/internal.index.d.mts +3 -2
- package/dist/internal.index.mjs +4 -15
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.cjs +645 -0
- package/dist/node-internal.index.cjs.map +1 -0
- package/dist/node-internal.index.d.cts +1 -0
- package/dist/{node.index.d.mts → node-internal.index.d.mts} +104 -143
- package/dist/node-internal.index.mjs +636 -0
- package/dist/node-internal.index.mjs.map +1 -0
- package/dist/{permissions-DQL5ViZn.cjs → permissions-DSW_EZfN.cjs} +19 -1
- package/dist/{permissions-DQL5ViZn.cjs.map → permissions-DSW_EZfN.cjs.map} +1 -1
- package/dist/{permissions-SokZLJMj.mjs → permissions-RVCBhL2n.mjs} +14 -2
- package/dist/{permissions-SokZLJMj.mjs.map → permissions-RVCBhL2n.mjs.map} +1 -1
- package/dist/phases-BfpAG6w3.mjs +31 -0
- package/dist/phases-BfpAG6w3.mjs.map +1 -0
- package/dist/phases-C_BGNyVo.cjs +43 -0
- package/dist/phases-C_BGNyVo.cjs.map +1 -0
- package/dist/plugin.index.cjs +4 -0
- package/dist/plugin.index.d.cts +1 -0
- package/dist/plugin.index.d.mts +3 -0
- package/dist/plugin.index.mjs +3 -0
- package/package.json +24 -13
- package/dist/node.index.cjs +0 -521
- package/dist/node.index.cjs.map +0 -1
- package/dist/node.index.d.cts +0 -1
- package/dist/node.index.mjs +0 -513
- package/dist/node.index.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-internal.index.cjs","names":["SeedcordError","SeedcordErrorCode","TypedEventEmitter","Logger","SeedcordError","SeedcordErrorCode","PHASE_ORDER","ShutdownPhase","StartupPhase","SeedcordError","SeedcordErrorCode","Logger","paint","TypedEventEmitter","Logger","SeedcordError","SeedcordErrorCode","resolvedLifecycleSpecOf"],"sources":["../src/node/Lifecycle/withTimeout.ts","../src/node/Lifecycle/CoordinatedLifecycle.ts","../src/node/Lifecycle/CoordinatedShutdown.ts","../src/node/Lifecycle/CoordinatedStartup.ts","../src/node/HealthCheck.ts","../src/node/Pluggable.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nasync function raceTimer(\n work: Promise<unknown>,\n timeoutMs: number,\n onTimeout: (resolve: () => void, reject: (error: unknown) => void) => void\n): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n work,\n new Promise<void>((resolve, reject) => {\n timer = setTimeout(() => onTimeout(resolve, reject), timeoutMs);\n })\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/** @internal rejects with a `LifecycleTaskTimeout` when `run` outlasts the bound. */\nexport function withTimeout(name: string, run: () => Promise<void>, timeoutMs: number): Promise<void> {\n return raceTimer(run(), timeoutMs, (_, reject) => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [name, timeoutMs]));\n });\n}\n\n/** @internal resolves when `work` settles or the bound elapses, whichever is first. */\nexport function settleWithin(work: Promise<unknown>, timeoutMs: number): Promise<void> {\n return raceTimer(work, timeoutMs, (resolve) => resolve());\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 { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { TypedEventEmitter } from '@seedcord/event-emitter';\nimport { Logger } from '@seedcord/logger';\nimport chalk from 'chalk';\n\nimport { withTimeout } from './withTimeout';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { EventMap } from '@seedcord/event-emitter';\n\n/**\n * Base for the startup and shutdown coordinators. Runs phase-ordered tasks off a per-phase map.\n *\n * @internal\n */\nexport abstract class CoordinatedLifecycle<\n TPhase extends number,\n TEvents extends EventMap<TEvents>\n> extends TypedEventEmitter<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 task to `phase`. Tasks run in phase order, each bounded by `timeoutMs`.\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 */\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 protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.debug(`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 // raw phase name here. 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 }\n\n this.logger.info(\n `Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green('completed successfully')}`\n );\n\n this.emitPhase(phase, 'complete');\n }\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 try {\n await withTimeout(task.name, task.task, task.timeout);\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 }\n }\n\n // TS can't correlate a template-literal key with the generic TEvents\n private emitPhase(phase: TPhase, action: 'start' | 'complete'): void {\n this.emitSafeRaw(`phase:${phase}:${action}`);\n }\n\n protected override onListenerError(error: unknown, event: string | symbol): void {\n this.logger.error(`listener for ${String(event)} threw`, error);\n }\n\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 { ShutdownPhase } from '@src/lifecycle/phases';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\nconst PHASE_ORDER: ShutdownPhase[] = [\n ShutdownPhase.Unbind,\n ShutdownPhase.Drain,\n ShutdownPhase.Disconnect,\n ShutdownPhase.Logout\n];\n\ntype CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;\n\n// gives the logger's file sink time to flush before process.exit\nconst LOG_FLUSH_DELAY_MS = 3000;\n\n/**\n * Runs registered shutdown tasks across `ShutdownPhase` in order. Registers SIGINT and SIGTERM\n * handlers. Tasks within a phase run in parallel.\n */\nexport class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {\n private isShuttingDown = false;\n private hasShutdown = false;\n private exitCode = 0;\n private onSigTerm: (() => void) | null = null;\n private onSigInt: (() => void) | null = null;\n private startupGate?: Promise<void>;\n\n public constructor() {\n super('Shutdown', PHASE_ORDER, ShutdownPhase);\n\n this.registerSignalHandlers();\n }\n\n protected canAddTask(): boolean {\n return true;\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 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 /** @internal */\n public 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 /** @internal run() awaits this so a shutdown during startup waits for startup to register its dispose tasks */\n public gateOnStartup(settled: Promise<void>): void {\n this.startupGate = settled;\n }\n\n /** Adds a shutdown-phase task. @param timeoutMs - Task timeout in ms. {@default 5000} */\n public override addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs = 5000): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n /** Removes a shutdown-phase task, returning whether one was removed. */\n public override removeTask(phase: ShutdownPhase, taskName: string): boolean {\n return super.removeTask(phase, taskName);\n }\n\n /**\n * Runs registered tasks across shutdown phases in `ShutdownPhase` order.\n * Tasks within each phase run in parallel.\n *\n * @param exitCode - Process exit code. {@default `0`}\n * @param exitProcess - Whether to exit the process after shutdown. {@default `true`}\n * @example\n * ```typescript\n * shutdown.addTask(ShutdownPhase.Disconnect, 'database', () => db.disconnect(), 5000);\n * await shutdown.run(0);\n * ```\n */\n public async run(exitCode = 0, exitProcess = true): Promise<void> {\n this.removeSignalHandlers();\n\n // a dev-mode run leaves the process alive, so guard against a re-run that re-executes tasks\n if (this.hasShutdown || this.isShuttingDown) {\n this.logger.warn('Shutdown sequence already ran or is 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.emitSafe('shutdown:start');\n\n try {\n if (this.startupGate) await this.startupGate;\n const failures: unknown[] = [];\n for (const phase of PHASE_ORDER) {\n // run every phase so a mid-shutdown failure still attempts the later teardowns\n try {\n await this.runPhase(phase);\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length > 0) this.emitFailures(failures);\n else {\n this.logger.info(`${chalk.bold.green('Coordinated shutdown completed')} successfully`);\n this.emitSafe('shutdown:complete');\n }\n } finally {\n this.hasShutdown = true;\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 private emitFailures(failures: unknown[]): void {\n this.logger.error(`${chalk.bold.red('Coordinated shutdown failed')}`);\n const error = failures.length === 1 ? failures[0] : new AggregateError(failures, 'shutdown phase failures');\n this.emitSafe('shutdown:error', error);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport chalk from 'chalk';\n\nimport { StartupPhase } from '@src/lifecycle/phases';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\nimport { withTimeout } from './withTimeout';\n\nimport type { LifecycleTask, PhaseEventMap } from './LifecycleTypes';\nimport type { UnionToTuple } from 'type-fest';\n\nconst PHASE_ORDER: StartupPhase[] = [StartupPhase.Configuration, StartupPhase.Login, StartupPhase.Ready];\n\ntype CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;\n\n/**\n * Runs registered startup tasks across `StartupPhase` in order. Tasks within a phase run in parallel.\n */\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {\n private isStartingUp = false;\n private hasStarted = false;\n\n public constructor() {\n super('Startup', PHASE_ORDER, StartupPhase);\n }\n\n /** Adds a startup-phase task. @param timeoutMs - Task timeout in ms. {@default 10000} */\n public override addTask(\n phase: StartupPhase,\n taskName: string,\n task: () => Promise<void>,\n timeoutMs = 10_000\n ): 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 * Runs the registered tasks across startup phases in `StartupPhase` order. Each phase completes\n * before the next begins, and tasks within a phase run concurrently.\n *\n * @throws {@link SeedcordError} if a startup task throws.\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.emitSafe('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.emitSafe('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.emitSafe('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 try {\n await withTimeout(task.name, task.task, task.timeout);\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 }\n }\n\n /**\n * Aborts the startup sequence if it is currently running.\n */\n public abort(): void {\n if (!this.isStartingUp) return;\n\n this.isStartingUp = false;\n this.logger.warn('Aborting coordinated startup sequence');\n }\n\n public get isReady(): boolean {\n return this.hasStarted;\n }\n\n public get isRunning(): boolean {\n return this.isStartingUp;\n }\n}\n","import { createServer } from 'node:http';\n\nimport { Logger, paint } from '@seedcord/logger';\n\nimport { ShutdownPhase } from '@src/lifecycle/phases';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig, HealthCheckOption } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\n\nconst HTTP_OK = 200;\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\nconst DEFAULT_HEALTH_CHECK_PATH = '/health';\n\n/**\n * HTTP server answering `GET` on the configured path with a JSON `{ status, timestamp }` body.\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.Drain, 'stop-healthcheck-server', () => this.stop());\n }\n\n /**\n * Resolves the `healthCheck` option. `undefined` and `true` build the defaults, an object uses\n * its config, `false` returns `undefined`.\n */\n public static fromOption(shutdown: CoordinatedShutdown, option?: HealthCheckOption): HealthCheck | undefined {\n if (option === false) return undefined;\n return new HealthCheck(shutdown, typeof option === 'object' ? option : undefined);\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 // the server binds all interfaces, the log shows an address a browser can open\n const address = this.host ?? 'localhost';\n this.logger.info(\n `${paint.mint.bold('✓')} Health check server listening on ${paint.sky(`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 * @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 // settle only in the callback, a 'close'-event resolve would swallow the callback's error\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(paint.coral.bold('Health check server stopped'));\n resolve();\n });\n });\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { TypedEventEmitter } from '@seedcord/event-emitter';\nimport { Logger } from '@seedcord/logger';\n\nimport { StartupPhase } from '@src/lifecycle/phases';\nimport { finalizePluginContext } from '@src/plugin/context';\nimport { resolvedLifecycleSpecOf } from '@src/plugin/Plugin';\n\nimport { withTimeout } from './Lifecycle/withTimeout';\n\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '@node/Lifecycle/CoordinatedShutdown';\nimport type { CoordinatedStartup } from '@node/Lifecycle/CoordinatedStartup';\nimport type { EventMap, NoEvents } from '@seedcord/event-emitter';\nimport type { Config, IRateLimiter, Store } from '@seedcord/types';\nimport type { ShutdownPhase } from '@src/lifecycle/phases';\nimport type { PluginCapabilities, PluginContext, StoredPluginContext } from '@src/plugin/context';\nimport type { PluginArgs, PluginCtor, Plugin } from '@src/plugin/Plugin';\n\ninterface Attachment {\n readonly key: string;\n readonly instance: Plugin;\n}\n\n/**\n * Base class for objects that can have plugins attached.\n *\n * Plugins are attached during configuration and initialized during startup, sequentially in attach\n * order within each phase. Not constructed directly, the host is a transport `Seedcord` class.\n */\nexport abstract class Pluggable<TPluggableEvents extends EventMap<TPluggableEvents> = NoEvents>\n extends TypedEventEmitter<TPluggableEvents>\n implements CoreBase\n{\n public abstract readonly config: Config;\n public abstract readonly rateLimiter: IRateLimiter;\n\n protected isInitialized = false;\n protected startFailed = false;\n protected readonly shutdown: CoordinatedShutdown;\n protected readonly startup: CoordinatedStartup;\n protected readonly plugins: Plugin[] = [];\n\n private readonly pluginLogger = new Logger('Plugins');\n\n private readonly attachments: Attachment[] = [];\n private readonly completedInits = new Set<Attachment>();\n private readonly disposePhases = new Set<ShutdownPhase>();\n private pluginTasksRegistered = false;\n\n private static isInstantiated = false;\n private static liveShutdown?: CoordinatedShutdown | undefined;\n\n constructor(shutdown: CoordinatedShutdown, startup: CoordinatedStartup) {\n if (Pluggable.isInstantiated) {\n // the caller constructed this shutdown, release its fresh handler pair before rejecting\n shutdown.removeSignalHandlers();\n throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);\n }\n\n super();\n Pluggable.isInstantiated = true;\n Pluggable.liveShutdown = shutdown;\n this.shutdown = shutdown;\n this.startup = startup;\n }\n\n /** @internal */\n protected async init(): Promise<this> {\n if (this.isInitialized) return this;\n // a rerun after a failed startup would re-init the rolled-back plugins\n if (this.startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);\n\n this.registerPluginTasks();\n\n const startupSettled: PromiseWithResolvers<void> = Promise.withResolvers();\n this.shutdown.gateOnStartup(startupSettled.promise);\n\n try {\n await this.startup.run();\n } catch (caught) {\n await this.rollback();\n throw caught;\n } finally {\n startupSettled.resolve();\n }\n\n this.isInitialized = true;\n return this;\n }\n\n // transports override to inject client/token/rest\n protected pluginCapabilities(): PluginCapabilities {\n return {};\n }\n\n protected pluginStore(): Store<'charge'> {\n // justified: both assignment branches of rateLimiter are Store<'charge'> at the hosts\n return this.config.store ?? (this.rateLimiter as Store<'charge'>);\n }\n\n /** @internal releases the signal handlers and clears the singleton so the next host can construct */\n protected static reset(): void {\n Pluggable.liveShutdown?.removeSignalHandlers();\n Pluggable.liveShutdown = undefined;\n Pluggable.isInstantiated = false;\n }\n\n /**\n * Attaches a plugin to this instance.\n *\n * The plugin initializes during startup, after every plugin attached before it, and becomes\n * available as a property on `core`. Augment the transport's `Core` interface with the plugin\n * type for intellisense.\n *\n * @typeParam Key - The property name for accessing the plugin\n * @typeParam Ctor - The plugin constructor type\n * @param key - Property name to access the plugin instance\n * @param Plugin - Plugin constructor class\n * @param args - Additional arguments to pass to the plugin constructor\n * @returns This instance with the plugin attached as a typed property\n * @throws A **SeedcordError** When called after initialization or if key already exists\n * @example\n * ```typescript\n * seedcord.attach('db', Mongo, { uri: 'mongodb://...', name: 'seedcord', dir: ... })\n * ```\n */\n public attach<Key extends string, Ctor extends PluginCtor>(\n this: this,\n key: Key,\n Plugin: Ctor,\n ...args: PluginArgs<Ctor>\n ): this & Record<Key, InstanceType<Ctor>> {\n if (this.isInitialized) {\n throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);\n }\n if (key in this) {\n throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);\n }\n\n const instance = new Plugin(this, ...args);\n this.plugins.push(instance);\n this.attachments.push({ key, instance });\n this.finalizeContext(key, instance);\n\n return Object.assign(this, { [key]: instance } as Record<Key, InstanceType<Ctor>>);\n }\n\n private finalizeContext(key: string, instance: Plugin): void {\n const caps = this.pluginCapabilities();\n const readToken = (): string | undefined => this.pluginCapabilities().token;\n const ctx: StoredPluginContext = {\n logger: new Logger(key),\n config: this.config,\n store: this.pluginStore(),\n client: caps.client,\n // http sets the token during Ready, read it live\n get token(): string | undefined {\n return readToken();\n },\n rest: caps.rest\n };\n finalizePluginContext(instance, ctx as PluginContext);\n }\n\n // one combined task per phase keeps plugin inits sequential while the phase's other tasks run concurrently\n private registerPluginTasks(): void {\n if (this.pluginTasksRegistered) return;\n this.pluginTasksRegistered = true;\n\n const groups = new Map<StartupPhase, Attachment[]>();\n for (const attachment of this.attachments) {\n const phase = resolvedLifecycleSpecOf(attachment.instance).init.phase;\n const group = groups.get(phase) ?? [];\n group.push(attachment);\n groups.set(phase, group);\n }\n\n for (const [phase, group] of groups) {\n // Ready inits run in the combined Ready task, before the ready hooks\n if (phase === StartupPhase.Ready) continue;\n const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);\n this.startup.addTask(phase, 'Plugins:init', () => this.runInits(group), budget);\n }\n\n this.registerReadyTask(groups.get(StartupPhase.Ready) ?? []);\n }\n\n private async runInits(group: readonly Attachment[]): Promise<void> {\n for (const attachment of group) {\n const { key, instance } = attachment;\n const spec = resolvedLifecycleSpecOf(instance);\n\n instance.logger.utils.initialization(key, 'start');\n await withTimeout(`Plugin:${key}`, () => instance.init(), spec.init.timeout);\n instance.logger.utils.initialization(key, 'end');\n\n this.completedInits.add(attachment);\n if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);\n }\n }\n\n private registerReadyTask(readyInits: readonly Attachment[]): void {\n const steps = this.attachments.reduce<{ key: string; run: () => Promise<void>; timeout: number }[]>(\n (acc, a) => {\n const ready = a.instance.ready?.bind(a.instance);\n if (ready) {\n acc.push({\n key: a.key,\n run: ready,\n timeout: resolvedLifecycleSpecOf(a.instance).ready.timeout\n });\n }\n return acc;\n },\n []\n );\n if (readyInits.length === 0 && steps.length === 0) return;\n\n const budget =\n readyInits.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0) +\n steps.reduce((sum, step) => sum + step.timeout, 0);\n this.startup.addTask(\n StartupPhase.Ready,\n 'Plugins',\n async () => {\n await this.runInits(readyInits);\n for (const step of steps) {\n await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);\n }\n },\n budget\n );\n }\n\n private registerDisposeTask(phase: ShutdownPhase): void {\n if (this.disposePhases.has(phase)) return;\n this.disposePhases.add(phase);\n\n // registered once per phase, later inits of that phase run in the same task\n const budget = this.attachments.reduce((sum, a) => {\n const spec = resolvedLifecycleSpecOf(a.instance);\n return a.instance.dispose && spec.dispose.phase === phase ? sum + spec.dispose.timeout : sum;\n }, 0);\n this.shutdown.addTask(phase, 'Plugins:dispose', () => this.runDisposals(phase), budget);\n }\n\n private async runDisposals(phase: ShutdownPhase): Promise<void> {\n const failures: unknown[] = [];\n // keep disposing the rest even when one fails\n await this.disposeCompleted(phase, (caught) => failures.push(caught));\n if (failures.length === 1) throw failures[0];\n if (failures.length > 1) throw new AggregateError(failures, 'plugin dispose failures');\n }\n\n // reverse attach order\n private async disposeCompleted(\n phase: ShutdownPhase | undefined,\n onError: (caught: unknown) => void\n ): Promise<void> {\n for (const attachment of [...this.attachments].reverse()) {\n if (!this.completedInits.has(attachment)) continue;\n const dispose = attachment.instance.dispose?.bind(attachment.instance);\n if (!dispose) continue;\n const spec = resolvedLifecycleSpecOf(attachment.instance);\n if (phase !== undefined && spec.dispose.phase !== phase) continue;\n\n try {\n await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout);\n } catch (caught) {\n onError(caught);\n }\n }\n }\n\n private async rollback(): Promise<void> {\n this.startFailed = true;\n // log rollback dispose failures so they do not hide the startup error\n await this.disposeCompleted(undefined, (caught) => this.pluginLogger.warn('rollback dispose failed', caught));\n // prevents re-dispose when shutdown runs after rollback\n this.completedInits.clear();\n }\n\n /** @internal */\n override removeListener<TEventKey extends Extract<keyof TPluggableEvents, string | symbol>>(\n event: TEventKey,\n listener: (...args: TPluggableEvents[TEventKey]) => void\n ): this {\n return super.removeListener(event, listener);\n }\n\n /** @internal */\n override removeAllListeners(event?: Extract<keyof TPluggableEvents, string | symbol>): this {\n return super.removeAllListeners(event);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from './node-internal.index.d.mts'
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { t as IsNever } from "./is-never-Ddaba1Su.mjs";
|
|
2
|
+
import { c as PluginCapabilities, f as CoreBase, i as PluginCtor, n as Plugin, o as ShutdownPhase, r as PluginArgs, s as StartupPhase } from "./Plugin-CNyRKDHT.mjs";
|
|
2
3
|
import { Logger } from "@seedcord/logger";
|
|
3
|
-
import { EventMap, TypedEventEmitter } from "@seedcord/event-emitter";
|
|
4
|
-
import { HealthCheckConfig, TypedExclude } from "@seedcord/types";
|
|
4
|
+
import { EventMap, NoEvents, TypedEventEmitter } from "@seedcord/event-emitter";
|
|
5
|
+
import { Config, HealthCheckConfig, HealthCheckOption, IRateLimiter, Store, TypedExclude } from "@seedcord/types";
|
|
5
6
|
|
|
6
7
|
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/characters.d.ts
|
|
7
8
|
/**
|
|
@@ -2124,34 +2125,24 @@ type Hundreds = IntClosedRange<100, 900, 100>;
|
|
|
2124
2125
|
type IntClosedRange<Start extends number, End extends number, Skip extends number = 1> = IntRange<Start, Sum<End, 1>, Skip>;
|
|
2125
2126
|
//#endregion
|
|
2126
2127
|
//#region src/node/Lifecycle/LifecycleTypes.d.ts
|
|
2127
|
-
/** Actions that can occur during lifecycle phases */
|
|
2128
2128
|
type LifecycleAction = 'start' | 'complete' | 'error';
|
|
2129
|
-
/**
|
|
2130
|
-
* Creates event names for lifecycle managers with phase numbers and actions
|
|
2131
|
-
* @typeParam Prefix - The prefix string for lifecycle events
|
|
2132
|
-
* @typeParam Phases - Array of phase numbers to generate events for
|
|
2133
|
-
*/
|
|
2134
2129
|
type PhaseEvents<Prefix extends string, Phases extends number[]> = `phase:${IntClosedRange<1, Phases['length']>}:${TypedExclude<LifecycleAction, 'error'>}` | `${Prefix}:${LifecycleAction}`;
|
|
2135
2130
|
/**
|
|
2136
2131
|
* Event payload map for a lifecycle. The phase and prefix `start`/`complete` events have an empty
|
|
2137
2132
|
* payload, and the `${Prefix}:error` event's payload is the thrown error.
|
|
2138
|
-
* @typeParam Prefix - The prefix string for lifecycle events
|
|
2139
|
-
* @typeParam Phases - Array of phase numbers to generate events for
|
|
2140
2133
|
*/
|
|
2141
2134
|
type PhaseEventMap<Prefix extends string, Phases extends number[]> = { [K in PhaseEvents<Prefix, Phases>]: K extends `${string}:error` ? readonly [error: unknown] : readonly [] };
|
|
2142
|
-
/** Base interface for a lifecycle task */
|
|
2143
2135
|
interface LifecycleTask {
|
|
2144
|
-
/** Name of the task */
|
|
2145
2136
|
name: string;
|
|
2146
|
-
/** Function to execute the task */
|
|
2147
2137
|
task: () => Promise<void>;
|
|
2148
|
-
/** Timeout for the task */
|
|
2149
2138
|
timeout: number;
|
|
2150
2139
|
}
|
|
2151
2140
|
//#endregion
|
|
2152
2141
|
//#region src/node/Lifecycle/CoordinatedLifecycle.d.ts
|
|
2153
2142
|
/**
|
|
2154
|
-
*
|
|
2143
|
+
* Base for the startup and shutdown coordinators. Runs phase-ordered tasks off a per-phase map.
|
|
2144
|
+
*
|
|
2145
|
+
* @internal
|
|
2155
2146
|
*/
|
|
2156
2147
|
declare abstract class CoordinatedLifecycle<TPhase extends number, TEvents extends EventMap<TEvents>> extends TypedEventEmitter<TEvents> {
|
|
2157
2148
|
protected readonly phaseOrder: TPhase[];
|
|
@@ -2160,21 +2151,12 @@ declare abstract class CoordinatedLifecycle<TPhase extends number, TEvents exten
|
|
|
2160
2151
|
protected readonly tasksMap: Map<TPhase, LifecycleTask[]>;
|
|
2161
2152
|
protected constructor(loggerName: string, phaseOrder: TPhase[], phaseEnum: Record<number, string>);
|
|
2162
2153
|
/**
|
|
2163
|
-
* Adds a
|
|
2164
|
-
*
|
|
2165
|
-
* Tasks are executed in phase order during lifecycle operations.
|
|
2166
|
-
* Each task has a timeout to prevent hanging operations.
|
|
2154
|
+
* Adds a task to `phase`. Tasks run in phase order, each bounded by `timeoutMs`.
|
|
2167
2155
|
*
|
|
2168
2156
|
* @param phase - The lifecycle phase to add the task to
|
|
2169
|
-
* @param taskName - Unique name for the task
|
|
2157
|
+
* @param taskName - Unique name for the task, used for logging and removal
|
|
2170
2158
|
* @param task - Async function to execute during the phase
|
|
2171
2159
|
* @param timeoutMs - Maximum time allowed for task execution in milliseconds
|
|
2172
|
-
* @example
|
|
2173
|
-
* ```typescript
|
|
2174
|
-
* lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {
|
|
2175
|
-
* await database.connect();
|
|
2176
|
-
* }, 10000);
|
|
2177
|
-
* ```
|
|
2178
2160
|
*/
|
|
2179
2161
|
addTask(phase: TPhase, taskName: string, task: () => Promise<void>, timeoutMs: number): void;
|
|
2180
2162
|
/**
|
|
@@ -2185,13 +2167,7 @@ declare abstract class CoordinatedLifecycle<TPhase extends number, TEvents exten
|
|
|
2185
2167
|
* @returns True if the task was found and removed, false otherwise
|
|
2186
2168
|
*/
|
|
2187
2169
|
removeTask(phase: TPhase, taskName: string): boolean;
|
|
2188
|
-
/**
|
|
2189
|
-
* Run all tasks in a specific phase
|
|
2190
|
-
*/
|
|
2191
2170
|
protected runPhase(phase: TPhase): Promise<void>;
|
|
2192
|
-
/**
|
|
2193
|
-
* Run a single task with timeout
|
|
2194
|
-
*/
|
|
2195
2171
|
protected runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void>;
|
|
2196
2172
|
private emitPhase;
|
|
2197
2173
|
protected onListenerError(error: unknown, event: string | symbol): void;
|
|
@@ -2202,144 +2178,68 @@ declare abstract class CoordinatedLifecycle<TPhase extends number, TEvents exten
|
|
|
2202
2178
|
}
|
|
2203
2179
|
//#endregion
|
|
2204
2180
|
//#region src/node/Lifecycle/CoordinatedShutdown.d.ts
|
|
2205
|
-
/**
|
|
2206
|
-
* Shutdown phases for coordinated application shutdown.
|
|
2207
|
-
*/
|
|
2208
|
-
declare enum ShutdownPhase {
|
|
2209
|
-
/** Stop accepting new requests/interactions */
|
|
2210
|
-
StopAcceptingRequests = 1,
|
|
2211
|
-
/** Stop background services (health checks, etc.) */
|
|
2212
|
-
StopServices = 2,
|
|
2213
|
-
/** Disconnect from external resources (database, APIs) */
|
|
2214
|
-
ExternalResources = 3,
|
|
2215
|
-
/** Disconnect from Discord */
|
|
2216
|
-
DiscordCleanup = 4,
|
|
2217
|
-
/** Final cleanup tasks */
|
|
2218
|
-
FinalCleanup = 5
|
|
2219
|
-
}
|
|
2220
|
-
/**
|
|
2221
|
-
* Strict-event-emitter payload map for coordinated shutdown phases.
|
|
2222
|
-
*/
|
|
2223
2181
|
type CoordinatedShutdownEvents = PhaseEventMap<'shutdown', UnionToTuple<ShutdownPhase>>;
|
|
2224
2182
|
/**
|
|
2225
|
-
*
|
|
2226
|
-
*
|
|
2227
|
-
* It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.
|
|
2228
|
-
* Tasks can be added or removed dynamically, and each task has an associated timeout.
|
|
2183
|
+
* Runs registered shutdown tasks across `ShutdownPhase` in order. Registers SIGINT and SIGTERM
|
|
2184
|
+
* handlers. Tasks within a phase run in parallel.
|
|
2229
2185
|
*/
|
|
2230
2186
|
declare class CoordinatedShutdown extends CoordinatedLifecycle<ShutdownPhase, CoordinatedShutdownEvents> {
|
|
2231
|
-
private readonly isShutdownEnabled;
|
|
2232
2187
|
private isShuttingDown;
|
|
2188
|
+
private hasShutdown;
|
|
2233
2189
|
private exitCode;
|
|
2234
2190
|
private onSigTerm;
|
|
2235
2191
|
private onSigInt;
|
|
2236
|
-
|
|
2192
|
+
private startupGate?;
|
|
2193
|
+
constructor();
|
|
2237
2194
|
protected canAddTask(): boolean;
|
|
2238
2195
|
protected canRemoveTask(): boolean;
|
|
2239
2196
|
protected getTaskType(): string;
|
|
2240
2197
|
protected executeTasksInPhase(phase: ShutdownPhase, tasks: LifecycleTask[]): Promise<PromiseSettledResult<void>[]>;
|
|
2241
2198
|
private registerSignalHandlers;
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
* @param taskName - Unique identifier for the task
|
|
2248
|
-
* @param task - Async function to execute
|
|
2249
|
-
* @param timeoutMs - Task timeout in milliseconds. {@default `5000`}
|
|
2250
|
-
*/
|
|
2199
|
+
/** @internal */
|
|
2200
|
+
removeSignalHandlers(): void;
|
|
2201
|
+
/** @internal run() awaits this so a shutdown during startup waits for startup to register its dispose tasks */
|
|
2202
|
+
gateOnStartup(settled: Promise<void>): void;
|
|
2203
|
+
/** Adds a shutdown-phase task. @param timeoutMs - Task timeout in ms. {@default 5000} */
|
|
2251
2204
|
addTask(phase: ShutdownPhase, taskName: string, task: () => Promise<void>, timeoutMs?: number): void;
|
|
2252
|
-
/**
|
|
2253
|
-
* Removes a task from a specific shutdown phase.
|
|
2254
|
-
*
|
|
2255
|
-
* @param phase - The shutdown phase to remove from
|
|
2256
|
-
* @param taskName - Name of the task to remove
|
|
2257
|
-
* @returns True if task was found and removed
|
|
2258
|
-
*/
|
|
2205
|
+
/** Removes a shutdown-phase task, returning whether one was removed. */
|
|
2259
2206
|
removeTask(phase: ShutdownPhase, taskName: string): boolean;
|
|
2260
2207
|
/**
|
|
2261
|
-
*
|
|
2262
|
-
*
|
|
2263
|
-
* Runs all registered tasks across shutdown phases in reverse order.
|
|
2264
|
-
* Tasks within each phase are executed in parallel for faster shutdown.
|
|
2265
|
-
* Process exits with the specified code when complete.
|
|
2208
|
+
* Runs registered tasks across shutdown phases in `ShutdownPhase` order.
|
|
2209
|
+
* Tasks within each phase run in parallel.
|
|
2266
2210
|
*
|
|
2267
2211
|
* @param exitCode - Process exit code. {@default `0`}
|
|
2268
2212
|
* @param exitProcess - Whether to exit the process after shutdown. {@default `true`}
|
|
2269
|
-
* @returns Promise that resolves when shutdown is complete
|
|
2270
2213
|
* @example
|
|
2271
2214
|
* ```typescript
|
|
2272
|
-
* shutdown.addTask(ShutdownPhase.
|
|
2273
|
-
* await shutdown.run(0);
|
|
2215
|
+
* shutdown.addTask(ShutdownPhase.Disconnect, 'database', () => db.disconnect(), 5000);
|
|
2216
|
+
* await shutdown.run(0);
|
|
2274
2217
|
* ```
|
|
2275
2218
|
*/
|
|
2276
2219
|
run(exitCode?: number, exitProcess?: boolean): Promise<void>;
|
|
2220
|
+
private emitFailures;
|
|
2277
2221
|
}
|
|
2278
2222
|
//#endregion
|
|
2279
2223
|
//#region src/node/Lifecycle/CoordinatedStartup.d.ts
|
|
2280
|
-
/**
|
|
2281
|
-
* Startup phases for coordinated initialization
|
|
2282
|
-
*
|
|
2283
|
-
* Defines the order in which different components are initialized during bot startup.
|
|
2284
|
-
*/
|
|
2285
|
-
declare enum StartupPhase {
|
|
2286
|
-
/** Validate environment variables and config files */
|
|
2287
|
-
Validation = 1,
|
|
2288
|
-
/** Discover plugin constructors via decorators or registry */
|
|
2289
|
-
Discovery = 2,
|
|
2290
|
-
/** Register plugin metadata and declared dependencies */
|
|
2291
|
-
Registration = 3,
|
|
2292
|
-
/** Inject and validate plugin-specific configuration */
|
|
2293
|
-
Configuration = 4,
|
|
2294
|
-
/** Instantiate plugin classes with Core and arguments */
|
|
2295
|
-
Instantiation = 5,
|
|
2296
|
-
/** Activate plugins by calling their init/setup methods */
|
|
2297
|
-
Activation = 6,
|
|
2298
|
-
/** Mark seedcord as ready and start handling interactions */
|
|
2299
|
-
Ready = 7
|
|
2300
|
-
}
|
|
2301
|
-
/**
|
|
2302
|
-
* Strict-event-emitter payload map for coordinated startup phases.
|
|
2303
|
-
*/
|
|
2304
2224
|
type CoordinatedStartupEvents = PhaseEventMap<'startup', UnionToTuple<StartupPhase>>;
|
|
2305
2225
|
/**
|
|
2306
|
-
*
|
|
2307
|
-
*
|
|
2308
|
-
* Coordinates initialization of all bot components in a predictable sequence.
|
|
2309
|
-
* Tasks are executed within their designated phases to ensure proper dependency order.
|
|
2226
|
+
* Runs registered startup tasks across `StartupPhase` in order. Tasks within a phase run in parallel.
|
|
2310
2227
|
*/
|
|
2311
2228
|
declare class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, CoordinatedStartupEvents> {
|
|
2312
2229
|
private isStartingUp;
|
|
2313
2230
|
private hasStarted;
|
|
2314
2231
|
constructor();
|
|
2315
|
-
/**
|
|
2316
|
-
* Adds a task to a specific startup phase with timeout.
|
|
2317
|
-
*
|
|
2318
|
-
* @param phase - The startup phase from {@link StartupPhase}
|
|
2319
|
-
* @param taskName - Unique identifier for the task
|
|
2320
|
-
* @param task - Async function to execute
|
|
2321
|
-
* @param timeoutMs - Task timeout in milliseconds. {@default `10000`}
|
|
2322
|
-
*/
|
|
2232
|
+
/** Adds a startup-phase task. @param timeoutMs - Task timeout in ms. {@default 10000} */
|
|
2323
2233
|
addTask(phase: StartupPhase, taskName: string, task: () => Promise<void>, timeoutMs?: number): void;
|
|
2324
2234
|
protected canAddTask(): boolean;
|
|
2325
2235
|
protected canRemoveTask(): boolean;
|
|
2326
2236
|
protected getTaskType(): string;
|
|
2327
2237
|
protected executeTasksInPhase(phase: StartupPhase, tasks: LifecycleTask[]): Promise<PromiseSettledResult<void>[]>;
|
|
2328
2238
|
/**
|
|
2329
|
-
*
|
|
2239
|
+
* Runs the registered tasks across startup phases in `StartupPhase` order. Each phase completes
|
|
2240
|
+
* before the next begins, and tasks within a phase run concurrently.
|
|
2330
2241
|
*
|
|
2331
|
-
*
|
|
2332
|
-
* Each phase completes before the next phase begins. Tasks within a phase
|
|
2333
|
-
* are executed sequentially to maintain predictable initialization.
|
|
2334
|
-
*
|
|
2335
|
-
* @returns Promise that resolves when startup is complete
|
|
2336
|
-
* @throws An {@link Error} If startup fails or is called multiple times
|
|
2337
|
-
* @example
|
|
2338
|
-
* ```typescript
|
|
2339
|
-
* const startup = new CoordinatedStartup();
|
|
2340
|
-
* startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);
|
|
2341
|
-
* await startup.run();
|
|
2342
|
-
* ```
|
|
2242
|
+
* @throws {@link SeedcordError} if a startup task throws.
|
|
2343
2243
|
*/
|
|
2344
2244
|
run(): Promise<void>;
|
|
2345
2245
|
protected runTaskWithTimeout(phase: StartupPhase, task: LifecycleTask): Promise<void>;
|
|
@@ -2347,22 +2247,13 @@ declare class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase, Coor
|
|
|
2347
2247
|
* Aborts the startup sequence if it is currently running.
|
|
2348
2248
|
*/
|
|
2349
2249
|
abort(): void;
|
|
2350
|
-
/**
|
|
2351
|
-
* Check if startup has completed
|
|
2352
|
-
*/
|
|
2353
2250
|
get isReady(): boolean;
|
|
2354
|
-
/**
|
|
2355
|
-
* Check if startup is currently running
|
|
2356
|
-
*/
|
|
2357
2251
|
get isRunning(): boolean;
|
|
2358
2252
|
}
|
|
2359
2253
|
//#endregion
|
|
2360
2254
|
//#region src/node/HealthCheck.d.ts
|
|
2361
2255
|
/**
|
|
2362
|
-
* HTTP
|
|
2363
|
-
*
|
|
2364
|
-
* Provides a simple HTTP endpoint that responds with JSON status
|
|
2365
|
-
* information, useful for container orchestration and monitoring.
|
|
2256
|
+
* HTTP server answering `GET` on the configured path with a JSON `{ status, timestamp }` body.
|
|
2366
2257
|
*/
|
|
2367
2258
|
declare class HealthCheck {
|
|
2368
2259
|
/** @internal */
|
|
@@ -2372,6 +2263,11 @@ declare class HealthCheck {
|
|
|
2372
2263
|
readonly host: string | undefined;
|
|
2373
2264
|
private server?;
|
|
2374
2265
|
constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig);
|
|
2266
|
+
/**
|
|
2267
|
+
* Resolves the `healthCheck` option. `undefined` and `true` build the defaults, an object uses
|
|
2268
|
+
* its config, `false` returns `undefined`.
|
|
2269
|
+
*/
|
|
2270
|
+
static fromOption(shutdown: CoordinatedShutdown, option?: HealthCheckOption): HealthCheck | undefined;
|
|
2375
2271
|
/**
|
|
2376
2272
|
* Starts the health check server.
|
|
2377
2273
|
* @returns Promise that resolves when the server is listening
|
|
@@ -2380,12 +2276,77 @@ declare class HealthCheck {
|
|
|
2380
2276
|
init(): Promise<void>;
|
|
2381
2277
|
/**
|
|
2382
2278
|
* Stops the health check server.
|
|
2383
|
-
*
|
|
2384
|
-
* @returns Promise that resolves when the server is closed
|
|
2385
2279
|
* @internal
|
|
2386
2280
|
*/
|
|
2387
2281
|
stop(): Promise<void>;
|
|
2388
2282
|
}
|
|
2389
2283
|
//#endregion
|
|
2390
|
-
|
|
2391
|
-
|
|
2284
|
+
//#region src/node/Pluggable.d.ts
|
|
2285
|
+
/**
|
|
2286
|
+
* Base class for objects that can have plugins attached.
|
|
2287
|
+
*
|
|
2288
|
+
* Plugins are attached during configuration and initialized during startup, sequentially in attach
|
|
2289
|
+
* order within each phase. Not constructed directly, the host is a transport `Seedcord` class.
|
|
2290
|
+
*/
|
|
2291
|
+
declare abstract class Pluggable<TPluggableEvents extends EventMap<TPluggableEvents> = NoEvents> extends TypedEventEmitter<TPluggableEvents> implements CoreBase {
|
|
2292
|
+
abstract readonly config: Config;
|
|
2293
|
+
abstract readonly rateLimiter: IRateLimiter;
|
|
2294
|
+
protected isInitialized: boolean;
|
|
2295
|
+
protected startFailed: boolean;
|
|
2296
|
+
protected readonly shutdown: CoordinatedShutdown;
|
|
2297
|
+
protected readonly startup: CoordinatedStartup;
|
|
2298
|
+
protected readonly plugins: Plugin[];
|
|
2299
|
+
private readonly pluginLogger;
|
|
2300
|
+
private readonly attachments;
|
|
2301
|
+
private readonly completedInits;
|
|
2302
|
+
private readonly disposePhases;
|
|
2303
|
+
private pluginTasksRegistered;
|
|
2304
|
+
private static isInstantiated;
|
|
2305
|
+
private static liveShutdown?;
|
|
2306
|
+
constructor(shutdown: CoordinatedShutdown, startup: CoordinatedStartup);
|
|
2307
|
+
/** @internal */
|
|
2308
|
+
protected init(): Promise<this>;
|
|
2309
|
+
protected pluginCapabilities(): PluginCapabilities;
|
|
2310
|
+
protected pluginStore(): Store<'charge'>;
|
|
2311
|
+
/** @internal releases the signal handlers and clears the singleton so the next host can construct */
|
|
2312
|
+
protected static reset(): void;
|
|
2313
|
+
/**
|
|
2314
|
+
* Attaches a plugin to this instance.
|
|
2315
|
+
*
|
|
2316
|
+
* The plugin initializes during startup, after every plugin attached before it, and becomes
|
|
2317
|
+
* available as a property on `core`. Augment the transport's `Core` interface with the plugin
|
|
2318
|
+
* type for intellisense.
|
|
2319
|
+
*
|
|
2320
|
+
* @typeParam Key - The property name for accessing the plugin
|
|
2321
|
+
* @typeParam Ctor - The plugin constructor type
|
|
2322
|
+
* @param key - Property name to access the plugin instance
|
|
2323
|
+
* @param Plugin - Plugin constructor class
|
|
2324
|
+
* @param args - Additional arguments to pass to the plugin constructor
|
|
2325
|
+
* @returns This instance with the plugin attached as a typed property
|
|
2326
|
+
* @throws A **SeedcordError** When called after initialization or if key already exists
|
|
2327
|
+
* @example
|
|
2328
|
+
* ```typescript
|
|
2329
|
+
* seedcord.attach('db', Mongo, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
|
|
2330
|
+
* ```
|
|
2331
|
+
*/
|
|
2332
|
+
attach<Key extends string, Ctor extends PluginCtor>(this: this, key: Key, Plugin: Ctor, ...args: PluginArgs<Ctor>): this & Record<Key, InstanceType<Ctor>>;
|
|
2333
|
+
private finalizeContext;
|
|
2334
|
+
private registerPluginTasks;
|
|
2335
|
+
private runInits;
|
|
2336
|
+
private registerReadyTask;
|
|
2337
|
+
private registerDisposeTask;
|
|
2338
|
+
private runDisposals;
|
|
2339
|
+
private disposeCompleted;
|
|
2340
|
+
private rollback;
|
|
2341
|
+
/** @internal */
|
|
2342
|
+
removeListener<TEventKey extends Extract<keyof TPluggableEvents, string | symbol>>(event: TEventKey, listener: (...args: TPluggableEvents[TEventKey]) => void): this;
|
|
2343
|
+
/** @internal */
|
|
2344
|
+
removeAllListeners(event?: Extract<keyof TPluggableEvents, string | symbol>): this;
|
|
2345
|
+
}
|
|
2346
|
+
//#endregion
|
|
2347
|
+
//#region src/node/Lifecycle/withTimeout.d.ts
|
|
2348
|
+
/** @internal resolves when `work` settles or the bound elapses, whichever is first. */
|
|
2349
|
+
declare function settleWithin(work: Promise<unknown>, timeoutMs: number): Promise<void>;
|
|
2350
|
+
//#endregion
|
|
2351
|
+
export { CoordinatedShutdown, CoordinatedStartup, HealthCheck, type LifecycleTask, Pluggable, type PluginCapabilities, ShutdownPhase, StartupPhase, settleWithin };
|
|
2352
|
+
//# sourceMappingURL=node-internal.index.d.mts.map
|