@seedcord/core 0.1.0 → 0.1.1-next.0
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/{HmrModuleHandler-CTBng7wV.cjs → HmrModuleHandler-CxGOo1LT.cjs} +43 -31
- package/dist/HmrModuleHandler-CxGOo1LT.cjs.map +1 -0
- package/dist/{HmrModuleHandler-3sNgKHdf.mjs → HmrModuleHandler-KxRP8lqJ.mjs} +43 -29
- package/dist/HmrModuleHandler-KxRP8lqJ.mjs.map +1 -0
- package/dist/{Plugin-BZit5HUI.d.mts → Plugin-BPz0obbN.d.mts} +19 -13
- package/dist/Plugin-CDAV7P9l.mjs.map +1 -1
- package/dist/{Plugin-BlkhHNRm.cjs → Plugin-DVNh1kvX.cjs} +3 -3
- package/dist/{Plugin-BlkhHNRm.cjs.map → Plugin-DVNh1kvX.cjs.map} +1 -1
- package/dist/{basic-1hebYVcN.d.mts → basic-CeSFvbaz.d.mts} +2 -3
- package/dist/{contextMenuLeaves-f2rbyo83.d.mts → contextMenuLeaves-CC3-qObq.d.mts} +1 -2
- package/dist/{cursor-j5bRcn2O.d.mts → cursor-Bk-W5MTq.d.mts} +16 -16
- package/dist/{cursor-BMkFAwO2.cjs → cursor-D2iP-CCS.cjs} +7 -4
- package/dist/cursor-D2iP-CCS.cjs.map +1 -0
- package/dist/{cursor-BvSH8tgw.mjs → cursor-Y6ct-N_8.mjs} +7 -4
- package/dist/{cursor-BvSH8tgw.mjs.map → cursor-Y6ct-N_8.mjs.map} +1 -1
- package/dist/{devChannel-CoCmAVDx.cjs → devChannel-Cdnmz6hd.cjs} +1 -2
- package/dist/{devChannel-CoCmAVDx.cjs.map → devChannel-Cdnmz6hd.cjs.map} +1 -1
- package/dist/hmr.index.cjs +1 -1
- package/dist/hmr.index.d.mts +2 -1
- package/dist/hmr.index.mjs +1 -1
- package/dist/{index-DafutfX8.d.mts → index-Dm0WKN37.d.mts} +4 -4
- package/dist/index.cjs +11 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +10 -6
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.cjs +28 -29
- package/dist/internal.index.cjs.map +1 -1
- package/dist/internal.index.d.mts +9 -15
- package/dist/internal.index.mjs +28 -27
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.cjs +63 -74
- package/dist/node-internal.index.cjs.map +1 -1
- package/dist/node-internal.index.d.mts +17 -24
- package/dist/node-internal.index.mjs +59 -70
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/{phases-DcuDHoQl.cjs → phases-8qiiLKjS.cjs} +1 -2
- package/dist/{phases-DcuDHoQl.cjs.map → phases-8qiiLKjS.cjs.map} +1 -1
- package/dist/plugin.index.cjs +1 -1
- package/dist/plugin.index.d.mts +1 -1
- package/dist/slashRouteLeaves-nfxOAM7Q.cjs.map +1 -1
- package/dist/{subscribers-CojcCgj1.cjs → subscribers-CDbep0RH.cjs} +36 -4
- package/dist/subscribers-CDbep0RH.cjs.map +1 -0
- package/dist/{subscribers-CmDKqC7y.mjs → subscribers-xO6DZ-70.mjs} +2 -2
- package/dist/{subscribers-CmDKqC7y.mjs.map → subscribers-xO6DZ-70.mjs.map} +1 -1
- package/package.json +18 -15
- package/dist/HmrModuleHandler-3sNgKHdf.mjs.map +0 -1
- package/dist/HmrModuleHandler-CTBng7wV.cjs.map +0 -1
- package/dist/cursor-BMkFAwO2.cjs.map +0 -1
- package/dist/rolldown-runtime-C_NdSu1c.cjs +0 -34
- package/dist/subscribers-CojcCgj1.cjs.map +0 -1
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
let _seedcord_logger = require("@seedcord/logger");
|
|
2
2
|
let _seedcord_utils = require("@seedcord/utils");
|
|
3
|
-
let chalk = require("chalk");
|
|
4
|
-
chalk = require_rolldown_runtime.__toESM(chalk, 1);
|
|
5
3
|
let node_fs = require("node:fs");
|
|
6
4
|
let node_path = require("node:path");
|
|
7
5
|
let node_url = require("node:url");
|
|
@@ -32,40 +30,56 @@ var HmrModuleHandler = class {
|
|
|
32
30
|
/** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */
|
|
33
31
|
async handle(event) {
|
|
34
32
|
const { file, affectedModules, type } = event;
|
|
35
|
-
const { logger
|
|
33
|
+
const { logger } = this.options;
|
|
36
34
|
if (type === "delete" || type === "deleteDir") {
|
|
37
|
-
this.unload(file);
|
|
35
|
+
this.report("Unloaded", this.unload(file), [file]);
|
|
38
36
|
return;
|
|
39
37
|
}
|
|
40
38
|
if (type === "createDir") return;
|
|
41
39
|
if (type === "update" && !(0, node_fs.existsSync)(file)) {
|
|
42
40
|
if (this.isTracked(file)) {
|
|
43
|
-
logger.
|
|
44
|
-
this.unload(file);
|
|
41
|
+
logger.debug(`Received update for non-existent file: ${(0, _seedcord_utils.formatFilePath)(file)}, treating as delete`);
|
|
42
|
+
this.report("Unloaded", this.unload(file), [file]);
|
|
45
43
|
}
|
|
46
44
|
return;
|
|
47
45
|
}
|
|
48
46
|
const rollbackEnabled = event.rollback ?? true;
|
|
49
47
|
const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];
|
|
50
|
-
const
|
|
51
|
-
const
|
|
48
|
+
const startedAt = Date.now();
|
|
49
|
+
const names = [];
|
|
50
|
+
const touched = [];
|
|
52
51
|
for (const fileToReload of filesToReload) {
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
|
|
52
|
+
if (!this.inScope(fileToReload)) continue;
|
|
53
|
+
const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);
|
|
54
|
+
if (reloaded.length === 0) continue;
|
|
55
|
+
names.push(...reloaded);
|
|
56
|
+
touched.push(fileToReload);
|
|
57
57
|
}
|
|
58
|
+
this.report(type === "create" ? "Registered" : "Reloaded", names, touched, Date.now() - startedAt);
|
|
59
|
+
}
|
|
60
|
+
inScope(file) {
|
|
61
|
+
const { handlersDir, middlewaresDir } = this.options;
|
|
62
|
+
if (file.startsWith((0, node_path.resolve)(process.cwd(), handlersDir) + node_path.sep)) return true;
|
|
63
|
+
return middlewaresDir ? file.startsWith((0, node_path.resolve)(process.cwd(), middlewaresDir) + node_path.sep) : false;
|
|
64
|
+
}
|
|
65
|
+
report(verb, names, files, elapsedMs) {
|
|
66
|
+
if (names.length === 0) return;
|
|
67
|
+
const from = files.length === 1 ? (0, _seedcord_utils.formatFilePath)(files[0] ?? "") : `${String(files.length)} files`;
|
|
68
|
+
const timing = elapsedMs === void 0 ? "" : ` ${_seedcord_logger.paint.mute("in")} ${String(elapsedMs)}ms`;
|
|
69
|
+
this.options.logger.debug(`${verb} ${_seedcord_logger.paint.sky.bold(names.join(", "))} from ${_seedcord_logger.paint.mute(from)}${timing}`);
|
|
58
70
|
}
|
|
59
71
|
async reloadWithRollback(file, rollbackEnabled) {
|
|
60
72
|
const { logger } = this.options;
|
|
61
73
|
const snapshot = this.snapshotUnits(file);
|
|
62
74
|
this.unload(file);
|
|
63
|
-
|
|
75
|
+
const reloaded = await this.reloadFile(file);
|
|
76
|
+
if (reloaded !== null) return reloaded;
|
|
64
77
|
this.unload(file);
|
|
65
|
-
if (!rollbackEnabled) return;
|
|
66
|
-
if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return;
|
|
78
|
+
if (!rollbackEnabled) return [];
|
|
79
|
+
if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return [];
|
|
67
80
|
this.restoreUnits(file, snapshot);
|
|
68
|
-
logger.warn(`${
|
|
81
|
+
logger.warn(`${_seedcord_logger.paint.amber.bold("Rolled back")} ${_seedcord_logger.paint.mute((0, _seedcord_utils.formatFilePath)(file))} to the last-good version`);
|
|
82
|
+
return [];
|
|
69
83
|
}
|
|
70
84
|
/** Links the handler to its source file so a later update can unload it. */
|
|
71
85
|
trackHandler(file, handler) {
|
|
@@ -93,15 +107,14 @@ var HmrModuleHandler = class {
|
|
|
93
107
|
return this.store.fileToHandlers.has(file) || this.store.fileToMiddlewares.has(file);
|
|
94
108
|
}
|
|
95
109
|
unload(file) {
|
|
96
|
-
const
|
|
110
|
+
const names = [];
|
|
97
111
|
const handlers = this.store.fileToHandlers.get(file);
|
|
98
112
|
if (handlers) {
|
|
99
113
|
for (const handler of handlers) {
|
|
100
114
|
const artifacts = this.store.handlerArtifacts.get(handler);
|
|
101
115
|
this.options.unregisterHandler(handler, artifacts);
|
|
102
116
|
this.store.handlerArtifacts.delete(handler);
|
|
103
|
-
|
|
104
|
-
logger.info(`${chalk.default.red.bold("Unloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
|
|
117
|
+
names.push(displayName(handler, "Handler"));
|
|
105
118
|
}
|
|
106
119
|
this.store.fileToHandlers.delete(file);
|
|
107
120
|
}
|
|
@@ -109,37 +122,36 @@ var HmrModuleHandler = class {
|
|
|
109
122
|
if (middlewares) {
|
|
110
123
|
for (const middleware of middlewares) {
|
|
111
124
|
this.options.unregisterMiddleware?.(middleware);
|
|
112
|
-
|
|
113
|
-
logger.info(`${chalk.default.red.bold("Unloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
|
|
125
|
+
names.push(displayName(middleware, "Middleware"));
|
|
114
126
|
}
|
|
115
127
|
this.store.fileToMiddlewares.delete(file);
|
|
116
128
|
}
|
|
129
|
+
return names;
|
|
117
130
|
}
|
|
118
131
|
async reloadFile(file) {
|
|
119
132
|
const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;
|
|
120
133
|
if (!(0, node_fs.existsSync)(file)) {
|
|
121
|
-
logger.
|
|
122
|
-
return
|
|
134
|
+
logger.debug(`File does not exist, skipping reload: ${(0, _seedcord_utils.formatFilePath)(file)}`);
|
|
135
|
+
return null;
|
|
123
136
|
}
|
|
124
137
|
try {
|
|
125
138
|
let fileUrl = (0, node_url.pathToFileURL)(file).href;
|
|
126
139
|
if (process.env.VITEST === "true") fileUrl += `?update=${Date.now()}`;
|
|
127
140
|
const imported = await import(fileUrl);
|
|
141
|
+
const names = [];
|
|
128
142
|
for (const val of Object.values(imported)) if (isHandler(val)) {
|
|
129
143
|
registerHandler(val, file);
|
|
130
144
|
this.trackHandler(file, val);
|
|
131
|
-
|
|
132
|
-
logger.info(`${chalk.default.blue.bold("Reloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
|
|
145
|
+
names.push(displayName(val, "Handler"));
|
|
133
146
|
} else if (isMiddleware && registerMiddleware && isMiddleware(val)) {
|
|
134
147
|
registerMiddleware(val, file);
|
|
135
148
|
this.trackMiddleware(file, val);
|
|
136
|
-
|
|
137
|
-
logger.info(`${chalk.default.blue.bold("Reloaded")} ${chalk.default.cyan.bold(name)} from ${chalk.default.gray((0, _seedcord_utils.formatFilePath)(file))}`);
|
|
149
|
+
names.push(displayName(val, "Middleware"));
|
|
138
150
|
}
|
|
139
|
-
return
|
|
151
|
+
return names;
|
|
140
152
|
} catch (error) {
|
|
141
153
|
logger.error(`Failed to reload file: ${file}`, error);
|
|
142
|
-
return
|
|
154
|
+
return null;
|
|
143
155
|
}
|
|
144
156
|
}
|
|
145
157
|
snapshotUnits(file) {
|
|
@@ -167,4 +179,4 @@ Object.defineProperty(exports, 'HmrModuleHandler', {
|
|
|
167
179
|
return HmrModuleHandler;
|
|
168
180
|
}
|
|
169
181
|
});
|
|
170
|
-
//# sourceMappingURL=HmrModuleHandler-
|
|
182
|
+
//# sourceMappingURL=HmrModuleHandler-CxGOo1LT.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HmrModuleHandler-CxGOo1LT.cjs","names":["existsSync","formatFilePath","resolve","sep","paint","pathToFileURL"],"sources":["../src/hmr/HmrModuleHandler.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { paint } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\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 } = this.options;\n\n if (type === 'delete' || type === 'deleteDir') {\n this.report('Unloaded', this.unload(file), [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.debug(`Received update for non-existent file: ${formatFilePath(file)}, treating as delete`);\n this.report('Unloaded', this.unload(file), [file]);\n }\n return;\n }\n\n const rollbackEnabled = event.rollback ?? true;\n const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];\n\n const startedAt = Date.now();\n const names: string[] = [];\n const touched: string[] = [];\n\n for (const fileToReload of filesToReload) {\n if (!this.inScope(fileToReload)) continue;\n\n const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);\n if (reloaded.length === 0) continue;\n\n names.push(...reloaded);\n touched.push(fileToReload);\n }\n\n this.report(type === 'create' ? 'Registered' : 'Reloaded', names, touched, Date.now() - startedAt);\n }\n\n private inScope(file: string): boolean {\n const { handlersDir, middlewaresDir } = this.options;\n\n // the separator keeps a sibling dir sharing the prefix (commands-extra vs commands) out\n if (file.startsWith(resolve(process.cwd(), handlersDir) + sep)) return true;\n return middlewaresDir ? file.startsWith(resolve(process.cwd(), middlewaresDir) + sep) : false;\n }\n\n // one line per save, since one edit can reload every module that imports the file\n private report(verb: string, names: string[], files: string[], elapsedMs?: number): void {\n if (names.length === 0) return;\n\n const from = files.length === 1 ? formatFilePath(files[0] ?? '') : `${String(files.length)} files`;\n const timing = elapsedMs === undefined ? '' : ` ${paint.mute('in')} ${String(elapsedMs)}ms`;\n this.options.logger.debug(`${verb} ${paint.sky.bold(names.join(', '))} from ${paint.mute(from)}${timing}`);\n }\n\n private async reloadWithRollback(file: string, rollbackEnabled: boolean): Promise<string[]> {\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 !== null) return reloaded;\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 the last-good units live until the next good save\n this.restoreUnits(file, snapshot);\n logger.warn(`${paint.amber.bold('Rolled back')} ${paint.mute(formatFilePath(file))} to the last-good version`);\n return [];\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): string[] {\n const names: string[] = [];\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 names.push(displayName(handler, 'Handler'));\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 names.push(displayName(middleware, 'Middleware'));\n }\n this.store.fileToMiddlewares.delete(file);\n }\n\n return names;\n }\n\n // only a failed reload returns null\n private async reloadFile(file: string): Promise<string[] | null> {\n const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;\n\n if (!existsSync(file)) {\n logger.debug(`File does not exist, skipping reload: ${formatFilePath(file)}`);\n return null;\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 const names: string[] = [];\n for (const val of Object.values(imported)) {\n if (isHandler(val)) {\n registerHandler(val, file);\n this.trackHandler(file, val);\n names.push(displayName(val, 'Handler'));\n } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {\n registerMiddleware(val, file);\n this.trackMiddleware(file, val);\n names.push(displayName(val, 'Middleware'));\n }\n }\n\n return names;\n } catch (error) {\n logger.error(`Failed to reload file: ${file}`, error);\n return null;\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":";;;;;;;AAiBA,MAAM,eAAe,MAAe,aAA8B,KAA2B,QAAQ;;;;;;;;AAuBrG,IAAa,mBAAb,MAAkF;CAC9E,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAAqE;EAE7E,KAAK,UAAU;GAAE,GAAG;GAAS,QAAQ,QAAQ,OAAO,UAAU,KAAK;EAAE;EAGrE,KAAK,QAAQ;GACT,gCAAgB,IAAI,IAAI;GACxB,mCAAmB,IAAI,IAAI;GAC3B,kCAAkB,IAAI,IAAI;EAC9B;CACJ;;CAGA,MAAa,OAAO,OAAsC;EACtD,MAAM,EAAE,MAAM,iBAAiB,SAAS;EACxC,MAAM,EAAE,WAAW,KAAK;EAExB,IAAI,SAAS,YAAY,SAAS,aAAa;GAC3C,KAAK,OAAO,YAAY,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC;GACjD;EACJ;EAGA,IAAI,SAAS,aAAa;EAE1B,IAAI,SAAS,YAAY,KAACA,oBAAW,IAAI,GAAG;GACxC,IAAI,KAAK,UAAU,IAAI,GAAG;IACtB,OAAO,MAAM,8CAA0CC,gCAAe,IAAI,EAAE,qBAAqB;IACjG,KAAK,OAAO,YAAY,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC;GACrD;GACA;EACJ;EAEA,MAAM,kBAAkB,MAAM,YAAY;EAC1C,MAAM,gBAAgB,mBAAmB,gBAAgB,SAAS,IAAI,kBAAkB,CAAC,IAAI;EAE7F,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,QAAkB,CAAC;EACzB,MAAM,UAAoB,CAAC;EAE3B,KAAK,MAAM,gBAAgB,eAAe;GACtC,IAAI,CAAC,KAAK,QAAQ,YAAY,GAAG;GAEjC,MAAM,WAAW,MAAM,KAAK,mBAAmB,cAAc,eAAe;GAC5E,IAAI,SAAS,WAAW,GAAG;GAE3B,MAAM,KAAK,GAAG,QAAQ;GACtB,QAAQ,KAAK,YAAY;EAC7B;EAEA,KAAK,OAAO,SAAS,WAAW,eAAe,YAAY,OAAO,SAAS,KAAK,IAAI,IAAI,SAAS;CACrG;CAEA,AAAQ,QAAQ,MAAuB;EACnC,MAAM,EAAE,aAAa,mBAAmB,KAAK;EAG7C,IAAI,KAAK,eAAWC,mBAAQ,QAAQ,IAAI,GAAG,WAAW,IAAIC,aAAG,GAAG,OAAO;EACvE,OAAO,iBAAiB,KAAK,eAAWD,mBAAQ,QAAQ,IAAI,GAAG,cAAc,IAAIC,aAAG,IAAI;CAC5F;CAGA,AAAQ,OAAO,MAAc,OAAiB,OAAiB,WAA0B;EACrF,IAAI,MAAM,WAAW,GAAG;EAExB,MAAM,OAAO,MAAM,WAAW,QAAIF,gCAAe,MAAM,MAAM,EAAE,IAAI,GAAG,OAAO,MAAM,MAAM,EAAE;EAC3F,MAAM,SAAS,cAAc,SAAY,KAAK,IAAIG,uBAAM,KAAK,IAAI,EAAE,GAAG,OAAO,SAAS,EAAE;EACxF,KAAK,QAAQ,OAAO,MAAM,GAAG,KAAK,GAAGA,uBAAM,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE,QAAQA,uBAAM,KAAK,IAAI,IAAI,QAAQ;CAC7G;CAEA,MAAc,mBAAmB,MAAc,iBAA6C;EACxF,MAAM,EAAE,WAAW,KAAK;EACxB,MAAM,WAAW,KAAK,cAAc,IAAI;EACxC,KAAK,OAAO,IAAI;EAEhB,MAAM,WAAW,MAAM,KAAK,WAAW,IAAI;EAC3C,IAAI,aAAa,MAAM,OAAO;EAG9B,KAAK,OAAO,IAAI;EAChB,IAAI,CAAC,iBAAiB,OAAO,CAAC;EAC9B,IAAI,SAAS,SAAS,WAAW,KAAK,SAAS,YAAY,WAAW,GAAG,OAAO,CAAC;EAGjF,KAAK,aAAa,MAAM,QAAQ;EAChC,OAAO,KAAK,GAAGA,uBAAM,MAAM,KAAK,aAAa,EAAE,GAAGA,uBAAM,SAAKH,gCAAe,IAAI,CAAC,EAAE,0BAA0B;EAC7G,OAAO,CAAC;CACZ;;CAGA,AAAO,aAAa,MAAc,SAAyB;EACvD,IAAI,MAAM,KAAK,MAAM,eAAe,IAAI,IAAI;EAC5C,IAAI,CAAC,KAAK;GACN,sBAAM,IAAI,IAAI;GACd,KAAK,MAAM,eAAe,IAAI,MAAM,GAAG;EAC3C;EACA,IAAI,IAAI,OAAO;EAEf,IAAI,KAAK,QAAQ,cAAc;GAC3B,MAAM,YAAY,KAAK,QAAQ,aAAa,OAAO;GACnD,KAAK,MAAM,iBAAiB,IAAI,SAAS,SAAS;EACtD;CACJ;;CAGA,AAAO,gBAAgB,MAAc,YAA+B;EAChE,IAAI,MAAM,KAAK,MAAM,kBAAkB,IAAI,IAAI;EAC/C,IAAI,CAAC,KAAK;GACN,sBAAM,IAAI,IAAI;GACd,KAAK,MAAM,kBAAkB,IAAI,MAAM,GAAG;EAC9C;EACA,IAAI,IAAI,UAAU;CACtB;CAEA,AAAQ,UAAU,MAAuB;EACrC,OAAO,KAAK,MAAM,eAAe,IAAI,IAAI,KAAK,KAAK,MAAM,kBAAkB,IAAI,IAAI;CACvF;CAEA,AAAQ,OAAO,MAAwB;EACnC,MAAM,QAAkB,CAAC;EACzB,MAAM,WAAW,KAAK,MAAM,eAAe,IAAI,IAAI;EACnD,IAAI,UAAU;GACV,KAAK,MAAM,WAAW,UAAU;IAC5B,MAAM,YAAY,KAAK,MAAM,iBAAiB,IAAI,OAAO;IACzD,KAAK,QAAQ,kBAAkB,SAAS,SAAS;IACjD,KAAK,MAAM,iBAAiB,OAAO,OAAO;IAC1C,MAAM,KAAK,YAAY,SAAS,SAAS,CAAC;GAC9C;GACA,KAAK,MAAM,eAAe,OAAO,IAAI;EACzC;EAEA,MAAM,cAAc,KAAK,MAAM,kBAAkB,IAAI,IAAI;EACzD,IAAI,aAAa;GACb,KAAK,MAAM,cAAc,aAAa;IAClC,KAAK,QAAQ,uBAAuB,UAAU;IAC9C,MAAM,KAAK,YAAY,YAAY,YAAY,CAAC;GACpD;GACA,KAAK,MAAM,kBAAkB,OAAO,IAAI;EAC5C;EAEA,OAAO;CACX;CAGA,MAAc,WAAW,MAAwC;EAC7D,MAAM,EAAE,QAAQ,WAAW,cAAc,iBAAiB,uBAAuB,KAAK;EAEtF,IAAI,KAACD,oBAAW,IAAI,GAAG;GACnB,OAAO,MAAM,6CAAyCC,gCAAe,IAAI,GAAG;GAC5E,OAAO;EACX;EAEA,IAAI;GACA,IAAI,cAAUI,wBAAc,IAAI,CAAC,CAAC;GAElC,IAAI,QAAQ,IAAI,WAAW,QAAQ,WAAW,WAAW,KAAK,IAAI;GAGlE,MAAM,WAAY,MAAM,OAAO;GAE/B,MAAM,QAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,OAAO,OAAO,QAAQ,GACpC,IAAI,UAAU,GAAG,GAAG;IAChB,gBAAgB,KAAK,IAAI;IACzB,KAAK,aAAa,MAAM,GAAG;IAC3B,MAAM,KAAK,YAAY,KAAK,SAAS,CAAC;GAC1C,OAAO,IAAI,gBAAgB,sBAAsB,aAAa,GAAG,GAAG;IAChE,mBAAmB,KAAK,IAAI;IAC5B,KAAK,gBAAgB,MAAM,GAAG;IAC9B,MAAM,KAAK,YAAY,KAAK,YAAY,CAAC;GAC7C;GAGJ,OAAO;EACX,SAAS,OAAO;GACZ,OAAO,MAAM,0BAA0B,QAAQ,KAAK;GACpD,OAAO;EACX;CACJ;CAGA,AAAQ,cAAc,MAAoE;EACtF,OAAO;GACH,UAAU,CAAC,GAAI,KAAK,MAAM,eAAe,IAAI,IAAI,KAAK,CAAC,CAAE;GACzD,aAAa,CAAC,GAAI,KAAK,MAAM,kBAAkB,IAAI,IAAI,KAAK,CAAC,CAAE;EACnE;CACJ;CAEA,AAAQ,aAAa,MAAc,UAAsE;EACrG,KAAK,MAAM,WAAW,SAAS,UAAU;GACrC,KAAK,QAAQ,gBAAgB,SAAS,IAAI;GAC1C,KAAK,aAAa,MAAM,OAAO;EACnC;EAEA,IAAI,KAAK,QAAQ,oBACb,KAAK,MAAM,cAAc,SAAS,aAAa;GAC3C,KAAK,QAAQ,mBAAmB,YAAY,IAAI;GAChD,KAAK,gBAAgB,MAAM,UAAU;EACzC;CAER;AACJ"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { paint } from "@seedcord/logger";
|
|
1
2
|
import { formatFilePath } from "@seedcord/utils";
|
|
2
|
-
import chalk from "chalk";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { resolve, sep } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
@@ -30,40 +30,56 @@ var HmrModuleHandler = class {
|
|
|
30
30
|
/** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */
|
|
31
31
|
async handle(event) {
|
|
32
32
|
const { file, affectedModules, type } = event;
|
|
33
|
-
const { logger
|
|
33
|
+
const { logger } = this.options;
|
|
34
34
|
if (type === "delete" || type === "deleteDir") {
|
|
35
|
-
this.unload(file);
|
|
35
|
+
this.report("Unloaded", this.unload(file), [file]);
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
if (type === "createDir") return;
|
|
39
39
|
if (type === "update" && !existsSync(file)) {
|
|
40
40
|
if (this.isTracked(file)) {
|
|
41
|
-
logger.
|
|
42
|
-
this.unload(file);
|
|
41
|
+
logger.debug(`Received update for non-existent file: ${formatFilePath(file)}, treating as delete`);
|
|
42
|
+
this.report("Unloaded", this.unload(file), [file]);
|
|
43
43
|
}
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
const rollbackEnabled = event.rollback ?? true;
|
|
47
47
|
const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];
|
|
48
|
-
const
|
|
49
|
-
const
|
|
48
|
+
const startedAt = Date.now();
|
|
49
|
+
const names = [];
|
|
50
|
+
const touched = [];
|
|
50
51
|
for (const fileToReload of filesToReload) {
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
|
|
52
|
+
if (!this.inScope(fileToReload)) continue;
|
|
53
|
+
const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);
|
|
54
|
+
if (reloaded.length === 0) continue;
|
|
55
|
+
names.push(...reloaded);
|
|
56
|
+
touched.push(fileToReload);
|
|
55
57
|
}
|
|
58
|
+
this.report(type === "create" ? "Registered" : "Reloaded", names, touched, Date.now() - startedAt);
|
|
59
|
+
}
|
|
60
|
+
inScope(file) {
|
|
61
|
+
const { handlersDir, middlewaresDir } = this.options;
|
|
62
|
+
if (file.startsWith(resolve(process.cwd(), handlersDir) + sep)) return true;
|
|
63
|
+
return middlewaresDir ? file.startsWith(resolve(process.cwd(), middlewaresDir) + sep) : false;
|
|
64
|
+
}
|
|
65
|
+
report(verb, names, files, elapsedMs) {
|
|
66
|
+
if (names.length === 0) return;
|
|
67
|
+
const from = files.length === 1 ? formatFilePath(files[0] ?? "") : `${String(files.length)} files`;
|
|
68
|
+
const timing = elapsedMs === void 0 ? "" : ` ${paint.mute("in")} ${String(elapsedMs)}ms`;
|
|
69
|
+
this.options.logger.debug(`${verb} ${paint.sky.bold(names.join(", "))} from ${paint.mute(from)}${timing}`);
|
|
56
70
|
}
|
|
57
71
|
async reloadWithRollback(file, rollbackEnabled) {
|
|
58
72
|
const { logger } = this.options;
|
|
59
73
|
const snapshot = this.snapshotUnits(file);
|
|
60
74
|
this.unload(file);
|
|
61
|
-
|
|
75
|
+
const reloaded = await this.reloadFile(file);
|
|
76
|
+
if (reloaded !== null) return reloaded;
|
|
62
77
|
this.unload(file);
|
|
63
|
-
if (!rollbackEnabled) return;
|
|
64
|
-
if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return;
|
|
78
|
+
if (!rollbackEnabled) return [];
|
|
79
|
+
if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return [];
|
|
65
80
|
this.restoreUnits(file, snapshot);
|
|
66
|
-
logger.warn(`${
|
|
81
|
+
logger.warn(`${paint.amber.bold("Rolled back")} ${paint.mute(formatFilePath(file))} to the last-good version`);
|
|
82
|
+
return [];
|
|
67
83
|
}
|
|
68
84
|
/** Links the handler to its source file so a later update can unload it. */
|
|
69
85
|
trackHandler(file, handler) {
|
|
@@ -91,15 +107,14 @@ var HmrModuleHandler = class {
|
|
|
91
107
|
return this.store.fileToHandlers.has(file) || this.store.fileToMiddlewares.has(file);
|
|
92
108
|
}
|
|
93
109
|
unload(file) {
|
|
94
|
-
const
|
|
110
|
+
const names = [];
|
|
95
111
|
const handlers = this.store.fileToHandlers.get(file);
|
|
96
112
|
if (handlers) {
|
|
97
113
|
for (const handler of handlers) {
|
|
98
114
|
const artifacts = this.store.handlerArtifacts.get(handler);
|
|
99
115
|
this.options.unregisterHandler(handler, artifacts);
|
|
100
116
|
this.store.handlerArtifacts.delete(handler);
|
|
101
|
-
|
|
102
|
-
logger.info(`${chalk.red.bold("Unloaded")} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`);
|
|
117
|
+
names.push(displayName(handler, "Handler"));
|
|
103
118
|
}
|
|
104
119
|
this.store.fileToHandlers.delete(file);
|
|
105
120
|
}
|
|
@@ -107,37 +122,36 @@ var HmrModuleHandler = class {
|
|
|
107
122
|
if (middlewares) {
|
|
108
123
|
for (const middleware of middlewares) {
|
|
109
124
|
this.options.unregisterMiddleware?.(middleware);
|
|
110
|
-
|
|
111
|
-
logger.info(`${chalk.red.bold("Unloaded")} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`);
|
|
125
|
+
names.push(displayName(middleware, "Middleware"));
|
|
112
126
|
}
|
|
113
127
|
this.store.fileToMiddlewares.delete(file);
|
|
114
128
|
}
|
|
129
|
+
return names;
|
|
115
130
|
}
|
|
116
131
|
async reloadFile(file) {
|
|
117
132
|
const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;
|
|
118
133
|
if (!existsSync(file)) {
|
|
119
|
-
logger.
|
|
120
|
-
return
|
|
134
|
+
logger.debug(`File does not exist, skipping reload: ${formatFilePath(file)}`);
|
|
135
|
+
return null;
|
|
121
136
|
}
|
|
122
137
|
try {
|
|
123
138
|
let fileUrl = pathToFileURL(file).href;
|
|
124
139
|
if (process.env.VITEST === "true") fileUrl += `?update=${Date.now()}`;
|
|
125
140
|
const imported = await import(fileUrl);
|
|
141
|
+
const names = [];
|
|
126
142
|
for (const val of Object.values(imported)) if (isHandler(val)) {
|
|
127
143
|
registerHandler(val, file);
|
|
128
144
|
this.trackHandler(file, val);
|
|
129
|
-
|
|
130
|
-
logger.info(`${chalk.blue.bold("Reloaded")} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`);
|
|
145
|
+
names.push(displayName(val, "Handler"));
|
|
131
146
|
} else if (isMiddleware && registerMiddleware && isMiddleware(val)) {
|
|
132
147
|
registerMiddleware(val, file);
|
|
133
148
|
this.trackMiddleware(file, val);
|
|
134
|
-
|
|
135
|
-
logger.info(`${chalk.blue.bold("Reloaded")} ${chalk.cyan.bold(name)} from ${chalk.gray(formatFilePath(file))}`);
|
|
149
|
+
names.push(displayName(val, "Middleware"));
|
|
136
150
|
}
|
|
137
|
-
return
|
|
151
|
+
return names;
|
|
138
152
|
} catch (error) {
|
|
139
153
|
logger.error(`Failed to reload file: ${file}`, error);
|
|
140
|
-
return
|
|
154
|
+
return null;
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
157
|
snapshotUnits(file) {
|
|
@@ -160,4 +174,4 @@ var HmrModuleHandler = class {
|
|
|
160
174
|
|
|
161
175
|
//#endregion
|
|
162
176
|
export { HmrModuleHandler as t };
|
|
163
|
-
//# sourceMappingURL=HmrModuleHandler-
|
|
177
|
+
//# sourceMappingURL=HmrModuleHandler-KxRP8lqJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HmrModuleHandler-KxRP8lqJ.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 { paint } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\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 } = this.options;\n\n if (type === 'delete' || type === 'deleteDir') {\n this.report('Unloaded', this.unload(file), [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.debug(`Received update for non-existent file: ${formatFilePath(file)}, treating as delete`);\n this.report('Unloaded', this.unload(file), [file]);\n }\n return;\n }\n\n const rollbackEnabled = event.rollback ?? true;\n const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];\n\n const startedAt = Date.now();\n const names: string[] = [];\n const touched: string[] = [];\n\n for (const fileToReload of filesToReload) {\n if (!this.inScope(fileToReload)) continue;\n\n const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);\n if (reloaded.length === 0) continue;\n\n names.push(...reloaded);\n touched.push(fileToReload);\n }\n\n this.report(type === 'create' ? 'Registered' : 'Reloaded', names, touched, Date.now() - startedAt);\n }\n\n private inScope(file: string): boolean {\n const { handlersDir, middlewaresDir } = this.options;\n\n // the separator keeps a sibling dir sharing the prefix (commands-extra vs commands) out\n if (file.startsWith(resolve(process.cwd(), handlersDir) + sep)) return true;\n return middlewaresDir ? file.startsWith(resolve(process.cwd(), middlewaresDir) + sep) : false;\n }\n\n // one line per save, since one edit can reload every module that imports the file\n private report(verb: string, names: string[], files: string[], elapsedMs?: number): void {\n if (names.length === 0) return;\n\n const from = files.length === 1 ? formatFilePath(files[0] ?? '') : `${String(files.length)} files`;\n const timing = elapsedMs === undefined ? '' : ` ${paint.mute('in')} ${String(elapsedMs)}ms`;\n this.options.logger.debug(`${verb} ${paint.sky.bold(names.join(', '))} from ${paint.mute(from)}${timing}`);\n }\n\n private async reloadWithRollback(file: string, rollbackEnabled: boolean): Promise<string[]> {\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 !== null) return reloaded;\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 the last-good units live until the next good save\n this.restoreUnits(file, snapshot);\n logger.warn(`${paint.amber.bold('Rolled back')} ${paint.mute(formatFilePath(file))} to the last-good version`);\n return [];\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): string[] {\n const names: string[] = [];\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 names.push(displayName(handler, 'Handler'));\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 names.push(displayName(middleware, 'Middleware'));\n }\n this.store.fileToMiddlewares.delete(file);\n }\n\n return names;\n }\n\n // only a failed reload returns null\n private async reloadFile(file: string): Promise<string[] | null> {\n const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;\n\n if (!existsSync(file)) {\n logger.debug(`File does not exist, skipping reload: ${formatFilePath(file)}`);\n return null;\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 const names: string[] = [];\n for (const val of Object.values(imported)) {\n if (isHandler(val)) {\n registerHandler(val, file);\n this.trackHandler(file, val);\n names.push(displayName(val, 'Handler'));\n } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {\n registerMiddleware(val, file);\n this.trackMiddleware(file, val);\n names.push(displayName(val, 'Middleware'));\n }\n }\n\n return names;\n } catch (error) {\n logger.error(`Failed to reload file: ${file}`, error);\n return null;\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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"}
|
|
@@ -3,7 +3,6 @@ import { TypedEventEmitter } from "@seedcord/event-emitter";
|
|
|
3
3
|
import { REST } from "@discordjs/rest";
|
|
4
4
|
import { Config, EventFrequency, HmrAware, HmrUpdateEvent, IRateLimiter, RenderContext, ReplyResponse, Tail, TypedExclude } from "@seedcord/types";
|
|
5
5
|
import { UUID } from "node:crypto";
|
|
6
|
-
|
|
7
6
|
//#region src/metadataKeys.d.ts
|
|
8
7
|
declare enum InteractionRoutes {
|
|
9
8
|
Slash = "slash",
|
|
@@ -50,7 +49,7 @@ declare function checkAckLegality(method: ReplyMethod, state: AckState, routeId:
|
|
|
50
49
|
declare function sendTarget(state: AckState): 'reply' | 'edit' | 'followUp';
|
|
51
50
|
//#endregion
|
|
52
51
|
//#region src/reply/responseReport.d.ts
|
|
53
|
-
/**
|
|
52
|
+
/** The bus a write publishes on, and the interaction it belongs to. */
|
|
54
53
|
interface ReplyTelemetry {
|
|
55
54
|
readonly bus: Bus;
|
|
56
55
|
readonly interactionId: string;
|
|
@@ -62,7 +61,7 @@ type WriteMethod = ReplyMethod | 'respond';
|
|
|
62
61
|
*
|
|
63
62
|
* @internal
|
|
64
63
|
*/
|
|
65
|
-
declare function reportedWrite<Result>(telemetry: ReplyTelemetry
|
|
64
|
+
declare function reportedWrite<Result>(telemetry: ReplyTelemetry, routeId: string, method: WriteMethod, write: () => Promise<Result>): Promise<Result>;
|
|
66
65
|
//#endregion
|
|
67
66
|
//#region src/stops/Notice.d.ts
|
|
68
67
|
/**
|
|
@@ -211,11 +210,14 @@ interface DefaultSubscriptions {
|
|
|
211
210
|
};
|
|
212
211
|
/** Triggered once per interaction dispatch, after the handler chain settles. */
|
|
213
212
|
interactionDispatched: {
|
|
214
|
-
routeId: string;
|
|
213
|
+
routeId: string;
|
|
214
|
+
/** The interaction this dispatch ran, for joining against `responseAttempted`. */
|
|
215
215
|
interactionId: string;
|
|
216
216
|
kind: `${InteractionRoutes}`;
|
|
217
|
-
outcome: DispatchOutcome;
|
|
218
|
-
|
|
217
|
+
outcome: DispatchOutcome;
|
|
218
|
+
/** True when no route matched and the unhandled default ran. */
|
|
219
|
+
fallback: boolean;
|
|
220
|
+
/** Dispatch entry until the user has a response, replies included. A clock change never affects it. */
|
|
219
221
|
durationMs: number;
|
|
220
222
|
/**
|
|
221
223
|
* Discord's interaction creation until dispatch entry, read from the snowflake. This covers
|
|
@@ -231,12 +233,16 @@ interface DefaultSubscriptions {
|
|
|
231
233
|
* unexpected shape publishes nothing, since the callback succeeded and carries no message to name.
|
|
232
234
|
*/
|
|
233
235
|
responseAttempted: {
|
|
234
|
-
routeId: string;
|
|
235
|
-
|
|
236
|
+
routeId: string;
|
|
237
|
+
/** The interaction this write belongs to, for joining against `interactionDispatched`. */
|
|
238
|
+
interactionId: string;
|
|
239
|
+
/** `send` routes to another verb, so it reports the verb that ran. */
|
|
236
240
|
method: WriteMethod;
|
|
237
241
|
outcome: ResponseOutcome;
|
|
238
|
-
durationMs: number;
|
|
239
|
-
|
|
242
|
+
durationMs: number;
|
|
243
|
+
/** Null for every ack-only or choice-only verb, and for a failed write. */
|
|
244
|
+
messageId: string | null;
|
|
245
|
+
/** Present when `outcome` is `failed`. */
|
|
240
246
|
error?: Error;
|
|
241
247
|
};
|
|
242
248
|
}
|
|
@@ -270,7 +276,7 @@ type PublishableKey = TypedExclude<SubscriptionKey, keyof DefaultSubscriptions>;
|
|
|
270
276
|
* @typeParam KeyOfSubscribers - The subscriber event name
|
|
271
277
|
*/
|
|
272
278
|
type SubscriptionData<KeyOfSubscribers extends SubscriptionKey> = AllSubscriptions[KeyOfSubscribers];
|
|
273
|
-
type SubscriptionTuples = { [K in SubscriptionKey]: [AllSubscriptions[K]] };
|
|
279
|
+
type SubscriptionTuples = { [K in SubscriptionKey]: [AllSubscriptions[K]]; };
|
|
274
280
|
//#endregion
|
|
275
281
|
//#region src/subscribers/Subscriber.d.ts
|
|
276
282
|
/**
|
|
@@ -522,7 +528,7 @@ declare abstract class Plugin<Opts extends PluginOptions = {}, TCore extends Cor
|
|
|
522
528
|
abstract init(): Promise<void>;
|
|
523
529
|
/**
|
|
524
530
|
* Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,
|
|
525
|
-
* and the http host
|
|
531
|
+
* and the http host binds its server in the same phase.
|
|
526
532
|
*/
|
|
527
533
|
ready?(): Promise<void>;
|
|
528
534
|
/** Runs during teardown, only when `init()` resolved. */
|
|
@@ -552,4 +558,4 @@ type CoreParamTooNarrow = Record<'this plugin constructor must take CoreBase as
|
|
|
552
558
|
type CoreParamAssert<Ctor extends PluginCtor> = CoreBase extends ConstructorParameters<Ctor>[0] ? unknown : CoreParamTooNarrow;
|
|
553
559
|
//#endregion
|
|
554
560
|
export { AckState as A, InteractionRouteKeys as B, FaultSource as C, Notice as D, Subscriptions as E, PublishDefault as F, MiddlewareMetadataKey as H, CommandMetadataKey as I, EventMetadataKey as L, checkAckLegality as M, sendTarget as N, ReplyTelemetry as O, AckTrace as P, GatedMetadataKey as R, DispatchOutcome as S, SubscriptionKey as T, SubscribeMetadataKey as U, InteractionRoutes as V, WebhookUrlMetadataKey as W, Bus as _, PluginCtor as a, Subscriber as b, PluginOptions as c, Transport as d, TransportAssert as f, CoreBase as g, StartupPhase as h, PluginArgs as i, ReplyMethod as j, reportedWrite as k, Runtime as l, ShutdownPhase as m, Initializeable as n, PluginLike as o, PluginLifecycleSpec as p, Plugin as r, ChannelKeyAssert as s, CoreParamAssert as t, RuntimeAssert as u, StoredSubscriberCtor as v, SubscriptionData as w, DefaultSubscriptions as x, SubscriberRegistration as y, InteractionMetadataKey as z };
|
|
555
|
-
//# sourceMappingURL=Plugin-
|
|
561
|
+
//# sourceMappingURL=Plugin-BPz0obbN.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin-CDAV7P9l.mjs","names":[],"sources":["../src/plugin/lifecycle.ts","../src/plugin/Plugin.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ShutdownPhase, StartupPhase } from '@src/lifecycle/phases';\n\n/**\n * The lifecycle a plugin declares through `super(host, spec)`. Unset fields resolve to the base\n * defaults. `ready` runs at a fixed `StartupPhase.Ready`, so it takes only a timeout.\n */\nexport interface PluginLifecycleSpec {\n init?: { phase?: StartupPhase; timeout?: number };\n ready?: { timeout?: number };\n dispose?: { phase?: ShutdownPhase; timeout?: number };\n}\n\n/** @internal */\nexport interface ResolvedPluginLifecycleSpec {\n readonly init: { readonly phase: StartupPhase; readonly timeout: number };\n readonly ready: { readonly timeout: number };\n readonly dispose: { readonly phase: ShutdownPhase; readonly timeout: number };\n}\n\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\nconst DEFAULT_READY_TIMEOUT_MS = 15_000;\nconst DEFAULT_DISPOSE_TIMEOUT_MS = 10_000;\n\nfunction timeoutOf(\n field: 'init' | 'ready' | 'dispose',\n declared: number | undefined,\n fallback: number,\n pluginName: string\n): number {\n if (declared === undefined) return fallback;\n if (!Number.isFinite(declared) || declared <= 0) {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [\n pluginName,\n `lifecycle ${field}.timeout must be a positive number of milliseconds`\n ]);\n }\n return declared;\n}\n\n/** @internal */\nexport function resolveLifecycleSpec(\n spec: PluginLifecycleSpec | undefined,\n pluginName: string\n): ResolvedPluginLifecycleSpec {\n return {\n init: {\n phase: spec?.init?.phase ?? StartupPhase.Configuration,\n timeout: timeoutOf('init', spec?.init?.timeout, DEFAULT_INIT_TIMEOUT_MS, pluginName)\n },\n ready: {\n timeout: timeoutOf('ready', spec?.ready?.timeout, DEFAULT_READY_TIMEOUT_MS, pluginName)\n },\n dispose: {\n phase: spec?.dispose?.phase ?? ShutdownPhase.Disconnect,\n timeout: timeoutOf('dispose', spec?.dispose?.timeout, DEFAULT_DISPOSE_TIMEOUT_MS, pluginName)\n }\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { getDevChannel } from '@hmr/devChannel';\n\nimport { resolveLifecycleSpec } from './lifecycle';\n\nimport type { ResolvedPluginLifecycleSpec, PluginLifecycleSpec } from './lifecycle';\nimport type { TransportOf, PluginOptions, RuntimeOf } from './options';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Tail, HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\nexport interface Initializeable {\n init(): Promise<void>;\n}\n\n/** @internal */\nconst resolvedSpecSlot = Symbol('seedcord.plugin.spec');\n\n/**\n * Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,\n * implements `init()`, and reads the framework surface through `this.core`.\n *\n * @typeParam Opts - The plugin's declared {@link PluginOptions}.\n * @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.\n */\nexport abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase>\n implements Initializeable, HmrAware\n{\n /** @internal phantom, never set at runtime */\n declare readonly __transport?: TransportOf<Opts>;\n /** @internal phantom, never set at runtime */\n declare readonly __runtime?: RuntimeOf<Opts>;\n\n /** @internal */\n readonly [resolvedSpecSlot]: ResolvedPluginLifecycleSpec;\n\n /** Logs under the plugin's class name, on the channel its attach key sets. */\n public readonly logger: Logger;\n\n // CoreBase here keeps the augmented Core out of ConstructorParameters, which attach reads\n constructor(\n private readonly host: CoreBase,\n spec?: PluginLifecycleSpec\n ) {\n this.logger = new Logger(this.constructor.name);\n this[resolvedSpecSlot] = resolveLifecycleSpec(spec, this.constructor.name);\n }\n\n /** The host, typed to the transport whose `Plugin` base this class extends. */\n protected get core(): TCore {\n // justified: each transport binds TCore to the Core its own host satisfies\n return this.host as TCore;\n }\n\n abstract init(): Promise<void>;\n\n /**\n * Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,\n * and the http host
|
|
1
|
+
{"version":3,"file":"Plugin-CDAV7P9l.mjs","names":[],"sources":["../src/plugin/lifecycle.ts","../src/plugin/Plugin.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ShutdownPhase, StartupPhase } from '@src/lifecycle/phases';\n\n/**\n * The lifecycle a plugin declares through `super(host, spec)`. Unset fields resolve to the base\n * defaults. `ready` runs at a fixed `StartupPhase.Ready`, so it takes only a timeout.\n */\nexport interface PluginLifecycleSpec {\n init?: { phase?: StartupPhase; timeout?: number };\n ready?: { timeout?: number };\n dispose?: { phase?: ShutdownPhase; timeout?: number };\n}\n\n/** @internal */\nexport interface ResolvedPluginLifecycleSpec {\n readonly init: { readonly phase: StartupPhase; readonly timeout: number };\n readonly ready: { readonly timeout: number };\n readonly dispose: { readonly phase: ShutdownPhase; readonly timeout: number };\n}\n\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\nconst DEFAULT_READY_TIMEOUT_MS = 15_000;\nconst DEFAULT_DISPOSE_TIMEOUT_MS = 10_000;\n\nfunction timeoutOf(\n field: 'init' | 'ready' | 'dispose',\n declared: number | undefined,\n fallback: number,\n pluginName: string\n): number {\n if (declared === undefined) return fallback;\n if (!Number.isFinite(declared) || declared <= 0) {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [\n pluginName,\n `lifecycle ${field}.timeout must be a positive number of milliseconds`\n ]);\n }\n return declared;\n}\n\n/** @internal */\nexport function resolveLifecycleSpec(\n spec: PluginLifecycleSpec | undefined,\n pluginName: string\n): ResolvedPluginLifecycleSpec {\n return {\n init: {\n phase: spec?.init?.phase ?? StartupPhase.Configuration,\n timeout: timeoutOf('init', spec?.init?.timeout, DEFAULT_INIT_TIMEOUT_MS, pluginName)\n },\n ready: {\n timeout: timeoutOf('ready', spec?.ready?.timeout, DEFAULT_READY_TIMEOUT_MS, pluginName)\n },\n dispose: {\n phase: spec?.dispose?.phase ?? ShutdownPhase.Disconnect,\n timeout: timeoutOf('dispose', spec?.dispose?.timeout, DEFAULT_DISPOSE_TIMEOUT_MS, pluginName)\n }\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { getDevChannel } from '@hmr/devChannel';\n\nimport { resolveLifecycleSpec } from './lifecycle';\n\nimport type { ResolvedPluginLifecycleSpec, PluginLifecycleSpec } from './lifecycle';\nimport type { TransportOf, PluginOptions, RuntimeOf } from './options';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Tail, HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\nexport interface Initializeable {\n init(): Promise<void>;\n}\n\n/** @internal */\nconst resolvedSpecSlot = Symbol('seedcord.plugin.spec');\n\n/**\n * Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,\n * implements `init()`, and reads the framework surface through `this.core`.\n *\n * @typeParam Opts - The plugin's declared {@link PluginOptions}.\n * @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.\n */\nexport abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase>\n implements Initializeable, HmrAware\n{\n /** @internal phantom, never set at runtime */\n declare readonly __transport?: TransportOf<Opts>;\n /** @internal phantom, never set at runtime */\n declare readonly __runtime?: RuntimeOf<Opts>;\n\n /** @internal */\n readonly [resolvedSpecSlot]: ResolvedPluginLifecycleSpec;\n\n /** Logs under the plugin's class name, on the channel its attach key sets. */\n public readonly logger: Logger;\n\n // CoreBase here keeps the augmented Core out of ConstructorParameters, which attach reads\n constructor(\n private readonly host: CoreBase,\n spec?: PluginLifecycleSpec\n ) {\n this.logger = new Logger(this.constructor.name);\n this[resolvedSpecSlot] = resolveLifecycleSpec(spec, this.constructor.name);\n }\n\n /** The host, typed to the transport whose `Plugin` base this class extends. */\n protected get core(): TCore {\n // justified: each transport binds TCore to the Core its own host satisfies\n return this.host as TCore;\n }\n\n abstract init(): Promise<void>;\n\n /**\n * Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,\n * and the http host binds its server in the same phase.\n */\n ready?(): Promise<void>;\n\n /** Runs during teardown, only when `init()` resolved. */\n dispose?(): Promise<void>;\n\n /**\n * Throws a `SeedcordError` that contains the plugin class name and the reason.\n *\n * @param reason - Why the options are invalid.\n */\n protected rejectOptions(reason: string): never {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [this.constructor.name, reason]);\n }\n\n /** Override to reload plugin state on an HMR update. */\n public onHmr(_event: HmrUpdateEvent): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Registers critical file patterns that should trigger a full restart when changed in Dev HMR.\n * @param patterns - Glob patterns relative to the project root\n */\n protected registerCriticalFiles(patterns: string[]): void {\n getDevChannel()?.send('seedcord:register-critical-files', { patterns });\n }\n}\n\n/** @internal */\nexport function resolvedLifecycleSpecOf(plugin: PluginLike): ResolvedPluginLifecycleSpec {\n return plugin[resolvedSpecSlot];\n}\n\nexport type { PluginLifecycleSpec } from './lifecycle';\nexport type { PluginOptions } from './options';\n\n// a bound of Plugin<{}> rejects every plugin that declares an option, since Plugin<A> and Plugin<B>\n// are mutually unassignable. every member here must stay Opts-independent.\n/** @internal */\nexport type PluginLike = Pick<Plugin, 'init' | 'ready' | 'dispose' | 'logger' | 'onHmr' | typeof resolvedSpecSlot>;\n\n// a `CoreBase` first parameter rejects a narrowing ctor on its own, and it also collapses\n// `InstanceType<Ctor>` to `PluginLike` at every attach call. CoreParamAssert checks it instead.\n/** @internal */\nexport type PluginCtor<TPlugin extends PluginLike = PluginLike> = new (...args: any[]) => TPlugin;\n\n/** @internal */\nexport type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;\n\ntype CoreParamTooNarrow = Record<\n 'this plugin constructor must take CoreBase as its first parameter and read the transport Core off this.core',\n never\n>;\n\n// a narrowed first parameter fails `CoreBase extends Param`\n/** @internal */\nexport type CoreParamAssert<Ctor extends PluginCtor> = CoreBase extends ConstructorParameters<Ctor>[0]\n ? unknown\n : CoreParamTooNarrow;\n"],"mappings":";;;;;;;AAsBA,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AAEnC,SAAS,UACL,OACA,UACA,UACA,YACM;CACN,IAAI,aAAa,QAAW,OAAO;CACnC,IAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,GAC1C,MAAM,IAAI,cAAc,kBAAkB,uBAAuB,CAC7D,YACA,aAAa,MAAM,mDACvB,CAAC;CAEL,OAAO;AACX;;AAGA,SAAgB,qBACZ,MACA,YAC2B;CAC3B,OAAO;EACH,MAAM;GACF,OAAO,MAAM,MAAM;GACnB,SAAS,UAAU,QAAQ,MAAM,MAAM,SAAS,yBAAyB,UAAU;EACvF;EACA,OAAO,EACH,SAAS,UAAU,SAAS,MAAM,OAAO,SAAS,0BAA0B,UAAU,EAC1F;EACA,SAAS;GACL,OAAO,MAAM,SAAS;GACtB,SAAS,UAAU,WAAW,MAAM,SAAS,SAAS,4BAA4B,UAAU;EAChG;CACJ;AACJ;;;;;AC1CA,MAAM,mBAAmB,OAAO,sBAAsB;;;;;;;;AAStD,IAAsB,SAAtB,MAEA;CAcyB;;CAPrB,CAAU;;CAGV,AAAgB;CAGhB,YACI,AAAiB,MACjB,MACF;EAFmB;EAGjB,KAAK,SAAS,IAAI,OAAO,KAAK,YAAY,IAAI;EAC9C,KAAK,oBAAoB,qBAAqB,MAAM,KAAK,YAAY,IAAI;CAC7E;;CAGA,IAAc,OAAc;EAExB,OAAO,KAAK;CAChB;;;;;;CAkBA,AAAU,cAAc,QAAuB;EAC3C,MAAM,IAAI,cAAc,kBAAkB,uBAAuB,CAAC,KAAK,YAAY,MAAM,MAAM,CAAC;CACpG;;CAGA,AAAO,MAAM,QAAuC;EAChD,OAAO,QAAQ,QAAQ;CAC3B;;;;;CAMA,AAAU,sBAAsB,UAA0B;EACtD,cAAc,CAAC,EAAE,KAAK,oCAAoC,EAAE,SAAS,CAAC;CAC1E;AACJ;;AAGA,SAAgB,wBAAwB,QAAiD;CACrF,OAAO,OAAO;AAClB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_phases = require('./phases-
|
|
2
|
-
const require_devChannel = require('./devChannel-
|
|
1
|
+
const require_phases = require('./phases-8qiiLKjS.cjs');
|
|
2
|
+
const require_devChannel = require('./devChannel-Cdnmz6hd.cjs');
|
|
3
3
|
let _seedcord_logger = require("@seedcord/logger");
|
|
4
4
|
let _seedcord_errors = require("@seedcord/errors");
|
|
5
5
|
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
@@ -92,4 +92,4 @@ Object.defineProperty(exports, 'resolvedLifecycleSpecOf', {
|
|
|
92
92
|
return resolvedLifecycleSpecOf;
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
|
-
//# sourceMappingURL=Plugin-
|
|
95
|
+
//# sourceMappingURL=Plugin-DVNh1kvX.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin-
|
|
1
|
+
{"version":3,"file":"Plugin-DVNh1kvX.cjs","names":["SeedcordError","SeedcordErrorCode","Logger","SeedcordError","SeedcordErrorCode"],"sources":["../src/plugin/lifecycle.ts","../src/plugin/Plugin.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { ShutdownPhase, StartupPhase } from '@src/lifecycle/phases';\n\n/**\n * The lifecycle a plugin declares through `super(host, spec)`. Unset fields resolve to the base\n * defaults. `ready` runs at a fixed `StartupPhase.Ready`, so it takes only a timeout.\n */\nexport interface PluginLifecycleSpec {\n init?: { phase?: StartupPhase; timeout?: number };\n ready?: { timeout?: number };\n dispose?: { phase?: ShutdownPhase; timeout?: number };\n}\n\n/** @internal */\nexport interface ResolvedPluginLifecycleSpec {\n readonly init: { readonly phase: StartupPhase; readonly timeout: number };\n readonly ready: { readonly timeout: number };\n readonly dispose: { readonly phase: ShutdownPhase; readonly timeout: number };\n}\n\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\nconst DEFAULT_READY_TIMEOUT_MS = 15_000;\nconst DEFAULT_DISPOSE_TIMEOUT_MS = 10_000;\n\nfunction timeoutOf(\n field: 'init' | 'ready' | 'dispose',\n declared: number | undefined,\n fallback: number,\n pluginName: string\n): number {\n if (declared === undefined) return fallback;\n if (!Number.isFinite(declared) || declared <= 0) {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [\n pluginName,\n `lifecycle ${field}.timeout must be a positive number of milliseconds`\n ]);\n }\n return declared;\n}\n\n/** @internal */\nexport function resolveLifecycleSpec(\n spec: PluginLifecycleSpec | undefined,\n pluginName: string\n): ResolvedPluginLifecycleSpec {\n return {\n init: {\n phase: spec?.init?.phase ?? StartupPhase.Configuration,\n timeout: timeoutOf('init', spec?.init?.timeout, DEFAULT_INIT_TIMEOUT_MS, pluginName)\n },\n ready: {\n timeout: timeoutOf('ready', spec?.ready?.timeout, DEFAULT_READY_TIMEOUT_MS, pluginName)\n },\n dispose: {\n phase: spec?.dispose?.phase ?? ShutdownPhase.Disconnect,\n timeout: timeoutOf('dispose', spec?.dispose?.timeout, DEFAULT_DISPOSE_TIMEOUT_MS, pluginName)\n }\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { getDevChannel } from '@hmr/devChannel';\n\nimport { resolveLifecycleSpec } from './lifecycle';\n\nimport type { ResolvedPluginLifecycleSpec, PluginLifecycleSpec } from './lifecycle';\nimport type { TransportOf, PluginOptions, RuntimeOf } from './options';\nimport type { CoreBase } from '@interfaces/CoreBase';\nimport type { Tail, HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\nexport interface Initializeable {\n init(): Promise<void>;\n}\n\n/** @internal */\nconst resolvedSpecSlot = Symbol('seedcord.plugin.spec');\n\n/**\n * Base class for Seedcord plugins. A subclass declares its options as the `Opts` type argument,\n * implements `init()`, and reads the framework surface through `this.core`.\n *\n * @typeParam Opts - The plugin's declared {@link PluginOptions}.\n * @typeParam TCore - The host type `this.core` resolves to. Each transport binds it to its own `Core`.\n */\nexport abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase>\n implements Initializeable, HmrAware\n{\n /** @internal phantom, never set at runtime */\n declare readonly __transport?: TransportOf<Opts>;\n /** @internal phantom, never set at runtime */\n declare readonly __runtime?: RuntimeOf<Opts>;\n\n /** @internal */\n readonly [resolvedSpecSlot]: ResolvedPluginLifecycleSpec;\n\n /** Logs under the plugin's class name, on the channel its attach key sets. */\n public readonly logger: Logger;\n\n // CoreBase here keeps the augmented Core out of ConstructorParameters, which attach reads\n constructor(\n private readonly host: CoreBase,\n spec?: PluginLifecycleSpec\n ) {\n this.logger = new Logger(this.constructor.name);\n this[resolvedSpecSlot] = resolveLifecycleSpec(spec, this.constructor.name);\n }\n\n /** The host, typed to the transport whose `Plugin` base this class extends. */\n protected get core(): TCore {\n // justified: each transport binds TCore to the Core its own host satisfies\n return this.host as TCore;\n }\n\n abstract init(): Promise<void>;\n\n /**\n * Runs in the startup Ready phase, after `init()`. The gateway client is logged in by this phase,\n * and the http host binds its server in the same phase.\n */\n ready?(): Promise<void>;\n\n /** Runs during teardown, only when `init()` resolved. */\n dispose?(): Promise<void>;\n\n /**\n * Throws a `SeedcordError` that contains the plugin class name and the reason.\n *\n * @param reason - Why the options are invalid.\n */\n protected rejectOptions(reason: string): never {\n throw new SeedcordError(SeedcordErrorCode.PluginOptionsRejected, [this.constructor.name, reason]);\n }\n\n /** Override to reload plugin state on an HMR update. */\n public onHmr(_event: HmrUpdateEvent): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Registers critical file patterns that should trigger a full restart when changed in Dev HMR.\n * @param patterns - Glob patterns relative to the project root\n */\n protected registerCriticalFiles(patterns: string[]): void {\n getDevChannel()?.send('seedcord:register-critical-files', { patterns });\n }\n}\n\n/** @internal */\nexport function resolvedLifecycleSpecOf(plugin: PluginLike): ResolvedPluginLifecycleSpec {\n return plugin[resolvedSpecSlot];\n}\n\nexport type { PluginLifecycleSpec } from './lifecycle';\nexport type { PluginOptions } from './options';\n\n// a bound of Plugin<{}> rejects every plugin that declares an option, since Plugin<A> and Plugin<B>\n// are mutually unassignable. every member here must stay Opts-independent.\n/** @internal */\nexport type PluginLike = Pick<Plugin, 'init' | 'ready' | 'dispose' | 'logger' | 'onHmr' | typeof resolvedSpecSlot>;\n\n// a `CoreBase` first parameter rejects a narrowing ctor on its own, and it also collapses\n// `InstanceType<Ctor>` to `PluginLike` at every attach call. CoreParamAssert checks it instead.\n/** @internal */\nexport type PluginCtor<TPlugin extends PluginLike = PluginLike> = new (...args: any[]) => TPlugin;\n\n/** @internal */\nexport type PluginArgs<Ctor extends PluginCtor> = Tail<ConstructorParameters<Ctor>>;\n\ntype CoreParamTooNarrow = Record<\n 'this plugin constructor must take CoreBase as its first parameter and read the transport Core off this.core',\n never\n>;\n\n// a narrowed first parameter fails `CoreBase extends Param`\n/** @internal */\nexport type CoreParamAssert<Ctor extends PluginCtor> = CoreBase extends ConstructorParameters<Ctor>[0]\n ? unknown\n : CoreParamTooNarrow;\n"],"mappings":";;;;;;;AAsBA,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AAEnC,SAAS,UACL,OACA,UACA,UACA,YACM;CACN,IAAI,aAAa,QAAW,OAAO;CACnC,IAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,GAC1C,MAAM,IAAIA,wCAAcC,mCAAkB,uBAAuB,CAC7D,YACA,aAAa,MAAM,mDACvB,CAAC;CAEL,OAAO;AACX;;AAGA,SAAgB,qBACZ,MACA,YAC2B;CAC3B,OAAO;EACH,MAAM;GACF,OAAO,MAAM,MAAM;GACnB,SAAS,UAAU,QAAQ,MAAM,MAAM,SAAS,yBAAyB,UAAU;EACvF;EACA,OAAO,EACH,SAAS,UAAU,SAAS,MAAM,OAAO,SAAS,0BAA0B,UAAU,EAC1F;EACA,SAAS;GACL,OAAO,MAAM,SAAS;GACtB,SAAS,UAAU,WAAW,MAAM,SAAS,SAAS,4BAA4B,UAAU;EAChG;CACJ;AACJ;;;;;AC1CA,MAAM,mBAAmB,OAAO,sBAAsB;;;;;;;;AAStD,IAAsB,SAAtB,MAEA;CAcyB;;CAPrB,CAAU;;CAGV,AAAgB;CAGhB,YACI,AAAiB,MACjB,MACF;EAFmB;EAGjB,KAAK,SAAS,IAAIC,wBAAO,KAAK,YAAY,IAAI;EAC9C,KAAK,oBAAoB,qBAAqB,MAAM,KAAK,YAAY,IAAI;CAC7E;;CAGA,IAAc,OAAc;EAExB,OAAO,KAAK;CAChB;;;;;;CAkBA,AAAU,cAAc,QAAuB;EAC3C,MAAM,IAAIC,wCAAcC,mCAAkB,uBAAuB,CAAC,KAAK,YAAY,MAAM,MAAM,CAAC;CACpG;;CAGA,AAAO,MAAM,QAAuC;EAChD,OAAO,QAAQ,QAAQ;CAC3B;;;;;CAMA,AAAU,sBAAsB,UAA0B;EACtD,iCAAc,CAAC,EAAE,KAAK,oCAAoC,EAAE,SAAS,CAAC;CAC1E;AACJ;;AAGA,SAAgB,wBAAwB,QAAiD;CACrF,OAAO,OAAO;AAClB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ContextMenuCommandBuilder, SlashCommandBuilder } from "@discordjs/builders";
|
|
2
2
|
import { APIApplicationCommand } from "discord-api-types/v10";
|
|
3
|
-
|
|
4
3
|
//#region src/commands/types.d.ts
|
|
5
4
|
/** @internal */
|
|
6
5
|
type CommandBuilder = SlashCommandBuilder | ContextMenuCommandBuilder;
|
|
@@ -10,7 +9,7 @@ interface DeployResult {
|
|
|
10
9
|
guilds: Map<string, Map<string, APIApplicationCommand>>;
|
|
11
10
|
}
|
|
12
11
|
//#endregion
|
|
13
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
12
|
+
//#region ../../node_modules/.pnpm/type-fest@5.8.0/node_modules/type-fest/source/basic.d.ts
|
|
14
13
|
/**
|
|
15
14
|
Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
|
|
16
15
|
|
|
@@ -19,4 +18,4 @@ Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/Jav
|
|
|
19
18
|
type Constructor<T, Arguments extends unknown[] = any[]> = new (...arguments_: Arguments) => T;
|
|
20
19
|
//#endregion
|
|
21
20
|
export { CommandBuilder as n, DeployResult as r, Constructor as t };
|
|
22
|
-
//# sourceMappingURL=basic-
|
|
21
|
+
//# sourceMappingURL=basic-CeSFvbaz.d.mts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ContextMenuCommandBuilder, SlashCommandBuilder } from "@discordjs/builders";
|
|
2
|
-
|
|
3
2
|
//#region src/commands/contextMenuLeaves.d.ts
|
|
4
3
|
/** The registered context-menu command names, split by kind. @internal */
|
|
5
4
|
interface ContextMenuLeaves {
|
|
@@ -16,4 +15,4 @@ interface ContextMenuLeaves {
|
|
|
16
15
|
declare function contextMenuLeaves(commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>): ContextMenuLeaves;
|
|
17
16
|
//#endregion
|
|
18
17
|
export { contextMenuLeaves as n, ContextMenuLeaves as t };
|
|
19
|
-
//# sourceMappingURL=contextMenuLeaves-
|
|
18
|
+
//# sourceMappingURL=contextMenuLeaves-CC3-qObq.d.mts.map
|