@seedcord/http 0.7.0 → 0.8.1

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.
@@ -1,4 +1,4 @@
1
- import { A as InjectedEmojiMap, C as SlashHandler, D as ReplySender, E as InteractionRef, F as EngineContext, I as Core, L as HttpConfig, M as SelectedMentionables, N as SlashOptions, O as SentMessage, P as createSeedcord, R as HttpEdgeConfig, S as UserContextMenuHandler, T as RepliableHandler, _ as ModalHandler, a as CursorSource, b as AutocompleteHandler, c as PageContext, d as ChannelMenuHandler, f as MentionableMenuHandler, g as UserMenuHandler, h as StringMenuHandler, i as ArraySource, j as ModalFields, k as Emojis, l as Subscriber, m as SelectMenuHandler, n as Gated, o as PageSource, p as RoleMenuHandler, r as InteractionGateContext, s as Paginator, t as version, u as WebhookLog, v as ButtonHandler, w as InteractionHandler, x as MessageContextMenuHandler, y as ComponentHandler, z as HttpServerConfig } from "./edge.index-LAWTa6NI.mjs";
1
+ import { A as ComponentHandler, B as HttpServerConfig, C as InteractionMiddleware, D as SelectMenuHandler, E as RoleMenuHandler, F as SentMessage, I as AutocompleteHandler, L as Core, M as RepliableHandler, N as InteractionRef, O as StringMenuHandler, P as ReplySender, R as HttpConfig, S as EngineContext, T as MentionableMenuHandler, _ as InjectedEmojiMap, a as CursorSource, b as SlashOptions, c as PageContext, d as ModalHandler, f as ButtonHandler, g as Emojis, h as SlashHandler, i as ArraySource, j as InteractionHandler, k as UserMenuHandler, l as Subscriber, m as UserContextMenuHandler, n as Gated, o as PageSource, p as MessageContextMenuHandler, r as InteractionGateContext, s as Paginator, t as version, u as WebhookLog, v as ModalFields, w as ChannelMenuHandler, x as createSeedcord, y as SelectedMentionables, z as HttpEdgeConfig } from "./edge.index-CgLU9PiF.mjs";
2
2
  import { t as RouteManifest } from "./RouteManifest-D7qXj8Ts.mjs";
3
3
  export * from "@seedcord/logger";
4
4
  export * from "@seedcord/core";
@@ -7,4 +7,4 @@ export * from "@seedcord/rate-limiter";
7
7
  export * from "@seedcord/event-emitter";
8
8
  export * from "@seedcord/utils";
9
9
  export type * from "@seedcord/types";
10
- export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, type HttpConfig, type HttpEdgeConfig, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, type InteractionRef, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, type PageContext, type PageSource, Paginator, RepliableHandler, ReplySender, RoleMenuHandler, type RouteManifest, SelectMenuHandler, type SelectedMentionables, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, createSeedcord, version };
10
+ export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, type HttpConfig, type HttpEdgeConfig, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, type InteractionRef, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, type PageContext, type PageSource, Paginator, RepliableHandler, ReplySender, RoleMenuHandler, type RouteManifest, SelectMenuHandler, type SelectedMentionables, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, createSeedcord, version };
@@ -1,4 +1,4 @@
1
- import { C as createSeedcord, D as AutocompleteHandler, E as InteractionHandler, O as RepliableHandler, S as ModalFields, _ as MessageContextMenuHandler, a as Paginator, c as ChannelMenuHandler, d as SelectMenuHandler, f as StringMenuHandler, g as ComponentHandler, h as ButtonHandler, i as CursorSource, k as ReplySender, l as MentionableMenuHandler, m as ModalHandler, n as Gated, o as Subscriber, p as UserMenuHandler, r as ArraySource, s as WebhookLog, t as version, u as RoleMenuHandler, v as UserContextMenuHandler, x as Emojis, y as SlashHandler } from "./edge.index-CWUU_d_X.mjs";
1
+ import { A as ReplySender, C as ModalFields, D as InteractionHandler, O as AutocompleteHandler, S as Emojis, _ as MessageContextMenuHandler, a as Paginator, b as InteractionMiddleware, c as ChannelMenuHandler, d as SelectMenuHandler, f as StringMenuHandler, g as ComponentHandler, h as ButtonHandler, i as CursorSource, k as RepliableHandler, l as MentionableMenuHandler, m as ModalHandler, n as Gated, o as Subscriber, p as UserMenuHandler, r as ArraySource, s as WebhookLog, t as version, u as RoleMenuHandler, v as UserContextMenuHandler, w as createSeedcord, y as SlashHandler } from "./edge.index-CdX0NeZA.mjs";
2
2
 
3
3
  export * from "@seedcord/logger"
4
4
 
@@ -12,4 +12,4 @@ export * from "@seedcord/event-emitter"
12
12
 
13
13
  export * from "@seedcord/utils"
14
14
 
15
- export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, CursorSource, Emojis, Gated, InteractionHandler, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, Paginator, RepliableHandler, ReplySender, RoleMenuHandler, SelectMenuHandler, SlashHandler, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, createSeedcord, version };
15
+ export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, CursorSource, Emojis, Gated, InteractionHandler, InteractionMiddleware, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, Paginator, RepliableHandler, ReplySender, RoleMenuHandler, SelectMenuHandler, SlashHandler, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, createSeedcord, version };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as InjectedEmojiMap, C as SlashHandler, D as ReplySender, E as InteractionRef, F as EngineContext, I as Core, L as HttpConfig, M as SelectedMentionables, N as SlashOptions, O as SentMessage, P as createSeedcord, R as HttpEdgeConfig, S as UserContextMenuHandler, T as RepliableHandler, _ as ModalHandler, a as CursorSource, b as AutocompleteHandler, c as PageContext, d as ChannelMenuHandler, f as MentionableMenuHandler, g as UserMenuHandler, h as StringMenuHandler, i as ArraySource, j as ModalFields, k as Emojis, l as Subscriber, m as SelectMenuHandler, n as Gated, o as PageSource, p as RoleMenuHandler, r as InteractionGateContext, s as Paginator, t as version, u as WebhookLog, v as ButtonHandler, w as InteractionHandler, x as MessageContextMenuHandler, y as ComponentHandler, z as HttpServerConfig } from "./edge.index-LAWTa6NI.mjs";
1
+ import { A as ComponentHandler, B as HttpServerConfig, C as InteractionMiddleware, D as SelectMenuHandler, E as RoleMenuHandler, F as SentMessage, I as AutocompleteHandler, L as Core, M as RepliableHandler, N as InteractionRef, O as StringMenuHandler, P as ReplySender, R as HttpConfig, S as EngineContext, T as MentionableMenuHandler, _ as InjectedEmojiMap, a as CursorSource, b as SlashOptions, c as PageContext, d as ModalHandler, f as ButtonHandler, g as Emojis, h as SlashHandler, i as ArraySource, j as InteractionHandler, k as UserMenuHandler, l as Subscriber, m as UserContextMenuHandler, n as Gated, o as PageSource, p as MessageContextMenuHandler, r as InteractionGateContext, s as Paginator, t as version, u as WebhookLog, v as ModalFields, w as ChannelMenuHandler, x as createSeedcord, y as SelectedMentionables, z as HttpEdgeConfig } from "./edge.index-CgLU9PiF.mjs";
2
2
  import { t as RouteManifest } from "./RouteManifest-D7qXj8Ts.mjs";
3
3
  import "reflect-metadata";
4
4
  import { REST } from "@discordjs/rest";
@@ -87,5 +87,5 @@ declare abstract class Plugin<Opts extends HttpPluginOptions = {
87
87
  transport: 'http';
88
88
  }> extends Plugin$1<Opts, Core> {}
89
89
  //#endregion
