@seedcord/core 0.1.0-next.5 → 0.1.0-next.7
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-DLp_B8as.d.mts} +66 -124
- package/dist/HmrModuleHandler-3sNgKHdf.mjs +163 -0
- package/dist/HmrModuleHandler-3sNgKHdf.mjs.map +1 -0
- package/dist/HmrModuleHandler-CTBng7wV.cjs +170 -0
- package/dist/HmrModuleHandler-CTBng7wV.cjs.map +1 -0
- package/dist/Plugin-3rlH0uxH.d.mts +620 -0
- package/dist/Plugin-Cgp1a0uQ.cjs +123 -0
- package/dist/Plugin-Cgp1a0uQ.cjs.map +1 -0
- package/dist/Plugin-j9GYdroS.mjs +106 -0
- package/dist/Plugin-j9GYdroS.mjs.map +1 -0
- package/dist/{permissions-DQL5ViZn.cjs → Silence-DiP95iwD.cjs} +54 -305
- package/dist/Silence-DiP95iwD.cjs.map +1 -0
- package/dist/{permissions-SokZLJMj.mjs → Silence-dbC2slOB.mjs} +47 -238
- package/dist/Silence-dbC2slOB.mjs.map +1 -0
- package/dist/basic-D7bDlgSP.d.mts +10 -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/hmr.index.cjs +2 -164
- package/dist/hmr.index.mjs +2 -162
- package/dist/index-Dueqztyy.d.mts +142 -0
- package/dist/index.cjs +53 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +7 -4
- package/dist/index.mjs +9 -35
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.cjs +267 -89
- package/dist/internal.index.cjs.map +1 -1
- package/dist/internal.index.d.mts +68 -3
- package/dist/internal.index.mjs +218 -48
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.cjs +716 -0
- package/dist/node-internal.index.cjs.map +1 -0
- package/dist/node-internal.index.d.cts +1 -0
- package/dist/node-internal.index.d.mts +261 -0
- package/dist/node-internal.index.mjs +706 -0
- package/dist/node-internal.index.mjs.map +1 -0
- 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/dist/publishDefault-CCf1azzW.mjs +264 -0
- package/dist/publishDefault-CCf1azzW.mjs.map +1 -0
- package/dist/publishDefault-l0NQ-SHo.cjs +353 -0
- package/dist/publishDefault-l0NQ-SHo.cjs.map +1 -0
- package/dist/subscribers-B1G6_LlK.cjs +517 -0
- package/dist/subscribers-B1G6_LlK.cjs.map +1 -0
- package/dist/subscribers-DYqFVP1X.mjs +480 -0
- package/dist/subscribers-DYqFVP1X.mjs.map +1 -0
- package/package.json +25 -13
- package/dist/hmr.index.cjs.map +0 -1
- package/dist/hmr.index.mjs.map +0 -1
- package/dist/is-never-Ddaba1Su.d.mts +0 -58
- 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.d.mts +0 -2391
- package/dist/node.index.mjs +0 -513
- package/dist/node.index.mjs.map +0 -1
- package/dist/permissions-DQL5ViZn.cjs.map +0 -1
- package/dist/permissions-SokZLJMj.mjs.map +0 -1
package/dist/hmr.index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.index.cjs","names":["sep"],"sources":["../src/hmr/HmrModuleHandler.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { formatFilePath } from '@seedcord/utils';\nimport chalk from 'chalk';\n\nimport type { Logger } from '@seedcord/logger';\nimport type { HmrUpdateEvent } from '@seedcord/types';\n\ninterface HmrStore<THandler, TMiddleware, TArtifacts> {\n fileToHandlers: Map<string, Set<THandler>>;\n fileToMiddlewares: Map<string, Set<TMiddleware>>;\n handlerArtifacts: Map<THandler, TArtifacts>;\n}\n\n// justified: units are caller-supplied classes, name is read for log labels only\nconst displayName = (unit: unknown, fallback: string): string => (unit as { name?: string }).name ?? fallback;\n\ninterface HmrModuleHandlerOptions<THandler, TMiddleware = void, TArtifacts = unknown> {\n handlersDir: string;\n middlewaresDir?: string;\n isHandler: (val: unknown) => val is THandler;\n isMiddleware?: (val: unknown) => val is TMiddleware;\n registerHandler: (handler: THandler, file: string) => void;\n registerMiddleware?: (middleware: TMiddleware, file: string) => void;\n unregisterHandler: (handler: THandler, artifacts?: TArtifacts) => void;\n unregisterMiddleware?: (middleware: TMiddleware) => void;\n /** Snapshotted per handler so a rollback re-registers with the same artifacts after the module re-imports. */\n getArtifacts?: (handler: THandler) => TArtifacts;\n logger: Logger;\n}\n\n/**\n * Reloads THandler and optional TMiddleware modules on HMR updates.\n *\n * Call `handle()` from your `HmrAware.onHmr()` for standard reload behavior, or write a fully custom `onHmr()` and skip this class.\n *\n * This is only useful during development.\n */\nexport class HmrModuleHandler<THandler, TMiddleware = void, TArtifacts = unknown> {\n private readonly store: HmrStore<THandler, TMiddleware, TArtifacts>;\n private readonly options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>;\n\n constructor(options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>) {\n // the caller's options object stays unmutated\n this.options = { ...options, logger: options.logger.inChannel('hmr') };\n\n // no import.meta.hot.data stash needed, a leaf reload never reconstructs the singleton that holds these Maps\n this.store = {\n fileToHandlers: new Map(),\n fileToMiddlewares: new Map(),\n handlerArtifacts: new Map()\n };\n }\n\n /** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */\n public async handle(event: HmrUpdateEvent): Promise<void> {\n const { file, affectedModules, type } = event;\n const { logger, handlersDir, middlewaresDir } = this.options;\n\n if (type === 'delete' || type === 'deleteDir') {\n this.unload(file);\n return;\n }\n\n // a directory is never importable, its files arrive as their own create events\n if (type === 'createDir') return;\n\n if (type === 'update' && !existsSync(file)) {\n if (this.isTracked(file)) {\n logger.info(`Received update for non-existent file: ${formatFilePath(file)}, treating as delete`);\n this.unload(file);\n }\n return;\n }\n\n const rollbackEnabled = event.rollback ?? true;\n const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];\n const absHandlersDir = resolve(process.cwd(), handlersDir);\n const absMiddlewaresDir = middlewaresDir ? resolve(process.cwd(), middlewaresDir) : null;\n\n for (const fileToReload of filesToReload) {\n // the separator keeps a sibling dir sharing the prefix (commands-extra vs commands) out\n const isHandler = fileToReload.startsWith(absHandlersDir + sep);\n const isMiddleware = absMiddlewaresDir ? fileToReload.startsWith(absMiddlewaresDir + sep) : false;\n\n if (!isHandler && !isMiddleware) {\n continue;\n }\n\n await this.reloadWithRollback(fileToReload, rollbackEnabled);\n }\n }\n\n private async reloadWithRollback(file: string, rollbackEnabled: boolean): Promise<void> {\n const { logger } = this.options;\n const snapshot = this.snapshotUnits(file);\n this.unload(file);\n\n const reloaded = await this.reloadFile(file);\n if (reloaded) return;\n\n // a failed reload can leave a partial registration\n this.unload(file);\n if (!rollbackEnabled) return;\n if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return;\n\n // a typo keeps them live until the next good save\n this.restoreUnits(file, snapshot);\n logger.warn(`${chalk.yellow.bold('Rolled back')} ${chalk.gray(formatFilePath(file))} to the last-good version`);\n }\n\n /** Links the handler to its source file so a later update can unload it. */\n public trackHandler(file: string, handler: THandler): void {\n let set = this.store.fileToHandlers.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToHandlers.set(file, set);\n }\n set.add(handler);\n\n if (this.options.getArtifacts) {\n const artifacts = this.options.getArtifacts(handler);\n this.store.handlerArtifacts.set(handler, artifacts);\n }\n }\n\n /** Links the middleware to its source file so a later update can unload it. */\n public trackMiddleware(file: string, middleware: TMiddleware): void {\n let set = this.store.fileToMiddlewares.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToMiddlewares.set(file, set);\n }\n set.add(middleware);\n }\n\n private isTracked(file: string): boolean {\n return this.store.fileToHandlers.has(file) || this.store.fileToMiddlewares.has(file);\n }\n\n private unload(file: string): void {\n const { logger } = this.options;\n const handlers = this.store.fileToHandlers.get(file);\n if (handlers) {\n for (const handler of handlers) {\n const artifacts = this.store.handlerArtifacts.get(handler);\n this.options.unregisterHandler(handler, artifacts);\n this.store.handlerArtifacts.delete(handler);\n const name = displayName(handler, 'Handler');\n logger.info(\n `${chalk.red.bold('Unloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n }\n this.store.fileToHandlers.delete(file);\n }\n\n const middlewares = this.store.fileToMiddlewares.get(file);\n if (middlewares) {\n for (const middleware of middlewares) {\n this.options.unregisterMiddleware?.(middleware);\n const name = displayName(middleware, 'Middleware');\n logger.info(\n `${chalk.red.bold('Unloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n }\n this.store.fileToMiddlewares.delete(file);\n }\n }\n\n private async reloadFile(file: string): Promise<boolean> {\n const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;\n\n if (!existsSync(file)) {\n logger.info(`File does not exist, skipping reload: ${formatFilePath(file)}`);\n return false;\n }\n\n try {\n let fileUrl = pathToFileURL(file).href;\n // vitest has no real vite server managing the module graph, so bust the import cache manually.\n if (process.env.VITEST === 'true') fileUrl += `?update=${Date.now()}`;\n\n // justified: a dynamic import resolves to an untyped export map\n const imported = (await import(fileUrl)) as Record<string, unknown>;\n\n for (const val of Object.values(imported)) {\n if (isHandler(val)) {\n registerHandler(val, file);\n this.trackHandler(file, val);\n const name = displayName(val, 'Handler');\n logger.info(\n `${chalk.blue.bold('Reloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {\n registerMiddleware(val, file);\n this.trackMiddleware(file, val);\n const name = displayName(val, 'Middleware');\n logger.info(\n `${chalk.blue.bold('Reloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n }\n }\n\n return true;\n } catch (error) {\n logger.error(`Failed to reload file: ${file}`, error);\n return false;\n }\n }\n\n // restoring re-registers the same class objects, so a live db connection survives rollback\n private snapshotUnits(file: string): { handlers: THandler[]; middlewares: TMiddleware[] } {\n return {\n handlers: [...(this.store.fileToHandlers.get(file) ?? [])],\n middlewares: [...(this.store.fileToMiddlewares.get(file) ?? [])]\n };\n }\n\n private restoreUnits(file: string, snapshot: { handlers: THandler[]; middlewares: TMiddleware[] }): void {\n for (const handler of snapshot.handlers) {\n this.options.registerHandler(handler, file);\n this.trackHandler(file, handler);\n }\n\n if (this.options.registerMiddleware) {\n for (const middleware of snapshot.middlewares) {\n this.options.registerMiddleware(middleware, file);\n this.trackMiddleware(file, middleware);\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/hmr.index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.index.mjs","names":[],"sources":["../src/hmr/HmrModuleHandler.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { formatFilePath } from '@seedcord/utils';\nimport chalk from 'chalk';\n\nimport type { Logger } from '@seedcord/logger';\nimport type { HmrUpdateEvent } from '@seedcord/types';\n\ninterface HmrStore<THandler, TMiddleware, TArtifacts> {\n fileToHandlers: Map<string, Set<THandler>>;\n fileToMiddlewares: Map<string, Set<TMiddleware>>;\n handlerArtifacts: Map<THandler, TArtifacts>;\n}\n\n// justified: units are caller-supplied classes, name is read for log labels only\nconst displayName = (unit: unknown, fallback: string): string => (unit as { name?: string }).name ?? fallback;\n\ninterface HmrModuleHandlerOptions<THandler, TMiddleware = void, TArtifacts = unknown> {\n handlersDir: string;\n middlewaresDir?: string;\n isHandler: (val: unknown) => val is THandler;\n isMiddleware?: (val: unknown) => val is TMiddleware;\n registerHandler: (handler: THandler, file: string) => void;\n registerMiddleware?: (middleware: TMiddleware, file: string) => void;\n unregisterHandler: (handler: THandler, artifacts?: TArtifacts) => void;\n unregisterMiddleware?: (middleware: TMiddleware) => void;\n /** Snapshotted per handler so a rollback re-registers with the same artifacts after the module re-imports. */\n getArtifacts?: (handler: THandler) => TArtifacts;\n logger: Logger;\n}\n\n/**\n * Reloads THandler and optional TMiddleware modules on HMR updates.\n *\n * Call `handle()` from your `HmrAware.onHmr()` for standard reload behavior, or write a fully custom `onHmr()` and skip this class.\n *\n * This is only useful during development.\n */\nexport class HmrModuleHandler<THandler, TMiddleware = void, TArtifacts = unknown> {\n private readonly store: HmrStore<THandler, TMiddleware, TArtifacts>;\n private readonly options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>;\n\n constructor(options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>) {\n // the caller's options object stays unmutated\n this.options = { ...options, logger: options.logger.inChannel('hmr') };\n\n // no import.meta.hot.data stash needed, a leaf reload never reconstructs the singleton that holds these Maps\n this.store = {\n fileToHandlers: new Map(),\n fileToMiddlewares: new Map(),\n handlerArtifacts: new Map()\n };\n }\n\n /** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */\n public async handle(event: HmrUpdateEvent): Promise<void> {\n const { file, affectedModules, type } = event;\n const { logger, handlersDir, middlewaresDir } = this.options;\n\n if (type === 'delete' || type === 'deleteDir') {\n this.unload(file);\n return;\n }\n\n // a directory is never importable, its files arrive as their own create events\n if (type === 'createDir') return;\n\n if (type === 'update' && !existsSync(file)) {\n if (this.isTracked(file)) {\n logger.info(`Received update for non-existent file: ${formatFilePath(file)}, treating as delete`);\n this.unload(file);\n }\n return;\n }\n\n const rollbackEnabled = event.rollback ?? true;\n const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];\n const absHandlersDir = resolve(process.cwd(), handlersDir);\n const absMiddlewaresDir = middlewaresDir ? resolve(process.cwd(), middlewaresDir) : null;\n\n for (const fileToReload of filesToReload) {\n // the separator keeps a sibling dir sharing the prefix (commands-extra vs commands) out\n const isHandler = fileToReload.startsWith(absHandlersDir + sep);\n const isMiddleware = absMiddlewaresDir ? fileToReload.startsWith(absMiddlewaresDir + sep) : false;\n\n if (!isHandler && !isMiddleware) {\n continue;\n }\n\n await this.reloadWithRollback(fileToReload, rollbackEnabled);\n }\n }\n\n private async reloadWithRollback(file: string, rollbackEnabled: boolean): Promise<void> {\n const { logger } = this.options;\n const snapshot = this.snapshotUnits(file);\n this.unload(file);\n\n const reloaded = await this.reloadFile(file);\n if (reloaded) return;\n\n // a failed reload can leave a partial registration\n this.unload(file);\n if (!rollbackEnabled) return;\n if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return;\n\n // a typo keeps them live until the next good save\n this.restoreUnits(file, snapshot);\n logger.warn(`${chalk.yellow.bold('Rolled back')} ${chalk.gray(formatFilePath(file))} to the last-good version`);\n }\n\n /** Links the handler to its source file so a later update can unload it. */\n public trackHandler(file: string, handler: THandler): void {\n let set = this.store.fileToHandlers.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToHandlers.set(file, set);\n }\n set.add(handler);\n\n if (this.options.getArtifacts) {\n const artifacts = this.options.getArtifacts(handler);\n this.store.handlerArtifacts.set(handler, artifacts);\n }\n }\n\n /** Links the middleware to its source file so a later update can unload it. */\n public trackMiddleware(file: string, middleware: TMiddleware): void {\n let set = this.store.fileToMiddlewares.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToMiddlewares.set(file, set);\n }\n set.add(middleware);\n }\n\n private isTracked(file: string): boolean {\n return this.store.fileToHandlers.has(file) || this.store.fileToMiddlewares.has(file);\n }\n\n private unload(file: string): void {\n const { logger } = this.options;\n const handlers = this.store.fileToHandlers.get(file);\n if (handlers) {\n for (const handler of handlers) {\n const artifacts = this.store.handlerArtifacts.get(handler);\n this.options.unregisterHandler(handler, artifacts);\n this.store.handlerArtifacts.delete(handler);\n const name = displayName(handler, 'Handler');\n logger.info(\n `${chalk.red.bold('Unloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n }\n this.store.fileToHandlers.delete(file);\n }\n\n const middlewares = this.store.fileToMiddlewares.get(file);\n if (middlewares) {\n for (const middleware of middlewares) {\n this.options.unregisterMiddleware?.(middleware);\n const name = displayName(middleware, 'Middleware');\n logger.info(\n `${chalk.red.bold('Unloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n }\n this.store.fileToMiddlewares.delete(file);\n }\n }\n\n private async reloadFile(file: string): Promise<boolean> {\n const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;\n\n if (!existsSync(file)) {\n logger.info(`File does not exist, skipping reload: ${formatFilePath(file)}`);\n return false;\n }\n\n try {\n let fileUrl = pathToFileURL(file).href;\n // vitest has no real vite server managing the module graph, so bust the import cache manually.\n if (process.env.VITEST === 'true') fileUrl += `?update=${Date.now()}`;\n\n // justified: a dynamic import resolves to an untyped export map\n const imported = (await import(fileUrl)) as Record<string, unknown>;\n\n for (const val of Object.values(imported)) {\n if (isHandler(val)) {\n registerHandler(val, file);\n this.trackHandler(file, val);\n const name = displayName(val, 'Handler');\n logger.info(\n `${chalk.blue.bold('Reloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {\n registerMiddleware(val, file);\n this.trackMiddleware(file, val);\n const name = displayName(val, 'Middleware');\n logger.info(\n `${chalk.blue.bold('Reloaded')} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`\n );\n }\n }\n\n return true;\n } catch (error) {\n logger.error(`Failed to reload file: ${file}`, error);\n return false;\n }\n }\n\n // restoring re-registers the same class objects, so a live db connection survives rollback\n private snapshotUnits(file: string): { handlers: THandler[]; middlewares: TMiddleware[] } {\n return {\n handlers: [...(this.store.fileToHandlers.get(file) ?? [])],\n middlewares: [...(this.store.fileToMiddlewares.get(file) ?? [])]\n };\n }\n\n private restoreUnits(file: string, snapshot: { handlers: THandler[]; middlewares: TMiddleware[] }): void {\n for (const handler of snapshot.handlers) {\n this.options.registerHandler(handler, file);\n this.trackHandler(file, handler);\n }\n\n if (this.options.registerMiddleware) {\n for (const middleware of snapshot.middlewares) {\n this.options.registerMiddleware(middleware, file);\n this.trackMiddleware(file, middleware);\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/is-never.d.ts
|
|
2
|
-
/**
|
|
3
|
-
Returns a boolean for whether the given type is `never`.
|
|
4
|
-
|
|
5
|
-
@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919
|
|
6
|
-
@link https://stackoverflow.com/a/53984913/10292952
|
|
7
|
-
@link https://www.zhenghao.io/posts/ts-never
|
|
8
|
-
|
|
9
|
-
Useful in type utilities, such as checking if something does not occur.
|
|
10
|
-
|
|
11
|
-
@example
|
|
12
|
-
```
|
|
13
|
-
import type {IsNever, And} from 'type-fest';
|
|
14
|
-
|
|
15
|
-
type A = IsNever<never>;
|
|
16
|
-
//=> true
|
|
17
|
-
|
|
18
|
-
type B = IsNever<any>;
|
|
19
|
-
//=> false
|
|
20
|
-
|
|
21
|
-
type C = IsNever<unknown>;
|
|
22
|
-
//=> false
|
|
23
|
-
|
|
24
|
-
type D = IsNever<never[]>;
|
|
25
|
-
//=> false
|
|
26
|
-
|
|
27
|
-
type E = IsNever<object>;
|
|
28
|
-
//=> false
|
|
29
|
-
|
|
30
|
-
type F = IsNever<string>;
|
|
31
|
-
//=> false
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
@example
|
|
35
|
-
```
|
|
36
|
-
import type {IsNever} from 'type-fest';
|
|
37
|
-
|
|
38
|
-
type IsTrue<T> = T extends true ? true : false;
|
|
39
|
-
|
|
40
|
-
// When a distributive conditional is instantiated with `never`, the entire conditional results in `never`.
|
|
41
|
-
type A = IsTrue<never>;
|
|
42
|
-
//=> never
|
|
43
|
-
|
|
44
|
-
// If you don't want that behaviour, you can explicitly add an `IsNever` check before the distributive conditional.
|
|
45
|
-
type IsTrueFixed<T> =
|
|
46
|
-
IsNever<T> extends true ? false : T extends true ? true : false;
|
|
47
|
-
|
|
48
|
-
type B = IsTrueFixed<never>;
|
|
49
|
-
//=> false
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
@category Type Guard
|
|
53
|
-
@category Utilities
|
|
54
|
-
*/
|
|
55
|
-
type IsNever<T> = [T] extends [never] ? true : false;
|
|
56
|
-
//#endregion
|
|
57
|
-
export { IsNever as t };
|
|
58
|
-
//# sourceMappingURL=is-never-Ddaba1Su.d.mts.map
|
package/dist/node.index.cjs
DELETED
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_rolldown_runtime = require('./rolldown-runtime-C_NdSu1c.cjs');
|
|
3
|
-
let _seedcord_errors = require("@seedcord/errors");
|
|
4
|
-
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
5
|
-
let _seedcord_logger = require("@seedcord/logger");
|
|
6
|
-
let chalk = require("chalk");
|
|
7
|
-
chalk = require_rolldown_runtime.__toESM(chalk, 1);
|
|
8
|
-
let _seedcord_event_emitter = require("@seedcord/event-emitter");
|
|
9
|
-
let node_http = require("node:http");
|
|
10
|
-
|
|
11
|
-
//#region src/node/Lifecycle/CoordinatedLifecycle.ts
|
|
12
|
-
/**
|
|
13
|
-
* Abstract base class for coordinated lifecycle management (startup/shutdown)
|
|
14
|
-
*/
|
|
15
|
-
var CoordinatedLifecycle = class extends _seedcord_event_emitter.TypedEventEmitter {
|
|
16
|
-
phaseOrder;
|
|
17
|
-
phaseEnum;
|
|
18
|
-
logger;
|
|
19
|
-
tasksMap = /* @__PURE__ */ new Map();
|
|
20
|
-
constructor(loggerName, phaseOrder, phaseEnum) {
|
|
21
|
-
super();
|
|
22
|
-
this.phaseOrder = phaseOrder;
|
|
23
|
-
this.phaseEnum = phaseEnum;
|
|
24
|
-
this.logger = new _seedcord_logger.Logger(loggerName);
|
|
25
|
-
this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Adds a lifecycle task to a specific phase.
|
|
29
|
-
*
|
|
30
|
-
* Tasks are executed in phase order during lifecycle operations.
|
|
31
|
-
* Each task has a timeout to prevent hanging operations.
|
|
32
|
-
*
|
|
33
|
-
* @param phase - The lifecycle phase to add the task to
|
|
34
|
-
* @param taskName - Unique name for the task (used for logging and removal)
|
|
35
|
-
* @param task - Async function to execute during the phase
|
|
36
|
-
* @param timeoutMs - Maximum time allowed for task execution in milliseconds
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* lifecycle.addTask(StartupPhase.Services, 'start-database', async () => {
|
|
40
|
-
* await database.connect();
|
|
41
|
-
* }, 10000);
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
addTask(phase, taskName, task, timeoutMs) {
|
|
45
|
-
if (!this.canAddTask()) return;
|
|
46
|
-
const tasks = this.tasksMap.get(phase);
|
|
47
|
-
if (!tasks) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleUnknownPhase, [phase]);
|
|
48
|
-
tasks.push({
|
|
49
|
-
name: taskName,
|
|
50
|
-
task,
|
|
51
|
-
timeout: timeoutMs
|
|
52
|
-
});
|
|
53
|
-
this.logger.debug(`${chalk.default.italic("Added")} ${this.getTaskType()} task ${chalk.default.bold.cyan(taskName)} to phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Removes a lifecycle task from a specific phase.
|
|
57
|
-
*
|
|
58
|
-
* @param phase - The lifecycle phase to remove the task from
|
|
59
|
-
* @param taskName - Name of the task to remove
|
|
60
|
-
* @returns True if the task was found and removed, false otherwise
|
|
61
|
-
*/
|
|
62
|
-
removeTask(phase, taskName) {
|
|
63
|
-
if (!this.canRemoveTask()) return false;
|
|
64
|
-
const tasks = this.tasksMap.get(phase);
|
|
65
|
-
if (!tasks) return false;
|
|
66
|
-
const initialLength = tasks.length;
|
|
67
|
-
const filteredTasks = tasks.filter((task) => task.name !== taskName);
|
|
68
|
-
this.tasksMap.set(phase, filteredTasks);
|
|
69
|
-
const removed = initialLength !== filteredTasks.length;
|
|
70
|
-
if (removed) this.logger.debug(`${chalk.default.italic("Removed")} ${this.getTaskType()} task ${chalk.default.bold.cyan(taskName)} from phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
71
|
-
return removed;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Run all tasks in a specific phase
|
|
75
|
-
*/
|
|
76
|
-
async runPhase(phase) {
|
|
77
|
-
const tasks = this.tasksMap.get(phase) ?? [];
|
|
78
|
-
if (tasks.length === 0) {
|
|
79
|
-
this.logger.warn(`No tasks to run in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
this.logger.info(`${chalk.default.bold.yellow("Running")} ${this.getTaskType()} phase ${chalk.default.bold.magenta(this.phaseEnum[phase])} with ${chalk.default.bold.cyan(tasks.length)} tasks`);
|
|
83
|
-
this.emitPhase(phase, "start");
|
|
84
|
-
const failures = (await this.executeTasksInPhase(phase, tasks)).filter((r) => r.status === "rejected").length;
|
|
85
|
-
if (failures > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);
|
|
86
|
-
this.logger.info(`Phase ${chalk.default.bold.magenta(this.phaseEnum[phase])} ${chalk.default.bold.green("completed successfully")}`);
|
|
87
|
-
this.emitPhase(phase, "complete");
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Run a single task with timeout
|
|
91
|
-
*/
|
|
92
|
-
async runTaskWithTimeout(phase, task) {
|
|
93
|
-
this.logger.info(`${chalk.default.italic("Starting")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
94
|
-
let timeoutId;
|
|
95
|
-
try {
|
|
96
|
-
await Promise.race([task.task(), new Promise((_, reject) => {
|
|
97
|
-
timeoutId = setTimeout(() => {
|
|
98
|
-
reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));
|
|
99
|
-
}, task.timeout);
|
|
100
|
-
})]);
|
|
101
|
-
this.logger.info(`${chalk.default.italic("Completed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}`);
|
|
102
|
-
} catch (error) {
|
|
103
|
-
this.logger.error(`${chalk.default.italic("Failed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(this.phaseEnum[phase])}:`, error);
|
|
104
|
-
throw error;
|
|
105
|
-
} finally {
|
|
106
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
emitPhase(phase, action) {
|
|
110
|
-
this.emitSafeRaw(`phase:${phase}:${action}`);
|
|
111
|
-
}
|
|
112
|
-
onListenerError(error, event) {
|
|
113
|
-
this.logger.error(`listener for ${String(event)} threw`, error);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region src/node/Lifecycle/CoordinatedShutdown.ts
|
|
119
|
-
/**
|
|
120
|
-
* Shutdown phases for coordinated application shutdown.
|
|
121
|
-
*/
|
|
122
|
-
let ShutdownPhase = /* @__PURE__ */ function(ShutdownPhase) {
|
|
123
|
-
/** Stop accepting new requests/interactions */
|
|
124
|
-
ShutdownPhase[ShutdownPhase["StopAcceptingRequests"] = 1] = "StopAcceptingRequests";
|
|
125
|
-
/** Stop background services (health checks, etc.) */
|
|
126
|
-
ShutdownPhase[ShutdownPhase["StopServices"] = 2] = "StopServices";
|
|
127
|
-
/** Disconnect from external resources (database, APIs) */
|
|
128
|
-
ShutdownPhase[ShutdownPhase["ExternalResources"] = 3] = "ExternalResources";
|
|
129
|
-
/** Disconnect from Discord */
|
|
130
|
-
ShutdownPhase[ShutdownPhase["DiscordCleanup"] = 4] = "DiscordCleanup";
|
|
131
|
-
/** Final cleanup tasks */
|
|
132
|
-
ShutdownPhase[ShutdownPhase["FinalCleanup"] = 5] = "FinalCleanup";
|
|
133
|
-
return ShutdownPhase;
|
|
134
|
-
}({});
|
|
135
|
-
/** Define the order of phases */
|
|
136
|
-
const PHASE_ORDER$1 = [
|
|
137
|
-
1,
|
|
138
|
-
2,
|
|
139
|
-
3,
|
|
140
|
-
4,
|
|
141
|
-
5
|
|
142
|
-
];
|
|
143
|
-
const LOG_FLUSH_DELAY_MS = 500;
|
|
144
|
-
/**
|
|
145
|
-
* CoordinatedShutdown manages graceful application shutdown by executing registered tasks across defined phases.
|
|
146
|
-
*
|
|
147
|
-
* It listens for termination signals (SIGINT, SIGTERM) and runs tasks in parallel within each phase.
|
|
148
|
-
* Tasks can be added or removed dynamically, and each task has an associated timeout.
|
|
149
|
-
*/
|
|
150
|
-
var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
151
|
-
isShutdownEnabled;
|
|
152
|
-
isShuttingDown = false;
|
|
153
|
-
exitCode = 0;
|
|
154
|
-
onSigTerm = null;
|
|
155
|
-
onSigInt = null;
|
|
156
|
-
constructor(enabled = true) {
|
|
157
|
-
super("Shutdown", PHASE_ORDER$1, ShutdownPhase);
|
|
158
|
-
this.isShutdownEnabled = enabled;
|
|
159
|
-
this.registerSignalHandlers();
|
|
160
|
-
}
|
|
161
|
-
canAddTask() {
|
|
162
|
-
return this.isShutdownEnabled;
|
|
163
|
-
}
|
|
164
|
-
canRemoveTask() {
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
getTaskType() {
|
|
168
|
-
return "shutdown";
|
|
169
|
-
}
|
|
170
|
-
async executeTasksInPhase(phase, tasks) {
|
|
171
|
-
const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
|
|
172
|
-
return Promise.allSettled(promises);
|
|
173
|
-
}
|
|
174
|
-
registerSignalHandlers() {
|
|
175
|
-
if (!this.isShutdownEnabled) return;
|
|
176
|
-
this.onSigTerm = () => {
|
|
177
|
-
this.logger.info(`Received ${chalk.default.yellow.bold("SIGTERM")} signal`);
|
|
178
|
-
this.run(0);
|
|
179
|
-
};
|
|
180
|
-
this.onSigInt = () => {
|
|
181
|
-
this.logger.info(`Received ${chalk.default.yellow.bold("SIGINT")} signal`);
|
|
182
|
-
this.run(0);
|
|
183
|
-
};
|
|
184
|
-
process.on("SIGTERM", this.onSigTerm);
|
|
185
|
-
process.on("SIGINT", this.onSigInt);
|
|
186
|
-
}
|
|
187
|
-
removeSignalHandlers() {
|
|
188
|
-
if (this.onSigTerm) {
|
|
189
|
-
process.off("SIGTERM", this.onSigTerm);
|
|
190
|
-
this.onSigTerm = null;
|
|
191
|
-
}
|
|
192
|
-
if (this.onSigInt) {
|
|
193
|
-
process.off("SIGINT", this.onSigInt);
|
|
194
|
-
this.onSigInt = null;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Adds a task to a specific shutdown phase with timeout.
|
|
199
|
-
*
|
|
200
|
-
* @param phase - The shutdown phase from {@link ShutdownPhase}
|
|
201
|
-
* @param taskName - Unique identifier for the task
|
|
202
|
-
* @param task - Async function to execute
|
|
203
|
-
* @param timeoutMs - Task timeout in milliseconds. {@default `5000`}
|
|
204
|
-
*/
|
|
205
|
-
addTask(phase, taskName, task, timeoutMs = 5e3) {
|
|
206
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Removes a task from a specific shutdown phase.
|
|
210
|
-
*
|
|
211
|
-
* @param phase - The shutdown phase to remove from
|
|
212
|
-
* @param taskName - Name of the task to remove
|
|
213
|
-
* @returns True if task was found and removed
|
|
214
|
-
*/
|
|
215
|
-
removeTask(phase, taskName) {
|
|
216
|
-
return super.removeTask(phase, taskName);
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Executes the coordinated shutdown sequence.
|
|
220
|
-
*
|
|
221
|
-
* Runs all registered tasks across shutdown phases in reverse order.
|
|
222
|
-
* Tasks within each phase are executed in parallel for faster shutdown.
|
|
223
|
-
* Process exits with the specified code when complete.
|
|
224
|
-
*
|
|
225
|
-
* @param exitCode - Process exit code. {@default `0`}
|
|
226
|
-
* @param exitProcess - Whether to exit the process after shutdown. {@default `true`}
|
|
227
|
-
* @returns Promise that resolves when shutdown is complete
|
|
228
|
-
* @example
|
|
229
|
-
* ```typescript
|
|
230
|
-
* shutdown.addTask(ShutdownPhase.Services, 'database', () => db.disconnect(), 5000);
|
|
231
|
-
* await shutdown.run(0); // Graceful shutdown
|
|
232
|
-
* ```
|
|
233
|
-
*/
|
|
234
|
-
async run(exitCode = 0, exitProcess = true) {
|
|
235
|
-
this.removeSignalHandlers();
|
|
236
|
-
if (this.isShuttingDown) {
|
|
237
|
-
this.logger.warn("Shutdown sequence already in progress");
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
this.isShuttingDown = true;
|
|
241
|
-
this.exitCode = exitCode;
|
|
242
|
-
this.logger.info(`${chalk.default.bold.yellow("Starting")} coordinated shutdown with exit code ${chalk.default.bold.cyan(exitCode)}`);
|
|
243
|
-
this.emitSafe("shutdown:start");
|
|
244
|
-
try {
|
|
245
|
-
for (const phase of PHASE_ORDER$1) await this.runPhase(phase);
|
|
246
|
-
this.logger.info(`${chalk.default.bold.green("Coordinated shutdown completed")} successfully`);
|
|
247
|
-
this.emitSafe("shutdown:complete");
|
|
248
|
-
} catch (error) {
|
|
249
|
-
this.logger.error(`${chalk.default.bold.red("Coordinated shutdown failed")}`);
|
|
250
|
-
this.emitSafe("shutdown:error", error);
|
|
251
|
-
} finally {
|
|
252
|
-
if (exitProcess) {
|
|
253
|
-
this.logger.info(`${chalk.default.bold.red("Exiting")} process with code ${chalk.default.bold.cyan(this.exitCode)}`);
|
|
254
|
-
setTimeout(() => {
|
|
255
|
-
process.exit(this.exitCode);
|
|
256
|
-
}, LOG_FLUSH_DELAY_MS);
|
|
257
|
-
} else {
|
|
258
|
-
this.logger.info(`${chalk.default.bold.yellow("Skipping")} process exit (dev mode)`);
|
|
259
|
-
this.isShuttingDown = false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
//#endregion
|
|
266
|
-
//#region src/node/Lifecycle/CoordinatedStartup.ts
|
|
267
|
-
/**
|
|
268
|
-
* Startup phases for coordinated initialization
|
|
269
|
-
*
|
|
270
|
-
* Defines the order in which different components are initialized during bot startup.
|
|
271
|
-
*/
|
|
272
|
-
let StartupPhase = /* @__PURE__ */ function(StartupPhase) {
|
|
273
|
-
/** Validate environment variables and config files */
|
|
274
|
-
StartupPhase[StartupPhase["Validation"] = 1] = "Validation";
|
|
275
|
-
/** Discover plugin constructors via decorators or registry */
|
|
276
|
-
StartupPhase[StartupPhase["Discovery"] = 2] = "Discovery";
|
|
277
|
-
/** Register plugin metadata and declared dependencies */
|
|
278
|
-
StartupPhase[StartupPhase["Registration"] = 3] = "Registration";
|
|
279
|
-
/** Inject and validate plugin-specific configuration */
|
|
280
|
-
StartupPhase[StartupPhase["Configuration"] = 4] = "Configuration";
|
|
281
|
-
/** Instantiate plugin classes with Core and arguments */
|
|
282
|
-
StartupPhase[StartupPhase["Instantiation"] = 5] = "Instantiation";
|
|
283
|
-
/** Activate plugins by calling their init/setup methods */
|
|
284
|
-
StartupPhase[StartupPhase["Activation"] = 6] = "Activation";
|
|
285
|
-
/** Mark seedcord as ready and start handling interactions */
|
|
286
|
-
StartupPhase[StartupPhase["Ready"] = 7] = "Ready";
|
|
287
|
-
return StartupPhase;
|
|
288
|
-
}({});
|
|
289
|
-
/** Define the order of phases */
|
|
290
|
-
const PHASE_ORDER = [
|
|
291
|
-
1,
|
|
292
|
-
2,
|
|
293
|
-
3,
|
|
294
|
-
4,
|
|
295
|
-
5,
|
|
296
|
-
6,
|
|
297
|
-
7
|
|
298
|
-
];
|
|
299
|
-
/**
|
|
300
|
-
* Manages bot startup lifecycle with ordered phases
|
|
301
|
-
*
|
|
302
|
-
* Coordinates initialization of all bot components in a predictable sequence.
|
|
303
|
-
* Tasks are executed within their designated phases to ensure proper dependency order.
|
|
304
|
-
*/
|
|
305
|
-
var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
306
|
-
isStartingUp = false;
|
|
307
|
-
hasStarted = false;
|
|
308
|
-
constructor() {
|
|
309
|
-
super("Startup", PHASE_ORDER, StartupPhase);
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Adds a task to a specific startup phase with timeout.
|
|
313
|
-
*
|
|
314
|
-
* @param phase - The startup phase from {@link StartupPhase}
|
|
315
|
-
* @param taskName - Unique identifier for the task
|
|
316
|
-
* @param task - Async function to execute
|
|
317
|
-
* @param timeoutMs - Task timeout in milliseconds. {@default `10000`}
|
|
318
|
-
*/
|
|
319
|
-
addTask(phase, taskName, task, timeoutMs = 1e4) {
|
|
320
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
321
|
-
}
|
|
322
|
-
canAddTask() {
|
|
323
|
-
if (this.hasStarted) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleAddAfterCompletion);
|
|
324
|
-
if (this.isStartingUp) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleAddDuringRun);
|
|
325
|
-
return true;
|
|
326
|
-
}
|
|
327
|
-
canRemoveTask() {
|
|
328
|
-
if (this.isStartingUp) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleRemoveDuringRun);
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
|
-
getTaskType() {
|
|
332
|
-
return "startup";
|
|
333
|
-
}
|
|
334
|
-
async executeTasksInPhase(phase, tasks) {
|
|
335
|
-
const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
|
|
336
|
-
return Promise.allSettled(promises);
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Executes the coordinated startup sequence.
|
|
340
|
-
*
|
|
341
|
-
* Runs all registered tasks across startup phases in the correct order.
|
|
342
|
-
* Each phase completes before the next phase begins. Tasks within a phase
|
|
343
|
-
* are executed sequentially to maintain predictable initialization.
|
|
344
|
-
*
|
|
345
|
-
* @returns Promise that resolves when startup is complete
|
|
346
|
-
* @throws An {@link Error} If startup fails or is called multiple times
|
|
347
|
-
* @example
|
|
348
|
-
* ```typescript
|
|
349
|
-
* const startup = new CoordinatedStartup();
|
|
350
|
-
* startup.addTask(StartupPhase.Services, 'database', () => db.connect(), 10000);
|
|
351
|
-
* await startup.run();
|
|
352
|
-
* ```
|
|
353
|
-
*/
|
|
354
|
-
async run() {
|
|
355
|
-
if (this.hasStarted) {
|
|
356
|
-
this.logger.warn("Startup sequence has already completed");
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
if (this.isStartingUp) {
|
|
360
|
-
this.logger.warn("Startup sequence already in progress");
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
this.isStartingUp = true;
|
|
364
|
-
this.logger.info(`${chalk.default.bold.green("Starting")} coordinated startup sequence`);
|
|
365
|
-
this.emitSafe("startup:start");
|
|
366
|
-
try {
|
|
367
|
-
for (const phase of PHASE_ORDER) {
|
|
368
|
-
if (!this.isStartingUp) {
|
|
369
|
-
this.logger.warn("Startup sequence aborted");
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
await this.runPhase(phase);
|
|
373
|
-
}
|
|
374
|
-
this.hasStarted = true;
|
|
375
|
-
this.logger.info(`${chalk.default.bold.green("Coordinated startup completed")} successfully`);
|
|
376
|
-
this.emitSafe("startup:complete");
|
|
377
|
-
} catch (error) {
|
|
378
|
-
if (!this.isStartingUp) {
|
|
379
|
-
this.logger.warn("Startup sequence aborted during error handling");
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
this.logger.error(`${chalk.default.bold.red("Coordinated startup failed")}`);
|
|
383
|
-
this.emitSafe("startup:error", error);
|
|
384
|
-
throw error;
|
|
385
|
-
} finally {
|
|
386
|
-
this.isStartingUp = false;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
async runTaskWithTimeout(phase, task) {
|
|
390
|
-
this.logger.info(`${chalk.default.italic("Starting")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}`);
|
|
391
|
-
let timeoutId;
|
|
392
|
-
try {
|
|
393
|
-
await Promise.race([task.task(), new Promise((_, reject) => {
|
|
394
|
-
timeoutId = setTimeout(() => {
|
|
395
|
-
reject(new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.LifecycleTaskTimeout, [task.name, task.timeout]));
|
|
396
|
-
}, task.timeout);
|
|
397
|
-
})]);
|
|
398
|
-
this.logger.info(`${chalk.default.italic("Completed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}`);
|
|
399
|
-
} catch (error) {
|
|
400
|
-
if (!this.isStartingUp) return;
|
|
401
|
-
this.logger.error(`${chalk.default.italic("Failed")} task ${chalk.default.bold.cyan(task.name)} in phase ${chalk.default.bold.magenta(StartupPhase[phase])}:`, error);
|
|
402
|
-
throw error;
|
|
403
|
-
} finally {
|
|
404
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Aborts the startup sequence if it is currently running.
|
|
409
|
-
*/
|
|
410
|
-
abort() {
|
|
411
|
-
if (!this.isStartingUp) return;
|
|
412
|
-
this.isStartingUp = false;
|
|
413
|
-
this.logger.warn("Aborting coordinated startup sequence");
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Check if startup has completed
|
|
417
|
-
*/
|
|
418
|
-
get isReady() {
|
|
419
|
-
return this.hasStarted;
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Check if startup is currently running
|
|
423
|
-
*/
|
|
424
|
-
get isRunning() {
|
|
425
|
-
return this.isStartingUp;
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
//#endregion
|
|
430
|
-
//#region src/node/HealthCheck.ts
|
|
431
|
-
const HTTP_OK = 200;
|
|
432
|
-
const HTTP_NOT_FOUND = 404;
|
|
433
|
-
const DEFAULT_HEALTH_CHECK_PORT = 6967;
|
|
434
|
-
const DEFAULT_HEALTH_CHECK_PATH = "/healthcheck";
|
|
435
|
-
/**
|
|
436
|
-
* HTTP health check service for monitoring bot status.
|
|
437
|
-
*
|
|
438
|
-
* Provides a simple HTTP endpoint that responds with JSON status
|
|
439
|
-
* information, useful for container orchestration and monitoring.
|
|
440
|
-
*/
|
|
441
|
-
var HealthCheck = class {
|
|
442
|
-
/** @internal */
|
|
443
|
-
logger = new _seedcord_logger.Logger("HealthCheck");
|
|
444
|
-
port;
|
|
445
|
-
path;
|
|
446
|
-
host;
|
|
447
|
-
server;
|
|
448
|
-
constructor(shutdown, options) {
|
|
449
|
-
this.port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;
|
|
450
|
-
this.path = options?.path ?? DEFAULT_HEALTH_CHECK_PATH;
|
|
451
|
-
this.host = options?.host;
|
|
452
|
-
shutdown.addTask(2, "stop-healthcheck-server", async () => await this.stop());
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Starts the health check server.
|
|
456
|
-
* @returns Promise that resolves when the server is listening
|
|
457
|
-
* @internal
|
|
458
|
-
*/
|
|
459
|
-
async init() {
|
|
460
|
-
return new Promise((resolve, reject) => {
|
|
461
|
-
const server = (0, node_http.createServer)((req, res) => {
|
|
462
|
-
if (req.method === "GET" && req.url === this.path) {
|
|
463
|
-
res.writeHead(HTTP_OK, { "Content-Type": "application/json" });
|
|
464
|
-
res.end(JSON.stringify({
|
|
465
|
-
status: "ok",
|
|
466
|
-
timestamp: Date.now()
|
|
467
|
-
}));
|
|
468
|
-
} else {
|
|
469
|
-
res.writeHead(HTTP_NOT_FOUND, { "Content-Type": "application/json" });
|
|
470
|
-
res.end(JSON.stringify({ status: "not found" }));
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
this.server = server;
|
|
474
|
-
const onListenError = (err) => reject(err);
|
|
475
|
-
server.on("error", onListenError);
|
|
476
|
-
server.once("listening", () => {
|
|
477
|
-
server.removeListener("error", onListenError);
|
|
478
|
-
server.on("error", (err) => this.logger.error("Health check server error", err));
|
|
479
|
-
const address = this.host ?? "localhost";
|
|
480
|
-
this.logger.info(`${chalk.default.green.bold("✓")} Health check server listening on ${chalk.default.cyan(`http://${address}:${this.port}${this.path}`)}`);
|
|
481
|
-
resolve();
|
|
482
|
-
});
|
|
483
|
-
if (this.host) {
|
|
484
|
-
this.logger.debug(`Binding health check server to ${this.host}`);
|
|
485
|
-
server.listen(this.port, this.host);
|
|
486
|
-
} else {
|
|
487
|
-
this.logger.debug("Binding health check server to all interfaces");
|
|
488
|
-
server.listen(this.port);
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Stops the health check server.
|
|
494
|
-
*
|
|
495
|
-
* @returns Promise that resolves when the server is closed
|
|
496
|
-
* @internal
|
|
497
|
-
*/
|
|
498
|
-
stop() {
|
|
499
|
-
const server = this.server;
|
|
500
|
-
if (!server?.listening) return Promise.resolve();
|
|
501
|
-
return new Promise((resolve, reject) => {
|
|
502
|
-
server.once("close", () => resolve());
|
|
503
|
-
server.close((err) => {
|
|
504
|
-
if (err) {
|
|
505
|
-
reject(err);
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
this.logger.info(chalk.default.bold.red("Health check server stopped"));
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
//#endregion
|
|
515
|
-
exports.CoordinatedLifecycle = CoordinatedLifecycle;
|
|
516
|
-
exports.CoordinatedShutdown = CoordinatedShutdown;
|
|
517
|
-
exports.CoordinatedStartup = CoordinatedStartup;
|
|
518
|
-
exports.HealthCheck = HealthCheck;
|
|
519
|
-
exports.ShutdownPhase = ShutdownPhase;
|
|
520
|
-
exports.StartupPhase = StartupPhase;
|
|
521
|
-
//# sourceMappingURL=node.index.cjs.map
|