@seedcord/core 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/{Bus-dGy6xq3F.mjs → Bus-Djm6688b.mjs} +6 -5
  2. package/dist/Bus-Djm6688b.mjs.map +1 -0
  3. package/dist/{Pluggable-C5uxmDb-.d.mts → Pluggable-CqKvMXm5.d.mts} +3 -3
  4. package/dist/{Pluggable-B7_EftHj.mjs → Pluggable-keys1cVr.mjs} +4 -3
  5. package/dist/Pluggable-keys1cVr.mjs.map +1 -0
  6. package/dist/{Plugin-Bnz61fs_.d.mts → Plugin-BdqfRjU0.d.mts} +2 -2
  7. package/dist/{cursor-8Y2zpF-Y.d.mts → cursor-BY5dmiz6.d.mts} +121 -7
  8. package/dist/{cursor-BkI9lxy2.mjs → cursor-CFNKLVlk.mjs} +49 -11
  9. package/dist/cursor-CFNKLVlk.mjs.map +1 -0
  10. package/dist/{index-CD8l15aZ.d.mts → index-BS1hH9Sh.d.mts} +2 -2
  11. package/dist/index.d.mts +16 -35
  12. package/dist/index.mjs +29 -17
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/internal.index.d.mts +79 -8
  15. package/dist/internal.index.mjs +121 -16
  16. package/dist/internal.index.mjs.map +1 -1
  17. package/dist/node-internal.index.d.mts +4 -4
  18. package/dist/node-internal.index.mjs +4 -4
  19. package/dist/node.index.d.mts +1 -1
  20. package/dist/node.index.mjs +1 -1
  21. package/dist/{phases-D0a8BV9Z.d.mts → phases-DJf12l5T.d.mts} +48 -9
  22. package/dist/plugin.index.d.mts +1 -1
  23. package/dist/{slashRouteLeaves-C1msUE9Q.mjs → slashRouteLeaves-DTFHzCKN.mjs} +2 -2
  24. package/dist/{slashRouteLeaves-C1msUE9Q.mjs.map → slashRouteLeaves-DTFHzCKN.mjs.map} +1 -1
  25. package/dist/{subscribers-A8lNU5yM.mjs → subscribers-Bf4cBTvK.mjs} +2 -2
  26. package/dist/{subscribers-A8lNU5yM.mjs.map → subscribers-Bf4cBTvK.mjs.map} +1 -1
  27. package/package.json +8 -8
  28. package/dist/Bus-dGy6xq3F.mjs.map +0 -1
  29. package/dist/Pluggable-B7_EftHj.mjs.map +0 -1
  30. package/dist/cursor-BkI9lxy2.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
- import { A as UnregisterSubscriber, B as SubscribeMetadataKey, C as ReplyMethod, D as RegisterDefaults, E as AckTrace, F as GatedMetadataKey, H as Notice, I as InteractionKind, L as InteractionMetadataKey, M as PublishDefault, N as CommandMetadataKey, O as RegisterSubscriber, P as EventMetadataKey, R as InteractionRouteKeys, S as AckState, T as sendTarget, V as WebhookUrlMetadataKey, a as StoredSubscriberCtor, b as ReplyTelemetry, d as DispatchOutcome, i as Bus, j as VerifyWebhooks, k as RegisteredCount, o as SubscriberRegistration, s as busLoggerOf, u as DefaultSubscriptions, w as checkAckLegality, x as reportedWrite, z as MiddlewareMetadataKey } from "./phases-D0a8BV9Z.mjs";
2
- import { A as serializeReply, B as IsNever, C as Gate, F as ContextMenuNamesBrand, G as CacheFor, I as SlashRouteBrand, J as SlashOption, K as OptionKind, L as CommandMeta, M as ComponentDefsBrand, N as ComponentKindBrand, P as ContextMenuKindBrand, Q as MenuCacheFor, V as BuilderComponent, X as SlashRouteEntry, Y as SlashRegistry, Z as ContextMenuKind, _ as pickNotice, c as FocusedField, et as NamesFor, i as AutocompletableNames, j as AutocompleteRouteBrand, k as SerializedReply, m as PermissionNames, n as PageCursor, o as ChoiceValueOf, q as RouteCache, r as pageCursor, s as EntryFor, t as PAGE_MAX, v as GateFitsWith, w as GateContextBase, z as Promisable } from "./cursor-8Y2zpF-Y.mjs";
1
+ import { A as UnregisterSubscriber, B as InteractionMiddlewareMetadataKey, C as ReplyMethod, D as RegisterDefaults, E as AckTrace, F as EventMetadataKey, G as Notice, H as MiddlewareKind, I as EventMiddlewareMetadataKey, L as GatedMetadataKey, M as PublishDefault, O as RegisterSubscriber, P as CommandMetadataKey, R as InteractionKind, S as AckState, T as sendTarget, U as SubscribeMetadataKey, V as InteractionRouteKeys, W as WebhookUrlMetadataKey, a as StoredSubscriberCtor, b as ReplyTelemetry, d as DispatchOutcome, i as Bus, j as VerifyWebhooks, k as RegisteredCount, o as SubscriberRegistration, s as busLoggerOf, u as DefaultSubscriptions, w as checkAckLegality, x as reportedWrite, z as InteractionMetadataKey } from "./phases-DJf12l5T.mjs";
2
+ import { A as serializeReply, B as AutocompleteRouteBrand, C as Gate, F as AnyMiddlewareCtor, G as MiddlewareKindsBrand, H as ComponentKindBrand, I as InteractionMiddlewareMetadata, K as SlashRouteBrand, M as EventDispatchResult, P as HandlerResult, U as ContextMenuKindBrand, V as ComponentDefsBrand, W as ContextMenuNamesBrand, X as IsNever, Y as Promisable, Z as BuilderComponent, _ as pickNotice, at as SlashRegistry, c as FocusedField, ct as MenuCacheFor, i as AutocompletableNames, it as SlashOption, j as DispatchResult, k as SerializedReply, m as PermissionNames, n as PageCursor, nt as OptionKind, o as ChoiceValueOf, ot as SlashRouteEntry, q as CommandMeta, r as pageCursor, rt as RouteCache, s as EntryFor, st as ContextMenuKind, t as PAGE_MAX, tt as CacheFor, ut as NamesFor, v as GateFitsWith, w as GateContextBase, z as interactionMiddlewareMetaOf } from "./cursor-BY5dmiz6.mjs";
3
3
  import { n as CommandBuilder, r as DeployResult } from "./basic-Dng__Ena.mjs";
4
- import { a as PluginCtor, i as PluginArgs, n as Initializeable } from "./Plugin-Bnz61fs_.mjs";
4
+ import { a as PluginCtor, i as PluginArgs, n as Initializeable } from "./Plugin-BdqfRjU0.mjs";
5
5
  import { n as contextMenuLeaves, t as ContextMenuLeaves } from "./contextMenuLeaves-COzDYa6c.mjs";
6
6
  import "reflect-metadata";
7
7
  import { AnyCustomId, DecodedParams } from "@seedcord/custom-id";
@@ -9,8 +9,54 @@ import { ContextMenuCommandBuilder, SlashCommandBuilder } from "@discordjs/build
9
9
  import { ChannelType } from "discord-api-types/v10";
10
10
  import { SeedcordErrorCode } from "@seedcord/errors";
11
11
  import { SeedcordError } from "@seedcord/errors/internal";
12
+ import { Logger } from "@seedcord/logger";
12
13
  import { DevChannel, SeedcordCliEvents, SeedcordFrameworkEvents } from "@seedcord/types/internal";
13
14
  import { BotColor, DeferOpts, EpochMs, HmrAware, ReplyResponse, SendOpts } from "@seedcord/types";
