@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.
Files changed (66) hide show
  1. package/dist/{AutocompleteOptions-eB87SZ4a.d.mts → AutocompleteOptions-DLp_B8as.d.mts} +66 -124
  2. package/dist/HmrModuleHandler-3sNgKHdf.mjs +163 -0
  3. package/dist/HmrModuleHandler-3sNgKHdf.mjs.map +1 -0
  4. package/dist/HmrModuleHandler-CTBng7wV.cjs +170 -0
  5. package/dist/HmrModuleHandler-CTBng7wV.cjs.map +1 -0
  6. package/dist/Plugin-3rlH0uxH.d.mts +620 -0
  7. package/dist/Plugin-Cgp1a0uQ.cjs +123 -0
  8. package/dist/Plugin-Cgp1a0uQ.cjs.map +1 -0
  9. package/dist/Plugin-j9GYdroS.mjs +106 -0
  10. package/dist/Plugin-j9GYdroS.mjs.map +1 -0
  11. package/dist/{permissions-DQL5ViZn.cjs → Silence-DiP95iwD.cjs} +54 -305
  12. package/dist/Silence-DiP95iwD.cjs.map +1 -0
  13. package/dist/{permissions-SokZLJMj.mjs → Silence-dbC2slOB.mjs} +47 -238
  14. package/dist/Silence-dbC2slOB.mjs.map +1 -0
  15. package/dist/basic-D7bDlgSP.d.mts +10 -0
  16. package/dist/devChannel-CQU7hq7Q.mjs +14 -0
  17. package/dist/devChannel-CQU7hq7Q.mjs.map +1 -0
  18. package/dist/devChannel-CoCmAVDx.cjs +26 -0
  19. package/dist/devChannel-CoCmAVDx.cjs.map +1 -0
  20. package/dist/hmr.index.cjs +2 -164
  21. package/dist/hmr.index.mjs +2 -162
  22. package/dist/index-Dueqztyy.d.mts +142 -0
  23. package/dist/index.cjs +53 -72
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.mts +7 -4
  26. package/dist/index.mjs +9 -35
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/internal.index.cjs +267 -89
  29. package/dist/internal.index.cjs.map +1 -1
  30. package/dist/internal.index.d.mts +68 -3
  31. package/dist/internal.index.mjs +218 -48
  32. package/dist/internal.index.mjs.map +1 -1
  33. package/dist/node-internal.index.cjs +716 -0
  34. package/dist/node-internal.index.cjs.map +1 -0
  35. package/dist/node-internal.index.d.cts +1 -0
  36. package/dist/node-internal.index.d.mts +261 -0
  37. package/dist/node-internal.index.mjs +706 -0
  38. package/dist/node-internal.index.mjs.map +1 -0
  39. package/dist/phases-BfpAG6w3.mjs +31 -0
  40. package/dist/phases-BfpAG6w3.mjs.map +1 -0
  41. package/dist/phases-C_BGNyVo.cjs +43 -0
  42. package/dist/phases-C_BGNyVo.cjs.map +1 -0
  43. package/dist/plugin.index.cjs +4 -0
  44. package/dist/plugin.index.d.cts +1 -0
  45. package/dist/plugin.index.d.mts +3 -0
  46. package/dist/plugin.index.mjs +3 -0
  47. package/dist/publishDefault-CCf1azzW.mjs +264 -0
  48. package/dist/publishDefault-CCf1azzW.mjs.map +1 -0
  49. package/dist/publishDefault-l0NQ-SHo.cjs +353 -0
  50. package/dist/publishDefault-l0NQ-SHo.cjs.map +1 -0
  51. package/dist/subscribers-B1G6_LlK.cjs +517 -0
  52. package/dist/subscribers-B1G6_LlK.cjs.map +1 -0
  53. package/dist/subscribers-DYqFVP1X.mjs +480 -0
  54. package/dist/subscribers-DYqFVP1X.mjs.map +1 -0
  55. package/package.json +25 -13
  56. package/dist/hmr.index.cjs.map +0 -1
  57. package/dist/hmr.index.mjs.map +0 -1
  58. package/dist/is-never-Ddaba1Su.d.mts +0 -58
  59. package/dist/node.index.cjs +0 -521
  60. package/dist/node.index.cjs.map +0 -1
  61. package/dist/node.index.d.cts +0 -1
  62. package/dist/node.index.d.mts +0 -2391
  63. package/dist/node.index.mjs +0 -513
  64. package/dist/node.index.mjs.map +0 -1
  65. package/dist/permissions-DQL5ViZn.cjs.map +0 -1
  66. package/dist/permissions-SokZLJMj.mjs.map +0 -1
@@ -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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"}
@@ -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
@@ -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