@seedcord/core 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Bus-BvF4VShN.mjs → Bus-yJGE6Fs2.mjs} +111 -92
- package/dist/Bus-yJGE6Fs2.mjs.map +1 -0
- package/dist/Pluggable-BI_LQuPm.mjs +280 -0
- package/dist/Pluggable-BI_LQuPm.mjs.map +1 -0
- package/dist/Pluggable-bFCmT4oB.d.mts +148 -0
- package/dist/{Plugin-D9ykXA5g.mjs → Plugin-B0SRHzMX.mjs} +7 -3
- package/dist/Plugin-B0SRHzMX.mjs.map +1 -0
- package/dist/{Plugin-CQ9IEj5X.d.mts → Plugin-BW2JH2_h.d.mts} +2 -2
- package/dist/{cursor-BANzac53.d.mts → cursor-CO_MTgdZ.d.mts} +8 -252
- package/dist/{cursor-CuoA9cRh.mjs → cursor-CaT051ii.mjs} +307 -425
- package/dist/cursor-CaT051ii.mjs.map +1 -0
- package/dist/{index-rt8rZg4d.d.mts → index-CvneCObW.d.mts} +2 -2
- package/dist/index.d.mts +78 -19
- package/dist/index.mjs +223 -21
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +13 -6
- package/dist/internal.index.mjs +8 -455
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.d.mts +6 -133
- package/dist/node-internal.index.mjs +16 -473
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.d.mts +2 -0
- package/dist/node.index.mjs +256 -0
- package/dist/node.index.mjs.map +1 -0
- package/dist/{phases-hBxMCD2V.d.mts → phases-C5Steaa2.d.mts} +151 -119
- package/dist/plugin.index.d.mts +1 -1
- package/dist/plugin.index.mjs +1 -1
- package/dist/publishDefault-D826XXDQ.mjs +14 -0
- package/dist/publishDefault-D826XXDQ.mjs.map +1 -0
- package/dist/{slashRouteLeaves-De1SxqnX.mjs → slashRouteLeaves-BQmbFm-S.mjs} +10 -2
- package/dist/slashRouteLeaves-BQmbFm-S.mjs.map +1 -0
- package/dist/{subscribers-wnMyNYZd.mjs → subscribers-DVa-Pr45.mjs} +3 -3
- package/dist/{subscribers-wnMyNYZd.mjs.map → subscribers-DVa-Pr45.mjs.map} +1 -1
- package/package.json +19 -12
- package/dist/Bus-BvF4VShN.mjs.map +0 -1
- package/dist/Plugin-D9ykXA5g.mjs.map +0 -1
- package/dist/cursor-CuoA9cRh.mjs.map +0 -1
- package/dist/slashRouteLeaves-De1SxqnX.mjs.map +0 -1
|
@@ -1,300 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as CommandInjector } from "./subscribers-
|
|
1
|
+
import { C as SubscribeMetadataKey, O as contextMenuLeaves, a as RegisterSubscriber, c as VerifyWebhooks, f as Subscriber, g as CommandMetadataKey, i as RegisterDefaults, n as busLoggerOf, o as RegisteredCount, r as registrationFor, s as UnregisterSubscriber } from "./Bus-yJGE6Fs2.mjs";
|
|
2
|
+
import { t as CommandInjector } from "./subscribers-DVa-Pr45.mjs";
|
|
3
|
+
import { t as PublishDefault } from "./publishDefault-D826XXDQ.mjs";
|
|
3
4
|
import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
|
|
4
5
|
import { t as getDevChannel } from "./devChannel-CQU7hq7Q.mjs";
|
|
5
|
-
import { n as
|
|
6
|
-
import { t as slashRouteLeaves } from "./slashRouteLeaves-De1SxqnX.mjs";
|
|
6
|
+
import { n as isCommandClass, t as slashRouteLeaves } from "./slashRouteLeaves-BQmbFm-S.mjs";
|
|
7
7
|
import { t as HmrModuleHandler } from "./HmrModuleHandler-9AUfKXRl.mjs";
|
|
8
|
+
import { i as settleWithin, n as shutdownOf, r as assertNodeVersion } from "./Pluggable-BI_LQuPm.mjs";
|
|
8
9
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
|
9
|
-
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
10
|
-
import { FRAMEWORK_CHANNELS, Logger } from "@seedcord/logger";
|
|
11
10
|
import { Routes } from "discord-api-types/v10";
|
|
12
|
-
import {
|
|
11
|
+
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
12
|
+
import { SeedcordError } from "@seedcord/errors/internal";
|
|
13
|
+
import { Logger } from "@seedcord/logger";
|
|
13
14
|
import { formatFilePath } from "@seedcord/utils";
|
|
14
15
|
import { Envapter } from "envapt";
|
|
15
16
|
import { resolve } from "node:path";
|
|
16
17
|
import { createServer } from "node:http";
|
|
17
18
|
import { traverseDirectory } from "@seedcord/utils/node";
|
|
18
19
|
|
|
19
|
-
//#region src/node/assertNodeVersion.ts
|
|
20
|
-
const RANGE = new RegExp(String.raw`^>=(\d+)(?:\.(\d+))?`);
|
|
21
|
-
const VERSION = /^v?(\d+)\.(\d+)/;
|
|
22
|
-
const SPACES = /\s/g;
|
|
23
|
-
/** @internal */
|
|
24
|
-
function assertNodeVersion(range, running) {
|
|
25
|
-
const required = RANGE.exec(range.replaceAll(SPACES, ""));
|
|
26
|
-
const current = VERSION.exec(running);
|
|
27
|
-
if (!required || !current) return;
|
|
28
|
-
const [, requiredMajor, requiredMinor = "0"] = required;
|
|
29
|
-
const [, currentMajor, currentMinor] = current;
|
|
30
|
-
if (Number(currentMajor) > Number(requiredMajor) || Number(currentMajor) === Number(requiredMajor) && Number(currentMinor) >= Number(requiredMinor)) return;
|
|
31
|
-
throw new SeedcordError(SeedcordErrorCode.UnsupportedNodeVersion, [range, running]);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/node/Lifecycle/withTimeout.ts
|
|
36
|
-
async function raceTimer(work, timeoutMs, onTimeout) {
|
|
37
|
-
let timer;
|
|
38
|
-
try {
|
|
39
|
-
await Promise.race([work, new Promise((resolve, reject) => {
|
|
40
|
-
timer = setTimeout(() => onTimeout(resolve, reject), timeoutMs);
|
|
41
|
-
})]);
|
|
42
|
-
} finally {
|
|
43
|
-
if (timer) clearTimeout(timer);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function withTimeout(name, run, timeoutMs) {
|
|
47
|
-
return raceTimer(run(), timeoutMs, (_, reject) => {
|
|
48
|
-
reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [name, timeoutMs]));
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
function settleWithin(work, timeoutMs) {
|
|
52
|
-
return raceTimer(work, timeoutMs, (resolve) => resolve());
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
//#endregion
|
|
56
|
-
//#region src/node/Lifecycle/CoordinatedLifecycle.ts
|
|
57
|
-
var CoordinatedLifecycle = class {
|
|
58
|
-
phaseOrder;
|
|
59
|
-
phaseEnum;
|
|
60
|
-
logger;
|
|
61
|
-
tasksMap = /* @__PURE__ */ new Map();
|
|
62
|
-
constructor(loggerName, phaseOrder, phaseEnum) {
|
|
63
|
-
this.phaseOrder = phaseOrder;
|
|
64
|
-
this.phaseEnum = phaseEnum;
|
|
65
|
-
this.logger = new Logger(loggerName, { channel: "lifecycle" });
|
|
66
|
-
this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
|
|
67
|
-
}
|
|
68
|
-
addTask(phase, taskName, task, timeoutMs) {
|
|
69
|
-
if (!this.canAddTask()) return;
|
|
70
|
-
const tasks = this.tasksMap.get(phase);
|
|
71
|
-
if (!tasks) throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);
|
|
72
|
-
tasks.push({
|
|
73
|
-
name: taskName,
|
|
74
|
-
task,
|
|
75
|
-
timeout: timeoutMs
|
|
76
|
-
});
|
|
77
|
-
this.logger.debug(`${paint.italic("Added")} ${this.getTaskType()} task ${paint.sky.bold(taskName)} to phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
78
|
-
}
|
|
79
|
-
removeTask(phase, taskName) {
|
|
80
|
-
if (!this.canRemoveTask()) return false;
|
|
81
|
-
const tasks = this.tasksMap.get(phase);
|
|
82
|
-
if (!tasks) return false;
|
|
83
|
-
const initialLength = tasks.length;
|
|
84
|
-
const filteredTasks = tasks.filter((task) => task.name !== taskName);
|
|
85
|
-
this.tasksMap.set(phase, filteredTasks);
|
|
86
|
-
const removed = initialLength !== filteredTasks.length;
|
|
87
|
-
if (removed) this.logger.debug(`${paint.italic("Removed")} ${this.getTaskType()} task ${paint.sky.bold(taskName)} from phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
88
|
-
return removed;
|
|
89
|
-
}
|
|
90
|
-
async runPhase(phase) {
|
|
91
|
-
const tasks = this.tasksMap.get(phase) ?? [];
|
|
92
|
-
if (tasks.length === 0) {
|
|
93
|
-
this.logger.trace(`No tasks to run in phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
this.logger.debug(`${paint.amber.bold("Running")} ${this.getTaskType()} phase ${paint.iris.bold(this.phaseEnum[phase])} with ${paint.sky.bold(String(tasks.length))} tasks`);
|
|
97
|
-
const reasons = (await this.executeTasksInPhase(phase, tasks)).reduce((rejected, result) => {
|
|
98
|
-
if (result.status === "rejected") rejected.push(result.reason);
|
|
99
|
-
return rejected;
|
|
100
|
-
}, []);
|
|
101
|
-
if (reasons.length > 0) throw new SeedcordAggregateError(SeedcordErrorCode.LifecyclePhaseFailures, reasons, [this.phaseEnum[phase], reasons.length]);
|
|
102
|
-
this.logger.debug(`Phase ${paint.iris.bold(this.phaseEnum[phase])} ${paint.mint.bold("completed successfully")}`);
|
|
103
|
-
}
|
|
104
|
-
async runTaskWithTimeout(phase, task) {
|
|
105
|
-
this.logger.trace(`${paint.italic("Starting")} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
106
|
-
try {
|
|
107
|
-
await withTimeout(task.name, task.task, task.timeout);
|
|
108
|
-
this.logger.trace(`${paint.italic("Completed")} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
if (this.isAborted()) return;
|
|
111
|
-
this.logger.error(`${paint.italic("Failed")} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}:`, error);
|
|
112
|
-
throw error;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
isAborted() {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
//#endregion
|
|
121
|
-
//#region src/node/Lifecycle/CoordinatedShutdown.ts
|
|
122
|
-
const PHASE_ORDER$1 = [
|
|
123
|
-
1,
|
|
124
|
-
2,
|
|
125
|
-
3,
|
|
126
|
-
4
|
|
127
|
-
];
|
|
128
|
-
const LOG_FLUSH_DELAY_MS = 3e3;
|
|
129
|
-
var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
130
|
-
isShuttingDown = false;
|
|
131
|
-
hasShutdown = false;
|
|
132
|
-
exitCode = 0;
|
|
133
|
-
onSigTerm = null;
|
|
134
|
-
onSigInt = null;
|
|
135
|
-
startupGate;
|
|
136
|
-
constructor() {
|
|
137
|
-
super("Shutdown", PHASE_ORDER$1, ShutdownPhase);
|
|
138
|
-
this.registerSignalHandlers();
|
|
139
|
-
}
|
|
140
|
-
canAddTask() {
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
canRemoveTask() {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
getTaskType() {
|
|
147
|
-
return "shutdown";
|
|
148
|
-
}
|
|
149
|
-
async executeTasksInPhase(phase, tasks) {
|
|
150
|
-
const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
|
|
151
|
-
return Promise.allSettled(promises);
|
|
152
|
-
}
|
|
153
|
-
registerSignalHandlers() {
|
|
154
|
-
this.onSigTerm = () => {
|
|
155
|
-
this.logger.info(`Received ${paint.amber.bold("SIGTERM")} signal`);
|
|
156
|
-
this.run(0);
|
|
157
|
-
};
|
|
158
|
-
this.onSigInt = () => {
|
|
159
|
-
this.logger.info(`Received ${paint.amber.bold("SIGINT")} signal`);
|
|
160
|
-
this.run(0);
|
|
161
|
-
};
|
|
162
|
-
process.on("SIGTERM", this.onSigTerm);
|
|
163
|
-
process.on("SIGINT", this.onSigInt);
|
|
164
|
-
}
|
|
165
|
-
/** @internal */
|
|
166
|
-
removeSignalHandlers() {
|
|
167
|
-
if (this.onSigTerm) {
|
|
168
|
-
process.off("SIGTERM", this.onSigTerm);
|
|
169
|
-
this.onSigTerm = null;
|
|
170
|
-
}
|
|
171
|
-
if (this.onSigInt) {
|
|
172
|
-
process.off("SIGINT", this.onSigInt);
|
|
173
|
-
this.onSigInt = null;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
/** @internal run() awaits this so boot finishes registering its dispose tasks first */
|
|
177
|
-
gateOnStartup(settled) {
|
|
178
|
-
this.startupGate = settled;
|
|
179
|
-
}
|
|
180
|
-
addTask(phase, taskName, task, timeoutMs = 5e3) {
|
|
181
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
182
|
-
}
|
|
183
|
-
removeTask(phase, taskName) {
|
|
184
|
-
return super.removeTask(phase, taskName);
|
|
185
|
-
}
|
|
186
|
-
async run(exitCode = 0, exitProcess = true) {
|
|
187
|
-
this.removeSignalHandlers();
|
|
188
|
-
if (this.hasShutdown || this.isShuttingDown) {
|
|
189
|
-
this.logger.warn("Shutdown sequence already ran or is in progress");
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
this.isShuttingDown = true;
|
|
193
|
-
this.exitCode = exitCode;
|
|
194
|
-
this.logger.info(`${paint.amber.bold("Starting")} coordinated shutdown with exit code ${paint.sky.bold(exitCode)}`);
|
|
195
|
-
try {
|
|
196
|
-
if (this.startupGate) await this.startupGate;
|
|
197
|
-
const failures = [];
|
|
198
|
-
for (const phase of PHASE_ORDER$1) try {
|
|
199
|
-
await this.runPhase(phase);
|
|
200
|
-
} catch (error) {
|
|
201
|
-
failures.push(error);
|
|
202
|
-
}
|
|
203
|
-
if (failures.length > 0) this.logger.error(`${paint.coral.bold("Coordinated shutdown failed")}`, ...failures);
|
|
204
|
-
else this.logger.info(`${paint.mint.bold("Coordinated shutdown completed")} successfully`);
|
|
205
|
-
} finally {
|
|
206
|
-
this.hasShutdown = true;
|
|
207
|
-
if (exitProcess) {
|
|
208
|
-
this.logger.debug(`${paint.coral.bold("Exiting")} process with code ${paint.sky.bold(this.exitCode)}`);
|
|
209
|
-
setTimeout(() => {
|
|
210
|
-
process.exit(this.exitCode);
|
|
211
|
-
}, LOG_FLUSH_DELAY_MS);
|
|
212
|
-
} else {
|
|
213
|
-
this.logger.debug(`${paint.amber.bold("Skipping")} process exit (dev mode)`);
|
|
214
|
-
this.isShuttingDown = false;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
//#endregion
|
|
221
|
-
//#region src/node/Lifecycle/CoordinatedStartup.ts
|
|
222
|
-
const PHASE_ORDER = [
|
|
223
|
-
1,
|
|
224
|
-
2,
|
|
225
|
-
3
|
|
226
|
-
];
|
|
227
|
-
var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
228
|
-
isStartingUp = false;
|
|
229
|
-
hasStarted = false;
|
|
230
|
-
constructor() {
|
|
231
|
-
super("Startup", PHASE_ORDER, StartupPhase);
|
|
232
|
-
}
|
|
233
|
-
addTask(phase, taskName, task, timeoutMs = 1e4) {
|
|
234
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
235
|
-
}
|
|
236
|
-
canAddTask() {
|
|
237
|
-
if (this.hasStarted) throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);
|
|
238
|
-
if (this.isStartingUp) throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
canRemoveTask() {
|
|
242
|
-
if (this.isStartingUp) throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
getTaskType() {
|
|
246
|
-
return "startup";
|
|
247
|
-
}
|
|
248
|
-
async executeTasksInPhase(phase, tasks) {
|
|
249
|
-
const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
|
|
250
|
-
return Promise.allSettled(promises);
|
|
251
|
-
}
|
|
252
|
-
async run() {
|
|
253
|
-
if (this.hasStarted) {
|
|
254
|
-
this.logger.warn("Startup sequence has already completed");
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
if (this.isStartingUp) {
|
|
258
|
-
this.logger.warn("Startup sequence already in progress");
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
this.isStartingUp = true;
|
|
262
|
-
this.logger.info(`${paint.mint.bold("Starting")} coordinated startup sequence`);
|
|
263
|
-
try {
|
|
264
|
-
for (const phase of PHASE_ORDER) {
|
|
265
|
-
if (!this.isStartingUp) {
|
|
266
|
-
this.logger.warn("Startup sequence aborted");
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
await this.runPhase(phase);
|
|
270
|
-
}
|
|
271
|
-
this.hasStarted = true;
|
|
272
|
-
this.logger.info(`${paint.mint.bold("Coordinated startup completed")} successfully`);
|
|
273
|
-
} catch (error) {
|
|
274
|
-
if (!this.isStartingUp) {
|
|
275
|
-
this.logger.warn("Startup sequence aborted during error handling");
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
this.logger.error(`${paint.coral.bold("Coordinated startup failed")}`);
|
|
279
|
-
throw error;
|
|
280
|
-
} finally {
|
|
281
|
-
this.isStartingUp = false;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
isAborted() {
|
|
285
|
-
return !this.isStartingUp;
|
|
286
|
-
}
|
|
287
|
-
abort() {
|
|
288
|
-
if (!this.isStartingUp) return;
|
|
289
|
-
this.isStartingUp = false;
|
|
290
|
-
this.logger.warn("Aborting coordinated startup sequence");
|
|
291
|
-
}
|
|
292
|
-
get isReady() {
|
|
293
|
-
return this.hasStarted;
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
//#endregion
|
|
298
20
|
//#region src/node/HealthResponder.ts
|
|
299
21
|
const HTTP_OK = 200;
|
|
300
22
|
const DEFAULT_HEALTH_CHECK_PATH = "/health";
|
|
@@ -385,181 +107,6 @@ var HealthCheck = class HealthCheck {
|
|
|
385
107
|
}
|
|
386
108
|
};
|
|
387
109
|
|
|
388
|
-
//#endregion
|
|
389
|
-
//#region src/node/Pluggable.ts
|
|
390
|
-
const RESERVED_KEYS = new Set(FRAMEWORK_CHANNELS);
|
|
391
|
-
/**
|
|
392
|
-
* Base class for a plugin host, a transport `Seedcord` class.
|
|
393
|
-
*
|
|
394
|
-
* You attach plugins while configuring the bot. Within one startup phase, their `init()` calls run
|
|
395
|
-
* one after another in attach order.
|
|
396
|
-
*/
|
|
397
|
-
var Pluggable = class Pluggable {
|
|
398
|
-
/** @see {@link CoordinatedShutdown} */
|
|
399
|
-
shutdown;
|
|
400
|
-
/** @see {@link CoordinatedStartup} */
|
|
401
|
-
startup;
|
|
402
|
-
isInitialized = false;
|
|
403
|
-
startFailed = false;
|
|
404
|
-
plugins = [];
|
|
405
|
-
pluginLogger = new Logger("Plugins", { channel: "plugins" });
|
|
406
|
-
attachments = [];
|
|
407
|
-
completedInits = /* @__PURE__ */ new Set();
|
|
408
|
-
disposePhases = /* @__PURE__ */ new Set();
|
|
409
|
-
pluginTasksRegistered = false;
|
|
410
|
-
static isInstantiated = false;
|
|
411
|
-
static liveShutdown;
|
|
412
|
-
constructor(shutdown, startup) {
|
|
413
|
-
assertNodeVersion(">=24.11", process.version);
|
|
414
|
-
if (Pluggable.isInstantiated) {
|
|
415
|
-
shutdown.removeSignalHandlers();
|
|
416
|
-
throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);
|
|
417
|
-
}
|
|
418
|
-
Pluggable.isInstantiated = true;
|
|
419
|
-
Pluggable.liveShutdown = shutdown;
|
|
420
|
-
this.shutdown = shutdown;
|
|
421
|
-
this.startup = startup;
|
|
422
|
-
}
|
|
423
|
-
/** @internal */
|
|
424
|
-
async init() {
|
|
425
|
-
if (this.isInitialized) return this;
|
|
426
|
-
if (this.startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);
|
|
427
|
-
this.registerPluginTasks();
|
|
428
|
-
const startupSettled = Promise.withResolvers();
|
|
429
|
-
this.shutdown.gateOnStartup(startupSettled.promise);
|
|
430
|
-
try {
|
|
431
|
-
await this.startup.run();
|
|
432
|
-
} catch (caught) {
|
|
433
|
-
await this.rollback();
|
|
434
|
-
throw caught;
|
|
435
|
-
} finally {
|
|
436
|
-
startupSettled.resolve();
|
|
437
|
-
}
|
|
438
|
-
this.isInitialized = true;
|
|
439
|
-
return this;
|
|
440
|
-
}
|
|
441
|
-
/** @internal codegen reads these to emit the `Core` augmentation */
|
|
442
|
-
get pluginKeys() {
|
|
443
|
-
return this.attachments.map((attachment) => attachment.key);
|
|
444
|
-
}
|
|
445
|
-
/** @internal so the next host can construct */
|
|
446
|
-
static reset() {
|
|
447
|
-
Pluggable.liveShutdown?.removeSignalHandlers();
|
|
448
|
-
Pluggable.liveShutdown = void 0;
|
|
449
|
-
Pluggable.isInstantiated = false;
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Attaches a plugin under `key`. Read the instance back as `core[key]`. `seedcord codegen`
|
|
453
|
-
* writes the `Core` augmentation that types it there.
|
|
454
|
-
*
|
|
455
|
-
* Startup runs each plugin's `init()` in attach order within its phase.
|
|
456
|
-
*
|
|
457
|
-
* Attaching a plugin whose `transport` or `runtime` differs from this host fails to compile.
|
|
458
|
-
* Your constructor takes `CoreBase` as its first parameter (you don't need to pass it though).
|
|
459
|
-
* A narrower one fails to compile here.
|
|
460
|
-
*
|
|
461
|
-
* @param key - Also the channel the plugin logs on. Reserved channel names throw.
|
|
462
|
-
* @param args - Whatever your constructor takes after the host.
|
|
463
|
-
* @throws A **SeedcordError** if you attach after the bot has started. A taken or reserved key throws too.
|
|
464
|
-
* @example
|
|
465
|
-
* ```ts
|
|
466
|
-
* seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... });
|
|
467
|
-
* ```
|
|
468
|
-
*/
|
|
469
|
-
attach(key, Plugin, ...args) {
|
|
470
|
-
if (this.isInitialized) throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);
|
|
471
|
-
if (RESERVED_KEYS.has(key)) throw new SeedcordError(SeedcordErrorCode.CorePluginReservedChannel, [key]);
|
|
472
|
-
if (key in this) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);
|
|
473
|
-
const instance = new Plugin(this, ...args);
|
|
474
|
-
pluginLoggerOf(instance).setChannel(key);
|
|
475
|
-
this.plugins.push(instance);
|
|
476
|
-
this.attachments.push({
|
|
477
|
-
key,
|
|
478
|
-
instance
|
|
479
|
-
});
|
|
480
|
-
return Object.assign(this, { [key]: instance });
|
|
481
|
-
}
|
|
482
|
-
registerPluginTasks() {
|
|
483
|
-
if (this.pluginTasksRegistered) return;
|
|
484
|
-
this.pluginTasksRegistered = true;
|
|
485
|
-
const groups = /* @__PURE__ */ new Map();
|
|
486
|
-
for (const attachment of this.attachments) {
|
|
487
|
-
const phase = resolvedLifecycleSpecOf(attachment.instance).init.phase;
|
|
488
|
-
const group = groups.get(phase) ?? [];
|
|
489
|
-
group.push(attachment);
|
|
490
|
-
groups.set(phase, group);
|
|
491
|
-
}
|
|
492
|
-
for (const [phase, group] of groups) {
|
|
493
|
-
if (phase === 3) continue;
|
|
494
|
-
const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);
|
|
495
|
-
this.startup.addTask(phase, "plugins-init", () => this.runInits(group), budget);
|
|
496
|
-
}
|
|
497
|
-
this.registerReadyTask(groups.get(3) ?? []);
|
|
498
|
-
}
|
|
499
|
-
async runInits(group) {
|
|
500
|
-
for (const attachment of group) {
|
|
501
|
-
const { key, instance } = attachment;
|
|
502
|
-
const spec = resolvedLifecycleSpecOf(instance);
|
|
503
|
-
pluginLoggerOf(instance).utils.initialization(key, "start");
|
|
504
|
-
await withTimeout(`Plugin (${key})`, () => instance.init(), spec.init.timeout);
|
|
505
|
-
pluginLoggerOf(instance).utils.initialization(key, "end");
|
|
506
|
-
this.completedInits.add(attachment);
|
|
507
|
-
if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
registerReadyTask(readyInits) {
|
|
511
|
-
const steps = this.attachments.reduce((acc, a) => {
|
|
512
|
-
const ready = a.instance.ready?.bind(a.instance);
|
|
513
|
-
if (ready) acc.push({
|
|
514
|
-
key: a.key,
|
|
515
|
-
run: ready,
|
|
516
|
-
timeout: resolvedLifecycleSpecOf(a.instance).ready.timeout
|
|
517
|
-
});
|
|
518
|
-
return acc;
|
|
519
|
-
}, []);
|
|
520
|
-
if (readyInits.length === 0 && steps.length === 0) return;
|
|
521
|
-
const budget = readyInits.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0) + steps.reduce((sum, step) => sum + step.timeout, 0);
|
|
522
|
-
this.startup.addTask(3, "plugins-ready", async () => {
|
|
523
|
-
await this.runInits(readyInits);
|
|
524
|
-
for (const step of steps) await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);
|
|
525
|
-
}, budget);
|
|
526
|
-
}
|
|
527
|
-
registerDisposeTask(phase) {
|
|
528
|
-
if (this.disposePhases.has(phase)) return;
|
|
529
|
-
this.disposePhases.add(phase);
|
|
530
|
-
const budget = this.attachments.reduce((sum, a) => {
|
|
531
|
-
const spec = resolvedLifecycleSpecOf(a.instance);
|
|
532
|
-
return a.instance.dispose && spec.dispose.phase === phase ? sum + spec.dispose.timeout : sum;
|
|
533
|
-
}, 0);
|
|
534
|
-
this.shutdown.addTask(phase, "plugins-dispose", () => this.runDisposals(phase), budget);
|
|
535
|
-
}
|
|
536
|
-
async runDisposals(phase) {
|
|
537
|
-
const failures = [];
|
|
538
|
-
await this.disposeCompleted(phase, (caught) => failures.push(caught));
|
|
539
|
-
if (failures.length === 1) throw failures[0];
|
|
540
|
-
if (failures.length > 1) throw new SeedcordAggregateError(SeedcordErrorCode.PluginDisposeFailures, failures, [failures.length]);
|
|
541
|
-
}
|
|
542
|
-
async disposeCompleted(phase, onError) {
|
|
543
|
-
for (const attachment of [...this.attachments].reverse()) {
|
|
544
|
-
if (!this.completedInits.has(attachment)) continue;
|
|
545
|
-
const dispose = attachment.instance.dispose?.bind(attachment.instance);
|
|
546
|
-
if (!dispose) continue;
|
|
547
|
-
const spec = resolvedLifecycleSpecOf(attachment.instance);
|
|
548
|
-
if (phase !== void 0 && spec.dispose.phase !== phase) continue;
|
|
549
|
-
try {
|
|
550
|
-
await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout);
|
|
551
|
-
} catch (caught) {
|
|
552
|
-
onError(caught);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
async rollback() {
|
|
557
|
-
this.startFailed = true;
|
|
558
|
-
await this.disposeCompleted(void 0, (caught) => this.pluginLogger.warn("rollback dispose failed", caught));
|
|
559
|
-
this.completedInits.clear();
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
|
|
563
110
|
//#endregion
|
|
564
111
|
//#region src/node/subscribers/SubscriberLoader.ts
|
|
565
112
|
var SubscriberLoader = class {
|
|
@@ -585,14 +132,14 @@ var SubscriberLoader = class {
|
|
|
585
132
|
async init() {
|
|
586
133
|
if (this.isInitialized) return;
|
|
587
134
|
this.isInitialized = true;
|
|
588
|
-
this.bus
|
|
135
|
+
this.bus[RegisterDefaults]();
|
|
589
136
|
const { directory } = this;
|
|
590
137
|
if (directory) {
|
|
591
138
|
busLoggerOf(this.bus).debug(paint.mute(directory));
|
|
592
139
|
await this.load(directory);
|
|
593
|
-
busLoggerOf(this.bus).utils.list([`${paint.iris.bold(this.bus
|
|
140
|
+
busLoggerOf(this.bus).utils.list([`${paint.iris.bold(this.bus[RegisteredCount])} subscribers`], paint.mint.bold("Loaded"), "debug");
|
|
594
141
|
}
|
|
595
|
-
if (!Envapter.isTest) await this.bus
|
|
142
|
+
if (!Envapter.isTest) await this.bus[VerifyWebhooks]();
|
|
596
143
|
}
|
|
597
144
|
/** @internal */
|
|
598
145
|
async onHmr(event) {
|
|
@@ -610,10 +157,10 @@ var SubscriberLoader = class {
|
|
|
610
157
|
});
|
|
611
158
|
}
|
|
612
159
|
registerSubscriber(ctor) {
|
|
613
|
-
this.bus
|
|
160
|
+
this.bus[RegisterSubscriber](registrationFor(ctor));
|
|
614
161
|
}
|
|
615
162
|
unregisterSubscriber(ctor, artifacts) {
|
|
616
|
-
this.bus
|
|
163
|
+
this.bus[UnregisterSubscriber](ctor, artifacts);
|
|
617
164
|
}
|
|
618
165
|
getArtifacts(ctor) {
|
|
619
166
|
return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
|
|
@@ -655,7 +202,7 @@ var CommandRegistry = class {
|
|
|
655
202
|
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
656
203
|
this.hmrHandler = new HmrModuleHandler({
|
|
657
204
|
handlersDir: dir,
|
|
658
|
-
isHandler:
|
|
205
|
+
isHandler: isCommandClass,
|
|
659
206
|
registerHandler: this.registerCommand.bind(this),
|
|
660
207
|
unregisterHandler: this.unregisterCommand.bind(this),
|
|
661
208
|
getArtifacts: this.getArtifacts.bind(this),
|
|
@@ -718,16 +265,12 @@ var CommandRegistry = class {
|
|
|
718
265
|
}
|
|
719
266
|
async loadCommands(dir) {
|
|
720
267
|
await traverseDirectory(dir, (fullPath, rel, mod) => {
|
|
721
|
-
for (const exported of Object.values(mod)) if (
|
|
268
|
+
for (const exported of Object.values(mod)) if (isCommandClass(exported)) {
|
|
722
269
|
this.registerCommand(exported, rel);
|
|
723
270
|
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
724
271
|
}
|
|
725
272
|
});
|
|
726
273
|
}
|
|
727
|
-
isCommandClass(obj) {
|
|
728
|
-
if (typeof obj !== "function") return false;
|
|
729
|
-
return obj.prototype instanceof BuilderComponent && Reflect.hasMetadata(CommandMetadataKey, obj);
|
|
730
|
-
}
|
|
731
274
|
registerCommand(ctor, rel) {
|
|
732
275
|
const meta = Reflect.getMetadata(CommandMetadataKey, ctor);
|
|
733
276
|
if (!meta) return;
|
|
@@ -819,5 +362,5 @@ function deployedPayload(result) {
|
|
|
819
362
|
assertNodeVersion(">=24.11", process.version);
|
|
820
363
|
|
|
821
364
|
//#endregion
|
|
822
|
-
export { CommandRegistry,
|
|
365
|
+
export { CommandRegistry, HealthCheck, HealthResponder, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin, shutdownOf };
|
|
823
366
|
//# sourceMappingURL=node-internal.index.mjs.map
|