15
+ //#region src/dispatch/MiddlewareRegistry.d.ts
16
+ /**
17
+ * A decorated middleware reduced to what the registry orders and filters on. An absent `keys` puts the
18
+ * middleware in every chain.
19
+ *
20
+ * @internal
21
+ */
22
+ interface MiddlewareRegistration<Key extends string> {
23
+ readonly priority: number;
24
+ readonly keys?: readonly Key[];
25
+ }
26
+ /**
27
+ * Reads a transport's decorator metadata off a class. Returns `undefined` for an undecorated one.
28
+ *
29
+ * @internal
30
+ */
31
+ type MiddlewareRegistrationOf<Ctor, Key extends string> = (ctor: Ctor) => MiddlewareRegistration<Key> | undefined;
32
+ /** @internal */
33
+ declare function interactionMiddleware(ctor: AnyMiddlewareCtor): MiddlewareRegistration<MiddlewareKind> | undefined;
34
+ /**
35
+ * Holds the middleware a transport loaded and keeps one chain per key. An interaction keys on its kind
36
+ * and an event on its name. Priority and the key filter behave the same on each.
37
+ *
38
+ * @typeParam Ctor - The transport's middleware constructor type.
39
+ * @typeParam Key - The chain lookup key.
40
+ *
41
+ * @internal
42
+ */
43
+ declare class MiddlewareRegistry<Ctor extends AnyMiddlewareCtor, Key extends string = MiddlewareKind> {
44
+ private readonly registrationOf;
45
+ private readonly entries;
46
+ private chains;
47
+ constructor(registrationOf: MiddlewareRegistrationOf<Ctor, Key>);
48
+ /**
49
+ * Adds a decorated middleware class and returns what it registered with. Returns `undefined` for an
50
+ * undecorated class or one already registered.
51
+ *
52
+ * @throws A **SeedcordError** If another class with the same name is registered.
53
+ */
54
+ register(ctor: Ctor): MiddlewareRegistration<Key> | undefined;
55
+ unregister(ctor: Ctor): void;
56
+ chainFor(key: Key): readonly Ctor[];
57
+ private rebuild;
58
+ }
59
+ //#endregion
14
60
  //#region src/components/botColorHolder.d.ts
15
61
  declare function setBotColor(color: BotColor | undefined): void;
16
62
  //#endregion
@@ -24,9 +70,8 @@ declare function contextMenuRouteOf(kind: ContextMenuKind): InteractionKind;
24
70
  //#endregion
25
71
  //#region src/gates/runGates.d.ts
26
72
  type GateObserver = (gateName: string, elapsedMs: number) => void;
27
- declare function routeIdOf(handlerCtor: object): string | null;
28
73
  declare function runGates(gates: readonly Gate<GateContextBase>[], ctx: GateContextBase, observe?: GateObserver): Promise<void>;
29
- declare function runHandlerGates(handlerCtor: object, ctx: GateContextBase, routeId?: string, observe?: GateObserver): Promise<void>;
74
+ declare function runHandlerGates(handlerCtor: object, ctx: GateContextBase, declaredRoute?: string, observe?: GateObserver): Promise<void>;
30
75
  //#endregion
31
76
  //#region src/gates/slowGate.d.ts
32
77
  /**
@@ -35,7 +80,7 @@ declare function runHandlerGates(handlerCtor: object, ctx: GateContextBase, rout
35
80
  */
36
81
  interface SlowGateMonitor {
37
82
  readonly observe: GateObserver;
38
- report(routeId: string | null): void;
83
+ report(declaredRoute: string | null): void;
39
84
  }
40
85
  declare function slowGateMonitor(): SlowGateMonitor | undefined;
41
86
  //#endregion
