@seedcord/http 0.1.0 → 0.2.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/{RouteManifest-DE-HCMwd.d.mts → RouteManifest-4jc-Lpnm.d.mts} +1 -2
- package/dist/{edge.index-fqANtbsm.mjs → edge.index-C10jvlZK.mjs} +44 -29
- package/dist/edge.index-C10jvlZK.mjs.map +1 -0
- package/dist/{edge.index-DR0Bjfjq.d.mts → edge.index-CrVektFx.d.mts} +16 -15
- package/dist/{edge.index-CweiaQ5R.cjs → edge.index-He_ZHDqC.cjs} +44 -28
- package/dist/edge.index-He_ZHDqC.cjs.map +1 -0
- package/dist/edge.index.cjs +1 -1
- package/dist/edge.index.d.mts +2 -2
- package/dist/edge.index.mjs +1 -1
- package/dist/index.cjs +11 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +5 -12
- package/dist/index.mjs +13 -48
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.index.d.mts +1 -2
- package/package.json +16 -16
- package/dist/edge.index-CweiaQ5R.cjs.map +0 -1
- package/dist/edge.index-fqANtbsm.mjs.map +0 -1
- package/dist/rolldown-runtime-BUUfjzRC.mjs +0 -36
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_edge_index = require('./edge.index-
|
|
2
|
+
const require_edge_index = require('./edge.index-He_ZHDqC.cjs');
|
|
3
|
+
require("reflect-metadata");
|
|
3
4
|
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
4
5
|
let envapt = require("envapt");
|
|
5
6
|
let _discordjs_rest = require("@discordjs/rest");
|
|
@@ -81,7 +82,7 @@ var InteractionDispatcher = class {
|
|
|
81
82
|
} finally {
|
|
82
83
|
this.loading = false;
|
|
83
84
|
}
|
|
84
|
-
this.logger.utils.block("Loaded handlers", [...this.logger.utils.entries(this.loadedHandlers), ...this.logger.utils.counts({ routes: this.rowOwners.size })]);
|
|
85
|
+
this.logger.utils.block("Loaded handlers", [...this.logger.utils.entries(this.loadedHandlers), ...this.logger.utils.counts({ routes: this.rowOwners.size })], "debug");
|
|
85
86
|
}
|
|
86
87
|
/** @internal */
|
|
87
88
|
async onHmr(event) {
|
|
@@ -195,7 +196,6 @@ var InteractionDispatcher = class {
|
|
|
195
196
|
name: ctor.name,
|
|
196
197
|
from
|
|
197
198
|
});
|
|
198
|
-
else this.logger.utils.registration(ctor.name, from);
|
|
199
199
|
}
|
|
200
200
|
unregisterHandler(ctor, artifacts) {
|
|
201
201
|
const routeIds = artifacts ?? this.getArtifacts(ctor);
|
|
@@ -247,7 +247,7 @@ const DRAIN_TIMEOUT_MS = 1e4;
|
|
|
247
247
|
* The HTTP-interactions bot host, a long-running node server around the engine.
|
|
248
248
|
*
|
|
249
249
|
* Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on
|
|
250
|
-
* `start(
|
|
250
|
+
* `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls
|
|
251
251
|
* `createSeedcord` from a generated entry.
|
|
252
252
|
*/
|
|
253
253
|
var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
@@ -270,12 +270,10 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
270
270
|
appId;
|
|
271
271
|
appIdPromise;
|
|
272
272
|
emojiInjector = new require_edge_index.EmojiInjector(this, () => this.applicationId());
|
|
273
|
-
healthCheck;
|
|
274
273
|
hmrManager;
|
|
275
274
|
logger = new _seedcord_logger.Logger("Server", { channel: "bot" });
|
|
276
275
|
server;
|
|
277
276
|
boundPort;
|
|
278
|
-
requestedPort = DEFAULT_PORT;
|
|
279
277
|
fetchedUsername;
|
|
280
278
|
constructor(config) {
|
|
281
279
|
super(new _seedcord_core_node_internal.CoordinatedShutdown(), new _seedcord_core_node_internal.CoordinatedStartup());
|
|
@@ -301,7 +299,6 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
301
299
|
this.rateLimiter = config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter();
|
|
302
300
|
this.bus = new _seedcord_core.Bus(this);
|
|
303
301
|
this.subscribers = new _seedcord_core_node_internal.SubscriberLoader(this.bus, config.subscribers.path);
|
|
304
|
-
this.healthCheck = config.runtime === "edge" ? void 0 : _seedcord_core_node_internal.HealthCheck.fromOption(this.shutdown, config.healthCheck);
|
|
305
302
|
this.registerStartupTasks();
|
|
306
303
|
}
|
|
307
304
|
/** The bot's discord username, populated by the ready fetch. */
|
|
@@ -314,11 +311,8 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
314
311
|
}
|
|
315
312
|
/**
|
|
316
313
|
* Starts the host and runs the startup tasks.
|
|
317
|
-
*
|
|
318
|
-
* @param port - The port the interaction server binds. {@default `3000` }
|
|
319
314
|
*/
|
|
320
|
-
async start(
|
|
321
|
-
this.requestedPort = port;
|
|
315
|
+
async start() {
|
|
322
316
|
try {
|
|
323
317
|
await super.init();
|
|
324
318
|
} catch (caught) {
|
|
@@ -330,7 +324,7 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
330
324
|
}
|
|
331
325
|
static reset() {
|
|
332
326
|
super.reset();
|
|
333
|
-
_seedcord_logger.LoggerChannelRegistry.instance.
|
|
327
|
+
_seedcord_logger.LoggerChannelRegistry.instance.configure({});
|
|
334
328
|
}
|
|
335
329
|
registerStartupTasks() {
|
|
336
330
|
if (envapt.Envapter.isDevelopment || envapt.Envapter.isTest) this.registerHmrAwareModules();
|
|
@@ -360,12 +354,6 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
360
354
|
});
|
|
361
355
|
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Ready, "http-server", () => this.listen());
|
|
362
356
|
if (!envapt.Envapter.isTest) this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Ready, "identity", () => this.fetchUsername());
|
|
363
|
-
const { healthCheck } = this;
|
|
364
|
-
if (healthCheck) this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Ready, "health-check", async () => {
|
|
365
|
-
healthCheck.logger.utils.initialization("HealthCheck", "start");
|
|
366
|
-
await healthCheck.init();
|
|
367
|
-
healthCheck.logger.utils.initialization("HealthCheck", "end");
|
|
368
|
-
});
|
|
369
357
|
}
|
|
370
358
|
registerHmrAwareModules() {
|
|
371
359
|
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "hmr-registration", async () => {
|
|
@@ -392,16 +380,18 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
392
380
|
const { handle, inFlight } = require_edge_index.buildEngine(this, maps);
|
|
393
381
|
const server = (0, node_http.createServer)((incoming, outgoing) => {
|
|
394
382
|
(async () => {
|
|
395
|
-
|
|
383
|
+
const response = await handle(await toWebRequest(incoming));
|
|
384
|
+
await writeWebResponse(response, outgoing);
|
|
396
385
|
})().catch((error) => {
|
|
397
386
|
outgoing.destroy(Error.isError(error) ? error : new Error(String(error)));
|
|
398
387
|
});
|
|
399
388
|
});
|
|
400
389
|
this.server = server;
|
|
401
|
-
server.listen(this.
|
|
390
|
+
server.listen(this.config.port ?? DEFAULT_PORT);
|
|
402
391
|
await (0, node_events.once)(server, "listening");
|
|
403
392
|
this.boundPort = server.address().port;
|
|
404
393
|
this.logger.info(`Interactions server listening on port ${_seedcord_logger.paint.sky.bold(String(this.boundPort))}`);
|
|
394
|
+
(0, _seedcord_core_internal.getDevChannel)()?.send("seedcord:server-listening", { port: this.boundPort });
|
|
405
395
|
this.shutdown.addTask(_seedcord_core_node_internal.ShutdownPhase.Unbind, "stop-http-server", () => this.stopServer(), SERVER_SHUTDOWN_TIMEOUT_MS);
|
|
406
396
|
this.shutdown.addTask(_seedcord_core_node_internal.ShutdownPhase.Drain, "drain-inflight", async () => {
|
|
407
397
|
await Promise.allSettled(inFlight);
|
|
@@ -411,6 +401,7 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
411
401
|
try {
|
|
412
402
|
const me = await this.rest.get(discord_api_types_v10.Routes.user("@me"));
|
|
413
403
|
this.fetchedUsername = me.username;
|
|
404
|
+
if (me.username) this.logger.info(`Running as ${_seedcord_logger.paint.iris.bold(me.username)}`);
|
|
414
405
|
} catch (caught) {
|
|
415
406
|
this.logger.warn("could not fetch the bot identity", caught);
|
|
416
407
|
}
|
|
@@ -444,31 +435,6 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
444
435
|
*/
|
|
445
436
|
var Plugin = class extends _seedcord_core_plugin.Plugin {};
|
|
446
437
|
|
|
447
|
-
//#endregion
|
|
448
|
-
//#region src/index.ts
|
|
449
|
-
var src_exports = /* @__PURE__ */ require_edge_index.__exportAll({
|
|
450
|
-
ArraySource: () => require_edge_index.ArraySource,
|
|
451
|
-
AutocompleteHandler: () => require_edge_index.AutocompleteHandler,
|
|
452
|
-
ButtonHandler: () => require_edge_index.ButtonHandler,
|
|
453
|
-
ComponentHandler: () => require_edge_index.ComponentHandler,
|
|
454
|
-
ContextMenuHandler: () => require_edge_index.ContextMenuHandler,
|
|
455
|
-
CursorSource: () => require_edge_index.CursorSource,
|
|
456
|
-
Emojis: () => require_edge_index.Emojis,
|
|
457
|
-
Gated: () => require_edge_index.Gated,
|
|
458
|
-
InteractionHandler: () => require_edge_index.InteractionHandler,
|
|
459
|
-
ModalHandler: () => require_edge_index.ModalHandler,
|
|
460
|
-
Paginator: () => require_edge_index.Paginator,
|
|
461
|
-
Plugin: () => Plugin,
|
|
462
|
-
RepliableHandler: () => require_edge_index.RepliableHandler,
|
|
463
|
-
Seedcord: () => Seedcord,
|
|
464
|
-
SelectMenuHandler: () => require_edge_index.SelectMenuHandler,
|
|
465
|
-
SlashHandler: () => require_edge_index.SlashHandler,
|
|
466
|
-
Subscriber: () => require_edge_index.Subscriber,
|
|
467
|
-
WebhookLog: () => require_edge_index.WebhookLog,
|
|
468
|
-
createSeedcord: () => require_edge_index.createSeedcord,
|
|
469
|
-
version: () => require_edge_index.version
|
|
470
|
-
});
|
|
471
|
-
|
|
472
438
|
//#endregion
|
|
473
439
|
exports.ArraySource = require_edge_index.ArraySource;
|
|
474
440
|
exports.AutocompleteHandler = require_edge_index.AutocompleteHandler;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Logger","buildRouteMaps","Envapter","HmrModuleHandler","paint","InteractionHandler","AutocompleteHandler","InteractionMetadataKey","InteractionRoutes","SeedcordError","SeedcordErrorCode","Pluggable","SeedcordBrand","packageVersion","REST","EmojiInjector","Logger","CoordinatedShutdown","CoordinatedStartup","HmrManager","CommandInjector","CommandRegistry","MemoryRateLimiter","Bus","SubscriberLoader","HealthCheck","Envapter","StartupPhase","fetchApplicationId","SeedcordError","SeedcordErrorCode","buildRouteMaps","buildEngine","paint","ShutdownPhase","Routes","CorePlugin"],"sources":["../src/manifest/RouteManifest.ts","../src/node/InteractionDispatcher.ts","../src/node/webBridge.ts","../src/node/Seedcord.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import type { SelectMenuKind, 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\n | ApplicationCommandType.User\n | ApplicationCommandType.Message;\n\n/** One file may export several handlers, so the row states which export it registers. */\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: 'button' | `${SelectMenuKind}Select` | 'modal';\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, so it carries no lookup key. */\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 { HmrModuleHandler } from '@seedcord/core/hmr';\nimport { interactionRoutesOf, InteractionMetadataKey, InteractionRoutes } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger, paint } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\nimport { traverseDirectory } from '@seedcord/utils/node';\nimport chalk from 'chalk';\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 { Initializeable } from '@seedcord/core';\nimport type { ContextMenuLeaves } from '@seedcord/core/internal';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { ResolvedRoute, RouteMap, RouteMaps } from '@src/dispatch/resolve';\n\ninterface RouteTarget {\n readonly map: RouteMap;\n readonly kind: ResolvedRoute['kind'];\n}\n\n/**\n * Discovers interaction handlers from the filesystem into the engine's live route maps.\n *\n * The node host runs it at startup and dev HMR swaps map entries in place. The engine's `resolve`\n * reads `maps` per request. The edge path builds its maps from the generated manifest.\n */\nexport class InteractionDispatcher implements Initializeable, HmrAware {\n public readonly maps: RouteMaps;\n private readonly targets: Record<InteractionRoutes, RouteTarget>;\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; target: RouteTarget; 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 this.targets = this.buildTargets();\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('Loaded handlers', [\n ...this.logger.utils.entries(this.loadedHandlers),\n ...this.logger.utils.counts({ routes: this.rowOwners.size })\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.slash, 'Slash route', '@SlashRoute');\n }\n\n public warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void {\n this.warnMissing(leaves.user, this.maps.userContextMenu, 'User context menu', '@ContextMenuRoute');\n this.warnMissing(leaves.message, this.maps.messageContextMenu, '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 ${chalk.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. 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 targetFor(route: InteractionRoutes): RouteTarget {\n return this.targets[route];\n }\n\n private buildTargets(): Record<InteractionRoutes, RouteTarget> {\n return {\n [InteractionRoutes.Slash]: { map: this.maps.slash, kind: 'slash' },\n [InteractionRoutes.UserContextMenu]: { map: this.maps.userContextMenu, kind: 'userContextMenu' },\n [InteractionRoutes.MessageContextMenu]: { map: this.maps.messageContextMenu, kind: 'messageContextMenu' },\n [InteractionRoutes.Autocomplete]: { map: this.maps.autocomplete, kind: 'autocomplete' },\n [InteractionRoutes.Button]: { map: this.maps.components.button, kind: 'button' },\n [InteractionRoutes.StringMenu]: { map: this.maps.components.stringSelect, kind: 'stringMenu' },\n [InteractionRoutes.UserMenu]: { map: this.maps.components.userSelect, kind: 'userMenu' },\n [InteractionRoutes.RoleMenu]: { map: this.maps.components.roleSelect, kind: 'roleMenu' },\n [InteractionRoutes.ChannelMenu]: { map: this.maps.components.channelSelect, kind: 'channelMenu' },\n [InteractionRoutes.MentionableMenu]: {\n map: this.maps.components.mentionableSelect,\n kind: 'mentionableMenu'\n },\n [InteractionRoutes.Modal]: { map: this.maps.components.modal, kind: 'modal' }\n };\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: [RouteTarget, string][] = [];\n\n for (const [route, keys] of interactionRoutesOf(ctor)) {\n const target = this.targetFor(route);\n for (const key of keys) {\n const routeId = `${target.kind}:${key}`;\n const existing = this.rowOwners.get(routeId);\n // a different class on the same route would silently shadow (last write wins), so this throws\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([target, key]);\n }\n }\n\n if (writes.length === 0) return;\n for (const [target, key] of writes) {\n const routeId = `${target.kind}:${key}`;\n target.map.set(key, { kind: target.kind, routeId, load: () => Promise.resolve(ctor) });\n this.rowOwners.set(routeId, { ctor, target, key, from });\n }\n\n if (this.loading) this.loadedHandlers.push({ name: ctor.name, from });\n else this.logger.utils.registration(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 owner.target.map.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 { CommandInjector, HmrManager, setBotColor } from '@seedcord/core/internal';\nimport {\n CommandRegistry,\n CoordinatedShutdown,\n CoordinatedStartup,\n HealthCheck,\n Pluggable,\n ShutdownPhase,\n StartupPhase,\n SubscriberLoader\n} from '@seedcord/core/node/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger, LoggerChannelRegistry, paint } from '@seedcord/logger';\nimport { installNodeDefaults } from '@seedcord/logger/node';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { SeedcordBrand } from '@seedcord/types/internal';\nimport { Routes } from 'discord-api-types/v10';\nimport { Envapter } from 'envapt';\n\nimport { fetchApplicationId } from '@src/applicationId';\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;\nconst DRAIN_TIMEOUT_MS = 10_000;\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(port)`, 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 augmentTarget = '@seedcord/http';\n /** @internal */\n public readonly version: 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 appId?: string;\n private appIdPromise?: Promise<string>;\n private readonly emojiInjector = new EmojiInjector(this, () => this.applicationId());\n private readonly healthCheck?: HealthCheck | undefined;\n private readonly hmrManager: HmrManager;\n private readonly logger = new Logger('Server', { channel: 'bot' });\n\n private server?: Server;\n private boundPort?: number;\n private requestedPort = DEFAULT_PORT;\n private fetchedUsername?: string | undefined;\n\n constructor(public readonly config: Cfg) {\n super(new CoordinatedShutdown(), 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 const commandsDir = this.config.bot.commands.path;\n if (commandsDir) {\n const injector = new CommandInjector();\n // onDeployed only fires at deploy time, so registry is assigned by then\n const registry: CommandRegistry = new CommandRegistry({\n dir: commandsDir,\n rest: this.rest,\n applicationId: () => this.requireApplicationId(),\n onDeployed: (result) => {\n injector.inject(result, registry.allCommands());\n }\n });\n this.commandRegistry = registry;\n }\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 // the edge arm types healthCheck never, and a dev run of an edge config needs no health server\n this.healthCheck =\n config.runtime === 'edge' ? undefined : HealthCheck.fromOption(this.shutdown, config.healthCheck);\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 * @param port - The port the interaction server binds. {@default `3000` }\n */\n public async start(port = DEFAULT_PORT): Promise<this> {\n this.requestedPort = port;\n try {\n await super.init();\n } catch (caught) {\n // shutdown releases any resource opened before the failure, then rethrow\n await this.shutdown.run(1, false);\n Seedcord.reset();\n throw caught;\n }\n return this;\n }\n\n protected static override reset(): void {\n super.reset();\n LoggerChannelRegistry.instance.reset();\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 this.bus.logger.utils.initialization('Subscribers', 'start');\n await this.subscribers.init();\n this.bus.logger.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 within a phase run concurrently and the deploy reads the id\n this.startup.addTask(StartupPhase.Login, 'command-deploy', async () => {\n await commandRegistry.init();\n this.appId = await this.applicationId();\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 const { healthCheck } = this;\n if (healthCheck) {\n this.startup.addTask(StartupPhase.Ready, 'health-check', async () => {\n healthCheck.logger.utils.initialization('HealthCheck', 'start');\n await healthCheck.init();\n healthCheck.logger.utils.initialization('HealthCheck', 'end');\n });\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.rest.setToken(validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN')));\n }\n\n // the promise is stored, so two concurrent Login tasks share one fetch\n private applicationId(): Promise<string> {\n this.appIdPromise ??= fetchApplicationId(this.rest);\n return this.appIdPromise;\n }\n\n // the registry deploys on a hot reload too, after the startup task resolved this\n private requireApplicationId(): string {\n if (!this.appId) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);\n return this.appId;\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 request 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.requestedPort);\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\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 async () => {\n await Promise.allSettled(inFlight);\n },\n DRAIN_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 } catch (caught) {\n // a bad token errors on the first real send. The identity fetch stays non-fatal\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 // idle keep-alive sockets would stall close() past the task timeout. Active responses\n // still flush, and the task 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' | 'any' };\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 = {}> extends CorePlugin<Opts, Core> {}\n\nexport type { PluginLifecycleSpec, PluginOptions } from '@seedcord/core/plugin';\n","export * from './edge.index';\nexport { Seedcord } from '@src/node/Seedcord';\n\nexport { Plugin } from './plugin';\nexport type { HttpPluginOptions, PluginLifecycleSpec, PluginOptions } from './plugin';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Logger","buildRouteMaps","Envapter","HmrModuleHandler","traverseDirectory","paint","InteractionHandler","AutocompleteHandler","InteractionMetadataKey","InteractionRoutes","formatFilePath","interactionRoutesOf","SeedcordError","SeedcordErrorCode","Pluggable","SeedcordBrand","packageVersion","REST","EmojiInjector","Logger","CoordinatedShutdown","CoordinatedStartup","HmrManager","CommandInjector","CommandRegistry","MemoryRateLimiter","Bus","SubscriberLoader","Envapter","StartupPhase","validateDiscordToken","fetchApplicationId","SeedcordError","SeedcordErrorCode","buildRouteMaps","buildEngine","createServer","once","paint","ShutdownPhase","Routes","CorePlugin"],"sources":["../src/manifest/RouteManifest.ts","../src/node/InteractionDispatcher.ts","../src/node/webBridge.ts","../src/node/Seedcord.ts","../src/plugin.ts"],"sourcesContent":["import type { SelectMenuKind, 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, so the row states which export it registers. */\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: 'button' | `${SelectMenuKind}Select` | 'modal';\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, so it carries no lookup key. */\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 { HmrModuleHandler } from '@seedcord/core/hmr';\nimport { interactionRoutesOf, InteractionMetadataKey, InteractionRoutes } from '@seedcord/core/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger, paint } from '@seedcord/logger';\nimport { formatFilePath } from '@seedcord/utils';\nimport { traverseDirectory } from '@seedcord/utils/node';\nimport chalk from 'chalk';\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 { Initializeable } from '@seedcord/core';\nimport type { ContextMenuLeaves } from '@seedcord/core/internal';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { ResolvedRoute, RouteMap, RouteMaps } from '@src/dispatch/resolve';\n\ninterface RouteTarget {\n readonly map: RouteMap;\n readonly kind: ResolvedRoute['kind'];\n}\n\n/**\n * Discovers interaction handlers from the filesystem into the engine's live route maps.\n *\n * The node host runs it at startup and dev HMR swaps map entries in place. The engine's `resolve`\n * reads `maps` per request. The edge path builds its maps from the generated manifest.\n */\nexport class InteractionDispatcher implements Initializeable, HmrAware {\n public readonly maps: RouteMaps;\n private readonly targets: Record<InteractionRoutes, RouteTarget>;\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; target: RouteTarget; 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 this.targets = this.buildTargets();\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.slash, 'Slash route', '@SlashRoute');\n }\n\n public warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void {\n this.warnMissing(leaves.user, this.maps.userContextMenu, 'User context menu', '@ContextMenuRoute');\n this.warnMissing(leaves.message, this.maps.messageContextMenu, '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 ${chalk.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. 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 targetFor(route: InteractionRoutes): RouteTarget {\n return this.targets[route];\n }\n\n private buildTargets(): Record<InteractionRoutes, RouteTarget> {\n return {\n [InteractionRoutes.Slash]: { map: this.maps.slash, kind: 'slash' },\n [InteractionRoutes.UserContextMenu]: { map: this.maps.userContextMenu, kind: 'userContextMenu' },\n [InteractionRoutes.MessageContextMenu]: { map: this.maps.messageContextMenu, kind: 'messageContextMenu' },\n [InteractionRoutes.Autocomplete]: { map: this.maps.autocomplete, kind: 'autocomplete' },\n [InteractionRoutes.Button]: { map: this.maps.components.button, kind: 'button' },\n [InteractionRoutes.StringMenu]: { map: this.maps.components.stringSelect, kind: 'stringMenu' },\n [InteractionRoutes.UserMenu]: { map: this.maps.components.userSelect, kind: 'userMenu' },\n [InteractionRoutes.RoleMenu]: { map: this.maps.components.roleSelect, kind: 'roleMenu' },\n [InteractionRoutes.ChannelMenu]: { map: this.maps.components.channelSelect, kind: 'channelMenu' },\n [InteractionRoutes.MentionableMenu]: {\n map: this.maps.components.mentionableSelect,\n kind: 'mentionableMenu'\n },\n [InteractionRoutes.Modal]: { map: this.maps.components.modal, kind: 'modal' }\n };\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: [RouteTarget, string][] = [];\n\n for (const [route, keys] of interactionRoutesOf(ctor)) {\n const target = this.targetFor(route);\n for (const key of keys) {\n const routeId = `${target.kind}:${key}`;\n const existing = this.rowOwners.get(routeId);\n // a different class on the same route would silently shadow (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([target, key]);\n }\n }\n\n if (writes.length === 0) return;\n for (const [target, key] of writes) {\n const routeId = `${target.kind}:${key}`;\n target.map.set(key, { kind: target.kind, routeId, load: () => Promise.resolve(ctor) });\n this.rowOwners.set(routeId, { ctor, target, 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 owner.target.map.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 { CommandInjector, getDevChannel, HmrManager, setBotColor } from '@seedcord/core/internal';\nimport {\n CommandRegistry,\n CoordinatedShutdown,\n CoordinatedStartup,\n Pluggable,\n ShutdownPhase,\n StartupPhase,\n SubscriberLoader\n} from '@seedcord/core/node/internal';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError, validateDiscordToken } from '@seedcord/errors/internal';\nimport { Logger, LoggerChannelRegistry, paint } from '@seedcord/logger';\nimport { installNodeDefaults } from '@seedcord/logger/node';\nimport { MemoryRateLimiter } from '@seedcord/rate-limiter';\nimport { SeedcordBrand } from '@seedcord/types/internal';\nimport { Routes } from 'discord-api-types/v10';\nimport { Envapter } from 'envapt';\n\nimport { fetchApplicationId } from '@src/applicationId';\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;\nconst DRAIN_TIMEOUT_MS = 10_000;\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 augmentTarget = '@seedcord/http';\n /** @internal */\n public readonly version: 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 appId?: string;\n private appIdPromise?: Promise<string>;\n private readonly emojiInjector = new EmojiInjector(this, () => this.applicationId());\n private readonly hmrManager: HmrManager;\n private readonly logger = new Logger('Server', { channel: 'bot' });\n\n private server?: Server;\n private boundPort?: number;\n private fetchedUsername?: string | undefined;\n\n constructor(public readonly config: Cfg) {\n super(new CoordinatedShutdown(), 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 const commandsDir = this.config.bot.commands.path;\n if (commandsDir) {\n const injector = new CommandInjector();\n // onDeployed only fires at deploy time, so registry is assigned by then\n const registry: CommandRegistry = new CommandRegistry({\n dir: commandsDir,\n rest: this.rest,\n applicationId: () => this.requireApplicationId(),\n onDeployed: (result) => {\n injector.inject(result, registry.allCommands());\n }\n });\n this.commandRegistry = registry;\n }\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 // shutdown releases any resource opened before the failure, then rethrow\n await this.shutdown.run(1, false);\n Seedcord.reset();\n throw caught;\n }\n return this;\n }\n\n protected static override reset(): void {\n super.reset();\n // reset() would drop the dev TUI's log sink\n LoggerChannelRegistry.instance.configure({});\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 this.bus.logger.utils.initialization('Subscribers', 'start');\n await this.subscribers.init();\n this.bus.logger.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 within a phase run concurrently and the deploy reads the id\n this.startup.addTask(StartupPhase.Login, 'command-deploy', async () => {\n await commandRegistry.init();\n this.appId = await this.applicationId();\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.rest.setToken(validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN')));\n }\n\n // the promise is stored, so two concurrent Login tasks share one fetch\n private applicationId(): Promise<string> {\n this.appIdPromise ??= fetchApplicationId(this.rest);\n return this.appIdPromise;\n }\n\n // the registry deploys on a hot reload too, after the startup task resolved this\n private requireApplicationId(): string {\n if (!this.appId) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);\n return this.appId;\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 request 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 async () => {\n await Promise.allSettled(inFlight);\n },\n DRAIN_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.iris.bold(me.username)}`);\n } catch (caught) {\n // a bad token errors on the first real send. The identity fetch stays non-fatal\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 // idle keep-alive sockets would stall close() past the task timeout. Active responses\n // still flush, and the task 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' | 'any' };\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 = {}> extends CorePlugin<Opts, Core> {}\n\nexport type { PluginLifecycleSpec, PluginOptions } from '@seedcord/core/plugin';\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { C as createSeedcord, D as HttpEdgeConfig, E as HttpConfig, O as HttpServerConfig, S as SlashOptions, T as Core, _ as InteractionHandler, a as CursorSource, b as InjectedEmojiMap, c as Subscriber, d as SelectMenuHandler, f as ButtonHandler, g as SlashHandler, h as ContextMenuHandler, i as ArraySource, l as WebhookLog, m as AutocompleteHandler, n as Gated, o as Paginator, p as ComponentHandler, r as InteractionGateContext, s as PageContext, t as version, u as ModalHandler, v as RepliableHandler, w as EngineContext, x as ResolvedEmoji, y as Emojis } from "./edge.index-
|
|
2
|
-
import { t as RouteManifest } from "./RouteManifest-
|
|
1
|
+
import { C as createSeedcord, D as HttpEdgeConfig, E as HttpConfig, O as HttpServerConfig, S as SlashOptions, T as Core, _ as InteractionHandler, a as CursorSource, b as InjectedEmojiMap, c as Subscriber, d as SelectMenuHandler, f as ButtonHandler, g as SlashHandler, h as ContextMenuHandler, i as ArraySource, l as WebhookLog, m as AutocompleteHandler, n as Gated, o as Paginator, p as ComponentHandler, r as InteractionGateContext, s as PageContext, t as version, u as ModalHandler, v as RepliableHandler, w as EngineContext, x as ResolvedEmoji, y as Emojis } from "./edge.index-CrVektFx.mjs";
|
|
2
|
+
import { t as RouteManifest } from "./RouteManifest-4jc-Lpnm.mjs";
|
|
3
|
+
import "reflect-metadata";
|
|
3
4
|
import { REST } from "@discordjs/rest";
|
|
4
5
|
import { Bus } from "@seedcord/core";
|
|
5
6
|
import { Pluggable } from "@seedcord/core/node/internal";
|
|
@@ -8,7 +9,6 @@ import { Plugin as Plugin$1, PluginLifecycleSpec, PluginOptions, PluginOptions a
|
|
|
8
9
|
import { IRateLimiter, TypedOmit } from "@seedcord/types";
|
|
9
10
|
export * from "@seedcord/core";
|
|
10
11
|
export * from "@seedcord/types";
|
|
11
|
-
|
|
12
12
|
//#region src/node/Seedcord.d.ts
|
|
13
13
|
type RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends {
|
|
14
14
|
runtime: 'edge';
|
|
@@ -17,7 +17,7 @@ type RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends {
|
|
|
17
17
|
* The HTTP-interactions bot host, a long-running node server around the engine.
|
|
18
18
|
*
|
|
19
19
|
* Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on
|
|
20
|
-
* `start(
|
|
20
|
+
* `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls
|
|
21
21
|
* `createSeedcord` from a generated entry.
|
|
22
22
|
*/
|
|
23
23
|
declare class Seedcord<Cfg extends HttpConfig = HttpConfig> extends Pluggable<'http', RuntimeOfConfig<Cfg>> implements Core, SeedcordInstance {
|
|
@@ -40,12 +40,10 @@ declare class Seedcord<Cfg extends HttpConfig = HttpConfig> extends Pluggable<'h
|
|
|
40
40
|
private appId?;
|
|
41
41
|
private appIdPromise?;
|
|
42
42
|
private readonly emojiInjector;
|
|
43
|
-
private readonly healthCheck?;
|
|
44
43
|
private readonly hmrManager;
|
|
45
44
|
private readonly logger;
|
|
46
45
|
private server?;
|
|
47
46
|
private boundPort?;
|
|
48
|
-
private requestedPort;
|
|
49
47
|
private fetchedUsername?;
|
|
50
48
|
constructor(config: Cfg);
|
|
51
49
|
/** The bot's discord username, populated by the ready fetch. */
|
|
@@ -54,10 +52,8 @@ declare class Seedcord<Cfg extends HttpConfig = HttpConfig> extends Pluggable<'h
|
|
|
54
52
|
get port(): number | undefined;
|
|
55
53
|
/**
|
|
56
54
|
* Starts the host and runs the startup tasks.
|
|
57
|
-
*
|
|
58
|
-
* @param port - The port the interaction server binds. {@default `3000` }
|
|
59
55
|
*/
|
|
60
|
-
start(
|
|
56
|
+
start(): Promise<this>;
|
|
61
57
|
protected static reset(): void;
|
|
62
58
|
private registerStartupTasks;
|
|
63
59
|
private registerHmrAwareModules;
|
|
@@ -83,9 +79,6 @@ type HttpPluginOptions = TypedOmit<PluginOptions$1, 'transport'> & {
|
|
|
83
79
|
* @typeParam Opts - The transport and runtime the plugin declares.
|
|
84
80
|
*/
|
|
85
81
|
declare abstract class Plugin<Opts extends HttpPluginOptions = {}> extends Plugin$1<Opts, Core> {}
|
|
86
|
-
declare namespace index_d_exports {
|
|
87
|
-
export { ArraySource, AutocompleteHandler, ButtonHandler, ComponentHandler, ContextMenuHandler, Core, CursorSource, Emojis, EngineContext, Gated, HttpConfig, HttpEdgeConfig, HttpPluginOptions, HttpServerConfig, InjectedEmojiMap, InteractionGateContext, InteractionHandler, ModalHandler, PageContext, Paginator, Plugin, PluginLifecycleSpec, PluginOptions, RepliableHandler, ResolvedEmoji, RouteManifest, Seedcord, SelectMenuHandler, SlashHandler, SlashOptions, Subscriber, WebhookLog, createSeedcord, version };
|
|
88
|
-
}
|
|
89
82
|
//#endregion
|
|
90
83
|
export { ArraySource, AutocompleteHandler, ButtonHandler, ComponentHandler, ContextMenuHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, type HttpConfig, type HttpEdgeConfig, type HttpPluginOptions, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, ModalHandler, type PageContext, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RepliableHandler, type ResolvedEmoji, type RouteManifest, Seedcord, SelectMenuHandler, SlashHandler, type SlashOptions, Subscriber, WebhookLog, createSeedcord, version };
|
|
91
84
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import
|
|
1
|
+
import { S as RepliableHandler, _ as createSeedcord, a as Paginator, b as InteractionHandler, c as ModalHandler, d as ComponentHandler, f as ContextMenuHandler, g as fetchApplicationId, h as Emojis, i as CursorSource, l as SelectMenuHandler, m as EmojiInjector, n as Gated, o as Subscriber, p as SlashHandler, r as ArraySource, s as WebhookLog, t as version, u as ButtonHandler, v as buildEngine, x as AutocompleteHandler, y as buildRouteMaps } from "./edge.index-C10jvlZK.mjs";
|
|
2
|
+
import "reflect-metadata";
|
|
3
3
|
import { SeedcordError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
4
4
|
import { Envapter } from "envapt";
|
|
5
5
|
import { REST } from "@discordjs/rest";
|
|
6
6
|
import { Bus } from "@seedcord/core";
|
|
7
|
-
import { CommandInjector, HmrManager, InteractionMetadataKey, InteractionRoutes, interactionRoutesOf, setBotColor } from "@seedcord/core/internal";
|
|
7
|
+
import { CommandInjector, HmrManager, InteractionMetadataKey, InteractionRoutes, getDevChannel, interactionRoutesOf, setBotColor } from "@seedcord/core/internal";
|
|
8
8
|
import { Logger, LoggerChannelRegistry, paint } from "@seedcord/logger";
|
|
9
9
|
import { MemoryRateLimiter } from "@seedcord/rate-limiter";
|
|
10
10
|
import { Routes } from "discord-api-types/v10";
|
|
11
11
|
import { SeedcordErrorCode } from "@seedcord/errors";
|
|
12
12
|
import { once } from "node:events";
|
|
13
13
|
import { createServer } from "node:http";
|
|
14
|
-
import { CommandRegistry, CoordinatedShutdown, CoordinatedStartup,
|
|
14
|
+
import { CommandRegistry, CoordinatedShutdown, CoordinatedStartup, Pluggable, ShutdownPhase, StartupPhase, SubscriberLoader } from "@seedcord/core/node/internal";
|
|
15
15
|
import { installNodeDefaults } from "@seedcord/logger/node";
|
|
16
16
|
import { SeedcordBrand } from "@seedcord/types/internal";
|
|
17
17
|
import { HmrModuleHandler } from "@seedcord/core/hmr";
|
|
@@ -82,7 +82,7 @@ var InteractionDispatcher = class {
|
|
|
82
82
|
} finally {
|
|
83
83
|
this.loading = false;
|
|
84
84
|
}
|
|
85
|
-
this.logger.utils.block("Loaded handlers", [...this.logger.utils.entries(this.loadedHandlers), ...this.logger.utils.counts({ routes: this.rowOwners.size })]);
|
|
85
|
+
this.logger.utils.block("Loaded handlers", [...this.logger.utils.entries(this.loadedHandlers), ...this.logger.utils.counts({ routes: this.rowOwners.size })], "debug");
|
|
86
86
|
}
|
|
87
87
|
/** @internal */
|
|
88
88
|
async onHmr(event) {
|
|
@@ -196,7 +196,6 @@ var InteractionDispatcher = class {
|
|
|
196
196
|
name: ctor.name,
|
|
197
197
|
from
|
|
198
198
|
});
|
|
199
|
-
else this.logger.utils.registration(ctor.name, from);
|
|
200
199
|
}
|
|
201
200
|
unregisterHandler(ctor, artifacts) {
|
|
202
201
|
const routeIds = artifacts ?? this.getArtifacts(ctor);
|
|
@@ -248,7 +247,7 @@ const DRAIN_TIMEOUT_MS = 1e4;
|
|
|
248
247
|
* The HTTP-interactions bot host, a long-running node server around the engine.
|
|
249
248
|
*
|
|
250
249
|
* Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on
|
|
251
|
-
* `start(
|
|
250
|
+
* `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls
|
|
252
251
|
* `createSeedcord` from a generated entry.
|
|
253
252
|
*/
|
|
254
253
|
var Seedcord = class Seedcord extends Pluggable {
|
|
@@ -271,12 +270,10 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
271
270
|
appId;
|
|
272
271
|
appIdPromise;
|
|
273
272
|
emojiInjector = new EmojiInjector(this, () => this.applicationId());
|
|
274
|
-
healthCheck;
|
|
275
273
|
hmrManager;
|
|
276
274
|
logger = new Logger("Server", { channel: "bot" });
|
|
277
275
|
server;
|
|
278
276
|
boundPort;
|
|
279
|
-
requestedPort = DEFAULT_PORT;
|
|
280
277
|
fetchedUsername;
|
|
281
278
|
constructor(config) {
|
|
282
279
|
super(new CoordinatedShutdown(), new CoordinatedStartup());
|
|
@@ -302,7 +299,6 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
302
299
|
this.rateLimiter = config.store ?? new MemoryRateLimiter();
|
|
303
300
|
this.bus = new Bus(this);
|
|
304
301
|
this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);
|
|
305
|
-
this.healthCheck = config.runtime === "edge" ? void 0 : HealthCheck.fromOption(this.shutdown, config.healthCheck);
|
|
306
302
|
this.registerStartupTasks();
|
|
307
303
|
}
|
|
308
304
|
/** The bot's discord username, populated by the ready fetch. */
|
|
@@ -315,11 +311,8 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
315
311
|
}
|
|
316
312
|
/**
|
|
317
313
|
* Starts the host and runs the startup tasks.
|
|
318
|
-
*
|
|
319
|
-
* @param port - The port the interaction server binds. {@default `3000` }
|
|
320
314
|
*/
|
|
321
|
-
async start(
|
|
322
|
-
this.requestedPort = port;
|
|
315
|
+
async start() {
|
|
323
316
|
try {
|
|
324
317
|
await super.init();
|
|
325
318
|
} catch (caught) {
|
|
@@ -331,7 +324,7 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
331
324
|
}
|
|
332
325
|
static reset() {
|
|
333
326
|
super.reset();
|
|
334
|
-
LoggerChannelRegistry.instance.
|
|
327
|
+
LoggerChannelRegistry.instance.configure({});
|
|
335
328
|
}
|
|
336
329
|
registerStartupTasks() {
|
|
337
330
|
if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();
|
|
@@ -361,12 +354,6 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
361
354
|
});
|
|
362
355
|
this.startup.addTask(StartupPhase.Ready, "http-server", () => this.listen());
|
|
363
356
|
if (!Envapter.isTest) this.startup.addTask(StartupPhase.Ready, "identity", () => this.fetchUsername());
|
|
364
|
-
const { healthCheck } = this;
|
|
365
|
-
if (healthCheck) this.startup.addTask(StartupPhase.Ready, "health-check", async () => {
|
|
366
|
-
healthCheck.logger.utils.initialization("HealthCheck", "start");
|
|
367
|
-
await healthCheck.init();
|
|
368
|
-
healthCheck.logger.utils.initialization("HealthCheck", "end");
|
|
369
|
-
});
|
|
370
357
|
}
|
|
371
358
|
registerHmrAwareModules() {
|
|
372
359
|
this.startup.addTask(StartupPhase.Configuration, "hmr-registration", async () => {
|
|
@@ -393,16 +380,18 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
393
380
|
const { handle, inFlight } = buildEngine(this, maps);
|
|
394
381
|
const server = createServer((incoming, outgoing) => {
|
|
395
382
|
(async () => {
|
|
396
|
-
|
|
383
|
+
const response = await handle(await toWebRequest(incoming));
|
|
384
|
+
await writeWebResponse(response, outgoing);
|
|
397
385
|
})().catch((error) => {
|
|
398
386
|
outgoing.destroy(Error.isError(error) ? error : new Error(String(error)));
|
|
399
387
|
});
|
|
400
388
|
});
|
|
401
389
|
this.server = server;
|
|
402
|
-
server.listen(this.
|
|
390
|
+
server.listen(this.config.port ?? DEFAULT_PORT);
|
|
403
391
|
await once(server, "listening");
|
|
404
392
|
this.boundPort = server.address().port;
|
|
405
393
|
this.logger.info(`Interactions server listening on port ${paint.sky.bold(String(this.boundPort))}`);
|
|
394
|
+
getDevChannel()?.send("seedcord:server-listening", { port: this.boundPort });
|
|
406
395
|
this.shutdown.addTask(ShutdownPhase.Unbind, "stop-http-server", () => this.stopServer(), SERVER_SHUTDOWN_TIMEOUT_MS);
|
|
407
396
|
this.shutdown.addTask(ShutdownPhase.Drain, "drain-inflight", async () => {
|
|
408
397
|
await Promise.allSettled(inFlight);
|
|
@@ -412,6 +401,7 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
412
401
|
try {
|
|
413
402
|
const me = await this.rest.get(Routes.user("@me"));
|
|
414
403
|
this.fetchedUsername = me.username;
|
|
404
|
+
if (me.username) this.logger.info(`Running as ${paint.iris.bold(me.username)}`);
|
|
415
405
|
} catch (caught) {
|
|
416
406
|
this.logger.warn("could not fetch the bot identity", caught);
|
|
417
407
|
}
|
|
@@ -445,31 +435,6 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
445
435
|
*/
|
|
446
436
|
var Plugin = class extends Plugin$1 {};
|
|
447
437
|
|
|
448
|
-
//#endregion
|
|
449
|
-
//#region src/index.ts
|
|
450
|
-
var src_exports = /* @__PURE__ */ __exportAll({
|
|
451
|
-
ArraySource: () => ArraySource,
|
|
452
|
-
AutocompleteHandler: () => AutocompleteHandler,
|
|
453
|
-
ButtonHandler: () => ButtonHandler,
|
|
454
|
-
ComponentHandler: () => ComponentHandler,
|
|
455
|
-
ContextMenuHandler: () => ContextMenuHandler,
|
|
456
|
-
CursorSource: () => CursorSource,
|
|
457
|
-
Emojis: () => Emojis,
|
|
458
|
-
Gated: () => Gated,
|
|
459
|
-
InteractionHandler: () => InteractionHandler,
|
|
460
|
-
ModalHandler: () => ModalHandler,
|
|
461
|
-
Paginator: () => Paginator,
|
|
462
|
-
Plugin: () => Plugin,
|
|
463
|
-
RepliableHandler: () => RepliableHandler,
|
|
464
|
-
Seedcord: () => Seedcord,
|
|
465
|
-
SelectMenuHandler: () => SelectMenuHandler,
|
|
466
|
-
SlashHandler: () => SlashHandler,
|
|
467
|
-
Subscriber: () => Subscriber,
|
|
468
|
-
WebhookLog: () => WebhookLog,
|
|
469
|
-
createSeedcord: () => createSeedcord,
|
|
470
|
-
version: () => version
|
|
471
|
-
});
|
|
472
|
-
|
|
473
438
|
//#endregion
|
|
474
439
|
export { ArraySource, AutocompleteHandler, ButtonHandler, ComponentHandler, ContextMenuHandler, CursorSource, Emojis, Gated, InteractionHandler, ModalHandler, Paginator, Plugin, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, WebhookLog, createSeedcord, version };
|
|
475
440
|
//# sourceMappingURL=index.mjs.map
|