@seedcord/core 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Bus-dGy6xq3F.mjs → Bus-Djm6688b.mjs} +6 -5
- package/dist/Bus-Djm6688b.mjs.map +1 -0
- package/dist/{Pluggable-B7_EftHj.mjs → Pluggable-BShEwGw9.mjs} +40 -7
- package/dist/Pluggable-BShEwGw9.mjs.map +1 -0
- package/dist/{Pluggable-C5uxmDb-.d.mts → Pluggable-CAteNmfy.d.mts} +11 -5
- package/dist/Plugin-BJfRk0s_.mjs.map +1 -1
- package/dist/{Plugin-Bnz61fs_.d.mts → Plugin-D3wIj0xX.d.mts} +8 -5
- package/dist/{cursor-8Y2zpF-Y.d.mts → cursor-BPaPFTic.d.mts} +186 -14
- package/dist/{cursor-BkI9lxy2.mjs → cursor-CFNKLVlk.mjs} +49 -11
- package/dist/cursor-CFNKLVlk.mjs.map +1 -0
- package/dist/{index-CD8l15aZ.d.mts → index-BS1hH9Sh.d.mts} +2 -2
- package/dist/index.d.mts +16 -35
- package/dist/index.mjs +29 -37
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +79 -8
- package/dist/internal.index.mjs +121 -16
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.d.mts +6 -4
- package/dist/node-internal.index.mjs +38 -14
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.d.mts +1 -1
- package/dist/node.index.mjs +1 -1
- package/dist/{phases-D0a8BV9Z.d.mts → phases-DJf12l5T.d.mts} +48 -9
- package/dist/plugin.index.d.mts +1 -1
- package/dist/{slashRouteLeaves-C1msUE9Q.mjs → slashRouteLeaves-DTFHzCKN.mjs} +2 -2
- package/dist/{slashRouteLeaves-C1msUE9Q.mjs.map → slashRouteLeaves-DTFHzCKN.mjs.map} +1 -1
- package/dist/{subscribers-A8lNU5yM.mjs → subscribers-kAQQt_YP.mjs} +32 -4
- package/dist/subscribers-kAQQt_YP.mjs.map +1 -0
- package/package.json +8 -8
- package/dist/Bus-dGy6xq3F.mjs.map +0 -1
- package/dist/Pluggable-B7_EftHj.mjs.map +0 -1
- package/dist/cursor-BkI9lxy2.mjs.map +0 -1
- package/dist/subscribers-A8lNU5yM.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { A as UnregisterSubscriber, B as
|
|
2
|
-
import { A as serializeReply, B as
|
|
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, Q as BuilderComponent, U as ContextMenuKindBrand, V as ComponentDefsBrand, W as ContextMenuNamesBrand, X as Promisable, Z as IsNever, _ as pickNotice, at as SlashOption, c as FocusedField, ct as ContextMenuKind, dt as NamesFor, i as AutocompletableNames, it as RouteCache, j as DispatchResult, k as SerializedReply, lt as MenuCacheFor, m as PermissionNames, n as PageCursor, nt as CacheFor, o as ChoiceValueOf, ot as SlashRegistry, q as CommandMeta, r as pageCursor, rt as OptionKind, s as EntryFor, st as SlashRouteEntry, t as PAGE_MAX, v as GateFitsWith, w as GateContextBase, z as interactionMiddlewareMetaOf } from "./cursor-BPaPFTic.mjs";
|
|
3
3
|
import { n as CommandBuilder, r as DeployResult } from "./basic-Dng__Ena.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { i as Initializeable, o as PluginArgs, s as PluginCtor } from "./Plugin-D3wIj0xX.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,
|
|
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(
|
|
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
|
-
|
|
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
|
package/dist/internal.index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { A as translateSerializationError, B as
|
|
2
|
-
import { C as
|
|
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-
|
|
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/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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${
|
|
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
|
-
|
|
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-
|
|
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-
|
|
4
|
-
import {
|
|
3
|
+
import "./index-BS1hH9Sh.mjs";
|
|
4
|
+
import { i as Initializeable } from "./Plugin-D3wIj0xX.mjs";
|
|
5
5
|
import { t as ContextMenuLeaves } from "./contextMenuLeaves-COzDYa6c.mjs";
|
|
6
|
-
import { a as LifecycleTask, i as CoordinatedShutdown, n as shutdownOf } from "./Pluggable-
|
|
6
|
+
import { a as LifecycleTask, i as CoordinatedShutdown, n as shutdownOf } from "./Pluggable-CAteNmfy.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";
|
|
@@ -75,6 +75,7 @@ declare class CommandRegistry implements Initializeable, HmrAware {
|
|
|
75
75
|
private readonly pendingEvents;
|
|
76
76
|
private readonly injector;
|
|
77
77
|
private readonly dir;
|
|
78
|
+
private readonly configuredGuilds;
|
|
78
79
|
constructor(core: CoreBase);
|
|
79
80
|
private getArtifacts;
|
|
80
81
|
/** @internal */
|
|
@@ -89,6 +90,7 @@ declare class CommandRegistry implements Initializeable, HmrAware {
|
|
|
89
90
|
private unregisterCommand;
|
|
90
91
|
/** @internal */
|
|
91
92
|
setCommands(): Promise<DeployResult>;
|
|
93
|
+
private putGuild;
|
|
92
94
|
private put;
|
|
93
95
|
/** Every registered builder, global first. A guild command in N guilds appears once per guild. @internal */
|
|
94
96
|
allCommands(): CommandBuilder[];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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 { r as CommandInjector, t as ConfigGuilds } from "./subscribers-kAQQt_YP.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-
|
|
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-
|
|
8
|
+
import { i as settleWithin, n as shutdownOf, r as assertNodeVersion } from "./Pluggable-BShEwGw9.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";
|
|
@@ -180,6 +180,21 @@ var SubscriberLoader = class {
|
|
|
180
180
|
}
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/commands/deployTarget.ts
|
|
185
|
+
function resolveDeployTarget(meta, configured, commandName) {
|
|
186
|
+
if (meta.scope === "global") return { scope: "global" };
|
|
187
|
+
if (meta.scope === "config") return configured.length > 0 ? guildTarget(configured) : { scope: "global" };
|
|
188
|
+
if (configured.length === 0 && meta.guilds.includes("config")) throw new SeedcordError(SeedcordErrorCode.CoreCommandGuildsEmpty, [commandName]);
|
|
189
|
+
return guildTarget(meta.guilds.flatMap((target) => target === "config" ? configured : target));
|
|
190
|
+
}
|
|
191
|
+
function guildTarget(guilds) {
|
|
192
|
+
return {
|
|
193
|
+
scope: "guild",
|
|
194
|
+
guilds: [...new Set(guilds)]
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
183
198
|
//#endregion
|
|
184
199
|
//#region src/node/commands/CommandRegistry.ts
|
|
185
200
|
function indexById(commands) {
|
|
@@ -203,14 +218,16 @@ var CommandRegistry = class {
|
|
|
203
218
|
pendingEvents = /* @__PURE__ */ new Map();
|
|
204
219
|
injector = new CommandInjector();
|
|
205
220
|
dir;
|
|
221
|
+
configuredGuilds;
|
|
206
222
|
constructor(core) {
|
|
207
223
|
this.core = core;
|
|
208
|
-
const
|
|
209
|
-
if (!
|
|
210
|
-
this.dir =
|
|
224
|
+
const { commands } = core.config.bot;
|
|
225
|
+
if (!commands.path) throw new SeedcordError(SeedcordErrorCode.CoreControllerPathMissing, ["CommandRegistry", "commands"]);
|
|
226
|
+
this.dir = commands.path;
|
|
227
|
+
this.configuredGuilds = commands.guilds ?? [];
|
|
211
228
|
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
212
229
|
this.hmrHandler = new HmrModuleHandler({
|
|
213
|
-
handlersDir: dir,
|
|
230
|
+
handlersDir: this.dir,
|
|
214
231
|
isHandler: isCommandClass,
|
|
215
232
|
registerHandler: this.registerCommand.bind(this),
|
|
216
233
|
unregisterHandler: this.unregisterCommand.bind(this),
|
|
@@ -285,16 +302,16 @@ var CommandRegistry = class {
|
|
|
285
302
|
if (!meta) return;
|
|
286
303
|
const comp = new Ctor().component;
|
|
287
304
|
const kind = comp instanceof SlashCommandBuilder ? "slash command" : "context menu";
|
|
288
|
-
|
|
289
|
-
|
|
305
|
+
const target = resolveDeployTarget(meta, this.configuredGuilds, Ctor.name);
|
|
306
|
+
if (target.scope === "global") this.globalCommands.push(comp);
|
|
307
|
+
else for (const g of target.guilds) {
|
|
290
308
|
const arr = this.guildCommands.get(g) ?? [];
|
|
291
309
|
arr.push(comp);
|
|
292
310
|
this.guildCommands.set(g, arr);
|
|
293
311
|
}
|
|
294
312
|
this.ctorToCommand.set(Ctor, {
|
|
295
313
|
name: comp.name,
|
|
296
|
-
|
|
297
|
-
...meta.scope === "guild" && { guilds: meta.guilds }
|
|
314
|
+
...target
|
|
298
315
|
});
|
|
299
316
|
if (this.loading) this.loadedCommands.push({
|
|
300
317
|
name: comp.name,
|
|
@@ -308,7 +325,7 @@ var CommandRegistry = class {
|
|
|
308
325
|
if (info.scope === "global") {
|
|
309
326
|
const idx = this.globalCommands.findIndex((c) => c.name === info.name);
|
|
310
327
|
if (idx !== -1) this.globalCommands.splice(idx, 1);
|
|
311
|
-
} else for (const g of info.guilds
|
|
328
|
+
} else for (const g of info.guilds) {
|
|
312
329
|
const arr = this.guildCommands.get(g);
|
|
313
330
|
if (arr) {
|
|
314
331
|
const idx = arr.findIndex((c) => c.name === info.name);
|
|
@@ -330,7 +347,7 @@ var CommandRegistry = class {
|
|
|
330
347
|
this.logger.utils.block(`Deployed ${this.globalCommands.length} global ${tag}`, this.logger.utils.wrap(this.globalCommands.map((command) => command.name)), "info");
|
|
331
348
|
}
|
|
332
349
|
for (const [guildId, commands] of this.guildCommands.entries()) {
|
|
333
|
-
const deployed = await this.
|
|
350
|
+
const deployed = await this.putGuild(appId, guildId, commands);
|
|
334
351
|
result.guilds.set(guildId, indexById(deployed));
|
|
335
352
|
const tag = commands.length === 1 ? "command" : "commands";
|
|
336
353
|
this.logger.utils.block(`Deployed ${paint.iris.bold(commands.length)} ${tag} to ${paint.sky.bold(guildId)}`, this.logger.utils.wrap(commands.map((command) => command.name)), "info");
|
|
@@ -339,6 +356,13 @@ var CommandRegistry = class {
|
|
|
339
356
|
this.core.bus[PublishDefault]("commandsDeployed", deployedPayload(result));
|
|
340
357
|
return result;
|
|
341
358
|
}
|
|
359
|
+
async putGuild(appId, guildId, commands) {
|
|
360
|
+
try {
|
|
361
|
+
return await this.put(Routes.applicationGuildCommands(appId, guildId), commands);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new SeedcordError(SeedcordErrorCode.CoreCommandGuildDeployFailed, [guildId], { cause: error });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
342
366
|
async put(route, commands) {
|
|
343
367
|
const body = commands.map((command) => command.toJSON());
|
|
344
368
|
const { rest } = this.core;
|