@@ -253,14 +298,17 @@ declare class ReportThrottle {
253
298
  *
254
299
  * @internal
255
300
  */
256
- declare function outcomeFor(caught: unknown): DispatchOutcome;
301
+ declare function outcomeFor(caught: unknown): Exclude<DispatchOutcome, 'handled'>;
257
302
  interface DispatchReport {
303
+ readonly dispatchId: string;
258
304
  readonly routeId: string;
259
305
  readonly interactionId: string;
260
306
  readonly kind: `${InteractionKind}`;
261
307
  readonly outcome: DispatchOutcome;
262
308
  /** True when no route matched and the unhandled default ran. */
263
309
  readonly fallback: boolean;
310
+ readonly userId: string | null;
311
+ readonly guildId: string | null;
264
312
  /** `performance.now()` captured as the dispatch began. */
265
313
  readonly startedAt: number;
266
314
  /** {@link queuedMsFor} read at the same moment as `startedAt`. */
@@ -275,5 +323,28 @@ interface DispatchReport {
275
323
  declare function queuedMsFor(interactionId: string): number;
276
324
  declare function reportDispatch(bus: Bus, report: DispatchReport): void;
277
325
  //#endregion
278
- export { type AckState, AckTrace, type AutocompletableNames, AutocompleteRouteBrand, type CacheFor, type ChoiceValueOf, type CommandBuilder, type CommandCtor, type CommandMeta, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, type ContextMenuLeaves, ContextMenuNamesBrand, type DecodedComponentRoute, type DefaultSubscriptions, type DeployResult, type EntryFor, EventMetadataKey, type FocusedField, type GateFitsWith, GateNotice, type GateObserver, GatedMetadataKey, type HasComponentDefs, HasDangerousPermissions, HmrManager, type Initializeable, InteractionMetadataKey, InteractionRouteKeys, type MatchArms, type MenuCacheFor, MiddlewareMetadataKey, MissingPermissions, MissingRole, type NamesFor, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, type ReplyMethod, type ReplyTelemetry, ReportThrottle, type RoutableConstructor, type RouteCache, type SerializedReply, type SingleParams, type SlashOption, type SlashOptions, SlashRouteBrand, type SlashRouteEntry, type SlowGateMonitor, type StoredSubscriberCtor, SubscribeMetadataKey, type SubscriberRegistration, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isCommandClass, isEmojiTuple, outcomeFor, pageCursor, pickNotice, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
326
+ //#region src/dispatch/runAfter.d.ts
327
+ interface Afterable<Result> {
328
+ after?(result: Result): Promise<void>;
329
+ }
330
+ /** @internal */
331
+ declare function resultFor(caught: unknown): DispatchResult;
332
+ /**
333
+ * Builds what an event middleware's `after()` receives. `handlers` is empty exactly when the chain
334
+ * stopped the event.
335
+ *
336
+ * @internal
337
+ */
338
+ declare function eventResultFor(stopped: {
339
+ caught: unknown;
340
+ } | null, handlers: readonly HandlerResult[]): EventDispatchResult;
341
+ /**
342
+ * Calls `after()` on each middleware that ran, newest first. A throw inside one is logged. Every
343
+ * remaining middleware still gets its call.
344
+ *
345
+ * @internal
346
+ */
347
+ declare function runAfter<Result>(ran: readonly Afterable<Result>[], result: Result, logger: Logger): Promise<void>;
348
+ //#endregion
349
+ export { type AckState, AckTrace, type AutocompletableNames, AutocompleteRouteBrand, type CacheFor, type ChoiceValueOf, type CommandBuilder, type CommandCtor, type CommandMeta, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, type ContextMenuLeaves, ContextMenuNamesBrand, type DecodedComponentRoute, type DefaultSubscriptions, type DeployResult, type EntryFor, EventMetadataKey, EventMiddlewareMetadataKey, type FocusedField, type GateFitsWith, GateNotice, type GateObserver, GatedMetadataKey, type HasComponentDefs, HasDangerousPermissions, HmrManager, type Initializeable, InteractionMetadataKey, type InteractionMiddlewareMetadata, InteractionMiddlewareMetadataKey, InteractionRouteKeys, type MatchArms, type MenuCacheFor, MiddlewareKindsBrand, type MiddlewareRegistration, type MiddlewareRegistrationOf, MiddlewareRegistry, MissingPermissions, MissingRole, type NamesFor, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, type ReplyMethod, type ReplyTelemetry, ReportThrottle, type RoutableConstructor, type RouteCache, type SerializedReply, type SingleParams, type SlashOption, type SlashOptions, SlashRouteBrand, type SlashRouteEntry, type SlowGateMonitor, type StoredSubscriberCtor, SubscribeMetadataKey, type SubscriberRegistration, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, deferFlags, eventResultFor, getDevChannel, guardedAccessor, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, outcomeFor, pageCursor, pickNotice, queuedMsFor, reportDispatch, reportedWrite, resultFor, runAfter, runGates, runHandlerGates, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
279
350
  //# sourceMappingURL=internal.index.d.mts.map
@@ -1,10 +1,11 @@
1
- import { A as translateSerializationError, B as storeComponentRoute, C as NotInDm, E as OnCooldown, F as checkAckLegality, G as Notice, H as ComponentDefsKey, I as sendTarget, L as areRoutes, N as reportedWrite, P as AckTrace, R as contextMenuRouteOf, S as NotAllowed, T as NotOwner, U as decodeComponentRoute, V as storeInteractionRoute, W as NoticeCard, _ as GateNotice, b as MissingRole, d as pickNotice, f as discardCommits, g as runCommits, h as runCheck, k as serializeReply, n as pageCursor, r as Silence, s as PermissionNames, t as PAGE_MAX, v as HasDangerousPermissions, w as NotInGuild, x as NeedsAny, y as MissingPermissions, z as interactionRoutesOf } from "./cursor-BkI9lxy2.mjs";
2
- import { C as SubscribeMetadataKey, D as guardedAccessor, E as clearStore, M as setBotColor, O as contextMenuLeaves, S as MiddlewareMetadataKey, T as accessorStore, _ as EventMetadataKey, a as RegisterSubscriber, b as InteractionMetadataKey, c as VerifyWebhooks, g as CommandMetadataKey, h as sendFlags, i as RegisterDefaults, m as deferFlags, n as busLoggerOf, o as RegisteredCount, p as ReportThrottle, s as UnregisterSubscriber, v as GatedMetadataKey, w as WebhookUrlMetadataKey, x as InteractionRouteKeys } from "./Bus-dGy6xq3F.mjs";
1
+ import { A as translateSerializationError, B as contextMenuRouteOf, C as NotInDm, E as OnCooldown, F as checkAckLegality, G as decodeComponentRoute, H as storeComponentRoute, I as sendTarget, K as NoticeCard, N as reportedWrite, P as AckTrace, R as interactionMiddlewareMetaOf, S as NotAllowed, T as NotOwner, U as storeInteractionRoute, V as interactionRoutesOf, W as ComponentDefsKey, _ as GateNotice, b as MissingRole, d as pickNotice, f as discardCommits, g as runCommits, h as runCheck, k as serializeReply, n as pageCursor, q as Notice, r as Silence, s as PermissionNames, t as PAGE_MAX, v as HasDangerousPermissions, w as NotInGuild, x as NeedsAny, y as MissingPermissions, z as areRoutes } from "./cursor-CFNKLVlk.mjs";
2
+ import { C as InteractionRouteKeys, D as clearStore, E as accessorStore, N as setBotColor, O as guardedAccessor, S as InteractionMiddlewareMetadataKey, T as WebhookUrlMetadataKey, _ as EventMetadataKey, a as RegisterSubscriber, c as VerifyWebhooks, g as CommandMetadataKey, h as sendFlags, i as RegisterDefaults, k as contextMenuLeaves, m as deferFlags, n as busLoggerOf, o as RegisteredCount, p as ReportThrottle, s as UnregisterSubscriber, v as EventMiddlewareMetadataKey, w as SubscribeMetadataKey, x as InteractionMetadataKey, y as GatedMetadataKey } from "./Bus-Djm6688b.mjs";
3
3
  import { n as asError, t as PublishDefault } from "./publishDefault-BuSZPbjT.mjs";
4
4
  import { n as setDevChannel, t as getDevChannel } from "./devChannel-CQU7hq7Q.mjs";
5
- import { n as isCommandClass, t as slashRouteLeaves } from "./slashRouteLeaves-C1msUE9Q.mjs";
5
+ import { n as isCommandClass, t as slashRouteLeaves } from "./slashRouteLeaves-DTFHzCKN.mjs";
6
6
  import "reflect-metadata";
7
- import { paint } from "@seedcord/errors";
7
+ import { SeedcordErrorCode, paint } from "@seedcord/errors";
8
+ import { SeedcordError } from "@seedcord/errors/internal";
8
9
  import { Logger } from "@seedcord/logger";
9
10
  import { timestampFromSnowflake } from "@seedcord/utils";
10
11
  import { Envapter } from "envapt";
@@ -16,16 +17,75 @@ const ContextMenuKindBrand = Symbol("seedcord:brand:context-menu-kind");
16
17
  const ContextMenuNamesBrand = Symbol("seedcord:brand:context-menu-names");
17
18
  const ComponentKindBrand = Symbol("seedcord:brand:component-kind");
18
19
  const ComponentDefsBrand = Symbol("seedcord:brand:component-defs");
20
+ const MiddlewareKindsBrand = Symbol("seedcord:brand:middleware-kinds");
19
21
 
20
22
  //#endregion
21
- //#region src/gates/runGates.ts
22
- function routeIdOf(handlerCtor) {
23
- for (const [kind, key] of Object.entries(InteractionRouteKeys)) {
24
- const routes = Reflect.getMetadata(key, handlerCtor);
25
- if (routes?.length) return `${kind}:${routes.join(",")}`;
26
- }
27
- return null;
23
+ //#region src/dispatch/MiddlewareRegistry.ts
24
+ /** @internal */
25
+ function interactionMiddleware(ctor) {
26
+ const metadata = interactionMiddlewareMetaOf(ctor);
27
+ if (!metadata) return void 0;
28
+ return {
29
+ priority: metadata.priority,
30
+ ...metadata.kinds && { keys: metadata.kinds }
31
+ };
28
32
  }
33
+ /**
34
+ * Holds the middleware a transport loaded and keeps one chain per key. An interaction keys on its kind
35
+ * and an event on its name. Priority and the key filter behave the same on each.
36
+ *
37
+ * @typeParam Ctor - The transport's middleware constructor type.
38
+ * @typeParam Key - The chain lookup key.
39
+ *
40
+ * @internal
41
+ */
42
+ var MiddlewareRegistry = class {
43
+ registrationOf;
44
+ entries = [];
45
+ chains = /* @__PURE__ */ new Map();
46
+ constructor(registrationOf) {
47
+ this.registrationOf = registrationOf;
48
+ }
49
+ /**
50
+ * Adds a decorated middleware class and returns what it registered with. Returns `undefined` for an
51
+ * undecorated class or one already registered.
52
+ *
53
+ * @throws A **SeedcordError** If another class with the same name is registered.
54
+ */
55
+ register(ctor) {
56
+ const registration = this.registrationOf(ctor);
57
+ if (!registration) return void 0;
58
+ if (this.entries.some((entry) => entry.ctor === ctor)) return void 0;
59
+ if (this.entries.some((entry) => entry.ctor.name === ctor.name)) throw new SeedcordError(SeedcordErrorCode.DuplicateMiddleware, [ctor.name]);
60
+ this.entries.push({
61
+ ctor,
62
+ priority: registration.priority,
63
+ ...registration.keys && { keys: new Set(registration.keys) }
64
+ });
65
+ this.rebuild();
66
+ return registration;
67
+ }
68
+ unregister(ctor) {
69
+ const index = this.entries.findIndex((entry) => entry.ctor === ctor);
70
+ if (index === -1) return;
71
+ this.entries.splice(index, 1);
72
+ this.rebuild();
73
+ }
74
+ chainFor(key) {
75
+ const built = this.chains.get(key);
76
+ if (built) return built;
77
+ const chain = this.entries.filter((entry) => !entry.keys || entry.keys.has(key)).map((entry) => entry.ctor);
78
+ this.chains.set(key, chain);
79
+ return chain;
80
+ }
81
+ rebuild() {
82
+ this.entries.sort((a, b) => a.priority - b.priority);
83
+ this.chains = /* @__PURE__ */ new Map();
84
+ }
85
+ };
86
+
87
+ //#endregion
88
+ //#region src/gates/runGates.ts
29
89
  async function timedCheck(gate, ctx, observe) {
30
90
  const start = performance.now();
31
91
  try {
@@ -42,12 +102,12 @@ async function runGates(gates, ctx, observe) {
42
102
  discardCommits(ctx);
43
103
  }
44
104
  }
45
- async function runHandlerGates(handlerCtor, ctx, routeId, observe) {
105
+ async function runHandlerGates(handlerCtor, ctx, declaredRoute, observe) {
46
106
  const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor);
47
107
  if (!gates) return;
48
108
  await runGates(gates, {
49
109
  ...ctx,
50
- routeId: routeId ?? routeIdOf(handlerCtor)
110
+ declaredRoute: declaredRoute ?? null
51
111
  }, observe);
52
112
  }
53
113
 
@@ -67,11 +127,11 @@ function slowGateMonitor() {
67
127
  name,
68
128
  ms
69
129
  }),
70
- report(routeId) {
130
+ report(declaredRoute) {
71
131
  const total = readings.reduce((sum, reading) => sum + reading.ms, 0);
72
132
  if (total < SLOW_GATE_MS) return;
73
133
  const shares = readings.toSorted((a, b) => b.ms - a.ms).map((reading) => `${paint.sky.bold(reading.name)} ${paint.mute(`${Math.round(reading.ms)}ms`)}`);
74
- const headline = `gates${routeId === null ? "" : ` for ${paint.sky.bold(routeId)}`} took ${paint.amber(`${Math.round(total)}ms`)} of the 3s ack budget`;
134
+ const headline = `gates${declaredRoute === null ? "" : ` for ${paint.sky.bold(declaredRoute)}`} took ${paint.amber(`${Math.round(total)}ms`)} of the 3s ack budget`;
75
135
  logger$1().utils.block(headline, shares, "warn", (text) => text);
76
136
  }
77
137
  };
@@ -158,11 +218,14 @@ function queuedMsFor(interactionId) {
158
218
  function reportDispatch(bus, report) {
159
219
  const durationMs = performance.now() - report.startedAt;
160
220
  bus[PublishDefault]("interactionDispatched", {
221
+ dispatchId: report.dispatchId,
161
222
  routeId: report.routeId,
162
223
  interactionId: report.interactionId,
163
224
  kind: report.kind,
164
225
  outcome: report.outcome,
165
226
  fallback: report.fallback,
227
+ userId: report.userId,
228
+ guildId: report.guildId,
166
229
  durationMs,
167
230
  queuedMs: report.queuedMs
168
231
  });
@@ -170,5 +233,47 @@ function reportDispatch(bus, report) {
170
233
  }
171
234
 
172
235
  //#endregion
173
- export { AckTrace, AutocompleteRouteBrand, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, EventMetadataKey, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionRouteKeys, MiddlewareMetadataKey, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, deferFlags, getDevChannel, guardedAccessor, interactionRoutesOf, isCommandClass, isEmojiTuple, outcomeFor, pageCursor, pickNotice, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runGates, runHandlerGates, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
236
+ //#region src/dispatch/runAfter.ts
237
+ /** @internal */
238
+ function resultFor(caught) {
239
+ return {
240
+ outcome: outcomeFor(caught),
241
+ caught
242
+ };
243
+ }
244
+ /**
245
+ * Builds what an event middleware's `after()` receives. `handlers` is empty exactly when the chain
246
+ * stopped the event.
247
+ *
248
+ * @internal
249
+ */
250
+ function eventResultFor(stopped, handlers) {
251
+ if (!stopped) return {
252
+ outcome: "handled",
253
+ handlers
254
+ };
255
+ return {
256
+ ...resultFor(stopped.caught),
257
+ handlers: []
258
+ };
259
+ }
260
+ /**
261
+ * Calls `after()` on each middleware that ran, newest first. A throw inside one is logged. Every
262
+ * remaining middleware still gets its call.
263
+ *
264
+ * @internal
265
+ */
266
+ async function runAfter(ran, result, logger) {
267
+ for (let index = ran.length - 1; index >= 0; index--) {
268
+ const middleware = ran[index];
269
+ try {
270
+ await middleware?.after?.(result);
271
+ } catch (caught) {
272
+ logger.error(`${middleware?.constructor.name ?? "A middleware"} threw from after()`, asError(caught));
273
+ }
274
+ }
275
+ }
276
+
277
+ //#endregion
278
+ export { AckTrace, AutocompleteRouteBrand, CommandMetadataKey, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, EventMetadataKey, EventMiddlewareMetadataKey, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, MiddlewareKindsBrand, MiddlewareRegistry, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, areRoutes, asError, busLoggerOf, checkAckLegality, clearStore, contextMenuLeaves, contextMenuRouteOf, decodeComponentRoute, deferFlags, eventResultFor, getDevChannel, guardedAccessor, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, outcomeFor, pageCursor, pickNotice, queuedMsFor, reportDispatch, reportedWrite, resultFor, runAfter, runGates, runHandlerGates, sendFlags, sendTarget, serializeReply, setBotColor, setDevChannel, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, translateSerializationError, wrapHot };
174
279
  //# sourceMappingURL=internal.index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.index.mjs","names":["logger"],"sources":["../src/decorators/brands.ts","../src/gates/runGates.ts","../src/gates/slowGate.ts","../src/miscellaneous/emojiConfig.ts","../src/hmr/wrapHot.ts","../src/hmr/HmrManager.ts","../src/dispatch/dispatchReport.ts"],"sourcesContent":["// A handler base declares these so a route decorator can read what the handler is for. Nothing\n// assigns them at runtime.\n\nexport const SlashRouteBrand: unique symbol = Symbol('seedcord:brand:slash-route');\nexport const AutocompleteRouteBrand: unique symbol = Symbol('seedcord:brand:autocomplete-route');\nexport const ContextMenuKindBrand: unique symbol = Symbol('seedcord:brand:context-menu-kind');\nexport const ContextMenuNamesBrand: unique symbol = Symbol('seedcord:brand:context-menu-names');\nexport const ComponentKindBrand: unique symbol = Symbol('seedcord:brand:component-kind');\nexport const ComponentDefsBrand: unique symbol = Symbol('seedcord:brand:component-defs');\n","import 'reflect-metadata';\n\nimport { GatedMetadataKey, InteractionRouteKeys } from '#src/metadataKeys';\n\nimport { discardCommits, runCheck, runCommits } from './effects';\n\nimport type { Gate, GateContextBase } from './Gate';\n\n// a combinator (`and`/`or`) reports once under its joined name.\nexport type GateObserver = (gateName: string, elapsedMs: number) => void;\n\n// the stable id a route decorator stored, e.g. slash:daily or button:confirm. null for a plain event handler.\nexport function routeIdOf(handlerCtor: object): string | null {\n for (const [kind, key] of Object.entries(InteractionRouteKeys)) {\n // justified: getMetadata returns any, this key only ever stores the route/prefix string array\n const routes = Reflect.getMetadata(key, handlerCtor) as string[] | undefined;\n if (routes?.length) return `${kind}:${routes.join(',')}`;\n }\n return null;\n}\n\nasync function timedCheck(gate: Gate<GateContextBase>, ctx: GateContextBase, observe: GateObserver): Promise<void> {\n const start = performance.now();\n // finally so a refusing gate still reports\n try {\n await runCheck(gate, ctx);\n } finally {\n observe(gate.name, performance.now() - start);\n }\n}\n\n// the first refusal throws out to the dispatcher boundary. an effect gate's commit runs once the\n// whole set passes, and observe never times the commit phase.\nexport async function runGates(\n gates: readonly Gate<GateContextBase>[],\n ctx: GateContextBase,\n observe?: GateObserver\n): Promise<void> {\n try {\n for (const gate of gates) {\n await (observe ? timedCheck(gate, ctx, observe) : runCheck(gate, ctx));\n }\n await runCommits(ctx);\n } finally {\n discardCommits(ctx);\n }\n}\n\n// dispatchers call this before execute, inside the boundary, so a refusal renders or drops. an explicit\n// routeId is for manifest-driven handlers, which carry no route metadata to derive one from.\nexport async function runHandlerGates(\n handlerCtor: object,\n ctx: GateContextBase,\n routeId?: string,\n observe?: GateObserver\n): Promise<void> {\n // justified: getMetadata returns any, and this key only ever stores the @Gated gate array\n const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor) as readonly Gate<GateContextBase>[] | undefined;\n if (!gates) return;\n await runGates(gates, { ...ctx, routeId: routeId ?? routeIdOf(handlerCtor) }, observe);\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport type { GateObserver } from './runGates';\n\n// combined check time slower than this cuts into the 3s interaction-ack budget\nconst SLOW_GATE_MS = 750;\n\n// lazy because the logger reads the environment, which binds after this module loads\nlet gateLogger: Logger | undefined;\nfunction logger(): Logger {\n gateLogger ??= new Logger('Gates', { channel: 'gates' });\n return gateLogger;\n}\n\n/**\n * collects each gate check's elapsed time for one dispatch. `report` warns once when the checks sum\n * past the threshold, naming the total, the route, and each gate's share.\n */\nexport interface SlowGateMonitor {\n readonly observe: GateObserver;\n report(routeId: string | null): void;\n}\n\n// undefined in production, so timedCheck skips the clock there. read per call, the environment binds late\nexport function slowGateMonitor(): SlowGateMonitor | undefined {\n if (Envapter.isProduction) return undefined;\n const readings: { name: string; ms: number }[] = [];\n return {\n observe: (name, ms) => readings.push({ name, ms }),\n report(routeId) {\n const total = readings.reduce((sum, reading) => sum + reading.ms, 0);\n if (total < SLOW_GATE_MS) return;\n const shares = readings\n .toSorted((a, b) => b.ms - a.ms)\n .map((reading) => `${paint.sky.bold(reading.name)} ${paint.mute(`${Math.round(reading.ms)}ms`)}`);\n const route = routeId === null ? '' : ` for ${paint.sky.bold(routeId)}`;\n const headline = `gates${route} took ${paint.amber(`${Math.round(total)}ms`)} of the 3s ack budget`;\n logger().utils.block(headline, shares, 'warn', (text) => text);\n }\n };\n}\n","// narrows a configured emoji value to the [name, guildId] form\nexport function isEmojiTuple(value: unknown): value is readonly [string, string] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string';\n}\n","import type { DevChannel } from '@seedcord/types/internal';\n\n// the minimal raw-hot shape, satisfied by both vite's `import.meta.hot` and the CLI's `NormalizedHotChannel`.\ninterface RawHot {\n send(event: string, data?: unknown): void;\n on(event: string, cb: (data: unknown) => void): void;\n}\n\n/** @internal */\nexport function wrapHot<TSend, TRecv>(hot: RawHot): DevChannel<TSend, TRecv> {\n return {\n send: (event, data) => hot.send(event, data),\n // justified: RawHot types every payload as unknown, and the wire contract guarantees it matches TRecv[Key]\n on: (event, cb) => hot.on(event, cb as (data: unknown) => void)\n };\n}\n","// scoped here so import.meta.hot stays untyped in the rest of the package\n/// <reference types=\"vite/client\" />\nimport { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport { setDevChannel } from './devChannel';\nimport { wrapHot } from './wrapHot';\n\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { SeedcordCliEvents, SeedcordFrameworkEvents } from '@seedcord/types/internal';\n\nexport class HmrManager {\n private readonly logger = new Logger('HMR', { channel: 'hmr' });\n private readonly listeners = new Set<HmrAware>();\n\n /** @internal */\n public init(): void {\n if (!import.meta.hot) return;\n // the only raw `import.meta.hot` read in the framework\n const channel = wrapHot<SeedcordFrameworkEvents, SeedcordCliEvents>(import.meta.hot);\n setDevChannel(channel);\n\n if (Envapter.isDevelopment || Envapter.isTest) {\n this.logger.debug('Enabled');\n channel.on('seedcord:hmr', (payload) => void this.handleUpdate(payload));\n }\n }\n\n /** @internal */\n public register(listener: HmrAware): void {\n this.listeners.add(listener);\n }\n\n private async handleUpdate(event: HmrUpdateEvent): Promise<void> {\n const promises = [...this.listeners].map(async (listener) => {\n try {\n await listener.onHmr(event);\n } catch (error) {\n this.logger.error(`Error handling HMR update in ${paint.sky.bold(listener.constructor.name)}`, error);\n }\n });\n\n await Promise.all(promises);\n }\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { timestampFromSnowflake } from '@seedcord/utils';\n\nimport { Notice } from '#stops/Notice';\nimport { Silence } from '#stops/Silence';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { InteractionKind } from '#src/metadataKeys';\nimport type { Bus } from '#subscribers/Bus';\nimport type { DispatchOutcome } from '#subscribers/types/Subscriptions';\n\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatch', { channel: 'interactions' });\n return dispatchLogger;\n}\n\n/**\n * Classifies a caught stop for `interactionDispatched` so a gate and a\n * handler label the same stop identically.\n *\n * @internal\n */\nexport function outcomeFor(caught: unknown): DispatchOutcome {\n if (caught instanceof Silence) return 'refused';\n if (caught instanceof Notice) return caught.report ? 'failed' : 'refused';\n return 'failed';\n}\n\ninterface DispatchReport {\n readonly routeId: string;\n readonly interactionId: string;\n readonly kind: `${InteractionKind}`;\n readonly outcome: DispatchOutcome;\n /** True when no route matched and the unhandled default ran. */\n readonly fallback: boolean;\n /** `performance.now()` captured as the dispatch began. */\n readonly startedAt: number;\n /** {@link queuedMsFor} read at the same moment as `startedAt`. */\n readonly queuedMs: number;\n}\n\n/**\n * Discord's interaction creation to now, read from the snowflake. Called at dispatch entry, since\n * a later read would count the handler run into the queue time as well.\n *\n * @internal\n */\nexport function queuedMsFor(interactionId: string): number {\n // telemetry never breaks a dispatch, and BigInt() throws on a malformed id\n try {\n return Date.now() - timestampFromSnowflake(interactionId);\n } catch {\n return 0;\n }\n}\n\nexport function reportDispatch(bus: Bus, report: DispatchReport): void {\n const durationMs = performance.now() - report.startedAt;\n bus[PublishDefault]('interactionDispatched', {\n routeId: report.routeId,\n interactionId: report.interactionId,\n kind: report.kind,\n outcome: report.outcome,\n fallback: report.fallback,\n durationMs,\n queuedMs: report.queuedMs\n });\n\n // a throwing log sink must not stop the publish above\n logger().trace(\n `${paint.sky.bold(report.routeId)} ${report.outcome} ${paint.mute('in')} ${Math.round(durationMs)}ms`\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"internal.index.mjs","names":["logger"],"sources":["../src/decorators/brands.ts","../src/dispatch/MiddlewareRegistry.ts","../src/gates/runGates.ts","../src/gates/slowGate.ts","../src/miscellaneous/emojiConfig.ts","../src/hmr/wrapHot.ts","../src/hmr/HmrManager.ts","../src/dispatch/dispatchReport.ts","../src/dispatch/runAfter.ts"],"sourcesContent":["// phantom, nothing assigns these at runtime\n\nexport const SlashRouteBrand: unique symbol = Symbol('seedcord:brand:slash-route');\nexport const AutocompleteRouteBrand: unique symbol = Symbol('seedcord:brand:autocomplete-route');\nexport const ContextMenuKindBrand: unique symbol = Symbol('seedcord:brand:context-menu-kind');\nexport const ContextMenuNamesBrand: unique symbol = Symbol('seedcord:brand:context-menu-names');\nexport const ComponentKindBrand: unique symbol = Symbol('seedcord:brand:component-kind');\nexport const ComponentDefsBrand: unique symbol = Symbol('seedcord:brand:component-defs');\nexport const MiddlewareKindsBrand: unique symbol = Symbol('seedcord:brand:middleware-kinds');\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { interactionMiddlewareMetaOf } from '#decorators/middleware';\n\nimport type { AnyMiddlewareCtor } from '#decorators/middleware';\nimport type { MiddlewareKind } from '#src/metadataKeys';\n\n/**\n * A decorated middleware reduced to what the registry orders and filters on. An absent `keys` puts the\n * middleware in every chain.\n *\n * @internal\n */\nexport interface MiddlewareRegistration<Key extends string> {\n readonly priority: number;\n readonly keys?: readonly Key[];\n}\n\n/**\n * Reads a transport's decorator metadata off a class. Returns `undefined` for an undecorated one.\n *\n * @internal\n */\nexport type MiddlewareRegistrationOf<Ctor, Key extends string> = (\n ctor: Ctor\n) => MiddlewareRegistration<Key> | undefined;\n\n/** @internal */\nexport function interactionMiddleware(ctor: AnyMiddlewareCtor): MiddlewareRegistration<MiddlewareKind> | undefined {\n const metadata = interactionMiddlewareMetaOf(ctor);\n if (!metadata) return undefined;\n return { priority: metadata.priority, ...(metadata.kinds && { keys: metadata.kinds }) };\n}\n\ninterface Entry<Ctor, Key extends string> {\n readonly ctor: Ctor;\n readonly priority: number;\n readonly keys?: ReadonlySet<Key>;\n}\n\n/**\n * Holds the middleware a transport loaded and keeps one chain per key. An interaction keys on its kind\n * and an event on its name. Priority and the key filter behave the same on each.\n *\n * @typeParam Ctor - The transport's middleware constructor type.\n * @typeParam Key - The chain lookup key.\n *\n * @internal\n */\nexport class MiddlewareRegistry<Ctor extends AnyMiddlewareCtor, Key extends string = MiddlewareKind> {\n private readonly entries: Entry<Ctor, Key>[] = [];\n // an in-flight dispatch keeps walking the array chainFor gave it. never mutate one in place.\n private chains = new Map<Key, readonly Ctor[]>();\n\n public constructor(private readonly registrationOf: MiddlewareRegistrationOf<Ctor, Key>) {}\n\n /**\n * Adds a decorated middleware class and returns what it registered with. Returns `undefined` for an\n * undecorated class or one already registered.\n *\n * @throws A **SeedcordError** If another class with the same name is registered.\n */\n public register(ctor: Ctor): MiddlewareRegistration<Key> | undefined {\n const registration = this.registrationOf(ctor);\n if (!registration) return undefined;\n\n // a double import or an hmr re-scan registers the same class twice\n if (this.entries.some((entry) => entry.ctor === ctor)) return undefined;\n if (this.entries.some((entry) => entry.ctor.name === ctor.name)) {\n throw new SeedcordError(SeedcordErrorCode.DuplicateMiddleware, [ctor.name]);\n }\n\n this.entries.push({\n ctor,\n priority: registration.priority,\n ...(registration.keys && { keys: new Set(registration.keys) })\n });\n this.rebuild();\n return registration;\n }\n\n public unregister(ctor: Ctor): void {\n const index = this.entries.findIndex((entry) => entry.ctor === ctor);\n if (index === -1) return;\n this.entries.splice(index, 1);\n this.rebuild();\n }\n\n public chainFor(key: Key): readonly Ctor[] {\n const built = this.chains.get(key);\n if (built) return built;\n\n const chain = this.entries.filter((entry) => !entry.keys || entry.keys.has(key)).map((entry) => entry.ctor);\n this.chains.set(key, chain);\n return chain;\n }\n\n private rebuild(): void {\n // a stable sort keeps ties in registration order\n this.entries.sort((a, b) => a.priority - b.priority);\n this.chains = new Map();\n }\n}\n","import 'reflect-metadata';\n\nimport { GatedMetadataKey } from '#src/metadataKeys';\n\nimport { discardCommits, runCheck, runCommits } from './effects';\n\nimport type { Gate, GateContextBase } from './Gate';\n\n// a combinator (`and`/`or`) reports once under its joined name.\nexport type GateObserver = (gateName: string, elapsedMs: number) => void;\n\nasync function timedCheck(gate: Gate<GateContextBase>, ctx: GateContextBase, observe: GateObserver): Promise<void> {\n const start = performance.now();\n // finally so a refusing gate still reports\n try {\n await runCheck(gate, ctx);\n } finally {\n observe(gate.name, performance.now() - start);\n }\n}\n\n// the first refusal throws out to the dispatcher boundary. an effect gate's commit runs once the\n// whole set passes, and observe never times the commit phase.\nexport async function runGates(\n gates: readonly Gate<GateContextBase>[],\n ctx: GateContextBase,\n observe?: GateObserver\n): Promise<void> {\n try {\n for (const gate of gates) {\n await (observe ? timedCheck(gate, ctx, observe) : runCheck(gate, ctx));\n }\n await runCommits(ctx);\n } finally {\n discardCommits(ctx);\n }\n}\n\n// dispatchers call this before execute, inside the boundary, so a refusal renders or drops\nexport async function runHandlerGates(\n handlerCtor: object,\n ctx: GateContextBase,\n declaredRoute?: string,\n observe?: GateObserver\n): Promise<void> {\n // justified: getMetadata returns any, and this key only ever stores the @Gated gate array\n const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor) as readonly Gate<GateContextBase>[] | undefined;\n if (!gates) return;\n await runGates(gates, { ...ctx, declaredRoute: declaredRoute ?? null }, observe);\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport type { GateObserver } from './runGates';\n\n// combined check time slower than this cuts into the 3s interaction-ack budget\nconst SLOW_GATE_MS = 750;\n\n// lazy because the logger reads the environment, which binds after this module loads\nlet gateLogger: Logger | undefined;\nfunction logger(): Logger {\n gateLogger ??= new Logger('Gates', { channel: 'gates' });\n return gateLogger;\n}\n\n/**\n * collects each gate check's elapsed time for one dispatch. `report` warns once when the checks sum\n * past the threshold, naming the total, the route, and each gate's share.\n */\nexport interface SlowGateMonitor {\n readonly observe: GateObserver;\n report(declaredRoute: string | null): void;\n}\n\n// undefined in production, so timedCheck skips the clock there. read per call, the environment binds late\nexport function slowGateMonitor(): SlowGateMonitor | undefined {\n if (Envapter.isProduction) return undefined;\n const readings: { name: string; ms: number }[] = [];\n return {\n observe: (name, ms) => readings.push({ name, ms }),\n report(declaredRoute) {\n const total = readings.reduce((sum, reading) => sum + reading.ms, 0);\n if (total < SLOW_GATE_MS) return;\n const shares = readings\n .toSorted((a, b) => b.ms - a.ms)\n .map((reading) => `${paint.sky.bold(reading.name)} ${paint.mute(`${Math.round(reading.ms)}ms`)}`);\n const route = declaredRoute === null ? '' : ` for ${paint.sky.bold(declaredRoute)}`;\n const headline = `gates${route} took ${paint.amber(`${Math.round(total)}ms`)} of the 3s ack budget`;\n logger().utils.block(headline, shares, 'warn', (text) => text);\n }\n };\n}\n","// narrows a configured emoji value to the [name, guildId] form\nexport function isEmojiTuple(value: unknown): value is readonly [string, string] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string';\n}\n","import type { DevChannel } from '@seedcord/types/internal';\n\n// the minimal raw-hot shape, satisfied by both vite's `import.meta.hot` and the CLI's `NormalizedHotChannel`.\ninterface RawHot {\n send(event: string, data?: unknown): void;\n on(event: string, cb: (data: unknown) => void): void;\n}\n\n/** @internal */\nexport function wrapHot<TSend, TRecv>(hot: RawHot): DevChannel<TSend, TRecv> {\n return {\n send: (event, data) => hot.send(event, data),\n // justified: RawHot types every payload as unknown, and the wire contract guarantees it matches TRecv[Key]\n on: (event, cb) => hot.on(event, cb as (data: unknown) => void)\n };\n}\n","// scoped here so import.meta.hot stays untyped in the rest of the package\n/// <reference types=\"vite/client\" />\nimport { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport { setDevChannel } from './devChannel';\nimport { wrapHot } from './wrapHot';\n\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { SeedcordCliEvents, SeedcordFrameworkEvents } from '@seedcord/types/internal';\n\nexport class HmrManager {\n private readonly logger = new Logger('HMR', { channel: 'hmr' });\n private readonly listeners = new Set<HmrAware>();\n\n /** @internal */\n public init(): void {\n if (!import.meta.hot) return;\n // the only raw `import.meta.hot` read in the framework\n const channel = wrapHot<SeedcordFrameworkEvents, SeedcordCliEvents>(import.meta.hot);\n setDevChannel(channel);\n\n if (Envapter.isDevelopment || Envapter.isTest) {\n this.logger.debug('Enabled');\n channel.on('seedcord:hmr', (payload) => void this.handleUpdate(payload));\n }\n }\n\n /** @internal */\n public register(listener: HmrAware): void {\n this.listeners.add(listener);\n }\n\n private async handleUpdate(event: HmrUpdateEvent): Promise<void> {\n const promises = [...this.listeners].map(async (listener) => {\n try {\n await listener.onHmr(event);\n } catch (error) {\n this.logger.error(`Error handling HMR update in ${paint.sky.bold(listener.constructor.name)}`, error);\n }\n });\n\n await Promise.all(promises);\n }\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { timestampFromSnowflake } from '@seedcord/utils';\n\nimport { Notice } from '#stops/Notice';\nimport { Silence } from '#stops/Silence';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { InteractionKind } from '#src/metadataKeys';\nimport type { Bus } from '#subscribers/Bus';\nimport type { DispatchOutcome } from '#subscribers/types/Subscriptions';\n\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatch', { channel: 'interactions' });\n return dispatchLogger;\n}\n\n/**\n * Classifies a caught stop for `interactionDispatched` so a gate and a\n * handler label the same stop identically.\n *\n * @internal\n */\nexport function outcomeFor(caught: unknown): Exclude<DispatchOutcome, 'handled'> {\n if (caught instanceof Silence) return 'refused';\n if (caught instanceof Notice) return caught.report ? 'failed' : 'refused';\n return 'failed';\n}\n\ninterface DispatchReport {\n readonly dispatchId: string;\n readonly routeId: string;\n readonly interactionId: string;\n readonly kind: `${InteractionKind}`;\n readonly outcome: DispatchOutcome;\n /** True when no route matched and the unhandled default ran. */\n readonly fallback: boolean;\n readonly userId: string | null;\n readonly guildId: string | null;\n /** `performance.now()` captured as the dispatch began. */\n readonly startedAt: number;\n /** {@link queuedMsFor} read at the same moment as `startedAt`. */\n readonly queuedMs: number;\n}\n\n/**\n * Discord's interaction creation to now, read from the snowflake. Called at dispatch entry, since\n * a later read would count the handler run into the queue time as well.\n *\n * @internal\n */\nexport function queuedMsFor(interactionId: string): number {\n // timestampFromSnowflake throws on a malformed id\n try {\n return Date.now() - timestampFromSnowflake(interactionId);\n } catch {\n return 0;\n }\n}\n\nexport function reportDispatch(bus: Bus, report: DispatchReport): void {\n const durationMs = performance.now() - report.startedAt;\n bus[PublishDefault]('interactionDispatched', {\n dispatchId: report.dispatchId,\n routeId: report.routeId,\n interactionId: report.interactionId,\n kind: report.kind,\n outcome: report.outcome,\n fallback: report.fallback,\n userId: report.userId,\n guildId: report.guildId,\n durationMs,\n queuedMs: report.queuedMs\n });\n\n // a throwing log sink must not stop the publish above\n logger().trace(\n `${paint.sky.bold(report.routeId)} ${report.outcome} ${paint.mute('in')} ${Math.round(durationMs)}ms`\n );\n}\n","import { asError } from '#stops/asError';\n\nimport { outcomeFor } from './dispatchReport';\n\nimport type { DispatchResult, EventDispatchResult, HandlerResult } from './dispatchResult';\nimport type { Logger } from '@seedcord/logger';\n\ninterface Afterable<Result> {\n after?(result: Result): Promise<void>;\n}\n\n/** @internal */\nexport function resultFor(caught: unknown): DispatchResult {\n return { outcome: outcomeFor(caught), caught };\n}\n\n/**\n * Builds what an event middleware's `after()` receives. `handlers` is empty exactly when the chain\n * stopped the event.\n *\n * @internal\n */\nexport function eventResultFor(\n stopped: { caught: unknown } | null,\n handlers: readonly HandlerResult[]\n): EventDispatchResult {\n if (!stopped) return { outcome: 'handled', handlers };\n return { ...resultFor(stopped.caught), handlers: [] };\n}\n\n/**\n * Calls `after()` on each middleware that ran, newest first. A throw inside one is logged. Every\n * remaining middleware still gets its call.\n *\n * @internal\n */\nexport async function runAfter<Result>(\n ran: readonly Afterable<Result>[],\n result: Result,\n logger: Logger\n): Promise<void> {\n for (let index = ran.length - 1; index >= 0; index--) {\n const middleware = ran[index];\n try {\n await middleware?.after?.(result);\n } catch (caught) {\n logger.error(`${middleware?.constructor.name ?? 'A middleware'} threw from after()`, asError(caught));\n }\n }\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import { i as Bus, n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-D0a8BV9Z.mjs";
1
+ import { i as Bus, n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-DJf12l5T.mjs";
2
2
  import { n as CommandBuilder, r as DeployResult } from "./basic-Dng__Ena.mjs";
3
- import "./index-CD8l15aZ.mjs";
4
- import { n as Initializeable } from "./Plugin-Bnz61fs_.mjs";
3
+ import "./index-BS1hH9Sh.mjs";
4
+ import { n as Initializeable } from "./Plugin-BdqfRjU0.mjs";
5
5
  import { t as ContextMenuLeaves } from "./contextMenuLeaves-COzDYa6c.mjs";
6
- import { a as LifecycleTask, i as CoordinatedShutdown, n as shutdownOf } from "./Pluggable-C5uxmDb-.mjs";
6
+ import { a as LifecycleTask, i as CoordinatedShutdown, n as shutdownOf } from "./Pluggable-CqKvMXm5.mjs";
7
7
  import { Logger } from "@seedcord/logger";
8
8
  import { IncomingMessage, ServerResponse } from "node:http";
9
9
  import { HealthCheckConfig, HealthCheckOption, HmrAware, HmrUpdateEvent } from "@seedcord/types";
@@ -1,11 +1,11 @@
1
- import { C as SubscribeMetadataKey, O as contextMenuLeaves, a as RegisterSubscriber, c as VerifyWebhooks, f as Subscriber, g as CommandMetadataKey, i as RegisterDefaults, n as busLoggerOf, o as RegisteredCount, r as registrationFor, s as UnregisterSubscriber } from "./Bus-dGy6xq3F.mjs";
2
- import { t as CommandInjector } from "./subscribers-A8lNU5yM.mjs";
1
+ import { a as RegisterSubscriber, c as VerifyWebhooks, f as Subscriber, g as CommandMetadataKey, i as RegisterDefaults, k as contextMenuLeaves, n as busLoggerOf, o as RegisteredCount, r as registrationFor, s as UnregisterSubscriber, w as SubscribeMetadataKey } from "./Bus-Djm6688b.mjs";
2
+ import { t as CommandInjector } from "./subscribers-Bf4cBTvK.mjs";
3
3
  import { t as PublishDefault } from "./publishDefault-BuSZPbjT.mjs";
4
4
  import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
5
5
  import { t as getDevChannel } from "./devChannel-CQU7hq7Q.mjs";
6
- import { n as isCommandClass, t as slashRouteLeaves } from "./slashRouteLeaves-C1msUE9Q.mjs";
6
+ import { n as isCommandClass, t as slashRouteLeaves } from "./slashRouteLeaves-DTFHzCKN.mjs";
7
7
  import { t as HmrModuleHandler } from "./HmrModuleHandler-9AUfKXRl.mjs";
8
- import { i as settleWithin, n as shutdownOf, r as assertNodeVersion } from "./Pluggable-B7_EftHj.mjs";
8
+ import { i as settleWithin, n as shutdownOf, r as assertNodeVersion } from "./Pluggable-keys1cVr.mjs";
9
9
  import { SlashCommandBuilder } from "@discordjs/builders";
10
10
  import { Routes } from "discord-api-types/v10";
11
11
  import { SeedcordErrorCode, paint } from "@seedcord/errors";
@@ -1,2 +1,2 @@
1
- import { i as CoordinatedShutdown, r as CoordinatedStartup, t as Pluggable } from "./Pluggable-C5uxmDb-.mjs";
1
+ import { i as CoordinatedShutdown, r as CoordinatedStartup, t as Pluggable } from "./Pluggable-CqKvMXm5.mjs";
2
2
  export { CoordinatedShutdown, CoordinatedStartup, Pluggable };
@@ -1,5 +1,5 @@
1
1
  import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
2
- import { a as withTimeout, i as settleWithin, t as Pluggable } from "./Pluggable-B7_EftHj.mjs";
2
+ import { a as withTimeout, i as settleWithin, t as Pluggable } from "./Pluggable-keys1cVr.mjs";
3
3
  import { SeedcordErrorCode, paint } from "@seedcord/errors";
4
4
  import { SeedcordAggregateError, SeedcordError, SeedcordRangeError, SeedcordTypeError } from "@seedcord/errors/internal";
5
5
  import { Logger } from "@seedcord/logger";
@@ -2,8 +2,8 @@ import { APIApplicationCommand } from "discord-api-types/v10";
2
2
  import { Logger } from "@seedcord/logger";
3
3
  import { TypedEventEmitter } from "@seedcord/event-emitter";
4
4
  import { REST } from "@discordjs/rest";
5
- import { UUID } from "node:crypto";
6
- import { Config, EventFrequency, IRateLimiter, RenderContext, ReplyResponse, TypedExclude } from "@seedcord/types";
5
+ import { UUID } from "@seedcord/types/internal";
6
+ import { Config, DispatchBag, DispatchState, EventFrequency, IRateLimiter, RenderContext, ReplyResponse, TypedExclude } from "@seedcord/types";
7
7
  //#region src/stops/Notice.d.ts
8
8
  /**
9
9
  * Base class for a user-facing refusal or a reported fault.
@@ -88,15 +88,41 @@ declare enum InteractionKind {
88
88
  UserContextMenu = "userContextMenu",
89
89
  Autocomplete = "autocomplete"
90
90
  }
91
+ /**
92
+ * The interaction kinds a middleware can filter on. Autocomplete carries no reply target.
93
+ */
94
+ type MiddlewareKind = TypedExclude<InteractionKind, InteractionKind.Autocomplete>;
91
95
  declare const CommandMetadataKey: unique symbol;
92
96
  declare const InteractionMetadataKey: unique symbol;
93
- declare const MiddlewareMetadataKey: unique symbol;
97
+ declare const InteractionMiddlewareMetadataKey: unique symbol;
98
+ declare const EventMiddlewareMetadataKey: unique symbol;
94
99
  declare const SubscribeMetadataKey: unique symbol;
95
100
  declare const EventMetadataKey: unique symbol;
96
101
  declare const GatedMetadataKey: unique symbol;
97
102
  declare const WebhookUrlMetadataKey: unique symbol;
98
103
  declare const InteractionRouteKeys: Record<InteractionKind, symbol>;
99
104
  //#endregion
105
+ //#region src/dispatch/DispatchContext.d.ts
106
+ /**
107
+ * A typed bag allocated once per interaction dispatch and handed to the handler. The middleware and i18n
108
+ * work reads and writes it through {@link DispatchState}. Today the bag is empty, so it only carries the
109
+ * dispatched `routeId`.
110
+ */
111
+ declare class DispatchContext implements DispatchBag {
112
+ readonly routeId: string;
113
+ private readonly state;
114
+ readonly id: `${string}-${string}-${string}-${string}-${string}`;
115
+ constructor(routeId: string);
116
+ set<Key extends keyof DispatchState>(key: Key, value: DispatchState[Key]): void;
117
+ get<Key extends keyof DispatchState>(key: Key): DispatchState[Key] | undefined;
118
+ /**
119
+ * Read a key that must be present, with `undefined` stripped from the result.
120
+ *
121
+ * @throws A **SeedcordError** If nothing set the key on this dispatch.
122
+ */
123
+ require<Key extends keyof DispatchState>(key: Key): Exclude<DispatchState[Key], undefined>;
124
+ }
125
+ //#endregion
100
126
  //#region src/subscribers/publishDefault.d.ts
101
127
  /** @internal PublishableKey excludes this key so publish() never emits it externally. */
102
128
  declare const PublishDefault: unique symbol;
@@ -122,10 +148,11 @@ declare function sendTarget(state: AckState): 'reply' | 'edit' | 'followUp';
122
148
  //#region src/reply/responseReport.d.ts
123
149
  interface ReplyTelemetry {
124
150
  readonly bus: Bus;
151
+ readonly dispatch: DispatchContext;
125
152
  readonly interactionId: string;
126
153
  }
127
154
  type WriteMethod = ReplyMethod | 'respond';
128
- declare function reportedWrite<Result>(telemetry: ReplyTelemetry, routeId: string, method: WriteMethod, write: () => Promise<Result>): Promise<Result>;
155
+ declare function reportedWrite<Result>(telemetry: ReplyTelemetry, method: WriteMethod, write: () => Promise<Result>): Promise<Result>;
129
156
  //#endregion
130
157
  //#region src/subscribers/types/Subscriptions.d.ts
131
158
  /**
@@ -176,6 +203,8 @@ interface EventFaultSource {
176
203
  * The fields both `responseAttempted` arms carry.
177
204
  */
178
205
  interface AttemptedWrite {
206
+ /** The dispatch this write belongs to, for joining against every other key it published. */
207
+ readonly dispatchId: string;
179
208
  readonly routeId: string;
180
209
  /** The interaction this write belongs to, for joining against `interactionDispatched`. */
181
210
  readonly interactionId: string;
@@ -216,9 +245,11 @@ interface DefaultSubscriptions {
216
245
  /** Triggered when an unhandled exception (a raw non-Notice throw) occurs. */
217
246
  readonly unknownException: {
218
247
  readonly uuid: UUID;
248
+ /** The dispatch that raised it. Null on a process-level throw, which has no dispatch behind it. */
249
+ readonly dispatchId: string | null;
219
250
  readonly error: Error;
220
251
  /** Where the throw came from, `slash:ban` for an interaction and `event:name:handler` for an event. */
221
- readonly routeId: string;
252
+ readonly origin: string;
222
253
  readonly guild?: {
223
254
  readonly id: string;
224
255
  readonly name: string;
@@ -233,8 +264,10 @@ interface DefaultSubscriptions {
233
264
  readonly handledException: {
234
265
  readonly denial: Notice;
235
266
  readonly uuid: UUID;
236
- /** The same id `interactionDispatched` publishes. An event reads `event:name:handler`. */
237
- readonly routeId: string;
267
+ /** The dispatch that reported it. */
268
+ readonly dispatchId: string;
269
+ /** Where the reported Notice came from, the same shape `unknownException.origin` carries. */
270
+ readonly origin: string;
238
271
  readonly source: FaultSource;
239
272
  };
240
273
  /** Triggered when an interaction dispatch throws past the fault boundary. */
@@ -243,6 +276,8 @@ interface DefaultSubscriptions {
243
276
  };
244
277
  /** Triggered once per interaction dispatch, after the handler chain settles. */
245
278
  readonly interactionDispatched: {
279
+ /** This dispatch, for joining against every other key it published. */
280
+ readonly dispatchId: string;
246
281
  readonly routeId: string;
247
282
  /** The interaction this dispatch ran, for joining against `responseAttempted`. */
248
283
  readonly interactionId: string;
@@ -250,6 +285,10 @@ interface DefaultSubscriptions {
250
285
  readonly outcome: DispatchOutcome;
251
286
  /** True when no route matched and the unhandled default ran. */
252
287
  readonly fallback: boolean;
288
+ /** Who ran the route. Null when the payload carried no user. */
289
+ readonly userId: string | null;
290
+ /** Guild the interaction came from, null in DMs. */
291
+ readonly guildId: string | null;
253
292
  /** Dispatch entry until the user has a response, replies included. A clock change never affects it. */
254
293
  readonly durationMs: number;
255
294
  /**
@@ -459,5 +498,5 @@ declare enum ShutdownPhase {
459
498
  Logout = 4
460
499
  }
461
500
  //#endregion
462
- export { UnregisterSubscriber as A, SubscribeMetadataKey as B, ReplyMethod as C, RegisterDefaults as D, AckTrace as E, GatedMetadataKey as F, Notice as H, InteractionKind as I, InteractionMetadataKey as L, PublishDefault as M, CommandMetadataKey as N, RegisterSubscriber as O, EventMetadataKey as P, InteractionRouteKeys as R, AckState as S, sendTarget as T, WebhookUrlMetadataKey as V, SubscriptionData as _, StoredSubscriberCtor as a, ReplyTelemetry as b, Subscriber as c, DispatchOutcome as d, FaultSource as f, ResponseSent as g, ResponseOutcome as h, Bus as i, VerifyWebhooks as j, RegisteredCount as k, AttemptedWrite as l, ResponseFailed as m, StartupPhase as n, SubscriberRegistration as o, ResponseAttempt as p, CoreBase as r, busLoggerOf as s, ShutdownPhase as t, DefaultSubscriptions as u, SubscriptionKey as v, checkAckLegality as w, reportedWrite as x, Subscriptions as y, MiddlewareMetadataKey as z };
463
- //# sourceMappingURL=phases-D0a8BV9Z.d.mts.map
501
+ export { UnregisterSubscriber as A, InteractionMiddlewareMetadataKey as B, ReplyMethod as C, RegisterDefaults as D, AckTrace as E, EventMetadataKey as F, Notice as G, MiddlewareKind as H, EventMiddlewareMetadataKey as I, GatedMetadataKey as L, PublishDefault as M, DispatchContext as N, RegisterSubscriber as O, CommandMetadataKey as P, InteractionKind as R, AckState as S, sendTarget as T, SubscribeMetadataKey as U, InteractionRouteKeys as V, WebhookUrlMetadataKey as W, SubscriptionData as _, StoredSubscriberCtor as a, ReplyTelemetry as b, Subscriber as c, DispatchOutcome as d, FaultSource as f, ResponseSent as g, ResponseOutcome as h, Bus as i, VerifyWebhooks as j, RegisteredCount as k, AttemptedWrite as l, ResponseFailed as m, StartupPhase as n, SubscriberRegistration as o, ResponseAttempt as p, CoreBase as r, busLoggerOf as s, ShutdownPhase as t, DefaultSubscriptions as u, SubscriptionKey as v, checkAckLegality as w, reportedWrite as x, Subscriptions as y, InteractionMetadataKey as z };
502
+ //# sourceMappingURL=phases-DJf12l5T.d.mts.map
@@ -1,3 +1,3 @@
1
- import { c as PluginOptions, p as PluginLifecycleSpec, r as Plugin } from "./Plugin-Bnz61fs_.mjs";
1
+ import { c as PluginOptions, p as PluginLifecycleSpec, r as Plugin } from "./Plugin-BdqfRjU0.mjs";
2
2
  import { HmrAware, HmrUpdateEvent } from "@seedcord/types";
3
3
  export { type HmrAware, type HmrUpdateEvent, Plugin, type PluginLifecycleSpec, type PluginOptions };
@@ -1,4 +1,4 @@
1
- import { g as CommandMetadataKey, j as isBuilderComponentClass } from "./Bus-dGy6xq3F.mjs";
1
+ import { M as isBuilderComponentClass, g as CommandMetadataKey } from "./Bus-Djm6688b.mjs";
2
2
  import { SlashCommandBuilder } from "@discordjs/builders";
3
3
  import { routeLeavesOf } from "@seedcord/utils/internal";
4
4
 
@@ -28,4 +28,4 @@ function slashRouteLeaves(commands) {
28
28
 
29
29
  //#endregion
30
30
  export { isCommandClass as n, slashRouteLeaves as t };
31
- //# sourceMappingURL=slashRouteLeaves-C1msUE9Q.mjs.map
31
+ //# sourceMappingURL=slashRouteLeaves-DTFHzCKN.mjs.map