@seedcord/core 0.1.0-next.6 → 0.1.0-next.8
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-3sNgKHdf.mjs +163 -0
- package/dist/HmrModuleHandler-3sNgKHdf.mjs.map +1 -0
- package/dist/HmrModuleHandler-CTBng7wV.cjs +170 -0
- package/dist/HmrModuleHandler-CTBng7wV.cjs.map +1 -0
- package/dist/Plugin-BZit5HUI.d.mts +555 -0
- package/dist/{Plugin-C2-VHrsX.cjs → Plugin-BlkhHNRm.cjs} +15 -34
- package/dist/Plugin-BlkhHNRm.cjs.map +1 -0
- package/dist/{Plugin-PcdPjAzc.mjs → Plugin-CDAV7P9l.mjs} +16 -29
- package/dist/Plugin-CDAV7P9l.mjs.map +1 -0
- package/dist/basic-1hebYVcN.d.mts +22 -0
- package/dist/contextMenuLeaves-f2rbyo83.d.mts +19 -0
- package/dist/{permissions-DSW_EZfN.cjs → cursor-BMkFAwO2.cjs} +308 -300
- package/dist/cursor-BMkFAwO2.cjs.map +1 -0
- package/dist/{permissions-RVCBhL2n.mjs → cursor-BvSH8tgw.mjs} +249 -247
- package/dist/cursor-BvSH8tgw.mjs.map +1 -0
- package/dist/{AutocompleteOptions-eKhIe6Qt.d.mts → cursor-D6nBjwaE.d.mts} +207 -116
- package/dist/hmr.index.cjs +2 -164
- package/dist/hmr.index.mjs +2 -162
- package/dist/index-Cr4-VV4u.d.mts +142 -0
- package/dist/index.cjs +362 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +239 -38
- package/dist/index.mjs +313 -26
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.cjs +278 -83
- package/dist/internal.index.cjs.map +1 -1
- package/dist/internal.index.d.mts +111 -42
- package/dist/internal.index.mjs +223 -43
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.cjs +314 -76
- package/dist/node-internal.index.cjs.map +1 -1
- package/dist/node-internal.index.d.mts +123 -2164
- package/dist/node-internal.index.mjs +313 -77
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/{phases-BfpAG6w3.mjs → phases-BauKJOTT.mjs} +2 -2
- package/dist/phases-BauKJOTT.mjs.map +1 -0
- package/dist/{phases-C_BGNyVo.cjs → phases-DcuDHoQl.cjs} +2 -2
- package/dist/phases-DcuDHoQl.cjs.map +1 -0
- package/dist/plugin.index.cjs +1 -1
- package/dist/plugin.index.d.mts +2 -2
- package/dist/plugin.index.mjs +1 -1
- package/dist/publishDefault-CVlwYUS5.cjs +382 -0
- package/dist/publishDefault-CVlwYUS5.cjs.map +1 -0
- package/dist/publishDefault-CldnxcT2.mjs +287 -0
- package/dist/publishDefault-CldnxcT2.mjs.map +1 -0
- package/dist/slashRouteLeaves-BANDtPFm.mjs +23 -0
- package/dist/slashRouteLeaves-BANDtPFm.mjs.map +1 -0
- package/dist/slashRouteLeaves-nfxOAM7Q.cjs +28 -0
- package/dist/slashRouteLeaves-nfxOAM7Q.cjs.map +1 -0
- package/dist/subscribers-BSJoyKNN.cjs +517 -0
- package/dist/subscribers-BSJoyKNN.cjs.map +1 -0
- package/dist/subscribers-DuxdM2cS.mjs +480 -0
- package/dist/subscribers-DuxdM2cS.mjs.map +1 -0
- package/package.json +7 -6
- package/dist/Plugin-C2-VHrsX.cjs.map +0 -1
- package/dist/Plugin-CNyRKDHT.d.mts +0 -226
- package/dist/Plugin-PcdPjAzc.mjs.map +0 -1
- package/dist/hmr.index.cjs.map +0 -1
- package/dist/hmr.index.mjs.map +0 -1
- package/dist/is-never-Ddaba1Su.d.mts +0 -58
- package/dist/permissions-DSW_EZfN.cjs.map +0 -1
- package/dist/permissions-RVCBhL2n.mjs.map +0 -1
- package/dist/phases-BfpAG6w3.mjs.map +0 -1
- package/dist/phases-C_BGNyVo.cjs.map +0 -1
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
1
|
+
import { g as contextMenuLeaves, i as BuilderComponent, m as SubscribeMetadataKey, s as CommandMetadataKey } from "./publishDefault-CldnxcT2.mjs";
|
|
2
|
+
import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
|
|
3
|
+
import { n as registrationFor, o as Subscriber } from "./subscribers-DuxdM2cS.mjs";
|
|
4
|
+
import { t as getDevChannel } from "./devChannel-CQU7hq7Q.mjs";
|
|
5
|
+
import { n as resolvedLifecycleSpecOf } from "./Plugin-CDAV7P9l.mjs";
|
|
6
|
+
import { t as slashRouteLeaves } from "./slashRouteLeaves-BANDtPFm.mjs";
|
|
7
|
+
import { t as HmrModuleHandler } from "./HmrModuleHandler-3sNgKHdf.mjs";
|
|
8
|
+
import { SlashCommandBuilder } from "@discordjs/builders";
|
|
9
|
+
import { FRAMEWORK_CHANNELS, Logger, paint } from "@seedcord/logger";
|
|
10
|
+
import { Routes } from "discord-api-types/v10";
|
|
3
11
|
import { SeedcordErrorCode } from "@seedcord/errors";
|
|
4
12
|
import { SeedcordError } from "@seedcord/errors/internal";
|
|
5
|
-
import {
|
|
13
|
+
import { formatFilePath } from "@seedcord/utils";
|
|
6
14
|
import chalk from "chalk";
|
|
7
|
-
import {
|
|
15
|
+
import { Envapter } from "envapt";
|
|
16
|
+
import { resolve } from "node:path";
|
|
8
17
|
import { createServer } from "node:http";
|
|
18
|
+
import { traverseDirectory } from "@seedcord/utils/node";
|
|
9
19
|
|
|
10
20
|
//#region src/node/Lifecycle/withTimeout.ts
|
|
11
21
|
async function raceTimer(work, timeoutMs, onTimeout) {
|
|
@@ -36,16 +46,15 @@ function settleWithin(work, timeoutMs) {
|
|
|
36
46
|
*
|
|
37
47
|
* @internal
|
|
38
48
|
*/
|
|
39
|
-
var CoordinatedLifecycle = class
|
|
49
|
+
var CoordinatedLifecycle = class {
|
|
40
50
|
phaseOrder;
|
|
41
51
|
phaseEnum;
|
|
42
52
|
logger;
|
|
43
53
|
tasksMap = /* @__PURE__ */ new Map();
|
|
44
54
|
constructor(loggerName, phaseOrder, phaseEnum) {
|
|
45
|
-
super();
|
|
46
55
|
this.phaseOrder = phaseOrder;
|
|
47
56
|
this.phaseEnum = phaseEnum;
|
|
48
|
-
this.logger = new Logger(loggerName);
|
|
57
|
+
this.logger = new Logger(loggerName, { channel: "lifecycle" });
|
|
49
58
|
this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));
|
|
50
59
|
}
|
|
51
60
|
/**
|
|
@@ -92,11 +101,9 @@ var CoordinatedLifecycle = class extends TypedEventEmitter {
|
|
|
92
101
|
return;
|
|
93
102
|
}
|
|
94
103
|
this.logger.info(`${chalk.bold.yellow("Running")} ${this.getTaskType()} phase ${chalk.bold.magenta(this.phaseEnum[phase])} with ${chalk.bold.cyan(tasks.length)} tasks`);
|
|
95
|
-
this.emitPhase(phase, "start");
|
|
96
104
|
const failures = (await this.executeTasksInPhase(phase, tasks)).filter((r) => r.status === "rejected").length;
|
|
97
105
|
if (failures > 0) throw new SeedcordError(SeedcordErrorCode.LifecyclePhaseFailures, [this.phaseEnum[phase], failures]);
|
|
98
106
|
this.logger.info(`Phase ${chalk.bold.magenta(this.phaseEnum[phase])} ${chalk.bold.green("completed successfully")}`);
|
|
99
|
-
this.emitPhase(phase, "complete");
|
|
100
107
|
}
|
|
101
108
|
async runTaskWithTimeout(phase, task) {
|
|
102
109
|
this.logger.info(`${chalk.italic("Starting")} task ${chalk.bold.cyan(task.name)} in phase ${chalk.bold.magenta(this.phaseEnum[phase])}`);
|
|
@@ -108,12 +115,6 @@ var CoordinatedLifecycle = class extends TypedEventEmitter {
|
|
|
108
115
|
throw error;
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
|
-
emitPhase(phase, action) {
|
|
112
|
-
this.emitSafeRaw(`phase:${phase}:${action}`);
|
|
113
|
-
}
|
|
114
|
-
onListenerError(error, event) {
|
|
115
|
-
this.logger.error(`listener for ${String(event)} threw`, error);
|
|
116
|
-
}
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
//#endregion
|
|
@@ -176,11 +177,18 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
176
177
|
this.onSigInt = null;
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
|
-
/** @internal run() awaits this so
|
|
180
|
+
/** @internal run() awaits this so boot finishes registering its dispose tasks first */
|
|
180
181
|
gateOnStartup(settled) {
|
|
181
182
|
this.startupGate = settled;
|
|
182
183
|
}
|
|
183
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Adds a shutdown-phase task.
|
|
186
|
+
*
|
|
187
|
+
* @param phase - The shutdown phase to run the task in.
|
|
188
|
+
* @param taskName - A descriptive name for the task.
|
|
189
|
+
* @param task - The async function to run.
|
|
190
|
+
* @param timeoutMs - Task timeout in ms. {@default `5000` }
|
|
191
|
+
*/
|
|
184
192
|
addTask(phase, taskName, task, timeoutMs = 5e3) {
|
|
185
193
|
super.addTask(phase, taskName, task, timeoutMs);
|
|
186
194
|
}
|
|
@@ -192,8 +200,8 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
192
200
|
* Runs registered tasks across shutdown phases in `ShutdownPhase` order.
|
|
193
201
|
* Tasks within each phase run in parallel.
|
|
194
202
|
*
|
|
195
|
-
* @param exitCode - Process exit code. {@default `0`}
|
|
196
|
-
* @param exitProcess - Whether to exit the process after shutdown. {@default `true`}
|
|
203
|
+
* @param exitCode - Process exit code. {@default `0` }
|
|
204
|
+
* @param exitProcess - Whether to exit the process after shutdown. {@default `true` }
|
|
197
205
|
* @example
|
|
198
206
|
* ```typescript
|
|
199
207
|
* shutdown.addTask(ShutdownPhase.Disconnect, 'database', () => db.disconnect(), 5000);
|
|
@@ -209,7 +217,6 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
209
217
|
this.isShuttingDown = true;
|
|
210
218
|
this.exitCode = exitCode;
|
|
211
219
|
this.logger.info(`${chalk.bold.yellow("Starting")} coordinated shutdown with exit code ${chalk.bold.cyan(exitCode)}`);
|
|
212
|
-
this.emitSafe("shutdown:start");
|
|
213
220
|
try {
|
|
214
221
|
if (this.startupGate) await this.startupGate;
|
|
215
222
|
const failures = [];
|
|
@@ -218,11 +225,8 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
218
225
|
} catch (error) {
|
|
219
226
|
failures.push(error);
|
|
220
227
|
}
|
|
221
|
-
if (failures.length > 0) this.
|
|
222
|
-
else {
|
|
223
|
-
this.logger.info(`${chalk.bold.green("Coordinated shutdown completed")} successfully`);
|
|
224
|
-
this.emitSafe("shutdown:complete");
|
|
225
|
-
}
|
|
228
|
+
if (failures.length > 0) this.logger.error(`${chalk.bold.red("Coordinated shutdown failed")}`, ...failures);
|
|
229
|
+
else this.logger.info(`${chalk.bold.green("Coordinated shutdown completed")} successfully`);
|
|
226
230
|
} finally {
|
|
227
231
|
this.hasShutdown = true;
|
|
228
232
|
if (exitProcess) {
|
|
@@ -236,11 +240,6 @@ var CoordinatedShutdown = class extends CoordinatedLifecycle {
|
|
|
236
240
|
}
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
|
-
emitFailures(failures) {
|
|
240
|
-
this.logger.error(`${chalk.bold.red("Coordinated shutdown failed")}`);
|
|
241
|
-
const error = failures.length === 1 ? failures[0] : new AggregateError(failures, "shutdown phase failures");
|
|
242
|
-
this.emitSafe("shutdown:error", error);
|
|
243
|
-
}
|
|
244
243
|
};
|
|
245
244
|
|
|
246
245
|
//#endregion
|
|
@@ -259,7 +258,14 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
259
258
|
constructor() {
|
|
260
259
|
super("Startup", PHASE_ORDER, StartupPhase);
|
|
261
260
|
}
|
|
262
|
-
/**
|
|
261
|
+
/**
|
|
262
|
+
* Adds a startup-phase task.
|
|
263
|
+
*
|
|
264
|
+
* @param phase - The startup phase to run the task in.
|
|
265
|
+
* @param taskName - A descriptive name for the task.
|
|
266
|
+
* @param task - The async function to run.
|
|
267
|
+
* @param timeoutMs - Task timeout in ms. {@default `10000` }
|
|
268
|
+
*/
|
|
263
269
|
addTask(phase, taskName, task, timeoutMs = 1e4) {
|
|
264
270
|
super.addTask(phase, taskName, task, timeoutMs);
|
|
265
271
|
}
|
|
@@ -296,7 +302,6 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
296
302
|
}
|
|
297
303
|
this.isStartingUp = true;
|
|
298
304
|
this.logger.info(`${chalk.bold.green("Starting")} coordinated startup sequence`);
|
|
299
|
-
this.emitSafe("startup:start");
|
|
300
305
|
try {
|
|
301
306
|
for (const phase of PHASE_ORDER) {
|
|
302
307
|
if (!this.isStartingUp) {
|
|
@@ -307,14 +312,12 @@ var CoordinatedStartup = class extends CoordinatedLifecycle {
|
|
|
307
312
|
}
|
|
308
313
|
this.hasStarted = true;
|
|
309
314
|
this.logger.info(`${chalk.bold.green("Coordinated startup completed")} successfully`);
|
|
310
|
-
this.emitSafe("startup:complete");
|
|
311
315
|
} catch (error) {
|
|
312
316
|
if (!this.isStartingUp) {
|
|
313
317
|
this.logger.warn("Startup sequence aborted during error handling");
|
|
314
318
|
return;
|
|
315
319
|
}
|
|
316
320
|
this.logger.error(`${chalk.bold.red("Coordinated startup failed")}`);
|
|
317
|
-
this.emitSafe("startup:error", error);
|
|
318
321
|
throw error;
|
|
319
322
|
} finally {
|
|
320
323
|
this.isStartingUp = false;
|
|
@@ -358,7 +361,7 @@ const DEFAULT_HEALTH_CHECK_PATH = "/health";
|
|
|
358
361
|
*/
|
|
359
362
|
var HealthCheck = class HealthCheck {
|
|
360
363
|
/** @internal */
|
|
361
|
-
logger = new Logger("HealthCheck");
|
|
364
|
+
logger = new Logger("HealthCheck", { channel: "health" });
|
|
362
365
|
port;
|
|
363
366
|
path;
|
|
364
367
|
host;
|
|
@@ -403,7 +406,7 @@ var HealthCheck = class HealthCheck {
|
|
|
403
406
|
server.removeListener("error", onListenError);
|
|
404
407
|
server.on("error", (err) => this.logger.error("Health check server error", err));
|
|
405
408
|
const address = this.host ?? "localhost";
|
|
406
|
-
this.logger.info(`${paint.mint.bold("
|
|
409
|
+
this.logger.info(`${paint.mint.bold("✔︎")} Health check server listening on ${paint.sky(`http://${address}:${this.port}${this.path}`)}`);
|
|
407
410
|
resolve();
|
|
408
411
|
});
|
|
409
412
|
if (this.host) {
|
|
@@ -437,19 +440,22 @@ var HealthCheck = class HealthCheck {
|
|
|
437
440
|
|
|
438
441
|
//#endregion
|
|
439
442
|
//#region src/node/Pluggable.ts
|
|
443
|
+
const RESERVED_KEYS = new Set(FRAMEWORK_CHANNELS);
|
|
440
444
|
/**
|
|
441
|
-
* Base class for
|
|
445
|
+
* Base class for a plugin host, a transport `Seedcord` class.
|
|
442
446
|
*
|
|
443
447
|
* Plugins are attached during configuration and initialized during startup, sequentially in attach
|
|
444
|
-
* order within each phase.
|
|
448
|
+
* order within each phase.
|
|
445
449
|
*/
|
|
446
|
-
var Pluggable = class Pluggable
|
|
447
|
-
|
|
448
|
-
startFailed = false;
|
|
450
|
+
var Pluggable = class Pluggable {
|
|
451
|
+
/** @see {@link CoordinatedShutdown} */
|
|
449
452
|
shutdown;
|
|
453
|
+
/** @see {@link CoordinatedStartup} */
|
|
450
454
|
startup;
|
|
455
|
+
isInitialized = false;
|
|
456
|
+
startFailed = false;
|
|
451
457
|
plugins = [];
|
|
452
|
-
pluginLogger = new Logger("Plugins");
|
|
458
|
+
pluginLogger = new Logger("Plugins", { channel: "plugins" });
|
|
453
459
|
attachments = [];
|
|
454
460
|
completedInits = /* @__PURE__ */ new Set();
|
|
455
461
|
disposePhases = /* @__PURE__ */ new Set();
|
|
@@ -461,7 +467,6 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
461
467
|
shutdown.removeSignalHandlers();
|
|
462
468
|
throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);
|
|
463
469
|
}
|
|
464
|
-
super();
|
|
465
470
|
Pluggable.isInstantiated = true;
|
|
466
471
|
Pluggable.liveShutdown = shutdown;
|
|
467
472
|
this.shutdown = shutdown;
|
|
@@ -485,13 +490,11 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
485
490
|
this.isInitialized = true;
|
|
486
491
|
return this;
|
|
487
492
|
}
|
|
488
|
-
|
|
489
|
-
|
|
493
|
+
/** @internal codegen reads these to emit the `Core` augmentation */
|
|
494
|
+
get pluginKeys() {
|
|
495
|
+
return this.attachments.map((attachment) => attachment.key);
|
|
490
496
|
}
|
|
491
|
-
|
|
492
|
-
return this.config.store ?? this.rateLimiter;
|
|
493
|
-
}
|
|
494
|
-
/** @internal releases the signal handlers and clears the singleton so the next host can construct */
|
|
497
|
+
/** @internal so the next host can construct */
|
|
495
498
|
static reset() {
|
|
496
499
|
Pluggable.liveShutdown?.removeSignalHandlers();
|
|
497
500
|
Pluggable.liveShutdown = void 0;
|
|
@@ -501,8 +504,12 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
501
504
|
* Attaches a plugin to this instance.
|
|
502
505
|
*
|
|
503
506
|
* The plugin initializes during startup, after every plugin attached before it, and becomes
|
|
504
|
-
* available as a property on `core`.
|
|
505
|
-
*
|
|
507
|
+
* available as a property on `core`. `seedcord codegen` writes the `Core` augmentation that
|
|
508
|
+
* types it.
|
|
509
|
+
*
|
|
510
|
+
* A plugin declaring a `transport` or `runtime` this host does not run is a compile error on
|
|
511
|
+
* this call. An edge host takes no plugins at all. A plugin constructor narrowing its first
|
|
512
|
+
* parameter past `CoreBase` is a compile error here too. Read the transport `Core` off `this.core`.
|
|
506
513
|
*
|
|
507
514
|
* @typeParam Key - The property name for accessing the plugin
|
|
508
515
|
* @typeParam Ctor - The plugin constructor type
|
|
@@ -510,38 +517,25 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
510
517
|
* @param Plugin - Plugin constructor class
|
|
511
518
|
* @param args - Additional arguments to pass to the plugin constructor
|
|
512
519
|
* @returns This instance with the plugin attached as a typed property
|
|
513
|
-
* @throws A **SeedcordError** When called after initialization or if key already exists
|
|
520
|
+
* @throws A **SeedcordError** When called after initialization or if key already exists or is reserved
|
|
514
521
|
* @example
|
|
515
522
|
* ```typescript
|
|
516
|
-
* seedcord.attach('db',
|
|
523
|
+
* seedcord.attach('db', Mongoose, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
|
|
517
524
|
* ```
|
|
518
525
|
*/
|
|
519
526
|
attach(key, Plugin, ...args) {
|
|
520
527
|
if (this.isInitialized) throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);
|
|
528
|
+
if (RESERVED_KEYS.has(key)) throw new SeedcordError(SeedcordErrorCode.CorePluginReservedChannel, [key]);
|
|
521
529
|
if (key in this) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);
|
|
522
530
|
const instance = new Plugin(this, ...args);
|
|
531
|
+
instance.logger.setChannel(key);
|
|
523
532
|
this.plugins.push(instance);
|
|
524
533
|
this.attachments.push({
|
|
525
534
|
key,
|
|
526
535
|
instance
|
|
527
536
|
});
|
|
528
|
-
this.finalizeContext(key, instance);
|
|
529
537
|
return Object.assign(this, { [key]: instance });
|
|
530
538
|
}
|
|
531
|
-
finalizeContext(key, instance) {
|
|
532
|
-
const caps = this.pluginCapabilities();
|
|
533
|
-
const readToken = () => this.pluginCapabilities().token;
|
|
534
|
-
finalizePluginContext(instance, {
|
|
535
|
-
logger: new Logger(key),
|
|
536
|
-
config: this.config,
|
|
537
|
-
store: this.pluginStore(),
|
|
538
|
-
client: caps.client,
|
|
539
|
-
get token() {
|
|
540
|
-
return readToken();
|
|
541
|
-
},
|
|
542
|
-
rest: caps.rest
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
539
|
registerPluginTasks() {
|
|
546
540
|
if (this.pluginTasksRegistered) return;
|
|
547
541
|
this.pluginTasksRegistered = true;
|
|
@@ -555,7 +549,7 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
555
549
|
for (const [phase, group] of groups) {
|
|
556
550
|
if (phase === 3) continue;
|
|
557
551
|
const budget = group.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0);
|
|
558
|
-
this.startup.addTask(phase, "
|
|
552
|
+
this.startup.addTask(phase, "plugins-init", () => this.runInits(group), budget);
|
|
559
553
|
}
|
|
560
554
|
this.registerReadyTask(groups.get(3) ?? []);
|
|
561
555
|
}
|
|
@@ -564,7 +558,7 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
564
558
|
const { key, instance } = attachment;
|
|
565
559
|
const spec = resolvedLifecycleSpecOf(instance);
|
|
566
560
|
instance.logger.utils.initialization(key, "start");
|
|
567
|
-
await withTimeout(`Plugin
|
|
561
|
+
await withTimeout(`Plugin (${key})`, () => instance.init(), spec.init.timeout);
|
|
568
562
|
instance.logger.utils.initialization(key, "end");
|
|
569
563
|
this.completedInits.add(attachment);
|
|
570
564
|
if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);
|
|
@@ -582,7 +576,7 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
582
576
|
}, []);
|
|
583
577
|
if (readyInits.length === 0 && steps.length === 0) return;
|
|
584
578
|
const budget = readyInits.reduce((sum, a) => sum + resolvedLifecycleSpecOf(a.instance).init.timeout, 0) + steps.reduce((sum, step) => sum + step.timeout, 0);
|
|
585
|
-
this.startup.addTask(3, "
|
|
579
|
+
this.startup.addTask(3, "plugins-ready", async () => {
|
|
586
580
|
await this.runInits(readyInits);
|
|
587
581
|
for (const step of steps) await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);
|
|
588
582
|
}, budget);
|
|
@@ -594,7 +588,7 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
594
588
|
const spec = resolvedLifecycleSpecOf(a.instance);
|
|
595
589
|
return a.instance.dispose && spec.dispose.phase === phase ? sum + spec.dispose.timeout : sum;
|
|
596
590
|
}, 0);
|
|
597
|
-
this.shutdown.addTask(phase, "
|
|
591
|
+
this.shutdown.addTask(phase, "plugins-dispose", () => this.runDisposals(phase), budget);
|
|
598
592
|
}
|
|
599
593
|
async runDisposals(phase) {
|
|
600
594
|
const failures = [];
|
|
@@ -621,16 +615,258 @@ var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
|
621
615
|
await this.disposeCompleted(void 0, (caught) => this.pluginLogger.warn("rollback dispose failed", caught));
|
|
622
616
|
this.completedInits.clear();
|
|
623
617
|
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/node/subscribers/SubscriberLoader.ts
|
|
622
|
+
/**
|
|
623
|
+
* Loads subscribers off the filesystem into a {@link Bus} and stores their hot-reload wiring. Node
|
|
624
|
+
* only, a bundled isolate registers from the manifest instead.
|
|
625
|
+
*
|
|
626
|
+
* @internal
|
|
627
|
+
*/
|
|
628
|
+
var SubscriberLoader = class {
|
|
629
|
+
bus;
|
|
630
|
+
directory;
|
|
631
|
+
isInitialized = false;
|
|
632
|
+
hmrHandler;
|
|
633
|
+
constructor(bus, directory) {
|
|
634
|
+
this.bus = bus;
|
|
635
|
+
this.directory = directory;
|
|
636
|
+
if (!this.directory) return;
|
|
637
|
+
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
638
|
+
this.hmrHandler = new HmrModuleHandler({
|
|
639
|
+
handlersDir: this.directory,
|
|
640
|
+
isHandler: this.isSubscriber.bind(this),
|
|
641
|
+
registerHandler: this.registerSubscriber.bind(this),
|
|
642
|
+
unregisterHandler: this.unregisterSubscriber.bind(this),
|
|
643
|
+
getArtifacts: this.getArtifacts.bind(this),
|
|
644
|
+
logger: this.bus.logger
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
/** @internal */
|
|
648
|
+
async init() {
|
|
649
|
+
if (this.isInitialized) return;
|
|
650
|
+
this.isInitialized = true;
|
|
651
|
+
this.bus.registerDefaults();
|
|
652
|
+
const { directory } = this;
|
|
653
|
+
if (directory) {
|
|
654
|
+
this.bus.logger.info(chalk.bold(directory));
|
|
655
|
+
await this.load(directory);
|
|
656
|
+
this.bus.logger.utils.list([`${chalk.bold.magenta(this.bus.registeredCount)} subscribers`], chalk.bold.green("Loaded"));
|
|
657
|
+
}
|
|
658
|
+
if (!Envapter.isTest) await this.bus.verifyWebhooks();
|
|
659
|
+
}
|
|
660
|
+
/** @internal */
|
|
661
|
+
async onHmr(event) {
|
|
662
|
+
if (this.hmrHandler) await this.hmrHandler.handle(event);
|
|
663
|
+
}
|
|
664
|
+
async load(dir) {
|
|
665
|
+
await traverseDirectory(dir, (fullPath, relativePath, imported) => {
|
|
666
|
+
for (const exportName of Object.keys(imported)) {
|
|
667
|
+
const value = imported[exportName];
|
|
668
|
+
if (!this.isSubscriber(value)) continue;
|
|
669
|
+
this.registerSubscriber(value);
|
|
670
|
+
this.hmrHandler?.trackHandler(fullPath, value);
|
|
671
|
+
this.bus.logger.utils.registration(value.name, relativePath);
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
registerSubscriber(ctor) {
|
|
676
|
+
this.bus.register(registrationFor(ctor));
|
|
677
|
+
}
|
|
678
|
+
unregisterSubscriber(ctor, artifacts) {
|
|
679
|
+
this.bus.unregister(ctor, artifacts);
|
|
680
|
+
}
|
|
681
|
+
getArtifacts(ctor) {
|
|
682
|
+
return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
|
|
683
|
+
}
|
|
684
|
+
isSubscriber(value) {
|
|
685
|
+
if (typeof value !== "function") return false;
|
|
686
|
+
return value.prototype instanceof Subscriber && Reflect.hasMetadata(SubscribeMetadataKey, value);
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/node/commands/CommandRegistry.ts
|
|
692
|
+
function indexById(commands) {
|
|
693
|
+
return new Map(commands.map((command) => [command.id, command]));
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Scans the command directory, builds each command, and PUTs the global and guild scopes to Discord.
|
|
697
|
+
*
|
|
698
|
+
* @internal
|
|
699
|
+
*/
|
|
700
|
+
var CommandRegistry = class {
|
|
701
|
+
options;
|
|
702
|
+
logger = new Logger("Commands", { channel: "commands" });
|
|
703
|
+
isInitialised = false;
|
|
704
|
+
globalCommands = [];
|
|
705
|
+
guildCommands = /* @__PURE__ */ new Map();
|
|
706
|
+
ctorToCommand = /* @__PURE__ */ new Map();
|
|
707
|
+
loading = false;
|
|
708
|
+
loadedCommands = [];
|
|
709
|
+
hmrHandler;
|
|
710
|
+
pendingEvents = /* @__PURE__ */ new Map();
|
|
711
|
+
constructor(options) {
|
|
712
|
+
this.options = options;
|
|
713
|
+
if (!options.dir) throw new SeedcordError(SeedcordErrorCode.CoreControllerPathMissing, ["CommandRegistry", "commands"]);
|
|
714
|
+
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
715
|
+
this.hmrHandler = new HmrModuleHandler({
|
|
716
|
+
handlersDir: options.dir,
|
|
717
|
+
isHandler: this.isCommandClass.bind(this),
|
|
718
|
+
registerHandler: this.registerCommand.bind(this),
|
|
719
|
+
unregisterHandler: this.unregisterCommand.bind(this),
|
|
720
|
+
getArtifacts: this.getArtifacts.bind(this),
|
|
721
|
+
logger: this.logger
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
getArtifacts(ctor) {
|
|
725
|
+
return this.ctorToCommand.get(ctor);
|
|
726
|
+
}
|
|
727
|
+
/** @internal */
|
|
728
|
+
async init() {
|
|
729
|
+
if (this.isInitialised) return;
|
|
730
|
+
this.isInitialised = true;
|
|
731
|
+
this.loading = true;
|
|
732
|
+
this.loadedCommands.length = 0;
|
|
733
|
+
try {
|
|
734
|
+
await this.loadCommands(this.options.dir);
|
|
735
|
+
} finally {
|
|
736
|
+
this.loading = false;
|
|
737
|
+
}
|
|
738
|
+
this.reportLoad();
|
|
739
|
+
getDevChannel()?.on("seedcord:refresh-commands", (data) => {
|
|
740
|
+
this.refresh(data.shouldRefresh);
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
reportLoad() {
|
|
744
|
+
const { utils } = this.logger;
|
|
745
|
+
utils.summary("Loaded commands", {
|
|
746
|
+
global: this.globalCommands.length,
|
|
747
|
+
"guild groups": this.guildCommands.size
|
|
748
|
+
});
|
|
749
|
+
let slash = 0;
|
|
750
|
+
let menu = 0;
|
|
751
|
+
for (const command of this.loadedCommands) if (command.kind === "slash command") slash++;
|
|
752
|
+
else menu++;
|
|
753
|
+
utils.block("Loaded commands", [...utils.entries(this.loadedCommands), ...utils.counts({
|
|
754
|
+
"slash commands": slash,
|
|
755
|
+
"context menus": menu
|
|
756
|
+
})]);
|
|
757
|
+
}
|
|
758
|
+
/** @internal */
|
|
759
|
+
async refresh(shouldRefresh = true) {
|
|
760
|
+
if (!shouldRefresh) {
|
|
761
|
+
this.logger.info(chalk.italic("Command refresh cancelled."));
|
|
762
|
+
this.pendingEvents.clear();
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
this.logger.info(chalk.italic("Refreshing commands..."));
|
|
766
|
+
for (const event of this.pendingEvents.values()) await this.hmrHandler?.handle(event);
|
|
767
|
+
this.pendingEvents.clear();
|
|
768
|
+
await this.setCommands();
|
|
769
|
+
}
|
|
770
|
+
/** @internal */
|
|
771
|
+
async onHmr(event) {
|
|
772
|
+
if (event.file.startsWith(resolve(process.cwd(), this.options.dir))) {
|
|
773
|
+
this.pendingEvents.delete(event.file);
|
|
774
|
+
this.pendingEvents.set(event.file, event);
|
|
775
|
+
getDevChannel()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => formatFilePath(f)) });
|
|
776
|
+
} else await this.hmrHandler?.handle(event);
|
|
777
|
+
}
|
|
778
|
+
async loadCommands(dir) {
|
|
779
|
+
await traverseDirectory(dir, (fullPath, rel, mod) => {
|
|
780
|
+
for (const exported of Object.values(mod)) if (this.isCommandClass(exported)) {
|
|
781
|
+
this.registerCommand(exported, rel);
|
|
782
|
+
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
isCommandClass(obj) {
|
|
787
|
+
if (typeof obj !== "function") return false;
|
|
788
|
+
return obj.prototype instanceof BuilderComponent && Reflect.hasMetadata(CommandMetadataKey, obj);
|
|
789
|
+
}
|
|
790
|
+
registerCommand(ctor, rel) {
|
|
791
|
+
const meta = Reflect.getMetadata(CommandMetadataKey, ctor);
|
|
792
|
+
if (!meta) return;
|
|
793
|
+
const comp = new ctor().component;
|
|
794
|
+
const kind = comp instanceof SlashCommandBuilder ? "slash command" : "context menu";
|
|
795
|
+
if (meta.scope === "global") this.globalCommands.push(comp);
|
|
796
|
+
else for (const g of meta.guilds) {
|
|
797
|
+
const arr = this.guildCommands.get(g) ?? [];
|
|
798
|
+
arr.push(comp);
|
|
799
|
+
this.guildCommands.set(g, arr);
|
|
800
|
+
}
|
|
801
|
+
this.ctorToCommand.set(ctor, {
|
|
802
|
+
name: comp.name,
|
|
803
|
+
scope: meta.scope,
|
|
804
|
+
...meta.scope === "guild" && { guilds: meta.guilds }
|
|
805
|
+
});
|
|
806
|
+
if (this.loading) {
|
|
807
|
+
this.loadedCommands.push({
|
|
808
|
+
name: comp.name,
|
|
809
|
+
from: formatFilePath(rel),
|
|
810
|
+
kind
|
|
811
|
+
});
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
this.logger.utils.registration(comp.name, rel, `${meta.scope} ${kind}`);
|
|
815
|
+
}
|
|
816
|
+
unregisterCommand(ctor, artifacts) {
|
|
817
|
+
const info = artifacts ?? this.ctorToCommand.get(ctor);
|
|
818
|
+
if (!info) return;
|
|
819
|
+
if (info.scope === "global") {
|
|
820
|
+
const idx = this.globalCommands.findIndex((c) => c.name === info.name);
|
|
821
|
+
if (idx !== -1) this.globalCommands.splice(idx, 1);
|
|
822
|
+
} else for (const g of info.guilds ?? []) {
|
|
823
|
+
const arr = this.guildCommands.get(g);
|
|
824
|
+
if (arr) {
|
|
825
|
+
const idx = arr.findIndex((c) => c.name === info.name);
|
|
826
|
+
if (idx !== -1) arr.splice(idx, 1);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
this.ctorToCommand.delete(ctor);
|
|
830
|
+
}
|
|
831
|
+
/** @internal */
|
|
832
|
+
async setCommands() {
|
|
833
|
+
const result = {
|
|
834
|
+
global: /* @__PURE__ */ new Map(),
|
|
835
|
+
guilds: /* @__PURE__ */ new Map()
|
|
836
|
+
};
|
|
837
|
+
const appId = this.options.applicationId();
|
|
838
|
+
if (this.globalCommands.length > 0) {
|
|
839
|
+
result.global = indexById(await this.put(Routes.applicationCommands(appId), this.globalCommands));
|
|
840
|
+
const tag = this.globalCommands.length === 1 ? "command" : "commands";
|
|
841
|
+
this.logger.utils.block(`Deployed ${this.globalCommands.length} global ${tag}`, this.logger.utils.wrap(this.globalCommands.map((command) => command.name)), "info");
|
|
842
|
+
}
|
|
843
|
+
for (const [guildId, commands] of this.guildCommands.entries()) {
|
|
844
|
+
const deployed = await this.put(Routes.applicationGuildCommands(appId, guildId), commands);
|
|
845
|
+
result.guilds.set(guildId, indexById(deployed));
|
|
846
|
+
const tag = commands.length === 1 ? "command" : "commands";
|
|
847
|
+
this.logger.utils.block(`Deployed ${commands.length} ${tag} to ${paint.amber.bold(guildId)}`, this.logger.utils.wrap(commands.map((command) => command.name)), "info");
|
|
848
|
+
}
|
|
849
|
+
this.options.onDeployed?.(result);
|
|
850
|
+
return result;
|
|
851
|
+
}
|
|
852
|
+
async put(route, commands) {
|
|
853
|
+
const body = commands.map((command) => command.toJSON());
|
|
854
|
+
return await this.options.rest.put(route, { body });
|
|
855
|
+
}
|
|
856
|
+
/** Every registered builder, global first. A guild command in N guilds appears once per guild. @internal */
|
|
857
|
+
allCommands() {
|
|
858
|
+
return [...this.globalCommands, ...this.guildCommands.values()].flat();
|
|
859
|
+
}
|
|
624
860
|
/** @internal */
|
|
625
|
-
|
|
626
|
-
return
|
|
861
|
+
routeLeaves() {
|
|
862
|
+
return slashRouteLeaves(this.allCommands());
|
|
627
863
|
}
|
|
628
864
|
/** @internal */
|
|
629
|
-
|
|
630
|
-
return
|
|
865
|
+
contextMenuLeaves() {
|
|
866
|
+
return contextMenuLeaves(this.allCommands());
|
|
631
867
|
}
|
|
632
868
|
};
|
|
633
869
|
|
|
634
870
|
//#endregion
|
|
635
|
-
export { CoordinatedShutdown, CoordinatedStartup, HealthCheck, Pluggable, ShutdownPhase, StartupPhase, settleWithin };
|
|
871
|
+
export { CommandRegistry, CoordinatedShutdown, CoordinatedStartup, HealthCheck, Pluggable, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin };
|
|
636
872
|
//# sourceMappingURL=node-internal.index.mjs.map
|