@seedcord/core 0.3.0 → 0.4.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/{Bus-DcuUtOEL.mjs → Bus-BVwmKc-A.mjs} +51 -17
- package/dist/Bus-BVwmKc-A.mjs.map +1 -0
- package/dist/Pluggable-Cf5-WWmW.mjs +280 -0
- package/dist/Pluggable-Cf5-WWmW.mjs.map +1 -0
- package/dist/Pluggable-DFNUIlJM.d.mts +148 -0
- package/dist/{Plugin-Bkeqcjy6.mjs → Plugin-C6oMqWf8.mjs} +32 -7
- package/dist/Plugin-C6oMqWf8.mjs.map +1 -0
- package/dist/Plugin-CG9pWbPF.d.mts +193 -0
- package/dist/{cursor-DKKKUa_7.d.mts → cursor-BcRfmTSo.d.mts} +27 -140
- package/dist/{cursor-Cv1Ui6gZ.mjs → cursor-GBUYz3jk.mjs} +377 -176
- package/dist/cursor-GBUYz3jk.mjs.map +1 -0
- package/dist/{index-uBwh8r7x.d.mts → index-ZzdQafFv.d.mts} +2 -2
- package/dist/index.d.mts +132 -19
- package/dist/index.mjs +186 -19
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +11 -4
- package/dist/internal.index.mjs +8 -455
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.d.mts +10 -146
- package/dist/node-internal.index.mjs +43 -470
- 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/{Plugin-B6iRzm3g.d.mts → phases-F8yvPd60.d.mts} +111 -207
- package/dist/plugin.index.d.mts +2 -2
- 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-CbYRwqMY.mjs} +10 -2
- package/dist/slashRouteLeaves-CbYRwqMY.mjs.map +1 -0
- package/dist/subscribers-CGGQkUfM.mjs +139 -0
- package/dist/subscribers-CGGQkUfM.mjs.map +1 -0
- package/package.json +14 -8
- package/dist/Bus-DcuUtOEL.mjs.map +0 -1
- package/dist/Plugin-Bkeqcjy6.mjs.map +0 -1
- package/dist/cursor-Cv1Ui6gZ.mjs.map +0 -1
- package/dist/slashRouteLeaves-De1SxqnX.mjs.map +0 -1
- package/dist/subscribers-DuHg-bGT.mjs +0 -3
|
@@ -1,284 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as SubscribeMetadataKey, M as contextMenuLeaves, a as RegisterSubscriber, b as CommandMetadataKey, c as VerifyWebhooks, f as Subscriber, i as RegisterDefaults, n as busLoggerOf, o as RegisteredCount, r as registrationFor, s as UnregisterSubscriber } from "./Bus-BVwmKc-A.mjs";
|
|
2
|
+
import { t as CommandInjector } from "./subscribers-CGGQkUfM.mjs";
|
|
3
|
+
import { t as PublishDefault } from "./publishDefault-D826XXDQ.mjs";
|
|
2
4
|
import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
|
|
3
|
-
import "./subscribers-DuHg-bGT.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-CbYRwqMY.mjs";
|
|
7
7
|
import { t as HmrModuleHandler } from "./HmrModuleHandler-9AUfKXRl.mjs";
|
|
8
|
+
import { i as settleWithin, n as shutdownOf, r as assertNodeVersion } from "./Pluggable-Cf5-WWmW.mjs";
|
|
8
9
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
|
9
10
|
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
10
|
-
import {
|
|
11
|
+
import { Logger } from "@seedcord/logger";
|
|
11
12
|
import { Routes } from "discord-api-types/v10";
|
|
12
|
-
import {
|
|
13
|
+
import { SeedcordError } from "@seedcord/errors/internal";
|
|
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/Lifecycle/withTimeout.ts
|
|
20
|
-
async function raceTimer(work, timeoutMs, onTimeout) {
|
|
21
|
-
let timer;
|
|
22
|
-
try {
|
|
23
|
-
await Promise.race([work, new Promise((resolve, reject) => {
|
|
24
|
-
timer = setTimeout(() => onTimeout(resolve, reject), timeoutMs);
|
|
25
|
-
})]);
|
|
26
|
-
} finally {
|
|
27
|
-
if (timer) clearTimeout(timer);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function withTimeout(name, run, timeoutMs) {
|
|
31
|
-
return raceTimer(run(), timeoutMs, (_, reject) => {
|
|
32
|
-
reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [name, timeoutMs]));
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
function settleWithin(work, timeoutMs) {
|
|
36
|
-
return raceTimer(work, timeoutMs, (resolve) => resolve());
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/node/Lifecycle/CoordinatedLifecycle.ts
|
|
41
|
-
var CoordinatedLifecycle = class {
|
|
42
|
-
phaseOrder;
|
|
43
|
-
phaseEnum;
|
|
44
|
-
logger;
|
|
45
|
-
tasksMap = /* @__PURE__ */ new Map();
|
|
46
|
-
constructor(loggerName, phaseOrder, phaseEnum) {
|
|
47
|
-
this.phaseOrder = phaseOrder;
|
|
48
|
-
this.phaseEnum = phaseEnum;
|
|
49
|
-
this.logger = new Logger(loggerName, { channel: "lifecycle" });
|
|
50
|
-
this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
|
|
51
|
-
}
|
|
52
|
-
addTask(phase, taskName, task, timeoutMs) {
|
|
53
|
-
if (!this.canAddTask()) return;
|
|
54
|
-
const tasks = this.tasksMap.get(phase);
|
|
55
|
-
if (!tasks) throw new SeedcordError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);
|
|
56
|
-
tasks.push({
|
|
57
|
-
name: taskName,
|
|
58
|
-
task,
|
|
59
|
-
timeout: timeoutMs
|
|
60
|
-
});
|
|
61
|
-
this.logger.debug(`${paint.italic("Added")} ${this.getTaskType()} task ${paint.sky.bold(taskName)} to phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
62
|
-
}
|
|
63
|
-
removeTask(phase, taskName) {
|
|
64
|
-
if (!this.canRemoveTask()) return false;
|
|
65
|
-
const tasks = this.tasksMap.get(phase);
|
|
66
|
-
if (!tasks) return false;
|
|
67
|
-
const initialLength = tasks.length;
|
|
68
|
-
const filteredTasks = tasks.filter((task) => task.name !== taskName);
|
|
69
|
-
this.tasksMap.set(phase, filteredTasks);
|
|
70
|
-
const removed = initialLength !== filteredTasks.length;
|
|
71
|
-
if (removed) this.logger.debug(`${paint.italic("Removed")} ${this.getTaskType()} task ${paint.sky.bold(taskName)} from phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
72
|
-
return removed;
|
|
73
|
-
}
|
|
74
|
-
async runPhase(phase) {
|
|
75
|
-
const tasks = this.tasksMap.get(phase) ?? [];
|
|
76
|
-
if (tasks.length === 0) {
|
|
77
|
-
this.logger.trace(`No tasks to run in phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
this.logger.debug(`${paint.amber.bold("Running")} ${this.getTaskType()} phase ${paint.iris.bold(this.phaseEnum[phase])} with ${paint.sky.bold(String(tasks.length))} tasks`);
|
|
81
|
-
const reasons = (await this.executeTasksInPhase(phase, tasks)).reduce((rejected, result) => {
|
|
82
|
-
if (result.status === "rejected") rejected.push(result.reason);
|
|
83
|
-
return rejected;
|
|
84
|
-
}, []);
|
|
85
|
-
if (reasons.length > 0) throw new SeedcordAggregateError(SeedcordErrorCode.LifecyclePhaseFailures, reasons, [this.phaseEnum[phase], reasons.length]);
|
|
86
|
-
this.logger.debug(`Phase ${paint.iris.bold(this.phaseEnum[phase])} ${paint.mint.bold("completed successfully")}`);
|
|
87
|
-
}
|
|
88
|
-
async runTaskWithTimeout(phase, task) {
|
|
89
|
-
this.logger.trace(`${paint.italic("Starting")} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
90
|
-
try {
|
|
91
|
-
await withTimeout(task.name, task.task, task.timeout);
|
|
92
|
-
this.logger.trace(`${paint.italic("Completed")} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}`);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
if (this.isAborted()) return;
|
|
95
|
-
this.logger.error(`${paint.italic("Failed")} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}:`, error);
|
|
96
|
-
throw error;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
isAborted() {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
//#endregion
|
|
105
|
-
//#region src/node/Lifecycle/CoordinatedShutdown.ts
|
|
106
|
-
const PHASE_ORDER$1 = [
|
|
107
|
-
1,
|
|
108
|
-
2,
|
|
109
|
-
3,
|
|
110
|
-
4
|
|
111
|
-
];
|
|
112
|
-
const LOG_FLUSH_DELAY_MS = 3e3;
|
|
113
|
-
var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
114
|
-
isShuttingDown = false;
|
|
115
|
-
hasShutdown = false;
|
|
116
|
-
exitCode = 0;
|
|
117
|
-
onSigTerm = null;
|
|
118
|
-
onSigInt = null;
|
|
119
|
-
startupGate;
|
|
120
|
-
constructor() {
|
|
121
|
-
super("Shutdown", PHASE_ORDER$1, ShutdownPhase);
|
|
122
|
-
this.registerSignalHandlers();
|
|
123
|
-
}
|
|
124
|
-
canAddTask() {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
canRemoveTask() {
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
getTaskType() {
|
|
131
|
-
return "shutdown";
|
|
132
|
-
}
|
|
133
|
-
async executeTasksInPhase(phase, tasks) {
|
|
134
|
-
const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
|
|
135
|
-
return Promise.allSettled(promises);
|
|
136
|
-
}
|
|
137
|
-
registerSignalHandlers() {
|
|
138
|
-
this.onSigTerm = () => {
|
|
139
|
-
this.logger.info(`Received ${paint.amber.bold("SIGTERM")} signal`);
|
|
140
|
-
this.run(0);
|
|
141
|
-
};
|
|
142
|
-
this.onSigInt = () => {
|
|
143
|
-
this.logger.info(`Received ${paint.amber.bold("SIGINT")} signal`);
|
|
144
|
-
this.run(0);
|
|
145
|
-
};
|
|
146
|
-
process.on("SIGTERM", this.onSigTerm);
|
|
147
|
-
process.on("SIGINT", this.onSigInt);
|
|
148
|
-
}
|
|
149
|
-
/** @internal */
|
|
150
|
-
removeSignalHandlers() {
|
|
151
|
-
if (this.onSigTerm) {
|
|
152
|
-
process.off("SIGTERM", this.onSigTerm);
|
|
153
|
-
this.onSigTerm = null;
|
|
154
|
-
}
|
|
155
|
-
if (this.onSigInt) {
|
|
156
|
-
process.off("SIGINT", this.onSigInt);
|
|
157
|
-
this.onSigInt = null;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/** @internal run() awaits this so boot finishes registering its dispose tasks first */
|
|
161
|
-
gateOnStartup(settled) {
|
|
162
|
-
this.startupGate = settled;
|
|
163
|
-
}
|
|
164
|
-
addTask(phase, taskName, task, timeoutMs = 5e3) {
|
|
165
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
166
|
-
}
|
|
167
|
-
removeTask(phase, taskName) {
|
|
168
|
-
return super.removeTask(phase, taskName);
|
|
169
|
-
}
|
|
170
|
-
async run(exitCode = 0, exitProcess = true) {
|
|
171
|
-
this.removeSignalHandlers();
|
|
172
|
-
if (this.hasShutdown || this.isShuttingDown) {
|
|
173
|
-
this.logger.warn("Shutdown sequence already ran or is in progress");
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
this.isShuttingDown = true;
|
|
177
|
-
this.exitCode = exitCode;
|
|
178
|
-
this.logger.info(`${paint.amber.bold("Starting")} coordinated shutdown with exit code ${paint.sky.bold(exitCode)}`);
|
|
179
|
-
try {
|
|
180
|
-
if (this.startupGate) await this.startupGate;
|
|
181
|
-
const failures = [];
|
|
182
|
-
for (const phase of PHASE_ORDER$1) try {
|
|
183
|
-
await this.runPhase(phase);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
failures.push(error);
|
|
186
|
-
}
|
|
187
|
-
if (failures.length > 0) this.logger.error(`${paint.coral.bold("Coordinated shutdown failed")}`, ...failures);
|
|
188
|
-
else this.logger.info(`${paint.mint.bold("Coordinated shutdown completed")} successfully`);
|
|
189
|
-
} finally {
|
|
190
|
-
this.hasShutdown = true;
|
|
191
|
-
if (exitProcess) {
|
|
192
|
-
this.logger.debug(`${paint.coral.bold("Exiting")} process with code ${paint.sky.bold(this.exitCode)}`);
|
|
193
|
-
setTimeout(() => {
|
|
194
|
-
process.exit(this.exitCode);
|
|
195
|
-
}, LOG_FLUSH_DELAY_MS);
|
|
196
|
-
} else {
|
|
197
|
-
this.logger.debug(`${paint.amber.bold("Skipping")} process exit (dev mode)`);
|
|
198
|
-
this.isShuttingDown = false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
//#endregion
|
|
205
|
-
//#region src/node/Lifecycle/CoordinatedStartup.ts
|
|
206
|
-
const PHASE_ORDER = [
|
|
207
|
-
1,
|
|
208
|
-
2,
|
|
209
|
-
3
|
|
210
|
-
];
|
|
211
|
-
var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
212
|
-
isStartingUp = false;
|
|
213
|
-
hasStarted = false;
|
|
214
|
-
constructor() {
|
|
215
|
-
super("Startup", PHASE_ORDER, StartupPhase);
|
|
216
|
-
}
|
|
217
|
-
addTask(phase, taskName, task, timeoutMs = 1e4) {
|
|
218
|
-
super.addTask(phase, taskName, task, timeoutMs);
|
|
219
|
-
}
|
|
220
|
-
canAddTask() {
|
|
221
|
-
if (this.hasStarted) throw new SeedcordError(SeedcordErrorCode.LifecycleAddAfterCompletion);
|
|
222
|
-
if (this.isStartingUp) throw new SeedcordError(SeedcordErrorCode.LifecycleAddDuringRun);
|
|
223
|
-
return true;
|
|
224
|
-
}
|
|
225
|
-
canRemoveTask() {
|
|
226
|
-
if (this.isStartingUp) throw new SeedcordError(SeedcordErrorCode.LifecycleRemoveDuringRun);
|
|
227
|
-
return true;
|
|
228
|
-
}
|
|
229
|
-
getTaskType() {
|
|
230
|
-
return "startup";
|
|
231
|
-
}
|
|
232
|
-
async executeTasksInPhase(phase, tasks) {
|
|
233
|
-
const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));
|
|
234
|
-
return Promise.allSettled(promises);
|
|
235
|
-
}
|
|
236
|
-
async run() {
|
|
237
|
-
if (this.hasStarted) {
|
|
238
|
-
this.logger.warn("Startup sequence has already completed");
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
if (this.isStartingUp) {
|
|
242
|
-
this.logger.warn("Startup sequence already in progress");
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
this.isStartingUp = true;
|
|
246
|
-
this.logger.info(`${paint.mint.bold("Starting")} coordinated startup sequence`);
|
|
247
|
-
try {
|
|
248
|
-
for (const phase of PHASE_ORDER) {
|
|
249
|
-
if (!this.isStartingUp) {
|
|
250
|
-
this.logger.warn("Startup sequence aborted");
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
await this.runPhase(phase);
|
|
254
|
-
}
|
|
255
|
-
this.hasStarted = true;
|
|
256
|
-
this.logger.info(`${paint.mint.bold("Coordinated startup completed")} successfully`);
|
|
257
|
-
} catch (error) {
|
|
258
|
-
if (!this.isStartingUp) {
|
|
259
|
-
this.logger.warn("Startup sequence aborted during error handling");
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
this.logger.error(`${paint.coral.bold("Coordinated startup failed")}`);
|
|
263
|
-
throw error;
|
|
264
|
-
} finally {
|
|
265
|
-
this.isStartingUp = false;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
isAborted() {
|
|
269
|
-
return !this.isStartingUp;
|
|
270
|
-
}
|
|
271
|
-
abort() {
|
|
272
|
-
if (!this.isStartingUp) return;
|
|
273
|
-
this.isStartingUp = false;
|
|
274
|
-
this.logger.warn("Aborting coordinated startup sequence");
|
|
275
|
-
}
|
|
276
|
-
get isReady() {
|
|
277
|
-
return this.hasStarted;
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
//#endregion
|
|
282
20
|
//#region src/node/HealthResponder.ts
|
|
283
21
|
const HTTP_OK = 200;
|
|
284
22
|
const DEFAULT_HEALTH_CHECK_PATH = "/health";
|
|
@@ -369,185 +107,6 @@ var HealthCheck = class HealthCheck {
|
|
|
369
107
|
}
|
|
370
108
|
};
|
|
371
109
|
|
|
372
|
-
//#endregion
|
|
373
|
-
//#region src/node/Pluggable.ts
|
|
374
|
-
const RESERVED_KEYS = new Set(FRAMEWORK_CHANNELS);
|
|
375
|
-
/**
|
|
376
|
-
* Base class for a plugin host, a transport `Seedcord` class.
|
|
377
|
-
*
|
|
378
|
-
* Plugins are attached during configuration and initialized during startup, sequentially in attach
|
|
379
|
-
* order within each phase.
|
|
380
|
-
*/
|
|
381
|
-
var Pluggable = class Pluggable {
|
|
382
|
-
/** @see {@link CoordinatedShutdown} */
|
|
383
|
-
shutdown;
|
|
384
|
-
/** @see {@link CoordinatedStartup} */
|
|
385
|
-
startup;
|
|
386
|
-
isInitialized = false;
|
|
387
|
-
startFailed = false;
|
|
388
|
-
plugins = [];
|
|
389
|
-
pluginLogger = new Logger("Plugins", { channel: "plugins" });
|
|
390
|
-
attachments = [];
|
|
391
|
-
completedInits = /* @__PURE__ */ new Set();
|
|
392
|
-
disposePhases = /* @__PURE__ */ new Set();
|
|
393
|
-
pluginTasksRegistered = false;
|
|
394
|
-
static isInstantiated = false;
|
|
395
|
-
static liveShutdown;
|
|
396
|
-
constructor(shutdown, startup) {
|
|
397
|
-
if (Pluggable.isInstantiated) {
|
|
398
|
-
shutdown.removeSignalHandlers();
|
|
399
|
-
throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);
|
|
400
|
-
}
|
|
401
|
-
Pluggable.isInstantiated = true;
|
|
402
|
-
Pluggable.liveShutdown = shutdown;
|
|
403
|
-
this.shutdown = shutdown;
|
|
404
|
-
this.startup = startup;
|
|
405
|
-
}
|
|
406
|
-
/** @internal */
|
|
407
|
-
async init() {
|
|
408
|
-
if (this.isInitialized) return this;
|
|
409
|
-
if (this.startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);
|
|
410
|
-
this.registerPluginTasks();
|
|
411
|
-
const startupSettled = Promise.withResolvers();
|
|
412
|
-
this.shutdown.gateOnStartup(startupSettled.promise);
|
|
413
|
-
try {
|
|
414
|
-
await this.startup.run();
|
|
415
|
-
} catch (caught) {
|
|
416
|
-
await this.rollback();
|
|
417
|
-
throw caught;
|
|
418
|
-
} finally {
|
|
419
|
-
startupSettled.resolve();
|
|
420
|
-
}
|
|
421
|
-
this.isInitialized = true;
|
|
422
|
-
return this;
|
|
423
|
-
}
|
|
424
|
-
/** @internal codegen reads these to emit the `Core` augmentation */
|
|
425
|
-
get pluginKeys() {
|
|
426
|
-
return this.attachments.map((attachment) => attachment.key);
|
|
427
|
-
}
|
|
428
|
-
/** @internal so the next host can construct */
|
|
429
|
-
static reset() {
|
|
430
|
-
Pluggable.liveShutdown?.removeSignalHandlers();
|
|
431
|
-
Pluggable.liveShutdown = void 0;
|
|
432
|
-
Pluggable.isInstantiated = false;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Attaches a plugin to this instance.
|
|
436
|
-
*
|
|
437
|
-
* The plugin initializes during startup, after every plugin attached before it, and becomes
|
|
438
|
-
* available as a property on `core`. `seedcord codegen` writes the `Core` augmentation that
|
|
439
|
-
* types it.
|
|
440
|
-
*
|
|
441
|
-
* A plugin declaring a `transport` or `runtime` this host does not run is a compile error on
|
|
442
|
-
* this call. An edge host takes no plugins at all. A plugin constructor narrowing its first
|
|
443
|
-
* parameter past `CoreBase` is a compile error here too. Read the transport `Core` off `this.core`.
|
|
444
|
-
*
|
|
445
|
-
* @typeParam Key - The property name for accessing the plugin
|
|
446
|
-
* @typeParam Ctor - The plugin constructor type
|
|
447
|
-
* @param key - Property name to access the plugin instance
|
|
448
|
-
* @param Plugin - Plugin constructor class
|
|
449
|
-
* @param args - Additional arguments to pass to the plugin constructor
|
|
450
|
-
* @returns This instance with the plugin attached as a typed property
|
|
451
|
-
* @throws A **SeedcordError** When called after initialization or if key already exists or is reserved
|
|
452
|
-
* @example
|
|
453
|
-
* ```typescript
|
|
454
|
-
* seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
|
|
455
|
-
* ```
|
|
456
|
-
*/
|
|
457
|
-
attach(key, Plugin, ...args) {
|
|
458
|
-
if (this.isInitialized) throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);
|
|
459
|
-
if (RESERVED_KEYS.has(key)) throw new SeedcordError(SeedcordErrorCode.CorePluginReservedChannel, [key]);
|
|
460
|
-
if (key in this) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);
|
|
461
|
-
const instance = new Plugin(this, ...args);
|
|
462
|
-
pluginLoggerOf(instance).setChannel(key);
|
|
463
|
-
this.plugins.push(instance);
|
|
464
|
-
this.attachments.push({
|
|
465
|
-
key,
|
|
466
|
-
instance
|
|
467
|
-
});
|
|
468
|
-
return Object.assign(this, { [key]: instance });
|
|
469
|
-
}
|
|
470
|
-
registerPluginTasks() {
|
|
471
|
-
if (this.pluginTasksRegistered) return;
|
|
472
|
-
this.pluginTasksRegistered = true;
|
|
473
|
-
const groups = /* @__PURE__ */ new Map();
|
|
474
|
-
for (const attachment of this.attachments) {
|
|
475
|
-
const phase = resolvedLifecycleSpecOf(attachment.instance).init.phase;
|
|
476
|
-
const group = groups.get(phase) ?? [];
|
|
477
|
-
group.push(attachment);
|
|
478
|
-
groups.set(phase, group);
|
|
479
|
-
}
|
|
480
|
-
for (const [phase, group] of groups) {
|
|
481
|
-
if (phase === 3) continue;
|
|
482
|
-
const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);
|
|
483
|
-
this.startup.addTask(phase, "plugins-init", () => this.runInits(group), budget);
|
|
484
|
-
}
|
|
485
|
-
this.registerReadyTask(groups.get(3) ?? []);
|
|
486
|
-
}
|
|
487
|
-
async runInits(group) {
|
|
488
|
-
for (const attachment of group) {
|
|
489
|
-
const { key, instance } = attachment;
|
|
490
|
-
const spec = resolvedLifecycleSpecOf(instance);
|
|
491
|
-
pluginLoggerOf(instance).utils.initialization(key, "start");
|
|
492
|
-
await withTimeout(`Plugin (${key})`, () => instance.init(), spec.init.timeout);
|
|
493
|
-
pluginLoggerOf(instance).utils.initialization(key, "end");
|
|
494
|
-
this.completedInits.add(attachment);
|
|
495
|
-
if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
registerReadyTask(readyInits) {
|
|
499
|
-
const steps = this.attachments.reduce((acc, a) => {
|
|
500
|
-
const ready = a.instance.ready?.bind(a.instance);
|
|
501
|
-
if (ready) acc.push({
|
|
502
|
-
key: a.key,
|
|
503
|
-
run: ready,
|
|
504
|
-
timeout: resolvedLifecycleSpecOf(a.instance).ready.timeout
|
|
505
|
-
});
|
|
506
|
-
return acc;
|
|
507
|
-
}, []);
|
|
508
|
-
if (readyInits.length === 0 && steps.length === 0) return;
|
|
509
|
-
const budget = readyInits.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0) + steps.reduce((sum, step) => sum + step.timeout, 0);
|
|
510
|
-
this.startup.addTask(3, "plugins-ready", async () => {
|
|
511
|
-
await this.runInits(readyInits);
|
|
512
|
-
for (const step of steps) await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);
|
|
513
|
-
}, budget);
|
|
514
|
-
}
|
|
515
|
-
registerDisposeTask(phase) {
|
|
516
|
-
if (this.disposePhases.has(phase)) return;
|
|
517
|
-
this.disposePhases.add(phase);
|
|
518
|
-
const budget = this.attachments.reduce((sum, a) => {
|
|
519
|
-
const spec = resolvedLifecycleSpecOf(a.instance);
|
|
520
|
-
return a.instance.dispose && spec.dispose.phase === phase ? sum + spec.dispose.timeout : sum;
|
|
521
|
-
}, 0);
|
|
522
|
-
this.shutdown.addTask(phase, "plugins-dispose", () => this.runDisposals(phase), budget);
|
|
523
|
-
}
|
|
524
|
-
async runDisposals(phase) {
|
|
525
|
-
const failures = [];
|
|
526
|
-
await this.disposeCompleted(phase, (caught) => failures.push(caught));
|
|
527
|
-
if (failures.length === 1) throw failures[0];
|
|
528
|
-
if (failures.length > 1) throw new SeedcordAggregateError(SeedcordErrorCode.PluginDisposeFailures, failures, [failures.length]);
|
|
529
|
-
}
|
|
530
|
-
async disposeCompleted(phase, onError) {
|
|
531
|
-
for (const attachment of [...this.attachments].reverse()) {
|
|
532
|
-
if (!this.completedInits.has(attachment)) continue;
|
|
533
|
-
const dispose = attachment.instance.dispose?.bind(attachment.instance);
|
|
534
|
-
if (!dispose) continue;
|
|
535
|
-
const spec = resolvedLifecycleSpecOf(attachment.instance);
|
|
536
|
-
if (phase !== void 0 && spec.dispose.phase !== phase) continue;
|
|
537
|
-
try {
|
|
538
|
-
await withTimeout(`Plugin:${attachment.key}:dispose`, dispose, spec.dispose.timeout);
|
|
539
|
-
} catch (caught) {
|
|
540
|
-
onError(caught);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
async rollback() {
|
|
545
|
-
this.startFailed = true;
|
|
546
|
-
await this.disposeCompleted(void 0, (caught) => this.pluginLogger.warn("rollback dispose failed", caught));
|
|
547
|
-
this.completedInits.clear();
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
|
|
551
110
|
//#endregion
|
|
552
111
|
//#region src/node/subscribers/SubscriberLoader.ts
|
|
553
112
|
var SubscriberLoader = class {
|
|
@@ -573,14 +132,14 @@ var SubscriberLoader = class {
|
|
|
573
132
|
async init() {
|
|
574
133
|
if (this.isInitialized) return;
|
|
575
134
|
this.isInitialized = true;
|
|
576
|
-
this.bus
|
|
135
|
+
this.bus[RegisterDefaults]();
|
|
577
136
|
const { directory } = this;
|
|
578
137
|
if (directory) {
|
|
579
138
|
busLoggerOf(this.bus).debug(paint.mute(directory));
|
|
580
139
|
await this.load(directory);
|
|
581
|
-
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");
|
|
582
141
|
}
|
|
583
|
-
if (!Envapter.isTest) await this.bus
|
|
142
|
+
if (!Envapter.isTest) await this.bus[VerifyWebhooks]();
|
|
584
143
|
}
|
|
585
144
|
/** @internal */
|
|
586
145
|
async onHmr(event) {
|
|
@@ -598,10 +157,10 @@ var SubscriberLoader = class {
|
|
|
598
157
|
});
|
|
599
158
|
}
|
|
600
159
|
registerSubscriber(ctor) {
|
|
601
|
-
this.bus
|
|
160
|
+
this.bus[RegisterSubscriber](registrationFor(ctor));
|
|
602
161
|
}
|
|
603
162
|
unregisterSubscriber(ctor, artifacts) {
|
|
604
|
-
this.bus
|
|
163
|
+
this.bus[UnregisterSubscriber](ctor, artifacts);
|
|
605
164
|
}
|
|
606
165
|
getArtifacts(ctor) {
|
|
607
166
|
return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
|
|
@@ -623,7 +182,7 @@ function indexById(commands) {
|
|
|
623
182
|
* @internal
|
|
624
183
|
*/
|
|
625
184
|
var CommandRegistry = class {
|
|
626
|
-
|
|
185
|
+
core;
|
|
627
186
|
logger = new Logger("Commands", { channel: "commands" });
|
|
628
187
|
isInitialised = false;
|
|
629
188
|
globalCommands = [];
|
|
@@ -633,13 +192,17 @@ var CommandRegistry = class {
|
|
|
633
192
|
loadedCommands = [];
|
|
634
193
|
hmrHandler;
|
|
635
194
|
pendingEvents = /* @__PURE__ */ new Map();
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
195
|
+
injector = new CommandInjector();
|
|
196
|
+
dir;
|
|
197
|
+
constructor(core) {
|
|
198
|
+
this.core = core;
|
|
199
|
+
const dir = core.config.bot.commands.path;
|
|
200
|
+
if (!dir) throw new SeedcordError(SeedcordErrorCode.CoreControllerPathMissing, ["CommandRegistry", "commands"]);
|
|
201
|
+
this.dir = dir;
|
|
639
202
|
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
640
203
|
this.hmrHandler = new HmrModuleHandler({
|
|
641
|
-
handlersDir:
|
|
642
|
-
isHandler:
|
|
204
|
+
handlersDir: dir,
|
|
205
|
+
isHandler: isCommandClass,
|
|
643
206
|
registerHandler: this.registerCommand.bind(this),
|
|
644
207
|
unregisterHandler: this.unregisterCommand.bind(this),
|
|
645
208
|
getArtifacts: this.getArtifacts.bind(this),
|
|
@@ -656,7 +219,7 @@ var CommandRegistry = class {
|
|
|
656
219
|
this.loading = true;
|
|
657
220
|
this.loadedCommands.length = 0;
|
|
658
221
|
try {
|
|
659
|
-
await this.loadCommands(this.
|
|
222
|
+
await this.loadCommands(this.dir);
|
|
660
223
|
} finally {
|
|
661
224
|
this.loading = false;
|
|
662
225
|
}
|
|
@@ -694,7 +257,7 @@ var CommandRegistry = class {
|
|
|
694
257
|
}
|
|
695
258
|
/** @internal */
|
|
696
259
|
async onHmr(event) {
|
|
697
|
-
if (event.file.startsWith(resolve(process.cwd(), this.
|
|
260
|
+
if (event.file.startsWith(resolve(process.cwd(), this.dir))) {
|
|
698
261
|
this.pendingEvents.delete(event.file);
|
|
699
262
|
this.pendingEvents.set(event.file, event);
|
|
700
263
|
getDevChannel()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => formatFilePath(f)) });
|
|
@@ -702,16 +265,12 @@ var CommandRegistry = class {
|
|
|
702
265
|
}
|
|
703
266
|
async loadCommands(dir) {
|
|
704
267
|
await traverseDirectory(dir, (fullPath, rel, mod) => {
|
|
705
|
-
for (const exported of Object.values(mod)) if (
|
|
268
|
+
for (const exported of Object.values(mod)) if (isCommandClass(exported)) {
|
|
706
269
|
this.registerCommand(exported, rel);
|
|
707
270
|
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
708
271
|
}
|
|
709
272
|
});
|
|
710
273
|
}
|
|
711
|
-
isCommandClass(obj) {
|
|
712
|
-
if (typeof obj !== "function") return false;
|
|
713
|
-
return obj.prototype instanceof BuilderComponent && Reflect.hasMetadata(CommandMetadataKey, obj);
|
|
714
|
-
}
|
|
715
274
|
registerCommand(ctor, rel) {
|
|
716
275
|
const meta = Reflect.getMetadata(CommandMetadataKey, ctor);
|
|
717
276
|
if (!meta) return;
|
|
@@ -755,7 +314,7 @@ var CommandRegistry = class {
|
|
|
755
314
|
global: /* @__PURE__ */ new Map(),
|
|
756
315
|
guilds: /* @__PURE__ */ new Map()
|
|
757
316
|
};
|
|
758
|
-
const appId = this.
|
|
317
|
+
const appId = this.core.applicationId;
|
|
759
318
|
if (this.globalCommands.length > 0) {
|
|
760
319
|
result.global = indexById(await this.put(Routes.applicationCommands(appId), this.globalCommands));
|
|
761
320
|
const tag = this.globalCommands.length === 1 ? "command" : "commands";
|
|
@@ -767,12 +326,14 @@ var CommandRegistry = class {
|
|
|
767
326
|
const tag = commands.length === 1 ? "command" : "commands";
|
|
768
327
|
this.logger.utils.block(`Deployed ${paint.iris.bold(commands.length)} ${tag} to ${paint.sky.bold(guildId)}`, this.logger.utils.wrap(commands.map((command) => command.name)), "info");
|
|
769
328
|
}
|
|
770
|
-
this.
|
|
329
|
+
this.injector.inject(result, this.allCommands());
|
|
330
|
+
this.core.bus[PublishDefault]("commandsDeployed", deployedPayload(result));
|
|
771
331
|
return result;
|
|
772
332
|
}
|
|
773
333
|
async put(route, commands) {
|
|
774
334
|
const body = commands.map((command) => command.toJSON());
|
|
775
|
-
|
|
335
|
+
const { rest } = this.core;
|
|
336
|
+
return await rest.put(route, { body });
|
|
776
337
|
}
|
|
777
338
|
/** Every registered builder, global first. A guild command in N guilds appears once per guild. @internal */
|
|
778
339
|
allCommands() {
|
|
@@ -787,7 +348,19 @@ var CommandRegistry = class {
|
|
|
787
348
|
return contextMenuLeaves(this.allCommands());
|
|
788
349
|
}
|
|
789
350
|
};
|
|
351
|
+
function deployedPayload(result) {
|
|
352
|
+
const guilds = {};
|
|
353
|
+
for (const [guildId, deployed] of result.guilds) guilds[guildId] = [...deployed.values()];
|
|
354
|
+
return {
|
|
355
|
+
global: [...result.global.values()],
|
|
356
|
+
guilds
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/node-internal.index.ts
|
|
362
|
+
assertNodeVersion(">=24.11", process.version);
|
|
790
363
|
|
|
791
364
|
//#endregion
|
|
792
|
-
export { CommandRegistry,
|
|
365
|
+
export { CommandRegistry, HealthCheck, HealthResponder, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin, shutdownOf };
|
|
793
366
|
//# sourceMappingURL=node-internal.index.mjs.map
|