@seedcord/core 0.11.0 → 0.12.0-next.2

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 (54) hide show
  1. package/README.md +3 -3
  2. package/dist/Bus-VJw-QpoY.mjs +986 -0
  3. package/dist/Bus-VJw-QpoY.mjs.map +1 -0
  4. package/dist/{CoordinatedShutdown-H5YjZn8A.d.mts → CoordinatedShutdown-fPKLWjc5.d.mts} +13 -4
  5. package/dist/{CoordinatedStartup-CGnJrqcl.mjs → CoordinatedStartup-Zw_Lk7Zk.mjs} +22 -20
  6. package/dist/CoordinatedStartup-Zw_Lk7Zk.mjs.map +1 -0
  7. package/dist/Generated-ByC53_A2.d.mts +26 -0
  8. package/dist/HmrModuleHandler-Btgu8r96.mjs +165 -0
  9. package/dist/HmrModuleHandler-Btgu8r96.mjs.map +1 -0
  10. package/dist/{PluginHost-DB4j8unu.d.mts → PluginHost-DFnkIldj.d.mts} +43 -15
  11. package/dist/PluginHost-DTtxWRiL.mjs +552 -0
  12. package/dist/PluginHost-DTtxWRiL.mjs.map +1 -0
  13. package/dist/{PluginLifecycle-AhQ46NIr.mjs → PluginLifecycle-hx5zUc-P.mjs} +4 -4
  14. package/dist/PluginLifecycle-hx5zUc-P.mjs.map +1 -0
  15. package/dist/{ServerHost-DYeh0KTk.d.mts → ServerHost-B5o3ScCF.d.mts} +7 -6
  16. package/dist/{ServerHost-CtmAhEXn.mjs → ServerHost-CDaEDR4C.mjs} +17 -6
  17. package/dist/{ServerHost-CtmAhEXn.mjs.map → ServerHost-CDaEDR4C.mjs.map} +1 -1
  18. package/dist/bindViteEnv-NFzvdqlT.mjs +32 -0
  19. package/dist/{bindViteEnv-DWq5rftJ.mjs.map → bindViteEnv-NFzvdqlT.mjs.map} +1 -1
  20. package/dist/env-internal.index.mjs +1 -1
  21. package/dist/hmr.index.d.mts +20 -20
  22. package/dist/hmr.index.mjs +1 -1
  23. package/dist/{index-Du71Xx69.d.mts → index-DGHng1IN.d.mts} +115 -201
  24. package/dist/index.d.mts +321 -74
  25. package/dist/index.mjs +323 -102
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/internal.index.d.mts +223 -83
  28. package/dist/internal.index.mjs +573 -74
  29. package/dist/internal.index.mjs.map +1 -1
  30. package/dist/node-internal.index.d.mts +8 -4
  31. package/dist/node-internal.index.mjs +19 -6
  32. package/dist/node-internal.index.mjs.map +1 -1
  33. package/dist/node.index.d.mts +3 -3
  34. package/dist/node.index.mjs +22 -28
  35. package/dist/node.index.mjs.map +1 -1
  36. package/dist/{phases-FTM69J3a.d.mts → phases-C2QCSqRu.d.mts} +164 -55
  37. package/dist/plugin.index.d.mts +3 -2
  38. package/dist/plugin.index.mjs +1 -1
  39. package/dist/subscribers-CGuVjbrg.mjs +982 -0
  40. package/dist/subscribers-CGuVjbrg.mjs.map +1 -0
  41. package/dist/{bindViteEnv-DWq5rftJ.mjs → usingCtx-Cf2cZi96.mjs} +1 -31
  42. package/package.json +14 -11
  43. package/dist/CoordinatedStartup-CGnJrqcl.mjs.map +0 -1
  44. package/dist/HmrModuleHandler-8ZC-UuZh.mjs +0 -162
  45. package/dist/HmrModuleHandler-8ZC-UuZh.mjs.map +0 -1
  46. package/dist/PluginHost-B8kxLOeb.mjs +0 -245
  47. package/dist/PluginHost-B8kxLOeb.mjs.map +0 -1
  48. package/dist/PluginLifecycle-AhQ46NIr.mjs.map +0 -1
  49. package/dist/phases-BauKJOTT.mjs +0 -31
  50. package/dist/phases-BauKJOTT.mjs.map +0 -1
  51. package/dist/publishDefault-D3dAyzZ9.mjs +0 -7
  52. package/dist/publishDefault-D3dAyzZ9.mjs.map +0 -1
  53. package/dist/subscribers-Ce3SWy23.mjs +0 -1913
  54. package/dist/subscribers-Ce3SWy23.mjs.map +0 -1