90
- export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, type HttpConfig, type HttpEdgeConfig, type HttpPluginOptions, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, type InteractionRef, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, type PageContext, type PageSource, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RepliableHandler, ReplySender, RoleMenuHandler, type RouteManifest, Seedcord, SelectMenuHandler, type SelectedMentionables, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, createSeedcord, version };
90
+ export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, type HttpConfig, type HttpEdgeConfig, type HttpPluginOptions, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, type InteractionRef, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, type PageContext, type PageSource, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RepliableHandler, ReplySender, RoleMenuHandler, type RouteManifest, Seedcord, SelectMenuHandler, type SelectedMentionables, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, createSeedcord, version };
91
91
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { C as createSeedcord, D as AutocompleteHandler, E as InteractionHandler, O as RepliableHandler, S as ModalFields, T as buildRouteMaps, _ as MessageContextMenuHandler, a as Paginator, b as EmojiInjector, c as ChannelMenuHandler, d as SelectMenuHandler, f as StringMenuHandler, g as ComponentHandler, h as ButtonHandler, i as CursorSource, k as ReplySender, l as MentionableMenuHandler, m as ModalHandler, n as Gated, o as Subscriber, p as UserMenuHandler, r as ArraySource, s as WebhookLog, t as version, u as RoleMenuHandler, v as UserContextMenuHandler, w as buildEngine, x as Emojis, y as SlashHandler } from "./edge.index-CWUU_d_X.mjs";
1
+ import { A as ReplySender, C as ModalFields, D as InteractionHandler, E as buildRouteMaps, O as AutocompleteHandler, S as Emojis, T as buildEngine, _ as MessageContextMenuHandler, a as Paginator, b as InteractionMiddleware, c as ChannelMenuHandler, d as SelectMenuHandler, f as StringMenuHandler, g as ComponentHandler, h as ButtonHandler, i as CursorSource, k as RepliableHandler, l as MentionableMenuHandler, m as ModalHandler, n as Gated, o as Subscriber, p as UserMenuHandler, r as ArraySource, s as WebhookLog, t as version, u as RoleMenuHandler, v as UserContextMenuHandler, w as createSeedcord, x as EmojiInjector, y as SlashHandler } from "./edge.index-CdX0NeZA.mjs";
2
2
  import "reflect-metadata";
3
- import { HmrManager, InteractionMetadataKey, busLoggerOf, getDevChannel, interactionRoutesOf, setBotColor } from "@seedcord/core/internal";
3
+ import { HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, MiddlewareRegistry, busLoggerOf, getDevChannel, interactionMiddleware, interactionRoutesOf, setBotColor } from "@seedcord/core/internal";
4
4
  import { SeedcordError, applicationIdFromToken, validateDiscordToken } from "@seedcord/errors/internal";
5
5
  import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
6
6
  import { Envapter } from "envapt";
