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