@@ -1,13 +1,13 @@
1
- import { At as guardedAccessor, B as NotInGuild, Bt as isBuilderComponentClass, C as PermissionNames, Ct as warnLinks, D as pickNotice, Dt as linkCommands, Et as indexById, F as MissingPermissions, Ft as skippedNote, G as serializeReply, H as OnCooldown, Ht as Notice, I as MissingRole, It as contextMenuLeaves, K as translateSerializationError, L as NeedsAny, Lt as NoticeCard, M as runCommits, Mt as commandsRoute, N as GateNotice, Nt as globalFirst, O as discardCommits, Ot as accessorStore, P as HasDangerousPermissions, Pt as scopeIdsOf, Q as sendTarget, R as NotAllowed, St as installLinks, Tt as commandRefOf, V as NotOwner, Vt as bindBotColor, X as AckTrace, Y as reportedWrite, Z as checkAckLegality, _ as pageCursor, _t as SubscribeMetadataKey, a as RegisterSubscriber, at as ComponentDefsKey, c as VerifyWebhooks, dt as EventMiddlewareMetadataKey, et as interactionMiddlewareMetaOf, f as Subscriber, ft as GatedMetadataKey, g as PAGE_MAX, gt as InteractionRouteKeys, h as sendFlags, ht as InteractionMiddlewareMetadataKey, i as RegisterDefaults, it as storeInteractionRoute, j as runCheck, jt as GLOBAL_ID, kt as clearStore, lt as CommandMetadataKey, m as deferFlags, mt as InteractionMetadataKey, n as busLoggerOf, nt as interactionRoutesOf, o as RegisteredCount, ot as decodeComponentRoute, p as ReportThrottle, r as registrationFor, rt as storeComponentRoute, s as UnregisterSubscriber, st as ConfigGuilds, tt as contextMenuRouteOf, ut as EventMetadataKey, v as ResolvedEmoji, vt as WebhookUrlMetadataKey, wt as commandKey, y as Silence, yt as CommandInjector, z as NotInDm } from "./subscribers-Ce3SWy23.mjs";
2
- import { t as PublishDefault } from "./publishDefault-D3dAyzZ9.mjs";
3
- import { c as setDevChannel, i as FixedScope, n as attachmentsOf, r as sealAttachments, s as getDevChannel } from "./PluginHost-B8kxLOeb.mjs";
4
- import { t as HmrModuleHandler } from "./HmrModuleHandler-8ZC-UuZh.mjs";
5
- import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-AhQ46NIr.mjs";
6
- import { t as CoordinatedStartup } from "./CoordinatedStartup-CGnJrqcl.mjs";
7
- import { t as bindEnv } from "./bindViteEnv-DWq5rftJ.mjs";
1
+ import { A as InteractionRouteKeys, C as CommandMetadataKey, D as InteractionKind, E as GatedMetadataKey, F as isBuilderComponentClass, I as bindBotColor, M as WebhookUrlMetadataKey, O as InteractionMetadataKey, S as assertUniqueKeys, T as EventMiddlewareMetadataKey, _ as OnceTracker, a as RegisterSubscriber, c as VerifyWebhooks, f as Subscriber, g as sendFlags, h as deferFlags, i as RegisterDefaults, j as SubscribeMetadataKey, k as InteractionMiddlewareMetadataKey, m as ReportThrottle, n as busLoggerOf, o as RegisteredCount, p as runCase, r as registrationsFor, s as UnregisterSubscriber, w as EventMetadataKey, x as PublishDefault } from "./Bus-VJw-QpoY.mjs";
2
+ import { $ as GLOBAL_ID, D as AckTrace, E as lazyLogger, F as storeComponentRoute, G as warnLinks, I as storeInteractionRoute, J as indexById, K as commandKey, L as ComponentDefsKey, M as contextMenuRouteOf, N as interactionRoutesOf, O as checkAckLegality, P as routeDecoratorOf, Q as guardedAccessor, R as decodeComponentRoute, S as translateSerializationError, T as reportedWrite, V as CommandInjector, W as installLinks, X as accessorStore, Y as linkCommands, Z as clearStore, _ as NotInGuild, a as discardCommits, at as Notice, c as runCheck, et as commandsRoute, g as NotInDm, h as NotAllowed, i as Silence, it as contextMenuLeaves, j as interactionMiddlewareMetaOf, k as sendTarget, l as runCommits, m as NeedsAny, n as pageCursor, nt as scopeIdsOf, ot as NoticeCard, p as MissingRole, q as commandRefOf, r as ResolvedEmoji, rt as skippedNote, t as PAGE_MAX, tt as globalFirst, u as Fault, v as NotOwner, x as serializeReply, y as OnCooldown, z as ConfigGuilds } from "./subscribers-CGuVjbrg.mjs";
3
+ import { a as KeyedQueue, i as splitPluginKey, l as getDevChannel, n as attachmentsOf, o as FixedScope, r as sealAttachments, u as setDevChannel } from "./PluginHost-DTtxWRiL.mjs";
4
+ import { t as HmrModuleHandler } from "./HmrModuleHandler-Btgu8r96.mjs";
5
+ import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-hx5zUc-P.mjs";
6
+ import { t as CoordinatedStartup } from "./CoordinatedStartup-Zw_Lk7Zk.mjs";
7
+ import { t as bindEnv } from "./bindViteEnv-NFzvdqlT.mjs";
8
8
  import "reflect-metadata";
9
9
  import { SlashCommandBuilder } from "@discordjs/builders";
10
- import { Routes } from "discord-api-types/v10";
10
+ import { ComponentType, RESTJSONErrorCodes, Routes } from "discord-api-types/v10";
11
11
  import { SeedcordErrorCode, paint } from "@seedcord/errors";
12
12
  import { SeedcordError, SeedcordTypeError, throwSingleOrAggregate, validateDiscordToken } from "@seedcord/errors/internal";
13
13
  import { LEVEL_COLOR, Logger, LoggerChannelRegistry } from "@seedcord/logger";
@@ -30,11 +30,13 @@ var SubscriberLoader = class {
30
30
  if (!this.directory) return;
31
31
  if (!Envapter.isDevelopment && !Envapter.isTest) return;
32
32
  this.hmrHandler = new HmrModuleHandler({
33
- handlersDir: this.directory,
34
- isHandler: this.isSubscriber.bind(this),
35
- registerHandler: this.registerSubscriber.bind(this),
36
- unregisterHandler: this.unregisterSubscriber.bind(this),
37
- getArtifacts: this.getArtifacts.bind(this),
33
+ handlers: {
34
+ dir: this.directory,
35
+ is: this.isSubscriber.bind(this),
36
+ register: this.registerSubscriber.bind(this),
37
+ unregister: this.unregisterSubscriber.bind(this),
38
+ getArtifacts: this.getArtifacts.bind(this)
39
+ },
38
40
  logger: busLoggerOf(this.bus)
39
41
  });
40
42
  }