@@ -46,23 +46,32 @@ const EMPTY_MANIFEST = {
46
46
  //#region src/node/InteractionDispatcher.ts
47
47
  var InteractionDispatcher = class {
48
48
  handlersDir;
49
+ middlewaresDir;
49
50
  maps;
50
51
  /** @internal */
52
+ middlewares = new MiddlewareRegistry(interactionMiddleware);
53
+ /** @internal */
51
54
  logger = new Logger("Interactions", { channel: "interactions" });
52
55
  isInitialized = false;
53
56
  hmrHandler;
54
57
  rowOwners = /* @__PURE__ */ new Map();
55
58
  loading = false;
56
59
  loadedHandlers = [];
57
- constructor(handlersDir) {
60
+ loadedMiddlewares = [];
61
+ constructor(handlersDir, middlewaresDir) {
58
62
  this.handlersDir = handlersDir;
63
+ this.middlewaresDir = middlewaresDir;
59
64
  this.maps = buildRouteMaps(EMPTY_MANIFEST);
60
65
  if (!Envapter.isDevelopment && !Envapter.isTest) return;
61
66
  this.hmrHandler = new HmrModuleHandler({
62
67
  handlersDir,
68
+ ...middlewaresDir && { middlewaresDir },
63
69
  isHandler: this.isHandler.bind(this),
70
+ isMiddleware: this.isMiddleware.bind(this),
64
71
  registerHandler: this.registerHandler.bind(this),
72
+ registerMiddleware: this.registerMiddleware.bind(this),
65
73
  unregisterHandler: this.unregisterHandler.bind(this),
74
+ unregisterMiddleware: this.unregisterMiddleware.bind(this),
66
75
  getArtifacts: this.getArtifacts.bind(this),
67
76
  logger: this.logger
68
77
  });
@@ -73,18 +82,37 @@ var InteractionDispatcher = class {
73
82
  this.isInitialized = true;
74
83
  this.loading = true;
75
84
  this.loadedHandlers.length = 0;
85
+ this.loadedMiddlewares.length = 0;
76
86
  try {
77
- await traverseDirectory(this.handlersDir, (fullPath, relativePath, imported) => {
78
- for (const value of Object.values(imported)) {
79
- if (!this.isHandler(value)) continue;
80
- this.registerHandler(value, relativePath);
81
- this.hmrHandler?.trackHandler(fullPath, value);
82
- }
83
- });
87
+ if (this.middlewaresDir) await this.loadMiddlewares(this.middlewaresDir);
88
+ await this.loadHandlers(this.handlersDir);
84
89
  } finally {
85
90
  this.loading = false;
86
91
  }
87
- this.logger.utils.block("Loaded handlers", [...this.logger.utils.entries(this.loadedHandlers), ...this.logger.utils.counts({ routes: this.rowOwners.size })], "debug");
92
+ this.reportLoad();
93
+ }
94
+ async loadHandlers(dir) {
95
+ await traverseDirectory(dir, (fullPath, relativePath, imported) => {
96
+ for (const value of Object.values(imported)) {
97
+ if (!this.isHandler(value)) continue;
98
+ this.registerHandler(value, relativePath);
99
+ this.hmrHandler?.trackHandler(fullPath, value);
100
+ }
101
+ });
102
+ }
103
+ async loadMiddlewares(dir) {
104
+ await traverseDirectory(dir, (fullPath, relativePath, imported) => {
105
+ for (const value of Object.values(imported)) {
106
+ if (!this.isMiddleware(value)) continue;
107
+ this.registerMiddleware(value, relativePath);
108
+ this.hmrHandler?.trackMiddleware(fullPath, value);
109
+ }
110
+ });
111
+ }
112
+ reportLoad() {
113
+ const { utils } = this.logger;
114
+ if (this.loadedMiddlewares.length > 0) utils.block("Loaded middlewares", utils.entries(this.loadedMiddlewares), "debug");
115
+ utils.block("Loaded handlers", [...utils.entries(this.loadedHandlers), ...utils.counts({ routes: this.rowOwners.size })], "debug");
88
116
  }
89
117
  /** @internal */
90
118
  async onHmr(event) {
@@ -109,6 +137,22 @@ var InteractionDispatcher = class {
109
137
  if (typeof value !== "function") return false;
110
138
  return (value.prototype instanceof InteractionHandler || value.prototype instanceof AutocompleteHandler) && Reflect.hasMetadata(InteractionMetadataKey, value);
111
139
  }
140
+ isMiddleware(value) {
141
+ if (typeof value !== "function") return false;
142
+ return value.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(InteractionMiddlewareMetadataKey, value);
143
+ }
144
+ registerMiddleware(ctor, relativePath) {
145
+ const metadata = this.middlewares.register(ctor);
146
+ if (!metadata || !this.loading) return;
147
+ const scope = metadata.keys ? `${metadata.priority}, ${metadata.keys.join(", ")}` : metadata.priority;
148
+ this.loadedMiddlewares.push({
149
+ name: `${ctor.name} (${String(scope)})`,
150
+ from: formatFilePath(relativePath)
151
+ });
152
+ }
153
+ unregisterMiddleware(ctor) {
154
+ this.middlewares.unregister(ctor);
155
+ }
112
156
  getArtifacts(ctor) {
113
157
  const routeIds = [];
114
158
  for (const [routeId, owner] of this.rowOwners) if (owner.ctor === ctor) routeIds.push(routeId);
@@ -233,7 +277,8 @@ var Seedcord = class Seedcord extends Pluggable {
233
277
  setBotColor(config.botColor);
234
278
  this.hmrManager = new HmrManager();
235
279
  this.hmrManager.init();
236
- if (this.config.bot.interactions.path) this.interactions = new InteractionDispatcher(this.config.bot.interactions.path);
280
+ const interactions = this.config.bot.interactions;
281
+ if (interactions.path) this.interactions = new InteractionDispatcher(interactions.path, interactions.middlewares);
237
282
  if (this.config.bot.commands.path) this.commandRegistry = new CommandRegistry(this);
238
283
  this.rateLimiter = config.store ?? new MemoryRateLimiter();
239
284
  this.bus = new Bus(this);
@@ -314,7 +359,8 @@ var Seedcord = class Seedcord extends Pluggable {
314
359
  }
315
360
  async listen() {
316
361
  const maps = this.interactions?.maps ?? buildRouteMaps(EMPTY_MANIFEST);
317
- const { handle, inFlight } = buildEngine(this, maps);
362
+ const middlewares = this.interactions?.middlewares ?? new MiddlewareRegistry(interactionMiddleware);
363
+ const { handle, inFlight } = buildEngine(this, maps, middlewares);
318
364
  const server = createServer((incoming, outgoing) => {
319
365
  (async () => {
320
366
  const response = await handle(await toWebRequest(incoming));
@@ -371,5 +417,5 @@ var Seedcord = class Seedcord extends Pluggable {
371
417
  var Plugin = class extends Plugin$1 {};
372
418
 
373
419
  //#endregion
374
- export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, CursorSource, Emojis, Gated, InteractionHandler, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, Paginator, Plugin, RepliableHandler, ReplySender, RoleMenuHandler, Seedcord, SelectMenuHandler, SlashHandler, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, createSeedcord, version };
420
+ export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, CursorSource, Emojis, Gated, InteractionHandler, InteractionMiddleware, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, Paginator, Plugin, RepliableHandler, ReplySender, RoleMenuHandler, Seedcord, SelectMenuHandler, SlashHandler, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, createSeedcord, version };
375
421
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["packageVersion","CorePlugin"],"sources":["../src/manifest/RouteManifest.ts","../src/node/InteractionDispatcher.ts","../src/node/webBridge.ts","../src/node/Seedcord.ts","../src/plugin.ts"],"sourcesContent":["import type { InteractionKind, SubscriptionKey } from '@seedcord/core';\nimport type { EventFrequency } from '@seedcord/types';\nimport type { ApplicationCommandType } from 'discord-api-types/v10';\n\n/** The command kinds a manifest row registers, the wire enum's other members have no handler base. */\ntype RegistrableCommandType =\n ApplicationCommandType.ChatInput | ApplicationCommandType.User | ApplicationCommandType.Message;\n\n/** One file may export several handlers. */\nexport interface RouteModule {\n /** The exported binding inside the loaded module. */\n readonly exportName: string;\n /** The source file the row came from, reported when the export is missing. */\n readonly from: string;\n /** Lazy import of the module, evaluated on first hit. */\n readonly load: () => Promise<Record<string, unknown>>;\n}\n\n/**\n * A slash or context-menu command row. `type` picks the per-kind map, `name` is the lookup key. For a\n * chat-input command with subcommands, `name` is the full route path (`'config/set'`), the same key\n * `@SlashRoute` registers.\n */\ninterface CommandRoute extends RouteModule {\n readonly name: string;\n readonly type: RegistrableCommandType;\n}\n\n/**\n * A button, select, or modal row. `kind` picks the per-kind map, `prefix` is the stable customId prefix\n * the receiver routes by (the routeKey minus its layout hash).\n */\nexport interface ComponentRoute extends RouteModule {\n readonly kind:\n | InteractionKind.Button\n | InteractionKind.Modal\n | InteractionKind.StringMenu\n | InteractionKind.UserMenu\n | InteractionKind.RoleMenu\n | InteractionKind.ChannelMenu\n | InteractionKind.MentionableMenu;\n readonly prefix: string;\n}\n\n/**\n * An autocomplete row, its own route keyed by the command's route path, separate from the slash row.\n */\ninterface AutocompleteRoute extends RouteModule {\n readonly name: string;\n}\n\n/**\n * A bus subscriber row. The Bus registers it without importing the subscriber module, and resolves the\n * class on the first publish of one of its keys.\n */\ninterface SubscriberRoute extends RouteModule {\n /** The subscription keys this subscriber registers for. */\n readonly keys: readonly SubscriptionKey[];\n /** Mirrors the `@Subscribe` frequency metadata. */\n readonly frequency: EventFrequency;\n}\n\n/** A middleware row. Middleware runs on every dispatch. */\ntype MiddlewareRoute = RouteModule;\n\n/**\n * The static route table the HTTP engine dispatches through, replacing the filesystem handler scan that\n * cannot run in a bundled isolate. `seedcord build` emits it, the mount path authors it by hand.\n */\nexport interface RouteManifest {\n readonly commandRoutes: readonly CommandRoute[];\n readonly componentRoutes: readonly ComponentRoute[];\n readonly autocompleteRoutes: readonly AutocompleteRoute[];\n readonly subscriberRoutes: readonly SubscriberRoute[];\n readonly middlewareRoutes: readonly MiddlewareRoute[];\n}\n\n/** @internal */\nexport const EMPTY_MANIFEST: RouteManifest = {\n commandRoutes: [],\n componentRoutes: [],\n autocompleteRoutes: [],\n subscriberRoutes: [],\n middlewareRoutes: []\n};\n","import { InteractionKind } from '@seedcord/core';\nimport { HmrModuleHandler } from '@seedcord/core/hmr';\nimport { interactionRoutesOf, InteractionMetadataKey } from '@seedcord/core/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\nimport { traverseDirectory } from '@seedcord/utils/node';\nimport { Envapter } from 'envapt';\n\nimport { AutocompleteHandler } from '#handlers/interaction/AutocompleteHandler';\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport type { HandlerConstructor } from '#handlers/constructors';\nimport type { RouteMap, RouteMaps } from '#src/dispatch/resolve';\nimport type { Initializeable, ContextMenuLeaves } from '@seedcord/core/internal';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\n// hmr swaps entries live and resolve() reads per request\nexport class InteractionDispatcher implements Initializeable, HmrAware {\n public readonly maps: RouteMaps;\n\n /** @internal */\n public readonly logger = new Logger('Interactions', { channel: 'interactions' });\n\n private isInitialized = false;\n private readonly hmrHandler?: HmrModuleHandler<HandlerConstructor, void, string[]>;\n // routeId -> owner row, the duplicate guard and the hmr unregister index\n private readonly rowOwners = new Map<\n string,\n { ctor: HandlerConstructor; kind: InteractionKind; key: string; from: string }\n >();\n\n private loading = false;\n private readonly loadedHandlers: { name: string; from: string }[] = [];\n\n constructor(private readonly handlersDir: string) {\n this.maps = buildRouteMaps(EMPTY_MANIFEST);\n\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n this.hmrHandler = new HmrModuleHandler({\n handlersDir,\n isHandler: this.isHandler.bind(this),\n registerHandler: this.registerHandler.bind(this),\n unregisterHandler: this.unregisterHandler.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: this.logger\n });\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n\n this.loading = true;\n this.loadedHandlers.length = 0;\n try {\n await traverseDirectory(this.handlersDir, (fullPath, relativePath, imported) => {\n for (const value of Object.values(imported)) {\n if (!this.isHandler(value)) continue;\n this.registerHandler(value, relativePath);\n this.hmrHandler?.trackHandler(fullPath, value);\n }\n });\n } finally {\n this.loading = false;\n }\n\n this.logger.utils.block(\n 'Loaded handlers',\n [\n ...this.logger.utils.entries(this.loadedHandlers),\n ...this.logger.utils.counts({ routes: this.rowOwners.size })\n ],\n 'debug'\n );\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n await this.hmrHandler?.handle(event);\n }\n\n public warnUnhandledRoutes(commandLeaves: Iterable<string>): void {\n this.warnMissing(commandLeaves, this.maps[InteractionKind.Slash], 'Slash route', '@SlashRoute');\n }\n\n public warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void {\n const user = this.maps[InteractionKind.UserContextMenu];\n const message = this.maps[InteractionKind.MessageContextMenu];\n this.warnMissing(leaves.user, user, 'User context menu', '@ContextMenuRoute');\n this.warnMissing(leaves.message, message, 'Message context menu', '@ContextMenuRoute');\n }\n\n private warnMissing(names: Iterable<string>, map: RouteMap, label: string, decorator: string): void {\n for (const name of names) {\n if (map.has(name)) continue;\n this.logger.warn(`${label} ${paint.sky.bold(name)} has no registered ${paint.bold(decorator)} handler.`);\n }\n }\n\n private isHandler(value: unknown): value is HandlerConstructor {\n if (typeof value !== 'function') return false;\n // this package's own family bases, so a gateway handler in the same dir stays unregistered\n return (\n (value.prototype instanceof InteractionHandler || value.prototype instanceof AutocompleteHandler) &&\n Reflect.hasMetadata(InteractionMetadataKey, value)\n );\n }\n\n private getArtifacts(ctor: HandlerConstructor): string[] {\n const routeIds: string[] = [];\n for (const [routeId, owner] of this.rowOwners) {\n if (owner.ctor === ctor) routeIds.push(routeId);\n }\n return routeIds;\n }\n\n private registerHandler(ctor: HandlerConstructor, relativePath: string): void {\n const from = formatFilePath(relativePath);\n // a partial registration would orphan routes and break hmr rollback\n const writes: { kind: InteractionKind; key: string }[] = [];\n\n for (const [kind, keys] of interactionRoutesOf(ctor)) {\n for (const key of keys) {\n const routeId = `${kind}:${key}`;\n const existing = this.rowOwners.get(routeId);\n // a different class on the same route would silently shadow the existing one (last write wins)\n if (existing && existing.ctor !== ctor) {\n throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [\n routeId,\n `${existing.ctor.name} (${existing.from})`,\n `${ctor.name} (${from})`\n ]);\n }\n writes.push({ kind, key });\n }\n }\n\n if (writes.length === 0) return;\n for (const { kind, key } of writes) {\n const routeId = `${kind}:${key}`;\n this.maps[kind].set(key, { kind, routeId, load: () => Promise.resolve(ctor) });\n this.rowOwners.set(routeId, { ctor, kind, key, from });\n }\n\n if (this.loading) this.loadedHandlers.push({ name: ctor.name, from });\n }\n\n private unregisterHandler(ctor: HandlerConstructor, artifacts?: string[]): void {\n const routeIds = artifacts ?? this.getArtifacts(ctor);\n for (const routeId of routeIds) {\n const owner = this.rowOwners.get(routeId);\n if (owner?.ctor !== ctor) continue;\n this.rowOwners.delete(routeId);\n this.maps[owner.kind].delete(owner.key);\n }\n }\n}\n","import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'node:http';\n\nfunction toHeaders(raw: IncomingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(raw)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) headers.append(name, item);\n } else {\n headers.set(name, value);\n }\n }\n return headers;\n}\n\n// buffers the raw body before any parse so ed25519 verifies the exact signed bytes\nexport async function toWebRequest(request: IncomingMessage): Promise<Request> {\n const chunks: Buffer[] = [];\n // justified: the async iterator is typed any and IncomingMessage yields Buffer chunks\n for await (const chunk of request) chunks.push(chunk as Buffer);\n\n const method = request.method ?? 'GET';\n const body = method === 'GET' || method === 'HEAD' ? undefined : new Uint8Array(Buffer.concat(chunks));\n\n return new Request(`http://${request.headers.host ?? 'localhost'}${request.url ?? '/'}`, {\n method,\n headers: toHeaders(request.headers),\n ...(body !== undefined && { body })\n });\n}\n\nexport async function writeWebResponse(response: Response, out: ServerResponse): Promise<void> {\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers);\n // iterating Headers comma-joins same-name values, which corrupts Set-Cookie (a per-cookie header)\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) headers['set-cookie'] = cookies;\n\n out.writeHead(response.status, headers);\n out.end(response.body === null ? undefined : Buffer.from(await response.arrayBuffer()));\n}\n","import { once } from 'node:events';\nimport { createServer } from 'node:http';\n\nimport { REST } from '@discordjs/rest';\nimport { Bus } from '@seedcord/core';\nimport { busLoggerOf, getDevChannel, HmrManager, setBotColor } from '@seedcord/core/internal';\nimport { CoordinatedShutdown, CoordinatedStartup, Pluggable } from '@seedcord/core/node';\nimport {\n CommandRegistry,\n DRAIN_TASK_TIMEOUT_MS,\n DRAIN_WINDOW_MS,\n drainInFlight,\n ShutdownPhase,\n shutdownOf,\n StartupPhase,\n SubscriberLoader\n} from '@seedcord/core/node/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { applicationIdFromToken, SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger, LoggerChannelRegistry } from '@seedcord/logger';\nimport { installNodeDefaults } from '@seedcord/logger/node';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { HostAugmentTarget, HostVersion, SeedcordBrand } from '@seedcord/types/internal';\nimport { Routes } from 'discord-api-types/v10';\nimport { Envapter } from 'envapt';\n\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EmojiInjector } from '#src/emojis/EmojiInjector';\nimport { buildEngine } from '#src/engine';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport { InteractionDispatcher } from './InteractionDispatcher';\nimport { toWebRequest, writeWebResponse } from './webBridge';\nimport { version as packageVersion } from '../version';\n\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { Core } from '#interfaces/Core';\nimport type { IRateLimiter } from '@seedcord/types';\nimport type { SeedcordInstance } from '@seedcord/types/internal';\nimport type { Server } from 'node:http';\nimport type { AddressInfo } from 'node:net';\n\nconst DEFAULT_PORT = 3000;\nconst SERVER_SHUTDOWN_TIMEOUT_MS = 5000;\n\ntype RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends { runtime: 'edge' } ? 'edge' : 'server';\n\n/**\n * The HTTP-interactions bot host, a long-running node server around the engine.\n *\n * Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on\n * `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls\n * `createSeedcord` from a generated entry.\n */\nexport class Seedcord<Cfg extends HttpConfig = HttpConfig>\n extends Pluggable<'http', RuntimeOfConfig<Cfg>>\n implements Core, SeedcordInstance\n{\n // the CLI reads these to detect and augment the instance\n /** @internal */\n public readonly [SeedcordBrand] = true;\n /** @internal */\n public readonly [HostAugmentTarget] = '@seedcord/http';\n /** @internal */\n public readonly [HostVersion]: string = packageVersion;\n\n /** Workerd-compatible Discord REST client. `start()` sets the token. */\n public readonly rest = new REST();\n\n /** @see {@link IRateLimiter} */\n public readonly rateLimiter: IRateLimiter;\n\n /** @see {@link Bus} */\n public readonly bus: Bus;\n\n private readonly subscribers: SubscriberLoader;\n\n private readonly interactions?: InteractionDispatcher;\n private readonly commandRegistry?: CommandRegistry;\n private readonly emojiInjector = new EmojiInjector(this);\n private readonly hmrManager: HmrManager;\n private readonly logger = new Logger('Server', { channel: 'bot' });\n\n private token?: string;\n private server?: Server;\n private boundPort?: number;\n private fetchedUsername?: string | undefined;\n\n constructor(public readonly config: Cfg) {\n super(new CoordinatedShutdown(config.lifecycle?.shutdownDeadline), new CoordinatedStartup());\n\n installNodeDefaults(config.logger);\n setBotColor(config.botColor);\n\n this.hmrManager = new HmrManager();\n this.hmrManager.init();\n\n if (this.config.bot.interactions.path) {\n this.interactions = new InteractionDispatcher(this.config.bot.interactions.path);\n }\n\n if (this.config.bot.commands.path) this.commandRegistry = new CommandRegistry(this);\n\n this.rateLimiter = config.store ?? new MemoryRateLimiter();\n this.bus = new Bus(this);\n this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);\n\n this.registerStartupTasks();\n }\n\n /** The bot's discord username, populated by the ready fetch. */\n public get username(): string | undefined {\n return this.fetchedUsername;\n }\n\n /** The bound server port, populated once `start()` is listening. */\n public get port(): number | undefined {\n return this.boundPort;\n }\n\n /**\n * Starts the host and runs the startup tasks.\n */\n public async start(): Promise<this> {\n try {\n await super.init();\n } catch (caught) {\n await shutdownOf(this).run(1, false);\n Seedcord.reset(this);\n throw caught;\n }\n return this;\n }\n\n protected static override reset(host?: object): boolean {\n if (!super.reset(host)) return false;\n // super.reset() drops the dev TUI's log sink\n LoggerChannelRegistry.instance.configure({});\n return true;\n }\n\n private registerStartupTasks(): void {\n if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();\n\n this.startup.addTask(StartupPhase.Configuration, 'bus-initialization', async () => {\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'start');\n await this.subscribers.init();\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'end');\n });\n\n const { interactions } = this;\n if (interactions) {\n this.startup.addTask(StartupPhase.Configuration, 'interactions-initialization', async () => {\n interactions.logger.utils.initialization('Interactions', 'start');\n await interactions.init();\n interactions.logger.utils.initialization('Interactions', 'end');\n });\n }\n\n this.startup.addTask(StartupPhase.Configuration, 'authenticate', () => {\n this.authenticate();\n return Promise.resolve();\n });\n\n // needs the token from Configuration, and must finish before Ready opens the server to interactions\n this.startup.addTask(StartupPhase.Login, 'emoji-injection', () => this.emojiInjector.init());\n\n const { commandRegistry } = this;\n if (commandRegistry) {\n // one task because tasks in a phase run concurrently and the deploy reads the id\n this.startup.addTask(StartupPhase.Login, 'command-deploy', async () => {\n await commandRegistry.init();\n await commandRegistry.setCommands();\n interactions?.warnUnhandledRoutes(commandRegistry.routeLeaves());\n interactions?.warnUnhandledContextMenuRoutes(commandRegistry.contextMenuLeaves());\n });\n }\n\n this.startup.addTask(StartupPhase.Ready, 'http-server', () => this.listen());\n\n if (!Envapter.isTest) {\n this.startup.addTask(StartupPhase.Ready, 'identity', () => this.fetchUsername());\n }\n }\n\n private registerHmrAwareModules(): void {\n this.startup.addTask(StartupPhase.Configuration, 'hmr-registration', async () => {\n if (this.interactions) this.hmrManager.register(this.interactions);\n if (this.commandRegistry) this.hmrManager.register(this.commandRegistry);\n this.hmrManager.register(this.subscribers);\n for (const plugin of this.plugins) {\n this.hmrManager.register(plugin);\n }\n await Promise.resolve();\n });\n }\n\n private authenticate(): void {\n this.token = validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n this.rest.setToken(this.token);\n }\n\n /** The bot's Discord application id. Throws if you read it before the Configuration phase. */\n public get applicationId(): string {\n if (!this.token) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);\n return applicationIdFromToken(this.token);\n }\n\n private async listen(): Promise<void> {\n const maps = this.interactions?.maps ?? buildRouteMaps(EMPTY_MANIFEST);\n const { handle, inFlight } = buildEngine(this, maps);\n\n const server = createServer((incoming, outgoing) => {\n void (async () => {\n const response = await handle(await toWebRequest(incoming));\n await writeWebResponse(response, outgoing);\n })().catch((error: unknown) => {\n // a swallowed throw would hang the client with no cause\n outgoing.destroy(Error.isError(error) ? error : new Error(String(error)));\n });\n });\n this.server = server;\n\n server.listen(this.config.port ?? DEFAULT_PORT);\n await once(server, 'listening');\n // justified: address() is AddressInfo once a TCP server is listening\n this.boundPort = (server.address() as AddressInfo).port;\n this.logger.info(`Interactions server listening on port ${paint.sky.bold(String(this.boundPort))}`);\n getDevChannel()?.send('seedcord:server-listening', { port: this.boundPort });\n\n this.shutdown.addTask(\n ShutdownPhase.Unbind,\n 'stop-http-server',\n () => this.stopServer(),\n SERVER_SHUTDOWN_TIMEOUT_MS\n );\n // Unbind already ran, so every accepted request is in the in-flight set here\n this.shutdown.addTask(\n ShutdownPhase.Drain,\n 'drain-inflight',\n () => drainInFlight(inFlight, DRAIN_WINDOW_MS, this.logger, 'Interactions'),\n DRAIN_TASK_TIMEOUT_MS\n );\n }\n\n private async fetchUsername(): Promise<void> {\n try {\n // justified: the @me payload carries username per the discord api contract\n const me = (await this.rest.get(Routes.user('@me'))) as { username?: string };\n this.fetchedUsername = me.username;\n if (me.username) this.logger.info(`Running as ${paint.sky.bold(me.username)}`);\n } catch (caught) {\n // a bad token errors on the first real send anyway\n this.logger.warn('could not fetch the bot identity', caught);\n }\n }\n\n private stopServer(): Promise<void> {\n const server = this.server;\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolveClose, rejectClose) => {\n server.close((err) => {\n if (err) {\n rejectClose(err);\n return;\n }\n this.logger.info(paint.coral.bold('Interactions server stopped'));\n resolveClose();\n });\n // close() waits on idle keep-alive sockets. active responses still flush, and the task\n // timeout bounds a hung one\n server.closeIdleConnections();\n });\n }\n}\n","import { Plugin as CorePlugin } from '@seedcord/core/plugin';\n\nimport type { Core } from '#interfaces/Core';\nimport type { PluginOptions } from '@seedcord/core/plugin';\nimport type { TypedOmit } from '@seedcord/types';\n\n/** The options an HTTP plugin declares, narrowed to the transports this base serves. */\nexport type HttpPluginOptions = TypedOmit<PluginOptions, 'transport'> & { transport?: 'http' };\n\n/**\n * Base class for an HTTP plugin, binding `this.core` to the HTTP {@link Core}.\n *\n * A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose\n * `this.core` is typed to the shared members only.\n *\n * @typeParam Opts - The transport and runtime the plugin declares.\n */\nexport abstract class Plugin<Opts extends HttpPluginOptions = { transport: 'http' }> extends CorePlugin<Opts, Core> {}\n\nexport type { PluginLifecycleSpec, PluginOptions } from '@seedcord/core/plugin';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["packageVersion","CorePlugin"],"sources":["../src/manifest/RouteManifest.ts","../src/node/InteractionDispatcher.ts","../src/node/webBridge.ts","../src/node/Seedcord.ts","../src/plugin.ts"],"sourcesContent":["import type { InteractionKind, SubscriptionKey } from '@seedcord/core';\nimport type { EventFrequency } from '@seedcord/types';\nimport type { ApplicationCommandType } from 'discord-api-types/v10';\n\n/** The command kinds a manifest row registers, the wire enum's other members have no handler base. */\ntype RegistrableCommandType =\n ApplicationCommandType.ChatInput | ApplicationCommandType.User | ApplicationCommandType.Message;\n\n/** One file may export several handlers. */\nexport interface RouteModule {\n /** The exported binding inside the loaded module. */\n readonly exportName: string;\n /** The source file the row came from, reported when the export is missing. */\n readonly from: string;\n /** Lazy import of the module, evaluated on first hit. */\n readonly load: () => Promise<Record<string, unknown>>;\n}\n\n/**\n * A slash or context-menu command row. `type` picks the per-kind map, `name` is the lookup key. For a\n * chat-input command with subcommands, `name` is the full route path (`'config/set'`), the same key\n * `@SlashRoute` registers.\n */\ninterface CommandRoute extends RouteModule {\n readonly name: string;\n readonly type: RegistrableCommandType;\n}\n\n/**\n * A button, select, or modal row. `kind` picks the per-kind map, `prefix` is the stable customId prefix\n * the receiver routes by (the routeKey minus its layout hash).\n */\nexport interface ComponentRoute extends RouteModule {\n readonly kind:\n | InteractionKind.Button\n | InteractionKind.Modal\n | InteractionKind.StringMenu\n | InteractionKind.UserMenu\n | InteractionKind.RoleMenu\n | InteractionKind.ChannelMenu\n | InteractionKind.MentionableMenu;\n readonly prefix: string;\n}\n\n/**\n * An autocomplete row, its own route keyed by the command's route path, separate from the slash row.\n */\ninterface AutocompleteRoute extends RouteModule {\n readonly name: string;\n}\n\n/**\n * A bus subscriber row. The Bus registers it without importing the subscriber module, and resolves the\n * class on the first publish of one of its keys.\n */\ninterface SubscriberRoute extends RouteModule {\n /** The subscription keys this subscriber registers for. */\n readonly keys: readonly SubscriptionKey[];\n /** Mirrors the `@Subscribe` frequency metadata. */\n readonly frequency: EventFrequency;\n}\n\n/** A middleware row. Middleware runs on every dispatch. */\ntype MiddlewareRoute = RouteModule;\n\n/**\n * The static route table the HTTP engine dispatches through, replacing the filesystem handler scan that\n * cannot run in a bundled isolate. `seedcord build` emits it, the mount path authors it by hand.\n */\nexport interface RouteManifest {\n readonly commandRoutes: readonly CommandRoute[];\n readonly componentRoutes: readonly ComponentRoute[];\n readonly autocompleteRoutes: readonly AutocompleteRoute[];\n readonly subscriberRoutes: readonly SubscriberRoute[];\n readonly middlewareRoutes: readonly MiddlewareRoute[];\n}\n\n/** @internal */\nexport const EMPTY_MANIFEST: RouteManifest = {\n commandRoutes: [],\n componentRoutes: [],\n autocompleteRoutes: [],\n subscriberRoutes: [],\n middlewareRoutes: []\n};\n","import { InteractionKind } from '@seedcord/core';\nimport { HmrModuleHandler } from '@seedcord/core/hmr';\nimport {\n interactionMiddleware,\n interactionRoutesOf,\n InteractionMetadataKey,\n InteractionMiddlewareMetadataKey,\n MiddlewareRegistry\n} from '@seedcord/core/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\nimport { traverseDirectory } from '@seedcord/utils/node';\nimport { Envapter } from 'envapt';\n\nimport { AutocompleteHandler } from '#handlers/interaction/AutocompleteHandler';\nimport { InteractionHandler } from '#handlers/interaction/InteractionHandler';\nimport { InteractionMiddleware } from '#handlers/interaction/InteractionMiddleware';\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport type { HandlerConstructor, InteractionMiddlewareConstructor } from '#handlers/constructors';\nimport type { RouteMap, RouteMaps } from '#src/dispatch/resolve';\nimport type { Initializeable, ContextMenuLeaves } from '@seedcord/core/internal';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\n// hmr swaps entries live and resolve() reads per request\nexport class InteractionDispatcher implements Initializeable, HmrAware {\n public readonly maps: RouteMaps;\n\n /** @internal */\n public readonly middlewares = new MiddlewareRegistry<InteractionMiddlewareConstructor>(interactionMiddleware);\n\n /** @internal */\n public readonly logger = new Logger('Interactions', { channel: 'interactions' });\n\n private isInitialized = false;\n private readonly hmrHandler?: HmrModuleHandler<HandlerConstructor, InteractionMiddlewareConstructor, string[]>;\n // keyed by routeId, read by the duplicate guard and the hmr unregister\n private readonly rowOwners = new Map<\n string,\n { ctor: HandlerConstructor; kind: InteractionKind; key: string; from: string }\n >();\n\n private loading = false;\n private readonly loadedHandlers: { name: string; from: string }[] = [];\n private readonly loadedMiddlewares: { name: string; from: string }[] = [];\n\n constructor(\n private readonly handlersDir: string,\n private readonly middlewaresDir?: string\n ) {\n this.maps = buildRouteMaps(EMPTY_MANIFEST);\n\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n this.hmrHandler = new HmrModuleHandler({\n handlersDir,\n ...(middlewaresDir && { middlewaresDir }),\n isHandler: this.isHandler.bind(this),\n isMiddleware: this.isMiddleware.bind(this),\n registerHandler: this.registerHandler.bind(this),\n registerMiddleware: this.registerMiddleware.bind(this),\n unregisterHandler: this.unregisterHandler.bind(this),\n unregisterMiddleware: this.unregisterMiddleware.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: this.logger\n });\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n\n this.loading = true;\n this.loadedHandlers.length = 0;\n this.loadedMiddlewares.length = 0;\n try {\n if (this.middlewaresDir) await this.loadMiddlewares(this.middlewaresDir);\n await this.loadHandlers(this.handlersDir);\n } finally {\n this.loading = false;\n }\n\n this.reportLoad();\n }\n\n private async loadHandlers(dir: string): Promise<void> {\n await traverseDirectory(dir, (fullPath, relativePath, imported) => {\n for (const value of Object.values(imported)) {\n if (!this.isHandler(value)) continue;\n this.registerHandler(value, relativePath);\n this.hmrHandler?.trackHandler(fullPath, value);\n }\n });\n }\n\n private async loadMiddlewares(dir: string): Promise<void> {\n await traverseDirectory(dir, (fullPath, relativePath, imported) => {\n for (const value of Object.values(imported)) {\n if (!this.isMiddleware(value)) continue;\n this.registerMiddleware(value, relativePath);\n this.hmrHandler?.trackMiddleware(fullPath, value);\n }\n });\n }\n\n private reportLoad(): void {\n const { utils } = this.logger;\n\n if (this.loadedMiddlewares.length > 0) {\n utils.block('Loaded middlewares', utils.entries(this.loadedMiddlewares), 'debug');\n }\n\n utils.block(\n 'Loaded handlers',\n [...utils.entries(this.loadedHandlers), ...utils.counts({ routes: this.rowOwners.size })],\n 'debug'\n );\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n await this.hmrHandler?.handle(event);\n }\n\n public warnUnhandledRoutes(commandLeaves: Iterable<string>): void {\n this.warnMissing(commandLeaves, this.maps[InteractionKind.Slash], 'Slash route', '@SlashRoute');\n }\n\n public warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void {\n const user = this.maps[InteractionKind.UserContextMenu];\n const message = this.maps[InteractionKind.MessageContextMenu];\n this.warnMissing(leaves.user, user, 'User context menu', '@ContextMenuRoute');\n this.warnMissing(leaves.message, message, 'Message context menu', '@ContextMenuRoute');\n }\n\n private warnMissing(names: Iterable<string>, map: RouteMap, label: string, decorator: string): void {\n for (const name of names) {\n if (map.has(name)) continue;\n this.logger.warn(`${label} ${paint.sky.bold(name)} has no registered ${paint.bold(decorator)} handler.`);\n }\n }\n\n private isHandler(value: unknown): value is HandlerConstructor {\n if (typeof value !== 'function') return false;\n // this package's own family bases, so a gateway handler in the same dir stays unregistered\n return (\n (value.prototype instanceof InteractionHandler || value.prototype instanceof AutocompleteHandler) &&\n Reflect.hasMetadata(InteractionMetadataKey, value)\n );\n }\n\n private isMiddleware(value: unknown): value is InteractionMiddlewareConstructor {\n if (typeof value !== 'function') return false;\n return (\n value.prototype instanceof InteractionMiddleware &&\n Reflect.hasMetadata(InteractionMiddlewareMetadataKey, value)\n );\n }\n\n private registerMiddleware(ctor: InteractionMiddlewareConstructor, relativePath: string): void {\n const metadata = this.middlewares.register(ctor);\n if (!metadata || !this.loading) return;\n\n // the kinds are the only place a dev sees that a middleware is scoped\n const scope = metadata.keys ? `${metadata.priority}, ${metadata.keys.join(', ')}` : metadata.priority;\n this.loadedMiddlewares.push({\n name: `${ctor.name} (${String(scope)})`,\n from: formatFilePath(relativePath)\n });\n }\n\n private unregisterMiddleware(ctor: InteractionMiddlewareConstructor): void {\n this.middlewares.unregister(ctor);\n }\n\n private getArtifacts(ctor: HandlerConstructor): string[] {\n const routeIds: string[] = [];\n for (const [routeId, owner] of this.rowOwners) {\n if (owner.ctor === ctor) routeIds.push(routeId);\n }\n return routeIds;\n }\n\n private registerHandler(ctor: HandlerConstructor, relativePath: string): void {\n const from = formatFilePath(relativePath);\n // a partial registration would orphan routes and break hmr rollback\n const writes: { kind: InteractionKind; key: string }[] = [];\n\n for (const [kind, keys] of interactionRoutesOf(ctor)) {\n for (const key of keys) {\n const routeId = `${kind}:${key}`;\n const existing = this.rowOwners.get(routeId);\n // a different class on the same route would silently shadow the existing one (last write wins)\n if (existing && existing.ctor !== ctor) {\n throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [\n routeId,\n `${existing.ctor.name} (${existing.from})`,\n `${ctor.name} (${from})`\n ]);\n }\n writes.push({ kind, key });\n }\n }\n\n if (writes.length === 0) return;\n for (const { kind, key } of writes) {\n const routeId = `${kind}:${key}`;\n this.maps[kind].set(key, { kind, routeId, load: () => Promise.resolve(ctor) });\n this.rowOwners.set(routeId, { ctor, kind, key, from });\n }\n\n if (this.loading) this.loadedHandlers.push({ name: ctor.name, from });\n }\n\n private unregisterHandler(ctor: HandlerConstructor, artifacts?: string[]): void {\n const routeIds = artifacts ?? this.getArtifacts(ctor);\n for (const routeId of routeIds) {\n const owner = this.rowOwners.get(routeId);\n if (owner?.ctor !== ctor) continue;\n this.rowOwners.delete(routeId);\n this.maps[owner.kind].delete(owner.key);\n }\n }\n}\n","import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'node:http';\n\nfunction toHeaders(raw: IncomingHttpHeaders): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(raw)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) headers.append(name, item);\n } else {\n headers.set(name, value);\n }\n }\n return headers;\n}\n\n// buffers the raw body before any parse so ed25519 verifies the exact signed bytes\nexport async function toWebRequest(request: IncomingMessage): Promise<Request> {\n const chunks: Buffer[] = [];\n // justified: the async iterator is typed any and IncomingMessage yields Buffer chunks\n for await (const chunk of request) chunks.push(chunk as Buffer);\n\n const method = request.method ?? 'GET';\n const body = method === 'GET' || method === 'HEAD' ? undefined : new Uint8Array(Buffer.concat(chunks));\n\n return new Request(`http://${request.headers.host ?? 'localhost'}${request.url ?? '/'}`, {\n method,\n headers: toHeaders(request.headers),\n ...(body !== undefined && { body })\n });\n}\n\nexport async function writeWebResponse(response: Response, out: ServerResponse): Promise<void> {\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers);\n // iterating Headers comma-joins same-name values, which corrupts Set-Cookie (a per-cookie header)\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) headers['set-cookie'] = cookies;\n\n out.writeHead(response.status, headers);\n out.end(response.body === null ? undefined : Buffer.from(await response.arrayBuffer()));\n}\n","import { once } from 'node:events';\nimport { createServer } from 'node:http';\n\nimport { REST } from '@discordjs/rest';\nimport { Bus } from '@seedcord/core';\nimport {\n busLoggerOf,\n getDevChannel,\n HmrManager,\n interactionMiddleware,\n MiddlewareRegistry,\n setBotColor\n} from '@seedcord/core/internal';\nimport { CoordinatedShutdown, CoordinatedStartup, Pluggable } from '@seedcord/core/node';\nimport {\n CommandRegistry,\n DRAIN_TASK_TIMEOUT_MS,\n DRAIN_WINDOW_MS,\n drainInFlight,\n ShutdownPhase,\n shutdownOf,\n StartupPhase,\n SubscriberLoader\n} from '@seedcord/core/node/internal';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { applicationIdFromToken, SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger, LoggerChannelRegistry } from '@seedcord/logger';\nimport { installNodeDefaults } from '@seedcord/logger/node';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { HostAugmentTarget, HostVersion, SeedcordBrand } from '@seedcord/types/internal';\nimport { Routes } from 'discord-api-types/v10';\nimport { Envapter } from 'envapt';\n\nimport { buildRouteMaps } from '#src/dispatch/resolve';\nimport { EmojiInjector } from '#src/emojis/EmojiInjector';\nimport { buildEngine } from '#src/engine';\nimport { EMPTY_MANIFEST } from '#src/manifest/RouteManifest';\n\nimport { InteractionDispatcher } from './InteractionDispatcher';\nimport { toWebRequest, writeWebResponse } from './webBridge';\nimport { version as packageVersion } from '../version';\n\nimport type { InteractionMiddlewareConstructor } from '#handlers/constructors';\nimport type { HttpConfig } from '#interfaces/Config';\nimport type { Core } from '#interfaces/Core';\nimport type { IRateLimiter } from '@seedcord/types';\nimport type { SeedcordInstance } from '@seedcord/types/internal';\nimport type { Server } from 'node:http';\nimport type { AddressInfo } from 'node:net';\n\nconst DEFAULT_PORT = 3000;\nconst SERVER_SHUTDOWN_TIMEOUT_MS = 5000;\n\ntype RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends { runtime: 'edge' } ? 'edge' : 'server';\n\n/**\n * The HTTP-interactions bot host, a long-running node server around the engine.\n *\n * Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on\n * `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls\n * `createSeedcord` from a generated entry.\n */\nexport class Seedcord<Cfg extends HttpConfig = HttpConfig>\n extends Pluggable<'http', RuntimeOfConfig<Cfg>>\n implements Core, SeedcordInstance\n{\n // the CLI reads these to detect and augment the instance\n /** @internal */\n public readonly [SeedcordBrand] = true;\n /** @internal */\n public readonly [HostAugmentTarget] = '@seedcord/http';\n /** @internal */\n public readonly [HostVersion]: string = packageVersion;\n\n /** Workerd-compatible Discord REST client. `start()` sets the token. */\n public readonly rest = new REST();\n\n /** @see {@link IRateLimiter} */\n public readonly rateLimiter: IRateLimiter;\n\n /** @see {@link Bus} */\n public readonly bus: Bus;\n\n private readonly subscribers: SubscriberLoader;\n\n private readonly interactions?: InteractionDispatcher;\n private readonly commandRegistry?: CommandRegistry;\n private readonly emojiInjector = new EmojiInjector(this);\n private readonly hmrManager: HmrManager;\n private readonly logger = new Logger('Server', { channel: 'bot' });\n\n private token?: string;\n private server?: Server;\n private boundPort?: number;\n private fetchedUsername?: string | undefined;\n\n constructor(public readonly config: Cfg) {\n super(new CoordinatedShutdown(config.lifecycle?.shutdownDeadline), new CoordinatedStartup());\n\n installNodeDefaults(config.logger);\n setBotColor(config.botColor);\n\n this.hmrManager = new HmrManager();\n this.hmrManager.init();\n\n const interactions = this.config.bot.interactions;\n if (interactions.path) {\n this.interactions = new InteractionDispatcher(interactions.path, interactions.middlewares);\n }\n\n if (this.config.bot.commands.path) this.commandRegistry = new CommandRegistry(this);\n\n this.rateLimiter = config.store ?? new MemoryRateLimiter();\n this.bus = new Bus(this);\n this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);\n\n this.registerStartupTasks();\n }\n\n /** The bot's discord username, populated by the ready fetch. */\n public get username(): string | undefined {\n return this.fetchedUsername;\n }\n\n /** The bound server port, populated once `start()` is listening. */\n public get port(): number | undefined {\n return this.boundPort;\n }\n\n /**\n * Starts the host and runs the startup tasks.\n */\n public async start(): Promise<this> {\n try {\n await super.init();\n } catch (caught) {\n await shutdownOf(this).run(1, false);\n Seedcord.reset(this);\n throw caught;\n }\n return this;\n }\n\n protected static override reset(host?: object): boolean {\n if (!super.reset(host)) return false;\n // super.reset() drops the dev TUI's log sink\n LoggerChannelRegistry.instance.configure({});\n return true;\n }\n\n private registerStartupTasks(): void {\n if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();\n\n this.startup.addTask(StartupPhase.Configuration, 'bus-initialization', async () => {\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'start');\n await this.subscribers.init();\n busLoggerOf(this.bus).utils.initialization('Subscribers', 'end');\n });\n\n const { interactions } = this;\n if (interactions) {\n this.startup.addTask(StartupPhase.Configuration, 'interactions-initialization', async () => {\n interactions.logger.utils.initialization('Interactions', 'start');\n await interactions.init();\n interactions.logger.utils.initialization('Interactions', 'end');\n });\n }\n\n this.startup.addTask(StartupPhase.Configuration, 'authenticate', () => {\n this.authenticate();\n return Promise.resolve();\n });\n\n // needs the token from Configuration, and must finish before Ready opens the server to interactions\n this.startup.addTask(StartupPhase.Login, 'emoji-injection', () => this.emojiInjector.init());\n\n const { commandRegistry } = this;\n if (commandRegistry) {\n // one task because tasks in a phase run concurrently and the deploy reads the id\n this.startup.addTask(StartupPhase.Login, 'command-deploy', async () => {\n await commandRegistry.init();\n await commandRegistry.setCommands();\n interactions?.warnUnhandledRoutes(commandRegistry.routeLeaves());\n interactions?.warnUnhandledContextMenuRoutes(commandRegistry.contextMenuLeaves());\n });\n }\n\n this.startup.addTask(StartupPhase.Ready, 'http-server', () => this.listen());\n\n if (!Envapter.isTest) {\n this.startup.addTask(StartupPhase.Ready, 'identity', () => this.fetchUsername());\n }\n }\n\n private registerHmrAwareModules(): void {\n this.startup.addTask(StartupPhase.Configuration, 'hmr-registration', async () => {\n if (this.interactions) this.hmrManager.register(this.interactions);\n if (this.commandRegistry) this.hmrManager.register(this.commandRegistry);\n this.hmrManager.register(this.subscribers);\n for (const plugin of this.plugins) {\n this.hmrManager.register(plugin);\n }\n await Promise.resolve();\n });\n }\n\n private authenticate(): void {\n this.token = validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n this.rest.setToken(this.token);\n }\n\n /** The bot's Discord application id. Throws if you read it before the Configuration phase. */\n public get applicationId(): string {\n if (!this.token) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);\n return applicationIdFromToken(this.token);\n }\n\n private async listen(): Promise<void> {\n const maps = this.interactions?.maps ?? buildRouteMaps(EMPTY_MANIFEST);\n const middlewares =\n this.interactions?.middlewares ??\n new MiddlewareRegistry<InteractionMiddlewareConstructor>(interactionMiddleware);\n const { handle, inFlight } = buildEngine(this, maps, middlewares);\n\n const server = createServer((incoming, outgoing) => {\n void (async () => {\n const response = await handle(await toWebRequest(incoming));\n await writeWebResponse(response, outgoing);\n })().catch((error: unknown) => {\n // a swallowed throw would hang the client with no cause\n outgoing.destroy(Error.isError(error) ? error : new Error(String(error)));\n });\n });\n this.server = server;\n\n server.listen(this.config.port ?? DEFAULT_PORT);\n await once(server, 'listening');\n // justified: address() is AddressInfo once a TCP server is listening\n this.boundPort = (server.address() as AddressInfo).port;\n this.logger.info(`Interactions server listening on port ${paint.sky.bold(String(this.boundPort))}`);\n getDevChannel()?.send('seedcord:server-listening', { port: this.boundPort });\n\n this.shutdown.addTask(\n ShutdownPhase.Unbind,\n 'stop-http-server',\n () => this.stopServer(),\n SERVER_SHUTDOWN_TIMEOUT_MS\n );\n // Unbind already ran, so every accepted request is in the in-flight set here\n this.shutdown.addTask(\n ShutdownPhase.Drain,\n 'drain-inflight',\n () => drainInFlight(inFlight, DRAIN_WINDOW_MS, this.logger, 'Interactions'),\n DRAIN_TASK_TIMEOUT_MS\n );\n }\n\n private async fetchUsername(): Promise<void> {\n try {\n // justified: the @me payload carries username per the discord api contract\n const me = (await this.rest.get(Routes.user('@me'))) as { username?: string };\n this.fetchedUsername = me.username;\n if (me.username) this.logger.info(`Running as ${paint.sky.bold(me.username)}`);\n } catch (caught) {\n // a bad token errors on the first real send anyway\n this.logger.warn('could not fetch the bot identity', caught);\n }\n }\n\n private stopServer(): Promise<void> {\n const server = this.server;\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolveClose, rejectClose) => {\n server.close((err) => {\n if (err) {\n rejectClose(err);\n return;\n }\n this.logger.info(paint.coral.bold('Interactions server stopped'));\n resolveClose();\n });\n // node's close() waits out idle keep-alive sockets\n server.closeIdleConnections();\n });\n }\n}\n","import { Plugin as CorePlugin } from '@seedcord/core/plugin';\n\nimport type { Core } from '#interfaces/Core';\nimport type { PluginOptions } from '@seedcord/core/plugin';\nimport type { TypedOmit } from '@seedcord/types';\n\n/** The options an HTTP plugin declares, narrowed to the transports this base serves. */\nexport type HttpPluginOptions = TypedOmit<PluginOptions, 'transport'> & { transport?: 'http' };\n\n/**\n * Base class for an HTTP plugin, binding `this.core` to the HTTP {@link Core}.\n *\n * A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose\n * `this.core` is typed to the shared members only.\n *\n * @typeParam Opts - The transport and runtime the plugin declares.\n */\nexport abstract class Plugin<Opts extends HttpPluginOptions = { transport: 'http' }> extends CorePlugin<Opts, Core> {}\n\nexport type { PluginLifecycleSpec, PluginOptions } from '@seedcord/core/plugin';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@seedcord/http",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.8.1",
5
5
  "description": "HTTP interactions transport for seedcord bots",
6
6
  "keywords": [
7
7
  "discord",
@@ -55,14 +55,14 @@
55
55
  "@discordjs/collection": "^2.1.1",
56
56
  "@discordjs/core": "^2.6.0",
57
57
  "@discordjs/rest": "^2.6.3",
58
- "@seedcord/core": "^0.6.0",
59
- "@seedcord/custom-id": "^0.2.0",
60
- "@seedcord/errors": "^0.7.0",
58
+ "@seedcord/core": "^0.8.0",
59
+ "@seedcord/custom-id": "^0.2.2",
60
+ "@seedcord/errors": "^0.9.0",
61
61
  "@seedcord/event-emitter": "^0.1.5",
62
- "@seedcord/logger": "^0.3.1",
63
- "@seedcord/rate-limiter": "^0.1.7",
64
- "@seedcord/types": "^0.12.0",
65
- "@seedcord/utils": "^0.8.10",
62
+ "@seedcord/logger": "^0.3.3",
63
+ "@seedcord/rate-limiter": "^0.1.9",
64
+ "@seedcord/types": "^0.14.0",
65
+ "@seedcord/utils": "^0.8.12",
66
66
  "discord-api-types": "^0.38.52",
67
67
  "reflect-metadata": "^0.2.2",
68
68
  "type-fest": "^5.8.0"
@@ -74,7 +74,7 @@
74
74
  "devDependencies": {
75
75
  "@cloudflare/vitest-pool-workers": "^0.20.1",
76
76
  "@discordjs/builders": "^1.14.1",
77
- "@seedcord/eslint-config": "^2.2.0",
77
+ "@seedcord/eslint-config": "^2.2.1",
78
78
  "@seedcord/tsconfig": "^2.0.3",
79
79
  "@seedcord/tsdown-config": "^2.0.1",
80
80
  "@seedcord/vitest-config": "^0.0.1",