@@ -64,13 +66,13 @@ var SubscriberLoader = class {
64
66
  }
65
67
  }
66
68
  registerSubscriber(ctor) {
67
- this.bus[RegisterSubscriber](registrationFor(ctor));
69
+ this.bus[RegisterSubscriber](ctor);
68
70
  }
69
71
  unregisterSubscriber(ctor, artifacts) {
70
72
  this.bus[UnregisterSubscriber](ctor, artifacts);
71
73
  }
72
74
  getArtifacts(ctor) {
73
- return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
75
+ return registrationsFor(ctor).map((registration) => registration.key);
74
76
  }
75
77
  isSubscriber(value) {
76
78
  if (typeof value !== "function") return false;
@@ -151,40 +153,142 @@ var MiddlewareRegistry = class {
151
153
  };
152
154
 
153
155
  //#endregion
154
- //#region src/gates/runGates.ts
155
- async function timedCheck(gate, ctx, observe) {
156
- const start = performance.now();
157
- try {
158
- await runCheck(gate, ctx);
159
- } finally {
160
- observe(gate.name, performance.now() - start);
156
+ //#region src/notices/FaultBoundary.ts
157
+ const logger$2 = lazyLogger("Faults", { channel: "errors" });
158
+ const HARMLESS_API_CODES = /* @__PURE__ */ new Set([
159
+ RESTJSONErrorCodes.UnknownInteraction,
160
+ RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,
161
+ RESTJSONErrorCodes.UnknownMessage
162
+ ]);
163
+ var FaultBoundary = class {
164
+ core;
165
+ apiError;
166
+ dispatch;
167
+ detailsOf;
168
+ ignoredCodes;
169
+ constructor(core, ignoredCodes, apiError, dispatch, detailsOf) {
170
+ this.core = core;
171
+ this.apiError = apiError;
172
+ this.dispatch = dispatch;
173
+ this.detailsOf = detailsOf;
174
+ this.ignoredCodes = new Set(ignoredCodes);
161
175
  }
162
- }
163
- async function runGates(gates, ctx, observe) {
164
- try {
165
- for (const gate of gates) await (observe ? timedCheck(gate, ctx, observe) : runCheck(gate, ctx));
166
- await runCommits(ctx);
167
- } finally {
168
- discardCommits(ctx);
176
+ report(caught) {
177
+ const { config } = this.core;
178
+ if (caught instanceof Silence) {
179
+ if (caught.reason !== void 0 && (config.errors?.logSilences ?? true)) logger$2().debug(`Silence: ${caught.reason}`);
180
+ return null;
181
+ }
182
+ const error = asError(caught);
183
+ if (error instanceof this.apiError && this.ignoredCodes.has(error.code)) {
184
+ logger$2().debug(`swallowed api code ${paint.amber(String(error.code))}`);
185
+ return null;
186
+ }
187
+ const uuid = crypto.randomUUID();
188
+ this.publish(error, uuid);
189
+ return {
190
+ error,
191
+ uuid
192
+ };
169
193
  }
170
- }
171
- async function runHandlerGates(handlerCtor, ctx, declaredRoute, observe) {
172
- const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor);
173
- if (!gates) return;
174
- await runGates(gates, {
175
- ...ctx,
176
- declaredRoute: declaredRoute ?? null
177
- }, observe);
178
- }
194
+ async answerWithReply(caught, sender) {
195
+ const fault = this.report(caught);
196
+ const reply = fault && this.rendered(fault);
197
+ if (reply) await this.sendGuarded(() => sender.send(reply.response, { ephemeral: reply.ephemeral }));
198
+ }
199
+ async answerWithEmptyChoices(caught, clearChoices) {
200
+ this.report(caught);
201
+ await this.sendGuarded(clearChoices);
202
+ }
203
+ publish(error, uuid) {
204
+ if (error instanceof Notice && !error.report) return;
205
+ const { bus, config } = this.core;
206
+ const { origin, source, guild, user, metadata } = this.detailsOf();
207
+ const from = {
208
+ uuid,
209
+ dispatchId: this.dispatch.id,
210
+ origin
211
+ };
212
+ if (error instanceof Notice) {
213
+ logger$2().error(`${error.name}: ${paint.mute(uuid)}`, error);
214
+ if (source) {
215
+ bus[PublishDefault]("handledException", {
216
+ denial: error,
217
+ ...from,
218
+ source
219
+ });
220
+ return;
221
+ }
222
+ } else logUnknownFault(logger$2(), paint.mute(uuid), error, config.errors);
223
+ bus[PublishDefault]("unknownException", {
224
+ ...from,
225
+ error,
226
+ guild,
227
+ user,
228
+ metadata
229
+ });
230
+ }
231
+ rendered(fault) {
232
+ try {
233
+ return this.render(fault);
234
+ } catch (renderError) {
235
+ return this.fallbackReply(renderError);
236
+ }
237
+ }
238
+ fallbackReply(renderError) {
239
+ const fallback = this.report(renderError);
240
+ if (!fallback) return null;
241
+ try {
242
+ return this.render(fallback);
243
+ } catch (error) {
244
+ logger$2().error(`could not render a fault card for route ${paint.sky.bold(this.dispatch.routeId)}`, error);
245
+ return {
246
+ response: new Fault().render(this.renderContext(fallback.uuid)),
247
+ ephemeral: true
248
+ };
249
+ }
250
+ }
251
+ render({ error, uuid }) {
252
+ const ctx = this.renderContext(uuid);
253
+ if (error instanceof Notice) return {
254
+ response: error.render(ctx),
255
+ ephemeral: error.ephemeral
256
+ };
257
+ const Override = this.core.config.errors?.defaultError;
258
+ return {
259
+ response: (Override ? new Override(uuid) : new Fault()).render(ctx),
260
+ ephemeral: true
261
+ };
262
+ }
263
+ renderContext(uuid) {
264
+ const { dispatch } = this;
265
+ const developerUsername = this.core.config.notifications?.developerUsername;
266
+ return developerUsername === void 0 ? {
267
+ uuid,
268
+ dispatch
269
+ } : {
270
+ uuid,
271
+ developerUsername,
272
+ dispatch
273
+ };
274
+ }
275
+ async sendGuarded(send) {
276
+ try {
277
+ await send();
278
+ } catch (error) {
279
+ if (error instanceof this.apiError && HARMLESS_API_CODES.has(error.code)) {
280
+ logger$2().debug(`fault reply hit harmless code ${paint.amber(String(error.code))}`);
281
+ return;
282
+ }
283
+ logger$2().error(`could not send a fault reply for route ${paint.sky.bold(this.dispatch.routeId)}`, error);
284
+ }
285
+ }
286
+ };
179
287
 
180
288
  //#endregion
181
289
  //#region src/gates/slowGate.ts
182
290
  const SLOW_GATE_MS = 750;
183
- let gateLogger;
184
- function logger$1() {
185
- gateLogger ??= new Logger("Gates", { channel: "gates" });
186
- return gateLogger;
187
- }
291
+ const logger$1 = lazyLogger("Gates", { channel: "gates" });
188
292
  function slowGateMonitor() {
189
293
  if (Envapter.isProduction) return void 0;
190
294
  const readings = [];
@@ -203,6 +307,38 @@ function slowGateMonitor() {
203
307
  };
204
308
  }
205
309
 
310
+ //#endregion
311
+ //#region src/gates/runGates.ts
312
+ async function timedCheck(gate, ctx, observe) {
313
+ const start = performance.now();
314
+ try {
315
+ await runCheck(gate, ctx);
316
+ } finally {
317
+ observe(gate.name, performance.now() - start);
318
+ }
319
+ }
320
+ async function runGates(gates, ctx, observe) {
321
+ try {
322
+ for (const gate of gates) await (observe ? timedCheck(gate, ctx, observe) : runCheck(gate, ctx));
323
+ await runCommits(ctx);
324
+ } finally {
325
+ discardCommits(ctx);
326
+ }
327
+ }
328
+ async function runHandlerGates(handlerCtor, ctx, observe) {
329
+ const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor);
330
+ if (!gates) return;
331
+ await runGates(gates, ctx, observe);
332
+ }
333
+ async function runInteractionGates(handlerCtor, ctx) {
334
+ const monitor = slowGateMonitor();
335
+ try {
336
+ await runHandlerGates(handlerCtor, ctx, monitor?.observe);
337
+ } finally {
338
+ monitor?.report(ctx.dispatch.declaredRoute);
339
+ }
340
+ }
341
+
206
342
  //#endregion
207
343
  //#region src/miscellaneous/emojiConfig.ts
208
344
  function isEmojiTuple(value) {
@@ -536,6 +672,7 @@ var CommandRegistry = class {
536
672
  loadedCommands = [];
537
673
  hmrHandler;
538
674
  pendingEvents = /* @__PURE__ */ new Map();
675
+ deploys = new KeyedQueue();
539
676
  injector = new CommandInjector();
540
677
  lastDeploy = new LastDeploy();
541
678
  reporter = new DeployReporter(this.logger);
@@ -549,11 +686,13 @@ var CommandRegistry = class {
549
686
  this.configuredGuilds = commands.guilds ?? [];
550
687
  if (!Envapter.isDevelopment && !Envapter.isTest) return;
551
688
  this.hmrHandler = new HmrModuleHandler({
552
- handlersDir: this.dir,
553
- isHandler: isCommandClass,
554
- registerHandler: this.registerCommand.bind(this),
555
- unregisterHandler: this.unregisterCommand.bind(this),
556
- getArtifacts: this.getArtifacts.bind(this),
689
+ handlers: {
690
+ dir: this.dir,
691
+ is: isCommandClass,
692
+ register: this.registerCommand.bind(this),
693
+ unregister: this.unregisterCommand.bind(this),
694
+ getArtifacts: this.getArtifacts.bind(this)
695
+ },
557
696
  logger: this.logger
558
697
  });
559
698
  }
@@ -573,7 +712,9 @@ var CommandRegistry = class {
573
712
  }
574
713
  this.reportLoad();
575
714
  getDevChannel()?.on("seedcord:refresh-commands", (data) => {
576
- this.refresh(data.shouldRefresh);
715
+ this.refresh(data.shouldRefresh).catch((error) => {
716
+ this.logger.error("Failed to refresh commands", error);
717
+ });
577
718
  });
578
719
  }
579
720
  reportLoad() {
@@ -592,15 +733,21 @@ var CommandRegistry = class {
592
733
  })], "debug");
593
734
  }
594
735
  /** @internal */
595
- async refresh(shouldRefresh = true) {
736
+ refresh(shouldRefresh = true) {
737
+ const answeredEvents = [...this.pendingEvents.values()];
738
+ this.pendingEvents.clear();
739
+ return this.afterRunningDeploy(() => this.applyEvents(answeredEvents, shouldRefresh));
740
+ }
741
+ afterRunningDeploy(task) {
742
+ return this.deploys.run("commands", task);
743
+ }
744
+ async applyEvents(events, shouldRefresh) {
596
745
  if (!shouldRefresh) {
597
746
  this.logger.debug(paint.italic("Command refresh cancelled."));
598
- this.pendingEvents.clear();
599
747
  return;
600
748
  }
601
749
  this.logger.debug(paint.italic("Refreshing commands..."));
602
- for (const event of this.pendingEvents.values()) await this.hmrHandler?.handle(event);
603
- this.pendingEvents.clear();
750
+ for (const event of events) await this.hmrHandler?.handle(event);
604
751
  await this.setCommands();
605
752
  }
606
753
  /** @internal */
@@ -653,7 +800,7 @@ var CommandRegistry = class {
653
800
  async deploy(signal, { routes, check }) {
654
801
  await this.init();
655
802
  check?.(this.allCommands());
656
- await this.setCommands(signal);
803
+ await this.afterRunningDeploy(() => this.setCommands(signal));
657
804
  routes?.warnUnhandledRoutes(this.routeLeaves());
658
805
  routes?.warnUnhandledContextMenuRoutes(this.contextMenuLeaves());
659
806
  }
@@ -879,29 +1026,85 @@ function forwardLogsToCli() {
879
1026
  }
880
1027
 
881
1028
  //#endregion
882
- //#region src/dispatch/dispatchReport.ts
883
- let dispatchLogger;
884
- function logger() {
885
- dispatchLogger ??= new Logger("Dispatch", { channel: "interactions" });
886
- return dispatchLogger;
1029
+ //#region src/reply/AutocompleteResponder.ts
1030
+ var AutocompleteResponder = class {
1031
+ telemetry;
1032
+ post;
1033
+ attempt;
1034
+ constructor(telemetry, post) {
1035
+ this.telemetry = telemetry;
1036
+ this.post = post;
1037
+ }
1038
+ respond(choices) {
1039
+ this.attempt = reportedWrite(this.telemetry, "respond", () => this.post(choices)).then(() => void 0);
1040
+ return this.attempt;
1041
+ }
1042
+ async clear() {
1043
+ if (!await this.attempt?.then(() => true, () => false)) await this.respond([]);
1044
+ }
1045
+ };
1046
+
1047
+ //#endregion
1048
+ //#region src/dispatch/routeIdOf.ts
1049
+ function routeIdOf(kind, route) {
1050
+ return `${kind}:${route || "unrouted"}`;
887
1051
  }
1052
+
1053
+ //#endregion
1054
+ //#region src/dispatch/DispatchContext.ts
888
1055
  /**
889
- * Classifies a caught stop for `interactionDispatched` so a gate and a
890
- * handler label the same stop identically.
1056
+ * State shared by middleware, gates, and handlers during one dispatch.
891
1057
  *
892
- * @internal
1058
+ * @typeParam Kind - What started this dispatch, an interaction kind or `event`.
893
1059
  */
1060
+ var DispatchContext = class {
1061
+ kind;
1062
+ route;
1063
+ fallback;
1064
+ state = Object.create(null);
1065
+ id = crypto.randomUUID();
1066
+ constructor(kind, route, fallback = false) {
1067
+ this.kind = kind;
1068
+ this.route = route;
1069
+ this.fallback = fallback;
1070
+ }
1071
+ /**
1072
+ * The dispatch identifier, such as `slash:search/docs`, `button:approve`, or `event:messageCreate`.
1073
+ * Component identifiers contain the custom-ID prefix without encoded params.
1074
+ */
1075
+ get routeId() {
1076
+ return routeIdOf(this.kind, this.route);
1077
+ }
1078
+ /** The {@link routeId} of the handler's own route. It's `null` on an event and on the unhandled default. */
1079
+ get declaredRoute() {
1080
+ return this.kind === "event" || this.fallback ? null : this.routeId;
1081
+ }
1082
+ set(key, value) {
1083
+ this.state[key] = value;
1084
+ }
1085
+ get(key) {
1086
+ return this.state[key];
1087
+ }
1088
+ /**
1089
+ * Read a key that must be present, with `undefined` stripped from the result.
1090
+ *
1091
+ * @throws A **SeedcordError** when nothing set the key on this dispatch.
1092
+ */
1093
+ require(key) {
1094
+ const value = this.state[key];
1095
+ if (value === void 0) throw new SeedcordError(SeedcordErrorCode.DispatchStateMissing, [String(key)]);
1096
+ return value;
1097
+ }
1098
+ };
1099
+
1100
+ //#endregion
1101
+ //#region src/dispatch/dispatchReport.ts
1102
+ const logger = lazyLogger("Dispatch", { channel: "interactions" });
894
1103
  function outcomeFor(caught) {
895
1104
  if (caught instanceof Silence) return "refused";
896
1105
  if (caught instanceof Notice) return caught.report ? "failed" : "refused";
897
1106
  return "failed";
898
1107
  }
899
- /**
900
- * Discord's interaction creation to now, read from the snowflake. Called at dispatch entry, since
901
- * a later read would count the handler run into the queue time as well.
902
- *
903
- * @internal
904
- */
905
1108
  function queuedMsFor(interactionId) {
906
1109
  try {
907
1110
  return Date.now() - timestampFromSnowflake(interactionId);
@@ -912,18 +1115,18 @@ function queuedMsFor(interactionId) {
912
1115
  function reportDispatch(bus, report) {
913
1116
  const durationMs = performance.now() - report.startedAt;
914
1117
  bus[PublishDefault]("interactionDispatched", {
915
- dispatchId: report.dispatchId,
916
- routeId: report.routeId,
1118
+ dispatchId: report.dispatch.id,
1119
+ routeId: report.dispatch.routeId,
917
1120
  interactionId: report.interactionId,
918
- kind: report.kind,
1121
+ kind: report.dispatch.kind,
919
1122
  outcome: report.outcome,
920
- fallback: report.fallback,
1123
+ fallback: report.dispatch.fallback,
921
1124
  userId: report.userId,
922
1125
  guildId: report.guildId,
923
1126
  durationMs,
924
1127
  queuedMs: report.queuedMs
925
1128
  });
926
- logger().trace(`${paint.sky.bold(report.routeId)} ${report.outcome} ${paint.mute("in")} ${Math.round(durationMs)}ms`);
1129
+ logger().trace(`${paint.sky.bold(report.dispatch.routeId)} ${report.outcome} ${paint.mute("in")} ${Math.round(durationMs)}ms`);
927
1130
  }
928
1131
 
929
1132
  //#endregion
@@ -969,5 +1172,301 @@ async function runAfter(ran, result, logger) {
969
1172
  }
970
1173
 
971
1174
  //#endregion
972
- export { AckTrace, AutocompleteRouteBrand, BAKED_IDS_KEY, COMMAND_DEPLOY_TIMEOUT_MS, CommandMetadataKey, CommandRegistry, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, CoordinatedStartup, EventMetadataKey, EventMiddlewareMetadataKey, FixedScope, GLOBAL_ID, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, MiddlewareKindsBrand, MiddlewareRegistry, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PluginLifecycle, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, SubscriberLoader, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, attachmentsOf, bindBotColor, bindEnv, busLoggerOf, checkAckLegality, clearStore, commandKey, commandRefOf, commandsRoute, connectDevChannel, contextMenuLeaves, contextMenuRouteOf, currentApplication, decodeComponentRoute, deferFlags, eventResultFor, forwardLogsToCli, fromDevLogLine, fromWireError, getDevChannel, globalFirst, guardedAccessor, guildSummaries, indexById, installLinks, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, linkCommands, logUnknownFault, outcomeFor, pageCursor, pickNotice, planDeploy, putCommands, queuedMsFor, readBotToken, readCommands, readCommandsAt, registrationFor, reportDispatch, reportedWrite, resolveDeployTarget, resolveEmojis, resultFor, runAfter, runGates, runHandlerGates, scopeIdsOf, sealAttachments, sendFlags, sendTarget, serializeReply, setDevChannel, skippedNote, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, toWireError, translateSerializationError, warnLinks, wrapHot };
1175
+ //#region src/dispatch/InteractionRun.ts
1176
+ var InteractionRun = class {
1177
+ config;
1178
+ ran = [];
1179
+ startedAt = performance.now();
1180
+ queuedMs;
1181
+ boundary;
1182
+ constructor(config) {
1183
+ this.config = config;
1184
+ const { core, apiError, interactionId, dispatch } = config;
1185
+ this.queuedMs = queuedMsFor(interactionId);
1186
+ this.boundary = new FaultBoundary(core, core.config.errors?.ignoreApiCodes, apiError, dispatch, () => config.faultDetails());
1187
+ }
1188
+ async prepare() {
1189
+ const { target } = this.config;
1190
+ this.warnIfUnhandled();
1191
+ return "responder" in target ? this.prepareAutocomplete(target) : this.prepareRepliable(target);
1192
+ }
1193
+ warnIfUnhandled() {
1194
+ const { dispatch, customId, HandlerCtor, logger } = this.config;
1195
+ if (!dispatch.fallback) return;
1196
+ if (dispatch.route === "") {
1197
+ logger.warn(`${paint.sky.bold(dispatch.kind)} has invalid customId: ${paint.mute(String(customId))}`);
1198
+ return;
1199
+ }
1200
+ logger.warn(`No handler found for ${paint.sky.bold(dispatch.routeId)}. Falling back to ${HandlerCtor.name}.`);
1201
+ }
1202
+ async prepareAutocomplete({ responder }) {
1203
+ const answer = (caught) => this.boundary.answerWithEmptyChoices(caught, () => responder.clear());
1204
+ const handler = await this.builtHandler(responder, answer);
1205
+ return handler && (() => this.start(handler, answer));
1206
+ }
1207
+ async prepareRepliable(target) {
1208
+ const { sender } = target;
1209
+ const answer = (caught) => this.boundary.answerWithReply(caught, sender);
1210
+ const handler = await this.builtHandler(sender, answer);
1211
+ if (!handler) return null;
1212
+ const refusal = await this.refusal(target);
1213
+ if (refusal) {
1214
+ await this.finish(answer, refusal.caught);
1215
+ return null;
1216
+ }
1217
+ return () => this.start(handler, answer);
1218
+ }
1219
+ async builtHandler(replyWith, answer) {
1220
+ const { HandlerCtor, event, core, dispatch, logger } = this.config;
1221
+ logger.trace(`Processing ${paint.sky.bold(dispatch.routeId)} with ${paint.mute(HandlerCtor.name)}`);
1222
+ try {
1223
+ return new HandlerCtor(event, core, dispatch, replyWith);
1224
+ } catch (caught) {
1225
+ await this.finish(answer, caught);
1226
+ return null;
1227
+ }
1228
+ }
1229
+ async refusal(target) {
1230
+ try {
1231
+ await this.runMiddlewares(target.sender);
1232
+ await runInteractionGates(this.config.HandlerCtor, target.gateContext());
1233
+ return null;
1234
+ } catch (caught) {
1235
+ return { caught };
1236
+ }
1237
+ }
1238
+ async runMiddlewares(sender) {
1239
+ const { middlewares, event, core, dispatch } = this.config;
1240
+ for (const Middleware of middlewares.chainFor(dispatch.kind)) {
1241
+ const middleware = new Middleware(event, core, dispatch, sender);
1242
+ this.ran.push(middleware);
1243
+ await middleware.execute();
1244
+ }
1245
+ }
1246
+ async start(handler, answer) {
1247
+ try {
1248
+ await handler.execute();
1249
+ } catch (caught) {
1250
+ await this.finish(answer, caught);
1251
+ return;
1252
+ }
1253
+ this.report("handled");
1254
+ await runAfter(this.ran, { outcome: "handled" }, this.config.logger);
1255
+ }
1256
+ async finish(answer, caught) {
1257
+ try {
1258
+ await answer(caught);
1259
+ } finally {
1260
+ this.report(outcomeFor(caught));
1261
+ await runAfter(this.ran, resultFor(caught), this.config.logger);
1262
+ }
1263
+ }
1264
+ report(outcome) {
1265
+ const { core, dispatch, interactionId, userId, guildId } = this.config;
1266
+ reportDispatch(core.bus, {
1267
+ dispatch,
1268
+ interactionId,
1269
+ outcome,
1270
+ userId,
1271
+ guildId,
1272
+ startedAt: this.startedAt,
1273
+ queuedMs: this.queuedMs
1274
+ });
1275
+ }
1276
+ };
1277
+
1278
+ //#endregion
1279
+ //#region src/dispatch/componentKindOf.ts
1280
+ const COMPONENT_KINDS = {
1281
+ [ComponentType.Button]: "button",
1282
+ [ComponentType.StringSelect]: "stringMenu",
1283
+ [ComponentType.UserSelect]: "userMenu",
1284
+ [ComponentType.RoleSelect]: "roleMenu",
1285
+ [ComponentType.ChannelSelect]: "channelMenu",
1286
+ [ComponentType.MentionableSelect]: "mentionableMenu"
1287
+ };
1288
+ function componentKindOf(type) {
1289
+ return COMPONENT_KINDS[type] ?? null;
1290
+ }
1291
+
1292
+ //#endregion
1293
+ //#region src/dispatch/HandlerLoader.ts
1294
+ var HandlerLoader = class {
1295
+ options;
1296
+ loadedHandlers = [];
1297
+ loadedMiddlewares = [];
1298
+ initialLoad = false;
1299
+ hmr;
1300
+ constructor(options) {
1301
+ this.options = options;
1302
+ if (!Envapter.isDevelopment && !Envapter.isTest) return;
1303
+ const { handlersDir, middlewaresDir, middlewares, logger } = options;
1304
+ this.hmr = new HmrModuleHandler({
1305
+ handlers: {
1306
+ dir: handlersDir,
1307
+ is: (value) => options.isHandler(value),
1308
+ register: (handler, file) => this.registerHandler(handler, file),
1309
+ unregister: (handler, artifacts) => options.removeHandler(handler, artifacts),
1310
+ getArtifacts: (handler) => options.artifactsOf(handler)
1311
+ },
1312
+ ...middlewaresDir && { middlewares: {
1313
+ dir: middlewaresDir,
1314
+ is: (value) => options.isMiddleware(value),
1315
+ register: (middleware, file) => this.registerMiddleware(middleware, file),
1316
+ unregister: (middleware) => middlewares.unregister(middleware)
1317
+ } },
1318
+ logger
1319
+ });
1320
+ }
1321
+ async load() {
1322
+ const { handlersDir, middlewaresDir } = this.options;
1323
+ this.initialLoad = true;
1324
+ this.loadedHandlers.length = 0;
1325
+ this.loadedMiddlewares.length = 0;
1326
+ try {
1327
+ if (middlewaresDir) await this.loadMiddlewares(middlewaresDir);
1328
+ await this.loadHandlers(handlersDir);
1329
+ } finally {
1330
+ this.initialLoad = false;
1331
+ }
1332
+ }
1333
+ async onHmr(event) {
1334
+ await this.hmr?.handle(event);
1335
+ }
1336
+ async loadHandlers(dir) {
1337
+ for await (const { fullPath, relativePath, imported } of importFiles(dir)) for (const value of Object.values(imported)) {
1338
+ if (!this.options.isHandler(value)) continue;
1339
+ this.registerHandler(value, relativePath);
1340
+ this.hmr?.trackHandler(fullPath, value);
1341
+ }
1342
+ }
1343
+ async loadMiddlewares(dir) {
1344
+ for await (const { fullPath, relativePath, imported } of importFiles(dir)) for (const value of Object.values(imported)) {
1345
+ if (!this.options.isMiddleware(value)) continue;
1346
+ this.registerMiddleware(value, relativePath);
1347
+ this.hmr?.trackMiddleware(fullPath, value);
1348
+ }
1349
+ }
1350
+ registerHandler(handler, relativePath) {
1351
+ const from = formatFilePath(relativePath);
1352
+ if (!this.options.storeHandler(handler, from)) return;
1353
+ if (this.initialLoad) this.loadedHandlers.push({
1354
+ name: handler.name,
1355
+ from
1356
+ });
1357
+ }
1358
+ registerMiddleware(middleware, relativePath) {
1359
+ const registration = this.options.middlewares.register(middleware);
1360
+ if (!registration || !this.initialLoad) return;
1361
+ const scope = registration.keys ? `${registration.priority}, ${registration.keys.join(", ")}` : registration.priority;
1362
+ this.loadedMiddlewares.push({
1363
+ name: `${middleware.name} (${String(scope)})`,
1364
+ from: formatFilePath(relativePath)
1365
+ });
1366
+ }
1367
+ };
1368
+
1369
+ //#endregion
1370
+ //#region src/dispatch/RouteRegistry.ts
1371
+ function emptyRouteMaps() {
1372
+ return {
1373
+ ["slash"]: /* @__PURE__ */ new Map(),
1374
+ ["userContextMenu"]: /* @__PURE__ */ new Map(),
1375
+ ["messageContextMenu"]: /* @__PURE__ */ new Map(),
1376
+ ["autocomplete"]: /* @__PURE__ */ new Map(),
1377
+ ["button"]: /* @__PURE__ */ new Map(),
1378
+ ["stringMenu"]: /* @__PURE__ */ new Map(),
1379
+ ["userMenu"]: /* @__PURE__ */ new Map(),
1380
+ ["roleMenu"]: /* @__PURE__ */ new Map(),
1381
+ ["channelMenu"]: /* @__PURE__ */ new Map(),
1382
+ ["mentionableMenu"]: /* @__PURE__ */ new Map(),
1383
+ ["modal"]: /* @__PURE__ */ new Map()
1384
+ };
1385
+ }
1386
+ function matchRoute(maps, kind, route, unhandled) {
1387
+ return maps[kind].get(route) ?? {
1388
+ kind,
1389
+ route,
1390
+ fallback: true,
1391
+ ctor: kind === "autocomplete" ? unhandled.autocomplete : unhandled.repliable
1392
+ };
1393
+ }
1394
+ var RouteRegistry = class {
1395
+ maps = emptyRouteMaps();
1396
+ get allRoutes() {
1397
+ return Object.values(this.maps).flatMap((routes) => [...routes.values()]);
1398
+ }
1399
+ get size() {
1400
+ return this.allRoutes.length;
1401
+ }
1402
+ register(ctor, from) {
1403
+ const writes = [];
1404
+ for (const [kind, routes] of interactionRoutesOf(ctor)) for (const route of routes) {
1405
+ const existing = this.maps[kind].get(route);
1406
+ if (existing && existing.ctor !== ctor) throw new SeedcordTypeError(SeedcordErrorCode.CoreDuplicateName, [
1407
+ "route",
1408
+ routeIdOf(kind, route),
1409
+ [`${paint.bold(existing.ctor.name)} (${paint.path(existing.from)})`, `${paint.bold(ctor.name)} (${paint.path(from)})`]
1410
+ ]);
1411
+ writes.push({
1412
+ kind,
1413
+ route,
1414
+ fallback: false,
1415
+ ctor,
1416
+ from
1417
+ });
1418
+ }
1419
+ for (const write of writes) this.maps[write.kind].set(write.route, write);
1420
+ return writes.length > 0;
1421
+ }
1422
+ unregister(ctor, routes = this.routesOf(ctor)) {
1423
+ for (const { kind, route } of routes) if (this.maps[kind].get(route)?.ctor === ctor) this.maps[kind].delete(route);
1424
+ }
1425
+ routesOf(ctor) {
1426
+ return this.allRoutes.filter((route) => route.ctor === ctor);
1427
+ }
1428
+ warnUnhandledRoutes(logger, leaves) {
1429
+ this.warnMissing(logger, leaves, "slash", "Slash route");
1430
+ }
1431
+ warnUnhandledContextMenuRoutes(logger, leaves) {
1432
+ this.warnMissing(logger, leaves.user, "userContextMenu", "User context menu");
1433
+ this.warnMissing(logger, leaves.message, "messageContextMenu", "Message context menu");
1434
+ }
1435
+ warnMissing(logger, names, kind, label) {
1436
+ const decorator = paint.bold(routeDecoratorOf(kind));
1437
+ for (const name of names) {
1438
+ if (this.maps[kind].has(name)) continue;
1439
+ logger.warn(`${label} ${paint.sky.bold(name)} doesn't have a registered ${decorator} handler.`);
1440
+ }
1441
+ }
1442
+ };
1443
+
1444
+ //#endregion
1445
+ //#region src/subscribers/interactionFaultSource.ts
1446
+ const FAULT_KINDS = {
1447
+ slash: "slash",
1448
+ userContextMenu: "context-menu",
1449
+ messageContextMenu: "context-menu",
1450
+ button: "button",
1451
+ stringMenu: "select",
1452
+ userMenu: "select",
1453
+ roleMenu: "select",
1454
+ channelMenu: "select",
1455
+ mentionableMenu: "select",
1456
+ modal: "modal",
1457
+ autocomplete: null
1458
+ };
1459
+ function interactionFaultSource(dispatch, fields) {
1460
+ const interactionKind = FAULT_KINDS[dispatch.kind];
1461
+ if (interactionKind === null) return null;
1462
+ return {
1463
+ kind: "interaction",
1464
+ interactionKind,
1465
+ command: interactionKind === "slash" || interactionKind === "context-menu" ? dispatch.route : null,
1466
+ ...fields
1467
+ };
1468
+ }
1469
+
1470
+ //#endregion
1471
+ export { AckTrace, AutocompleteResponder, AutocompleteRouteBrand, BAKED_IDS_KEY, COMMAND_DEPLOY_TIMEOUT_MS, CommandMetadataKey, CommandRegistry, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, CoordinatedStartup, DispatchContext, EventMetadataKey, EventMiddlewareMetadataKey, FaultBoundary, FixedScope, GLOBAL_ID, GatedMetadataKey, HandlerLoader, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, InteractionRun, MiddlewareKindsBrand, MiddlewareRegistry, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, OnceTracker, PAGE_MAX, PluginLifecycle, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, RouteRegistry, SlashRouteBrand, SubscribeMetadataKey, SubscriberLoader, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, assertUniqueKeys, attachmentsOf, bindBotColor, bindEnv, busLoggerOf, checkAckLegality, clearStore, commandKey, commandRefOf, commandsRoute, componentKindOf, connectDevChannel, contextMenuLeaves, contextMenuRouteOf, currentApplication, decodeComponentRoute, deferFlags, emptyRouteMaps, eventResultFor, forwardLogsToCli, fromDevLogLine, fromWireError, getDevChannel, globalFirst, guardedAccessor, guildSummaries, indexById, installLinks, interactionFaultSource, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, lazyLogger, linkCommands, matchRoute, pageCursor, planDeploy, putCommands, readBotToken, readCommands, readCommandsAt, reportedWrite, resolveDeployTarget, resolveEmojis, resultFor, runAfter, runCase, runHandlerGates, scopeIdsOf, sealAttachments, sendFlags, sendTarget, serializeReply, setDevChannel, skippedNote, slashRouteLeaves, splitPluginKey, storeComponentRoute, storeInteractionRoute, toWireError, translateSerializationError, warnLinks, wrapHot };
973
1472
  //# sourceMappingURL=internal.index.mjs.map