@seedcord/core 0.11.0-next.0 → 0.11.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/{ServerHost-CvGQr7oN.mjs → ServerHost-CtmAhEXn.mjs} +4 -27
- package/dist/ServerHost-CtmAhEXn.mjs.map +1 -0
- package/dist/bindViteEnv-BgFewlZ0.d.mts +11 -0
- package/dist/bindViteEnv-DWq5rftJ.mjs +88 -0
- package/dist/bindViteEnv-DWq5rftJ.mjs.map +1 -0
- package/dist/env-internal.index.d.mts +2 -0
- package/dist/env-internal.index.mjs +3 -0
- package/dist/index.mjs +1 -1
- package/dist/internal.index.d.mts +2 -1
- package/dist/internal.index.mjs +2 -1
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.mjs +2 -2
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.mjs +1 -1
- package/package.json +21 -14
- package/dist/ServerHost-CvGQr7oN.mjs.map +0 -1
|
@@ -7,45 +7,22 @@ import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
|
|
|
7
7
|
import { asError, runsUnderSeedcordDev } from "@seedcord/utils/internal";
|
|
8
8
|
import { HostShutdown, HostStartup } from "@seedcord/types/internal";
|
|
9
9
|
import { installNodeConsole, installNodeDefaults } from "@seedcord/logger/node";
|
|
10
|
+
import { isValid, isValidRange, satisfies } from "verkit";
|
|
10
11
|
import crypto from "node:crypto";
|
|
11
12
|
|
|
12
13
|
//#region src/node/assertRuntimeVersion.ts
|
|
13
|
-
const MINIMUM = ">=";
|
|
14
|
-
const SPACES = /\s/g;
|
|
15
|
-
function parseVersion(text) {
|
|
16
|
-
const [withoutBuild = ""] = text.replace(/^v/, "").split("+");
|
|
17
|
-
const [release = "", ...prerelease] = withoutBuild.split("-");
|
|
18
|
-
const parts = release.split(".").map(Number);
|
|
19
|
-
return parts.every(Number.isInteger) ? {
|
|
20
|
-
parts,
|
|
21
|
-
prerelease: prerelease.length > 0
|
|
22
|
-
} : void 0;
|
|
23
|
-
}
|
|
24
|
-
function meetsMinimum(required, running) {
|
|
25
|
-
const length = Math.max(required.parts.length, running.parts.length);
|
|
26
|
-
for (let index = 0; index < length; index++) {
|
|
27
|
-
const wanted = required.parts[index] ?? 0;
|
|
28
|
-
const got = running.parts[index] ?? 0;
|
|
29
|
-
if (got !== wanted) return got > wanted;
|
|
30
|
-
}
|
|
31
|
-
return !running.prerelease;
|
|
32
|
-
}
|
|
33
14
|
function assertDeclaredRuntime() {
|
|
34
15
|
const { bun, node } = process.versions;
|
|
35
16
|
const runtime = bun === void 0 ? {
|
|
36
17
|
name: "Node",
|
|
37
|
-
range: "
|
|
18
|
+
range: "^24.22.1 || >=26.12.1",
|
|
38
19
|
running: node
|
|
39
20
|
} : {
|
|
40
21
|
name: "Bun",
|
|
41
22
|
range: ">=1.4.2",
|
|
42
23
|
running: bun
|
|
43
24
|
};
|
|
44
|
-
|
|
45
|
-
if (!range.startsWith(MINIMUM)) return;
|
|
46
|
-
const required = parseVersion(range.slice(2));
|
|
47
|
-
const running = parseVersion(runtime.running);
|
|
48
|
-
if (!required || !running || meetsMinimum(required, running)) return;
|
|
25
|
+
if (!runtime.range || !isValidRange(runtime.range) || !isValid(runtime.running) || satisfies(runtime.running, runtime.range, { includePrerelease: true })) return;
|
|
49
26
|
throw new SeedcordError(SeedcordErrorCode.UnsupportedRuntimeVersion, [
|
|
50
27
|
runtime.name,
|
|
51
28
|
runtime.range,
|
|
@@ -188,4 +165,4 @@ function shutdownOf(host) {
|
|
|
188
165
|
|
|
189
166
|
//#endregion
|
|
190
167
|
export { shutdownOf as n, assertDeclaredRuntime as r, ServerHost as t };
|
|
191
|
-
//# sourceMappingURL=ServerHost-
|
|
168
|
+
//# sourceMappingURL=ServerHost-CtmAhEXn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ServerHost-CtmAhEXn.mjs","names":[],"sources":["../src/node/assertRuntimeVersion.ts","../src/node/processErrors.ts","../src/node/ServerHost.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { isValid, isValidRange, satisfies } from 'verkit';\n\nexport function assertDeclaredRuntime(): void {\n const { bun, node } = process.versions;\n // tsdown-config bakes both ranges in from this package's engines\n const runtime =\n bun === undefined\n ? { name: 'Node', range: process.env.PACKAGE_NODE_RANGE ?? '', running: node }\n : { name: 'Bun', range: process.env.PACKAGE_BUN_RANGE ?? '', running: bun };\n\n if (\n !runtime.range ||\n !isValidRange(runtime.range) ||\n !isValid(runtime.running) ||\n satisfies(runtime.running, runtime.range, { includePrerelease: true })\n )\n return;\n\n throw new SeedcordError(SeedcordErrorCode.UnsupportedRuntimeVersion, [\n runtime.name,\n runtime.range,\n runtime.running\n ]);\n}\n","import crypto from 'node:crypto';\n\nimport { Logger } from '@seedcord/logger';\nimport { asError } from '@seedcord/utils/internal';\n\nimport { logUnknownFault } from '#src/notices/logUnknownFault';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\n\nconst logger = new Logger('Faults', { channel: 'errors' });\n\n/** @internal */\nexport function registerProcessErrors(core: CoreBase, shutdown: CoordinatedShutdown): () => void {\n function report(caught: unknown, origin: string): void {\n const error = asError(caught);\n const uuid = crypto.randomUUID();\n\n logUnknownFault(logger, uuid, error, core.config.errors);\n\n core.bus[PublishDefault]('unknownException', { uuid, dispatchId: null, error, origin });\n }\n\n const onRejection = (reason: unknown): void => {\n report(reason, 'process:unhandledRejection');\n };\n\n const onException = (caught: unknown): void => {\n report(caught, 'process:uncaughtException');\n // node does not exit on its own once a listener is registered\n void shutdown.run(1);\n };\n\n process.on('unhandledRejection', onRejection);\n process.on('uncaughtException', onException);\n\n return () => {\n process.off('unhandledRejection', onRejection);\n process.off('uncaughtException', onException);\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { LoggerChannelRegistry } from '@seedcord/logger';\nimport { installNodeConsole, installNodeDefaults } from '@seedcord/logger/node';\nimport { HostShutdown, HostStartup } from '@seedcord/types/internal';\nimport { runsUnderSeedcordDev } from '@seedcord/utils/internal';\n\nimport { assertDeclaredRuntime } from '#node/assertRuntimeVersion';\nimport { PluginLifecycle } from '#src/lifecycle/PluginLifecycle';\nimport { attachmentsOf, PluginHost, sealAttachments } from '#src/plugin/PluginHost';\n\nimport { registerProcessErrors } from './processErrors';\n\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\nimport type { CoordinatedStartup } from '#src/lifecycle/CoordinatedStartup';\nimport type { Transport } from '#src/plugin/options';\nimport type { Promisable } from 'type-fest';\n\n/** Base class for a transport `Seedcord` class that runs as a long-lived node or bun process. */\nexport abstract class ServerHost<BotT extends Transport> extends PluginHost<BotT, 'server'> {\n /** @internal */\n readonly [HostShutdown]: CoordinatedShutdown;\n /** @internal */\n readonly [HostStartup]: CoordinatedStartup;\n\n /** Add a task that runs while the bot shuts down. */\n public readonly shutdown: Pick<CoordinatedShutdown, 'addTask'>;\n\n /** Add a task that runs while the bot starts. */\n public readonly startup: Pick<CoordinatedStartup, 'addTask'>;\n\n readonly #lifecycle: PluginLifecycle;\n #startFailed = false;\n #initPromise?: Promise<this> | undefined;\n\n static #isInstantiated = false;\n static #liveHost?: object | undefined;\n static #liveShutdown?: CoordinatedShutdown | undefined;\n static #liveProcessErrors?: (() => void) | undefined;\n\n constructor(transport: BotT, shutdown: CoordinatedShutdown, startup: CoordinatedStartup) {\n // a `sideEffects: false` build would drop the same call in the node entry\n assertDeclaredRuntime();\n super(transport, 'server');\n\n if (ServerHost.#isInstantiated) throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);\n\n // init() opens the log file\n installNodeConsole();\n\n ServerHost.#isInstantiated = true;\n ServerHost.#liveHost = this;\n ServerHost.#liveShutdown = shutdown;\n this[HostShutdown] = shutdown;\n this[HostStartup] = startup;\n // a getter returning the slot would expose run() and the signal handlers too\n this.shutdown = { addTask: shutdown.addTask.bind(shutdown) };\n this.startup = { addTask: startup.addTask.bind(startup) };\n this.#lifecycle = new PluginLifecycle(this.startup, this.shutdown);\n }\n\n /** @internal */\n protected init(): Promise<this> {\n // a retry after a rejection runs #runInit again and hits its restart guard\n this.#initPromise ??= this.#runInit().catch(async (caught: unknown) => {\n this.#initPromise = undefined;\n await this.#release(1);\n throw caught;\n });\n return this.#initPromise;\n }\n\n async #runInit(): Promise<this> {\n // a rerun after a failed startup would re-init the rolled-back plugins\n if (this.#startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);\n\n installNodeDefaults(this.config.logger);\n sealAttachments(this);\n this.#lifecycle.register(attachmentsOf(this));\n\n // codegen and the build construct the bot without starting it.\n // under seedcord dev the CLI handles SIGINT and SIGTERM\n if (!runsUnderSeedcordDev()) this[HostShutdown].registerSignalHandlers();\n if (this.config.errors?.catchProcessErrors ?? true) {\n ServerHost.#liveProcessErrors = registerProcessErrors(this, this[HostShutdown]);\n }\n\n const startupSettled: PromiseWithResolvers<void> = Promise.withResolvers();\n this[HostShutdown].gateOnStartup(startupSettled.promise);\n\n try {\n // nothing a plugin adds through startup.addTask runs after this\n await this[HostStartup].run(() => this.openHealth());\n } catch (caught) {\n this.#startFailed = true;\n await this.#lifecycle.rollback();\n throw caught;\n } finally {\n startupSettled.resolve();\n }\n\n return this;\n }\n\n /** @internal */\n protected openHealth(): Promisable<void> {}\n\n /**\n * Runs the coordinated shutdown without calling `process.exit`. A new `Seedcord` can be\n * constructed afterwards. `await using` calls this when its scope ends.\n *\n * @example\n * ```ts\n * // the bot shuts down when this block ends\n * await using bot = await new Seedcord(config).start();\n * console.log(await bot.rest.get(Routes.user('@me')));\n * ```\n */\n public async [Symbol.asyncDispose](): Promise<void> {\n await this.#release(0);\n }\n\n async #release(exitCode: number): Promise<void> {\n try {\n await this[HostShutdown].run(exitCode, false);\n } finally {\n ServerHost.reset(this);\n }\n }\n\n /** @internal */\n protected static reset(host?: object): boolean {\n if (host !== undefined && ServerHost.#liveHost !== host) return false;\n\n ServerHost.#liveShutdown?.removeSignalHandlers();\n ServerHost.#liveShutdown = undefined;\n ServerHost.#liveProcessErrors?.();\n ServerHost.#liveProcessErrors = undefined;\n ServerHost.#liveHost = undefined;\n ServerHost.#isInstantiated = false;\n // configure() keeps the dev TUI's capture sink\n LoggerChannelRegistry.instance.configure({});\n return true;\n }\n}\n\n// the public `shutdown` field carries addTask alone\nexport function shutdownOf(host: Pick<ServerHost<Transport>, typeof HostShutdown>): CoordinatedShutdown {\n return host[HostShutdown];\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Envapter, Source } from "envapt";
|
|
2
|
+
//#region src/miscellaneous/bindViteEnv.d.ts
|
|
3
|
+
declare function bindEnv(envapter: typeof Envapter, source: Source, directory?: string): Disposable;
|
|
4
|
+
interface ViteEnvOptions {
|
|
5
|
+
bindings?: Record<string, unknown>;
|
|
6
|
+
directory?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
declare function bindViteEnv(envapter: typeof Envapter, vite: Record<string, unknown>, { bindings, directory }?: ViteEnvOptions): Disposable;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { bindViteEnv as n, bindEnv as t };
|
|
11
|
+
//# sourceMappingURL=bindViteEnv-BgFewlZ0.d.mts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { PortableSource, merge } from "envapt";
|
|
2
|
+
|
|
3
|
+
//#region \0@oxc-project+runtime@0.152.0/helpers/esm/usingCtx.js
|
|
4
|
+
function _usingCtx() {
|
|
5
|
+
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
6
|
+
var n = Error();
|
|
7
|
+
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
8
|
+
}, e = {}, n = [];
|
|
9
|
+
function using(r, e) {
|
|
10
|
+
if (null != e) {
|
|
11
|
+
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
12
|
+
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
13
|
+
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
14
|
+
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
15
|
+
t && (o = function o() {
|
|
16
|
+
try {
|
|
17
|
+
t.call(e);
|
|
18
|
+
} catch (r) {
|
|
19
|
+
return Promise.reject(r);
|
|
20
|
+
}
|
|
21
|
+
}), n.push({
|
|
22
|
+
v: e,
|
|
23
|
+
d: o,
|
|
24
|
+
a: r
|
|
25
|
+
});
|
|
26
|
+
} else r && n.push({
|
|
27
|
+
d: e,
|
|
28
|
+
a: r
|
|
29
|
+
});
|
|
30
|
+
return e;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
e,
|
|
34
|
+
u: using.bind(null, !1),
|
|
35
|
+
a: using.bind(null, !0),
|
|
36
|
+
d: function d() {
|
|
37
|
+
var o, t = this.e, s = 0;
|
|
38
|
+
function next() {
|
|
39
|
+
for (; o = n.pop();) try {
|
|
40
|
+
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
41
|
+
if (o.d) {
|
|
42
|
+
var r = o.d.call(o.v);
|
|
43
|
+
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
44
|
+
} else s |= 1;
|
|
45
|
+
} catch (r) {
|
|
46
|
+
return err(r);
|
|
47
|
+
}
|
|
48
|
+
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
49
|
+
if (t !== e) throw t;
|
|
50
|
+
}
|
|
51
|
+
function err(n) {
|
|
52
|
+
return t = t !== e ? new r(n, t) : n, next();
|
|
53
|
+
}
|
|
54
|
+
return next();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/miscellaneous/bindViteEnv.ts
|
|
61
|
+
function bindEnv(envapter, source, directory) {
|
|
62
|
+
try {
|
|
63
|
+
var _usingCtx$1 = _usingCtx();
|
|
64
|
+
const previousSource = envapter.source;
|
|
65
|
+
const previousBaseDir = previousSource.supportsFiles ? envapter.baseDir : void 0;
|
|
66
|
+
const cleanup = _usingCtx$1.u(new DisposableStack());
|
|
67
|
+
cleanup.defer(() => {
|
|
68
|
+
envapter.useSource(previousSource);
|
|
69
|
+
if (previousSource.supportsFiles) envapter.baseDir = previousBaseDir;
|
|
70
|
+
});
|
|
71
|
+
if (previousSource.supportsFiles && directory !== void 0) envapter.baseDir = directory;
|
|
72
|
+
envapter.useSource(source);
|
|
73
|
+
return cleanup.move();
|
|
74
|
+
} catch (_) {
|
|
75
|
+
_usingCtx$1.e = _;
|
|
76
|
+
} finally {
|
|
77
|
+
_usingCtx$1.d();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function bindViteEnv(envapter, vite, { bindings, directory } = {}) {
|
|
81
|
+
const { MODE: mode, BASE_URL: _baseUrl, DEV: _dev, PROD: _prod, SSR: _ssr, ...values } = vite;
|
|
82
|
+
const source = bindings === void 0 ? envapter.source : merge((key) => key === "MODE" && typeof mode === "string" ? mode : void 0, new PortableSource(bindings));
|
|
83
|
+
return bindEnv(envapter, merge(source, new PortableSource(values)), directory);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
export { bindViteEnv as n, bindEnv as t };
|
|
88
|
+
//# sourceMappingURL=bindViteEnv-DWq5rftJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindViteEnv-DWq5rftJ.mjs","names":[],"sources":["../src/miscellaneous/bindViteEnv.ts"],"sourcesContent":["import { merge, PortableSource } from 'envapt';\n\nimport type { Envapter, Source } from 'envapt';\n\nexport function bindEnv(envapter: typeof Envapter, source: Source, directory?: string): Disposable {\n const previousSource = envapter.source;\n const previousBaseDir = previousSource.supportsFiles ? envapter.baseDir : undefined;\n using cleanup = new DisposableStack();\n cleanup.defer(() => {\n envapter.useSource(previousSource);\n if (previousSource.supportsFiles) envapter.baseDir = previousBaseDir;\n });\n if (previousSource.supportsFiles && directory !== undefined) envapter.baseDir = directory;\n envapter.useSource(source);\n return cleanup.move();\n}\n\ninterface ViteEnvOptions {\n bindings?: Record<string, unknown>;\n directory?: string | undefined;\n}\n\nexport function bindViteEnv(\n envapter: typeof Envapter,\n vite: Record<string, unknown>,\n { bindings, directory }: ViteEnvOptions = {}\n): Disposable {\n // vite has alrdy applied envPrefix to the exposed user vars\n const { MODE: mode, BASE_URL: _baseUrl, DEV: _dev, PROD: _prod, SSR: _ssr, ...values } = vite;\n const source =\n bindings === undefined\n ? envapter.source\n : merge(\n (key) => (key === 'MODE' && typeof mode === 'string' ? mode : undefined),\n new PortableSource(bindings)\n );\n return bindEnv(envapter, merge(source, new PortableSource(values)), directory);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,SAAgB,QAAQ,UAA2B,QAAgB,WAAgC;;;EAC/F,MAAM,iBAAiB,SAAS;EAChC,MAAM,kBAAkB,eAAe,gBAAgB,SAAS,UAAU;EAC1E,MAAM,wBAAU,IAAI,gBAAgB;EACpC,QAAQ,YAAY;GAChB,SAAS,UAAU,cAAc;GACjC,IAAI,eAAe,eAAe,SAAS,UAAU;EACzD,CAAC;EACD,IAAI,eAAe,iBAAiB,cAAc,QAAW,SAAS,UAAU;EAChF,SAAS,UAAU,MAAM;EACzB,OAAO,QAAQ,KAAK;;;;;;AACxB;AAOA,SAAgB,YACZ,UACA,MACA,EAAE,UAAU,cAA8B,CAAC,GACjC;CAEV,MAAM,EAAE,MAAM,MAAM,UAAU,UAAU,KAAK,MAAM,MAAM,OAAO,KAAK,MAAM,GAAG,WAAW;CACzF,MAAM,SACF,aAAa,SACP,SAAS,SACT,OACK,QAAS,QAAQ,UAAU,OAAO,SAAS,WAAW,OAAO,QAC9D,IAAI,eAAe,QAAQ,CAC/B;CACV,OAAO,QAAQ,UAAU,MAAM,QAAQ,IAAI,eAAe,MAAM,CAAC,GAAG,SAAS;AACjF"}
|
package/dist/index.mjs
CHANGED
|
@@ -1149,7 +1149,7 @@ var CursorSourceBase = class {
|
|
|
1149
1149
|
//#endregion
|
|
1150
1150
|
//#region src/index.ts
|
|
1151
1151
|
/** Package version */
|
|
1152
|
-
const version = "0.11.0
|
|
1152
|
+
const version = "0.11.0";
|
|
1153
1153
|
|
|
1154
1154
|
//#endregion
|
|
1155
1155
|
export { ArraySourceBase, AutocompleteRoute, BaseHandler, BaseReplySender, BuilderComponent, Bus, ButtonRoute, ChannelMenuRoute, Commands, ContextMenus, Cooldown, CursorSourceBase, CustomId, DispatchContext, DmOnly, Fault, GuildOnly, InteractionKind, MentionableMenuRoute, MessageContextMenuRoute, ModalRoute, Notice, OwnerOnly, PaginatorBase, RegisterCommand, RegisterInteractionMiddleware, RepliableHandler, RequireBotPermissions, RequirePermissions, RequireRole, ResolvedEmoji, RoleMenuRoute, RowComponent, ShutdownPhase, Silence, SlashRoute, StartupPhase, StringMenuRoute, Subscribe, Subscriber, UserContextMenuRoute, UserMenuRoute, WebhookLog, WebhookUrl, and, assertPermissions, decodeFor, defineEffectGate, defineGate, mergeRoles, or, paginate, prefixOf, setCustomIdErrors, version };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as bindEnv } from "./bindViteEnv-BgFewlZ0.mjs";
|
|
1
2
|
import { A as RegisteredCount, B as InteractionMetadataKey, C as AckState, D as AckTrace, E as sendTarget, F as CommandMetadataKey, G as WebhookUrlMetadataKey, H as InteractionRouteKeys, I as EventMetadataKey, K as Notice, L as EventMiddlewareMetadataKey, M as VerifyWebhooks, N as PublishDefault, O as RegisterDefaults, R as GatedMetadataKey, S as reportedWrite, T as checkAckLegality, U as MiddlewareKind, V as InteractionMiddlewareMetadataKey, W as SubscribeMetadataKey, a as StoredSubscriberCtor, c as registrationFor, d as DefaultSubscriptions, f as DispatchOutcome, i as Bus, j as UnregisterSubscriber, k as RegisterSubscriber, o as SubscriberRegistration, r as CoreBase, s as busLoggerOf, w as ReplyMethod, x as ReplyTelemetry, z as InteractionKind } from "./phases-FTM69J3a.mjs";
|
|
2
3
|
import { $ as SlashRouteBrand, A as Gate, At as CacheFor, Ct as CommandMeta, Et as BuilderComponent, Ft as SlashRouteEntry, H as AnyMiddlewareCtor, I as SerializedReply, It as ContextMenuKind, J as ComponentDefsBrand, K as interactionMiddlewareMetaOf, L as serializeReply, Lt as MenuCacheFor, Mt as RouteCache, Nt as SlashOption, Pt as SlashRegistry, Q as MiddlewareKindsBrand, R as DispatchResult, St as resolveDeployTarget, T as GateFitsWith, U as InteractionMiddlewareMetadata, V as HandlerResult, X as ContextMenuKindBrand, Y as ComponentKindBrand, Z as ContextMenuNamesBrand, _t as ScopeId, bt as scopeIdsOf, c as PageCursor, ct as CommandRef, dt as commandRefOf, f as ChoiceValueOf, ft as indexById, gt as GLOBAL_ID, h as ResolvedEmoji, ht as DeployResult, j as GateContextBase, jt as OptionKind, l as pageCursor, lt as LinkedCommands, m as FocusedField, mt as CommandBuilder, ot as installLinks, p as EntryFor, pt as linkCommands, q as AutocompleteRouteBrand, s as PAGE_MAX, st as warnLinks, u as AutocompletableNames, ut as commandKey, vt as commandsRoute, w as pickNotice, x as PermissionNames, xt as skippedNote, yt as globalFirst, z as EventDispatchResult, zt as NamesFor } from "./index-Du71Xx69.mjs";
|
|
3
4
|
import { a as FixedScope, c as PluginArgs, i as sealAttachments, l as PluginCtor, o as Initializeable, r as attachmentsOf, t as Attachment } from "./PluginHost-DB4j8unu.mjs";
|
|
@@ -510,5 +511,5 @@ export declare function eventResultFor(stopped: {
|
|
|
510
511
|
*/
|
|
511
512
|
export declare function runAfter<Result>(ran: readonly Afterable<Result>[], result: Result, logger: Logger): Promise<void>;
|
|
512
513
|
//#endregion
|
|
513
|
-
export { type AckState, AckTrace, type AutocompletableNames, AutocompleteRouteBrand, type BakedIds, type CacheFor, type ChoiceValueOf, type CommandBody, type CommandBuilder, type CommandCtor, type CommandMeta, CommandMetadataKey, type CommandRef, type CommandScopes, ComponentDefsBrand, ComponentKindBrand, ContextMenuKindBrand, type ContextMenuLeaves, ContextMenuNamesBrand, CoordinatedStartup, type DecodedComponentRoute, type DefaultSubscriptions, type DeployResult, type EntryFor, EventMetadataKey, EventMiddlewareMetadataKey, FixedScope, type FocusedField, GLOBAL_ID, type GateFitsWith, type GateObserver, GatedMetadataKey, type GuildSummary, type HasComponentDefs, type Initializeable, InteractionMetadataKey, type InteractionMiddlewareMetadata, InteractionMiddlewareMetadataKey, InteractionRouteKeys, type LinkedCommands, type MatchArms, type MenuCacheFor, MiddlewareKindsBrand, type MiddlewareRegistration, type MiddlewareRegistrationOf, type NamesFor, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, type ReplyMethod, type ReplyTelemetry, type RoutableConstructor, type RouteCache, type ScopeId, type SerializedReply, type SingleParams, type SlashOption, type SlashOptions, SlashRouteBrand, type SlashRouteEntry, type SlowGateMonitor, type StoredSubscriberCtor, SubscribeMetadataKey, type SubscriberRegistration, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, attachmentsOf, busLoggerOf, checkAckLegality, commandKey, commandRefOf, commandsRoute, globalFirst, indexById, installLinks, interactionMiddlewareMetaOf, linkCommands, pageCursor, pickNotice, registrationFor, reportedWrite, resolveDeployTarget, scopeIdsOf, sealAttachments, sendTarget, serializeReply, skippedNote, warnLinks };
|
|
514
|
+
export { type AckState, AckTrace, type AutocompletableNames, AutocompleteRouteBrand, type BakedIds, type CacheFor, type ChoiceValueOf, type CommandBody, type CommandBuilder, type CommandCtor, type CommandMeta, CommandMetadataKey, type CommandRef, type CommandScopes, ComponentDefsBrand, ComponentKindBrand, ContextMenuKindBrand, type ContextMenuLeaves, ContextMenuNamesBrand, CoordinatedStartup, type DecodedComponentRoute, type DefaultSubscriptions, type DeployResult, type EntryFor, EventMetadataKey, EventMiddlewareMetadataKey, FixedScope, type FocusedField, GLOBAL_ID, type GateFitsWith, type GateObserver, GatedMetadataKey, type GuildSummary, type HasComponentDefs, type Initializeable, InteractionMetadataKey, type InteractionMiddlewareMetadata, InteractionMiddlewareMetadataKey, InteractionRouteKeys, type LinkedCommands, type MatchArms, type MenuCacheFor, MiddlewareKindsBrand, type MiddlewareRegistration, type MiddlewareRegistrationOf, type NamesFor, type OptionKind, type OptionLens, PAGE_MAX, type PageCursor, PermissionNames, type PluginArgs, type PluginCtor, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, type ReplyMethod, type ReplyTelemetry, type RoutableConstructor, type RouteCache, type ScopeId, type SerializedReply, type SingleParams, type SlashOption, type SlashOptions, SlashRouteBrand, type SlashRouteEntry, type SlowGateMonitor, type StoredSubscriberCtor, SubscribeMetadataKey, type SubscriberRegistration, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, attachmentsOf, bindEnv, busLoggerOf, checkAckLegality, commandKey, commandRefOf, commandsRoute, globalFirst, indexById, installLinks, interactionMiddlewareMetaOf, linkCommands, pageCursor, pickNotice, registrationFor, reportedWrite, resolveDeployTarget, scopeIdsOf, sealAttachments, sendTarget, serializeReply, skippedNote, warnLinks };
|
|
514
515
|
//# sourceMappingURL=internal.index.d.mts.map
|
package/dist/internal.index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { c as setDevChannel, i as FixedScope, n as attachmentsOf, r as sealAttac
|
|
|
4
4
|
import { t as HmrModuleHandler } from "./HmrModuleHandler-8ZC-UuZh.mjs";
|
|
5
5
|
import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-AhQ46NIr.mjs";
|
|
6
6
|
import { t as CoordinatedStartup } from "./CoordinatedStartup-CGnJrqcl.mjs";
|
|
7
|
+
import { t as bindEnv } from "./bindViteEnv-DWq5rftJ.mjs";
|
|
7
8
|
import "reflect-metadata";
|
|
8
9
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
|
9
10
|
import { Routes } from "discord-api-types/v10";
|
|
@@ -968,5 +969,5 @@ async function runAfter(ran, result, logger) {
|
|
|
968
969
|
}
|
|
969
970
|
|
|
970
971
|
//#endregion
|
|
971
|
-
export { AckTrace, AutocompleteRouteBrand, BAKED_IDS_KEY, COMMAND_DEPLOY_TIMEOUT_MS, CommandMetadataKey, CommandRegistry, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, CoordinatedStartup, EventMetadataKey, EventMiddlewareMetadataKey, FixedScope, GLOBAL_ID, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, MiddlewareKindsBrand, MiddlewareRegistry, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PluginLifecycle, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, SubscriberLoader, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, attachmentsOf, bindBotColor, busLoggerOf, checkAckLegality, clearStore, commandKey, commandRefOf, commandsRoute, connectDevChannel, contextMenuLeaves, contextMenuRouteOf, currentApplication, decodeComponentRoute, deferFlags, eventResultFor, forwardLogsToCli, fromDevLogLine, fromWireError, getDevChannel, globalFirst, guardedAccessor, guildSummaries, indexById, installLinks, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, linkCommands, logUnknownFault, outcomeFor, pageCursor, pickNotice, planDeploy, putCommands, queuedMsFor, readBotToken, readCommands, readCommandsAt, registrationFor, reportDispatch, reportedWrite, resolveDeployTarget, resolveEmojis, resultFor, runAfter, runGates, runHandlerGates, scopeIdsOf, sealAttachments, sendFlags, sendTarget, serializeReply, setDevChannel, skippedNote, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, toWireError, translateSerializationError, warnLinks, wrapHot };
|
|
972
|
+
export { AckTrace, AutocompleteRouteBrand, BAKED_IDS_KEY, COMMAND_DEPLOY_TIMEOUT_MS, CommandMetadataKey, CommandRegistry, ComponentDefsBrand, ComponentDefsKey, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, CoordinatedStartup, EventMetadataKey, EventMiddlewareMetadataKey, FixedScope, GLOBAL_ID, GateNotice, GatedMetadataKey, HasDangerousPermissions, HmrManager, InteractionMetadataKey, InteractionMiddlewareMetadataKey, InteractionRouteKeys, MiddlewareKindsBrand, MiddlewareRegistry, MissingPermissions, MissingRole, NeedsAny, NotAllowed, NotInDm, NotInGuild, NotOwner, NoticeCard, OnCooldown, PAGE_MAX, PermissionNames, PluginLifecycle, PublishDefault, RegisterDefaults, RegisterSubscriber, RegisteredCount, ReportThrottle, SlashRouteBrand, SubscribeMetadataKey, SubscriberLoader, UnregisterSubscriber, VerifyWebhooks, WebhookUrlMetadataKey, accessorStore, attachmentsOf, bindBotColor, bindEnv, busLoggerOf, checkAckLegality, clearStore, commandKey, commandRefOf, commandsRoute, connectDevChannel, contextMenuLeaves, contextMenuRouteOf, currentApplication, decodeComponentRoute, deferFlags, eventResultFor, forwardLogsToCli, fromDevLogLine, fromWireError, getDevChannel, globalFirst, guardedAccessor, guildSummaries, indexById, installLinks, interactionMiddleware, interactionMiddlewareMetaOf, interactionRoutesOf, isCommandClass, isEmojiTuple, linkCommands, logUnknownFault, outcomeFor, pageCursor, pickNotice, planDeploy, putCommands, queuedMsFor, readBotToken, readCommands, readCommandsAt, registrationFor, reportDispatch, reportedWrite, resolveDeployTarget, resolveEmojis, resultFor, runAfter, runGates, runHandlerGates, scopeIdsOf, sealAttachments, sendFlags, sendTarget, serializeReply, setDevChannel, skippedNote, slashRouteLeaves, slowGateMonitor, storeComponentRoute, storeInteractionRoute, toWireError, translateSerializationError, warnLinks, wrapHot };
|
|
972
973
|
//# sourceMappingURL=internal.index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.index.mjs","names":["logger"],"sources":["../src/subscribers/SubscriberLoader.ts","../src/decorators/brands.ts","../src/dispatch/MiddlewareRegistry.ts","../src/gates/runGates.ts","../src/gates/slowGate.ts","../src/miscellaneous/emojiConfig.ts","../src/miscellaneous/currentApplication.ts","../src/miscellaneous/readBotToken.ts","../src/miscellaneous/resolveEmojis.ts","../src/bakedIds.ts","../src/commands/guildSummaries.ts","../src/commands/DeployReporter.ts","../src/commands/deployTarget.ts","../src/commands/isCommandClass.ts","../src/commands/LastDeploy.ts","../src/commands/planDeploy.ts","../src/commands/putCommands.ts","../src/commands/readCommands.ts","../src/commands/slashRouteLeaves.ts","../src/commands/CommandRegistry.ts","../src/hmr/wrapHot.ts","../src/hmr/HmrManager.ts","../src/hmr/wire.ts","../src/hmr/forwardLogsToCli.ts","../src/dispatch/dispatchReport.ts","../src/dispatch/runAfter.ts"],"sourcesContent":["import { paint } from '@seedcord/errors';\nimport { importFiles } from '@seedcord/utils';\nimport { Envapter } from 'envapt';\n\nimport { HmrModuleHandler } from '#hmr/HmrModuleHandler';\nimport { SubscribeMetadataKey } from '#src/metadataKeys';\nimport { busLoggerOf } from '#subscribers/Bus';\nimport { registrationFor, Subscriber } from '#subscribers/index';\nimport {\n RegisterDefaults,\n RegisteredCount,\n RegisterSubscriber,\n UnregisterSubscriber,\n VerifyWebhooks\n} from '#subscribers/slots';\n\nimport type { Initializeable } from '#src/plugin/Plugin';\nimport type { SubscribeMetadataEntry } from '#subscribers/decorators/Subscribe';\nimport type { StoredSubscriberCtor, Bus } from '#subscribers/index';\nimport type { SubscriptionKey } from '#subscribers/types/Subscriptions';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\ntype SubscriberArtifact = SubscriptionKey[];\n\nexport class SubscriberLoader implements Initializeable, HmrAware {\n private isInitialized = false;\n private readonly hmrHandler?: HmrModuleHandler<StoredSubscriberCtor, void, SubscriberArtifact>;\n\n constructor(\n private readonly bus: Bus,\n private readonly directory: string | null\n ) {\n if (!this.directory) return;\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n\n this.hmrHandler = new HmrModuleHandler({\n handlersDir: this.directory,\n isHandler: this.isSubscriber.bind(this),\n registerHandler: this.registerSubscriber.bind(this),\n unregisterHandler: this.unregisterSubscriber.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: busLoggerOf(this.bus)\n });\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n\n this.bus[RegisterDefaults]();\n\n const { directory } = this;\n if (directory) {\n busLoggerOf(this.bus).debug(paint.mute(directory));\n await this.load(directory);\n busLoggerOf(this.bus).utils.list(\n [`${paint.iris.bold(this.bus[RegisteredCount])} subscribers`],\n paint.mint.bold('Loaded'),\n 'debug'\n );\n }\n\n if (!Envapter.isTest) await this.bus[VerifyWebhooks]();\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n if (this.hmrHandler) await this.hmrHandler.handle(event);\n }\n\n private async load(dir: string): Promise<void> {\n for await (const { fullPath, relativePath, imported } of importFiles(dir)) {\n for (const value of Object.values(imported)) {\n if (!this.isSubscriber(value)) continue;\n\n this.registerSubscriber(value);\n this.hmrHandler?.trackHandler(fullPath, value);\n busLoggerOf(this.bus).utils.registration(value.name, relativePath, undefined, 'trace');\n }\n }\n }\n\n private registerSubscriber(ctor: StoredSubscriberCtor): void {\n this.bus[RegisterSubscriber](registrationFor(ctor));\n }\n\n private unregisterSubscriber(ctor: StoredSubscriberCtor, artifacts?: SubscriberArtifact): void {\n this.bus[UnregisterSubscriber](ctor, artifacts);\n }\n\n private getArtifacts(ctor: StoredSubscriberCtor): SubscriberArtifact {\n const meta = Reflect.getMetadata(SubscribeMetadataKey, ctor) as SubscribeMetadataEntry;\n return [meta.subscriber];\n }\n\n private isSubscriber(value: unknown): value is StoredSubscriberCtor {\n if (typeof value !== 'function') return false;\n return value.prototype instanceof Subscriber && Reflect.hasMetadata(SubscribeMetadataKey, value);\n }\n}\n","// phantom, nothing assigns these at runtime\n\nexport const SlashRouteBrand: unique symbol = Symbol('seedcord:brand:slash-route');\nexport const AutocompleteRouteBrand: unique symbol = Symbol('seedcord:brand:autocomplete-route');\nexport const ContextMenuKindBrand: unique symbol = Symbol('seedcord:brand:context-menu-kind');\nexport const ContextMenuNamesBrand: unique symbol = Symbol('seedcord:brand:context-menu-names');\nexport const ComponentKindBrand: unique symbol = Symbol('seedcord:brand:component-kind');\nexport const ComponentDefsBrand: unique symbol = Symbol('seedcord:brand:component-defs');\nexport const MiddlewareKindsBrand: unique symbol = Symbol('seedcord:brand:middleware-kinds');\n","import { interactionMiddlewareMetaOf } from '#decorators/middleware';\n\nimport type { AnyMiddlewareCtor } from '#decorators/middleware';\nimport type { MiddlewareKind } from '#src/metadataKeys';\n\n/**\n * A decorated middleware reduced to what the registry orders and filters on. An absent `keys` puts the\n * middleware in every chain.\n *\n * @internal\n */\nexport interface MiddlewareRegistration<Key extends string> {\n readonly priority: number;\n readonly keys?: readonly Key[];\n}\n\n/**\n * Reads a transport's decorator metadata off a class. Returns `undefined` for an undecorated one.\n *\n * @internal\n */\nexport type MiddlewareRegistrationOf<Ctor, Key extends string> = (\n ctor: Ctor\n) => MiddlewareRegistration<Key> | undefined;\n\n/** @internal */\nexport function interactionMiddleware(ctor: AnyMiddlewareCtor): MiddlewareRegistration<MiddlewareKind> | undefined {\n const metadata = interactionMiddlewareMetaOf(ctor);\n if (!metadata) return undefined;\n return { priority: metadata.priority, ...(metadata.kinds && { keys: metadata.kinds }) };\n}\n\ninterface Entry<Ctor, Key extends string> {\n readonly ctor: Ctor;\n readonly priority: number;\n readonly keys?: ReadonlySet<Key>;\n}\n\n/**\n * Holds the middleware a transport loaded and keeps one chain per key. An interaction keys on its kind\n * and an event on its name. Priority and the key filter behave the same on each.\n *\n * @typeParam Ctor - The transport's middleware constructor type.\n * @typeParam Key - The chain lookup key.\n *\n * @internal\n */\nexport class MiddlewareRegistry<Ctor extends AnyMiddlewareCtor, Key extends string = MiddlewareKind> {\n private readonly entries: Entry<Ctor, Key>[] = [];\n // an in-flight dispatch keeps walking the array chainFor gave it. never mutate one in place.\n private chains = new Map<Key, readonly Ctor[]>();\n\n public constructor(private readonly registrationOf: MiddlewareRegistrationOf<Ctor, Key>) {}\n\n /**\n * Adds a decorated middleware class and returns what it registered with. Returns `undefined` for an\n * undecorated class or one already registered.\n */\n public register(ctor: Ctor): MiddlewareRegistration<Key> | undefined {\n const registration = this.registrationOf(ctor);\n if (!registration) return undefined;\n\n // a double import or an hmr re-scan registers the same class twice\n if (this.entries.some((entry) => entry.ctor === ctor)) return undefined;\n\n this.entries.push({\n ctor,\n priority: registration.priority,\n ...(registration.keys && { keys: new Set(registration.keys) })\n });\n this.rebuild();\n return registration;\n }\n\n public unregister(ctor: Ctor): void {\n const index = this.entries.findIndex((entry) => entry.ctor === ctor);\n if (index === -1) return;\n this.entries.splice(index, 1);\n this.rebuild();\n }\n\n public chainFor(key: Key): readonly Ctor[] {\n const built = this.chains.get(key);\n if (built) return built;\n\n const chain = this.entries.filter((entry) => !entry.keys || entry.keys.has(key)).map((entry) => entry.ctor);\n this.chains.set(key, chain);\n return chain;\n }\n\n private rebuild(): void {\n // a stable sort keeps ties in registration order\n this.entries.sort((a, b) => a.priority - b.priority);\n this.chains = new Map();\n }\n}\n","import 'reflect-metadata';\n\nimport { GatedMetadataKey } from '#src/metadataKeys';\n\nimport { discardCommits, runCheck, runCommits } from './effects';\n\nimport type { Gate, GateContextBase } from './Gate';\n\n// a combinator (`and`/`or`) reports once under its joined name.\nexport type GateObserver = (gateName: string, elapsedMs: number) => void;\n\nasync function timedCheck(gate: Gate<GateContextBase>, ctx: GateContextBase, observe: GateObserver): Promise<void> {\n const start = performance.now();\n // finally so a refusing gate still reports\n try {\n await runCheck(gate, ctx);\n } finally {\n observe(gate.name, performance.now() - start);\n }\n}\n\n// the first refusal throws out to the dispatcher boundary. an effect gate's commit runs once the\n// whole set passes, and observe never times the commit phase.\nexport async function runGates(\n gates: readonly Gate<GateContextBase>[],\n ctx: GateContextBase,\n observe?: GateObserver\n): Promise<void> {\n try {\n for (const gate of gates) {\n await (observe ? timedCheck(gate, ctx, observe) : runCheck(gate, ctx));\n }\n await runCommits(ctx);\n } finally {\n discardCommits(ctx);\n }\n}\n\n// dispatchers call this before execute, inside the boundary, so a refusal renders or drops\nexport async function runHandlerGates(\n handlerCtor: object,\n ctx: GateContextBase,\n declaredRoute?: string,\n observe?: GateObserver\n): Promise<void> {\n // justified: getMetadata returns any, and this key only ever stores the @Gated gate array\n const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor) as readonly Gate<GateContextBase>[] | undefined;\n if (!gates) return;\n await runGates(gates, { ...ctx, declaredRoute: declaredRoute ?? null }, observe);\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport type { GateObserver } from './runGates';\n\n// combined check time slower than this cuts into the 3s interaction-ack budget\nconst SLOW_GATE_MS = 750;\n\n// lazy because the logger reads the environment, which binds after this module loads\nlet gateLogger: Logger | undefined;\nfunction logger(): Logger {\n gateLogger ??= new Logger('Gates', { channel: 'gates' });\n return gateLogger;\n}\n\n/**\n * collects each gate check's elapsed time for one dispatch. `report` warns once when the checks sum\n * past the threshold, naming the total, the route, and each gate's share.\n */\nexport interface SlowGateMonitor {\n readonly observe: GateObserver;\n report(declaredRoute: string | null): void;\n}\n\n// undefined in production, so timedCheck skips the clock there. read per call, the environment binds late\nexport function slowGateMonitor(): SlowGateMonitor | undefined {\n if (Envapter.isProduction) return undefined;\n const readings: { name: string; ms: number }[] = [];\n return {\n observe: (name, ms) => readings.push({ name, ms }),\n report(declaredRoute) {\n const total = readings.reduce((sum, reading) => sum + reading.ms, 0);\n if (total < SLOW_GATE_MS) return;\n const shares = readings\n .toSorted((a, b) => b.ms - a.ms)\n .map((reading) => `${paint.sky.bold(reading.name)} ${paint.mute(`${Math.round(reading.ms)}ms`)}`);\n const route = declaredRoute === null ? '' : ` for ${paint.sky.bold(declaredRoute)}`;\n const headline = `gates${route} took ${paint.amber(`${Math.round(total)}ms`)} of the 3s ack budget`;\n logger().utils.block(headline, shares, 'warn', (text) => text);\n }\n };\n}\n","// narrows a configured emoji value to the [name, guildId] form\nexport function isEmojiTuple(value: unknown): value is readonly [string, string] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string';\n}\n","import { Routes } from 'discord-api-types/v10';\n\nimport type { REST } from '@discordjs/rest';\nimport type { RESTGetCurrentApplicationResult } from 'discord-api-types/v10';\n\nexport async function currentApplication(rest: Pick<REST, 'get'>): Promise<RESTGetCurrentApplicationResult> {\n // justified: the discord api contract for this route\n return (await rest.get(Routes.currentApplication())) as RESTGetCurrentApplicationResult;\n}\n","import { validateDiscordToken } from '@seedcord/errors/internal';\nimport { Envapter } from 'envapt';\n\nimport type { BotToken } from '@seedcord/errors/internal';\n\nexport function readBotToken(): BotToken {\n return validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n}\n","import { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError, SeedcordTypeError, throwSingleOrAggregate } from '@seedcord/errors/internal';\nimport { asError } from '@seedcord/utils/internal';\nimport { Routes } from 'discord-api-types/v10';\n\nimport { isEmojiTuple } from '#src/miscellaneous/emojiConfig';\nimport { accessorStore } from '#src/miscellaneous/guarded';\nimport { ResolvedEmoji } from '#src/miscellaneous/ResolvedEmoji';\n\nimport type { REST } from '@discordjs/rest';\nimport type { APIEmoji, RESTGetAPIApplicationEmojisResult, RESTGetAPIGuildEmojisResult } from 'discord-api-types/v10';\n\ntype EmojiRest = Pick<REST, 'get'>;\n\ntype EmojisByName = Map<string, APIEmoji>;\n\nfunction byName(list: readonly APIEmoji[]): EmojisByName {\n const map: EmojisByName = new Map();\n for (const entry of list) if (entry.name) map.set(entry.name, entry);\n return map;\n}\n\nclass EmojiResolver {\n private readonly resolved = accessorStore<ResolvedEmoji>();\n private readonly problems: Error[] = [];\n private readonly guilds = new Map<string, EmojisByName | Error>();\n private application?: EmojisByName | Error;\n\n constructor(\n private readonly rest: EmojiRest,\n private readonly applicationId: string\n ) {}\n\n public async resolve(configured: Readonly<Record<string, unknown>>): Promise<Record<string, ResolvedEmoji>> {\n for (const [key, value] of Object.entries(configured)) {\n if (isEmojiTuple(value)) await this.fromGuild(key, value);\n else if (typeof value === 'string') await this.fromApplication(key, value);\n else this.problems.push(new SeedcordTypeError(SeedcordErrorCode.ConfigEmojiInvalid, [key]));\n }\n\n throwSingleOrAggregate(this.problems, SeedcordErrorCode.ConfigEmojiUnresolved);\n return this.resolved;\n }\n\n private async fromApplication(key: string, name: string): Promise<void> {\n this.application ??= await this.applicationEmojis();\n\n if (Error.isError(this.application)) {\n this.unreadable(\n key,\n name,\n 'the application',\n this.application,\n `Check that ${paint.bold('DISCORD_BOT_TOKEN')} is still valid.`\n );\n return;\n }\n this.pick(key, name, this.application, 'the application');\n }\n\n private async applicationEmojis(): Promise<EmojisByName | Error> {\n try {\n // justified: the discord api contract for this route\n const listed = (await this.rest.get(\n Routes.applicationEmojis(this.applicationId)\n )) as RESTGetAPIApplicationEmojisResult;\n return byName(listed.items);\n } catch (error) {\n return asError(error);\n }\n }\n\n private async fromGuild(key: string, [name, guildId]: readonly [string, string]): Promise<void> {\n let guild = this.guilds.get(guildId);\n if (!guild) {\n guild = await this.guildEmojis(guildId);\n this.guilds.set(guildId, guild);\n }\n\n if (Error.isError(guild)) {\n this.unreadable(key, name, `guild ${guildId}`, guild, 'Check that the bot is in that guild.');\n return;\n }\n this.pick(key, name, guild, `guild ${guildId}`);\n }\n\n private async guildEmojis(guildId: string): Promise<EmojisByName | Error> {\n try {\n // justified: the discord api contract for this route\n return byName((await this.rest.get(Routes.guildEmojis(guildId))) as RESTGetAPIGuildEmojisResult);\n } catch (error) {\n return asError(error);\n }\n }\n\n private pick(key: string, name: string, source: EmojisByName, where: string): void {\n const found = source.get(name);\n if (!found?.id) {\n this.problems.push(new SeedcordError(SeedcordErrorCode.ConfigEmojiNotFound, [key, name, where]));\n return;\n }\n this.resolved[key] = new ResolvedEmoji(name, found.id, found.animated ?? false);\n }\n\n private unreadable(key: string, name: string, where: string, cause: Error, fix: string): void {\n this.problems.push(\n new SeedcordError(SeedcordErrorCode.ConfigEmojiSourceUnreadable, [key, name, where, cause.message, fix], {\n cause\n })\n );\n }\n}\n\nexport function resolveEmojis(\n rest: EmojiRest,\n applicationId: string,\n configured: Readonly<Record<string, unknown>>\n): Promise<Record<string, ResolvedEmoji>> {\n return new EmojiResolver(rest, applicationId).resolve(configured);\n}\n","import type { LinkedCommands } from '#src/commands/linkCommands';\nimport type { ResolvedEmoji } from '#src/miscellaneous/ResolvedEmoji';\n\nexport const BAKED_IDS_KEY = 'seedcord:core:baked-ids';\n\nexport interface BakedIds {\n readonly publicKey: string;\n readonly emojis: Readonly<Record<string, Pick<ResolvedEmoji, 'name' | 'id' | 'animated'>>>;\n readonly commands: LinkedCommands['commands'];\n readonly contextMenus: LinkedCommands['contextMenus'];\n}\n","import { Routes } from 'discord-api-types/v10';\n\nimport type { REST } from '@discordjs/rest';\nimport type { RESTGetAPIGuildResult } from 'discord-api-types/v10';\n\nexport interface GuildSummary {\n id: string;\n name: string;\n}\n\ntype ReadRest = Pick<REST, 'get'>;\n\n// the name is only a label. a guild the bot cannot read keeps its id as one\nasync function guildName(rest: ReadRest, guildId: string): Promise<string> {\n try {\n // justified: the discord api contract for the guild route\n return ((await rest.get(Routes.guild(guildId))) as RESTGetAPIGuildResult).name;\n } catch {\n return guildId;\n }\n}\n\nexport function guildSummaries(\n rest: ReadRest,\n guildIds: Iterable<string>,\n knownNames: ReadonlyMap<string, string> = new Map()\n): Promise<GuildSummary[]> {\n return Promise.all(\n Iterator.from(guildIds).map(async (id) => ({ id, name: knownNames.get(id) ?? (await guildName(rest, id)) }))\n );\n}\n","import { paint } from '@seedcord/errors';\nimport { pluralize, unlessAborted } from '@seedcord/utils/internal';\n\nimport { getDevChannel } from '#hmr/devChannel';\nimport { guildSummaries } from '#src/commands/guildSummaries';\nimport { GLOBAL_ID, skippedNote } from '#src/commands/scopes';\n\nimport type { CommandBody } from '#src/commands/putCommands';\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { REST } from '@discordjs/rest';\nimport type { Logger } from '@seedcord/logger';\n\nexport class DeployReporter {\n private readonly guildNames = new Map<string, string>();\n\n constructor(private readonly logger: Logger) {}\n\n // only seedcord dev reads guild names\n public async readGuildNames(\n rest: Pick<REST, 'get'>,\n scopes: readonly ScopeId[],\n signal: AbortSignal | undefined\n ): Promise<void> {\n if (!getDevChannel()) return;\n const unread = scopes.filter((scope) => scope !== GLOBAL_ID && !this.guildNames.has(scope));\n for (const { id, name } of await unlessAborted(guildSummaries(rest, unread), signal)) {\n this.guildNames.set(id, name);\n }\n }\n\n public skipped(scopes: readonly ScopeId[]): void {\n if (scopes.length > 0) this.logger.info(skippedNote(scopes, (guildId) => this.guildLabel(guildId)));\n }\n\n public deployed(scope: ScopeId, commands: readonly CommandBody[]): void {\n const { utils } = this.logger;\n const count = paint.iris.bold(commands.length);\n const tag = pluralize(commands.length, 'command');\n const title =\n scope === GLOBAL_ID\n ? `Deployed ${count} global ${tag}`\n : `Deployed ${count} ${tag} to ${this.guildLabel(scope)}`;\n\n utils.block(title, utils.wrap(commands.map((command) => command.name)), 'info');\n }\n\n private guildLabel(guildId: string): string {\n return paint.sky.bold(this.guildNames.get(guildId) ?? guildId);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport { ConfigGuilds } from '#decorators/Command';\n\nimport type { CommandMeta } from '#decorators/Command';\n\nexport type DeployTarget = Exclude<CommandMeta, { scope: 'config' }>;\n\nexport function resolveDeployTarget(\n meta: CommandMeta,\n configured: readonly string[],\n commandName: string\n): DeployTarget {\n if (meta.scope === 'global') return { scope: 'global' };\n\n if (meta.scope === 'config') {\n return configured.length > 0 ? guildTarget(configured) : { scope: 'global' };\n }\n\n if (configured.length === 0 && meta.guilds.includes(ConfigGuilds)) {\n throw new SeedcordTypeError(SeedcordErrorCode.CoreCommandGuildsEmpty, [commandName]);\n }\n\n return guildTarget(meta.guilds.flatMap((target) => (target === ConfigGuilds ? configured : target)));\n}\n\n// a repeated id would push the same builder into one guild twice, and discord rejects a duplicate name in the put\nfunction guildTarget(guilds: readonly string[]): DeployTarget {\n return { scope: 'guild', guilds: [...new Set(guilds)] };\n}\n","import { isBuilderComponentClass } from '#components/Component';\nimport { CommandMetadataKey } from '#src/metadataKeys';\n\nimport type { BuilderComponent } from '#components/Component';\nimport type { Constructor } from 'type-fest';\n\n/** @internal */\nexport type CommandCtor = Constructor<BuilderComponent<'command' | 'context_menu'>, never[]>;\n\n// the cli's codegen scan reads this too\n/** @internal */\nexport function isCommandClass(value: unknown): value is CommandCtor {\n return isBuilderComponentClass(value) && Reflect.hasMetadata(CommandMetadataKey, value);\n}\n","import { unlessAborted } from '@seedcord/utils/internal';\n\nimport { getDevChannel } from '#hmr/devChannel';\n\nimport type { DeployedHashes, SeedcordCliEvents } from '@seedcord/types/internal';\n\n// seedcord dev answers from one small file\n// something has really gone wrong if this times out\nconst ANSWER_TIMEOUT_MS = 5000;\n\nexport class LastDeploy {\n private saved?: Promise<DeployedHashes | undefined>;\n\n public read(applicationId: string, signal: AbortSignal | undefined): Promise<DeployedHashes | undefined> {\n this.saved ??= askSeedcordDev(applicationId);\n return unlessAborted(this.saved, signal);\n }\n\n public save(deployed: DeployedHashes): void {\n this.saved = Promise.resolve(deployed);\n getDevChannel()?.send('seedcord:commands-deployed', deployed);\n }\n}\n\nfunction askSeedcordDev(applicationId: string): Promise<DeployedHashes | undefined> {\n const cli = getDevChannel();\n if (!cli) return Promise.resolve(undefined);\n const { promise, resolve } = Promise.withResolvers<DeployedHashes | undefined>();\n const settle = (deployed: DeployedHashes | undefined): void => {\n clearTimeout(timer);\n cli.off('seedcord:last-deploy', onAnswer);\n resolve(deployed);\n };\n const onAnswer = ({ deployed }: SeedcordCliEvents['seedcord:last-deploy']): void => settle(deployed);\n const timer = setTimeout(() => settle(undefined), ANSWER_TIMEOUT_MS);\n cli.on('seedcord:last-deploy', onAnswer);\n cli.send('seedcord:read-deployed', { applicationId });\n return promise;\n}\n","import { commandKey } from '#src/commands/linkCommands';\nimport { globalFirst } from '#src/commands/scopes';\n\nimport type { CommandBody, CommandScopes } from '#src/commands/putCommands';\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { DeployResult } from '#src/commands/types';\nimport type { DeployedHashes } from '@seedcord/types/internal';\nimport type { APIApplicationCommand } from 'discord-api-types/v10';\n\ntype ScopeHashes = DeployedHashes['scopes'];\n\ninterface DeployPlan {\n hashes: ScopeHashes;\n // saved before the PUTs in case one fails\n skippedHashes: ScopeHashes;\n toPut: CommandScopes;\n skipped: ScopeId[];\n onDiscord: DeployResult;\n}\n\nconst HEX = 16;\n\nfunction byCodePoint(a: string, b: string): number {\n return a < b ? -1 : Number(a > b);\n}\n\nasync function hashOf(commands: readonly CommandBody[]): Promise<string> {\n // load order differs between the CLI scan and the registry\n const sorted = commands.toSorted((a, b) => byCodePoint(commandKey(a), commandKey(b)));\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(sorted)));\n return [...new Uint8Array(digest)].map((byte) => byte.toString(HEX).padStart(2, '0')).join('');\n}\n\nasync function scopeHashes(scopes: CommandScopes): Promise<ScopeHashes> {\n const hashed = [...scopes].map(async ([scope, commands]) => [scope, await hashOf(commands)] as const);\n return Object.fromEntries(await Promise.all(hashed));\n}\n\nfunction sameCommandNames(\n inCode: readonly CommandBody[],\n deployed: Map<string, APIApplicationCommand> | undefined\n): boolean {\n const onDiscord = new Set(deployed?.values().map(commandKey));\n return new Set(inCode.map(commandKey)).symmetricDifference(onDiscord).size === 0;\n}\n\nexport async function planDeploy(\n scopes: CommandScopes,\n last: ScopeHashes | undefined,\n readUnchanged: (scopes: ScopeId[]) => Promise<DeployResult>\n): Promise<DeployPlan> {\n const hashes = await scopeHashes(scopes);\n const unchanged = globalFirst(scopes).filter(([scope]) => hashes[scope] === last?.[scope]);\n const onDiscord = await readUnchanged(unchanged.map(([scope]) => scope));\n const skipped = new Set(\n unchanged\n .filter(([scope, commands]) => sameCommandNames(commands, onDiscord.get(scope)))\n .map(([scope]) => scope)\n );\n return {\n hashes,\n skippedHashes: Object.fromEntries(Object.entries(hashes).filter(([scope]) => skipped.has(scope))),\n toPut: new Map(scopes.entries().filter(([scope]) => !skipped.has(scope))),\n skipped: [...skipped],\n onDiscord\n };\n}\n","import { DiscordAPIError, HTTPError, RESTEvents } from '@discordjs/rest';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { asError, unlessAborted } from '@seedcord/utils/internal';\n\nimport { indexById } from '#src/commands/linkCommands';\nimport { commandsRoute, GLOBAL_ID, globalFirst } from '#src/commands/scopes';\n\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { DeployResult } from '#src/commands/types';\nimport type { RateLimitData, REST } from '@discordjs/rest';\nimport type { ILogger } from '@seedcord/types';\nimport type { APIApplicationCommand, RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';\n\nexport type CommandBody = RESTPostAPIApplicationCommandsJSONBody;\n\n// a scope left out keeps what discord has. an empty list clears it\nexport type CommandScopes = ReadonlyMap<ScopeId, readonly CommandBody[]>;\n\ninterface PutCommandsOptions {\n readonly rest: Pick<REST, 'put' | 'on' | 'off'>;\n readonly applicationId: string;\n readonly logger: Pick<ILogger, 'warn'>;\n readonly signal?: AbortSignal | undefined;\n readonly onDeployed?: (scope: ScopeId, commands: readonly CommandBody[]) => void;\n}\n\nconst MS_PER_SECOND = 1000;\n\n// discord replaces every command in a scope with the PUT body\nexport async function putCommands(scopes: CommandScopes, options: PutCommandsOptions): Promise<DeployResult> {\n const result: DeployResult = new Map();\n for (const [scope, commands] of globalFirst(scopes)) {\n result.set(scope, indexById(await putScope(scope, commands, options)));\n options.onDeployed?.(scope, commands);\n }\n return result;\n}\n\nasync function putScope(\n scope: ScopeId,\n commands: readonly CommandBody[],\n options: PutCommandsOptions\n): Promise<APIApplicationCommand[]> {\n try {\n return await put(commandsRoute(options.applicationId, scope), commands, options);\n } catch (error) {\n if (options.signal?.aborted) throw error;\n const refusal: [string, number | undefined] = [asError(error).message, statusOf(error)];\n throw scope === GLOBAL_ID\n ? new SeedcordError(SeedcordErrorCode.CoreCommandGlobalDeployFailed, refusal, { cause: error })\n : new SeedcordError(SeedcordErrorCode.CoreCommandGuildDeployFailed, [scope, ...refusal], { cause: error });\n }\n}\n\nfunction statusOf(error: unknown): number | undefined {\n return error instanceof DiscordAPIError || error instanceof HTTPError ? error.status : undefined;\n}\n\nasync function put(\n route: `/${string}`,\n body: readonly CommandBody[],\n { rest, logger, signal }: PutCommandsOptions\n): Promise<APIApplicationCommand[]> {\n const reportWait = (info: RateLimitData): void => {\n if (info.method !== 'PUT' || !info.url.endsWith(route)) return;\n const seconds = Math.ceil(info.timeToReset / MS_PER_SECOND);\n logger.warn(`Discord rate-limited the command deploy. It continues in ${paint.amber(`${seconds}s`)}.`);\n };\n\n rest.on(RESTEvents.RateLimited, reportWait);\n try {\n // @discordjs/rest checks the signal before it sends a request. Its rate-limit sleep ignores it\n // justified: the bulk-overwrite routes return the deployed command array\n return (await unlessAborted(rest.put(route, { body, signal }), signal)) as APIApplicationCommand[];\n } finally {\n rest.off(RESTEvents.RateLimited, reportWait);\n }\n}\n","import { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { asError } from '@seedcord/utils/internal';\n\nimport { indexById } from '#src/commands/linkCommands';\nimport { commandsRoute, GLOBAL_ID } from '#src/commands/scopes';\n\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { DeployResult } from '#src/commands/types';\nimport type { REST } from '@discordjs/rest';\nimport type { APIApplicationCommand, RESTGetAPIApplicationCommandsResult } from 'discord-api-types/v10';\n\ninterface ReadCommandsOptions {\n readonly rest: Pick<REST, 'get'>;\n readonly applicationId: string;\n readonly signal?: AbortSignal | undefined;\n}\n\nexport async function readCommandsAt(\n scope: ScopeId,\n { rest, applicationId, signal }: ReadCommandsOptions\n): Promise<Map<string, APIApplicationCommand>> {\n const route = commandsRoute(applicationId, scope);\n try {\n // justified: the discord api contract for both command list routes\n return indexById((await rest.get(route, { signal })) as RESTGetAPIApplicationCommandsResult);\n } catch (error) {\n if (signal?.aborted) throw error;\n const reason = asError(error).message;\n throw new SeedcordError(\n SeedcordErrorCode.CoreDiscordReadFailed,\n scope === GLOBAL_ID\n ? ['the commands deployed globally', reason]\n : [\n `the commands deployed to guild ${paint.sky(scope)}`,\n reason,\n `Check that ${paint.bold('DISCORD_BOT_TOKEN')} is still valid and that the bot is in that guild.`\n ],\n { cause: error }\n );\n }\n}\n\nexport async function readCommands(scopes: Iterable<ScopeId>, options: ReadCommandsOptions): Promise<DeployResult> {\n const read = Iterator.from(scopes).map(async (scope) => [scope, await readCommandsAt(scope, options)] as const);\n return new Map(await Promise.all(read));\n}\n","import { SlashCommandBuilder } from '@discordjs/builders';\nimport { routeLeavesOf } from '@seedcord/utils/internal';\n\nimport type { ContextMenuCommandBuilder } from '@discordjs/builders';\n\n/**\n * dedupes route keys since the same builder appears once per guild.\n * these come from the same {@link routeLeavesOf} that `seedcord codegen` reads, so the boot-time\n * check compares against the generated registry's keys.\n *\n * @internal\n */\nexport function slashRouteLeaves(commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>): Set<string> {\n const leaves = new Set<string>();\n for (const command of commands) {\n if (!(command instanceof SlashCommandBuilder)) continue;\n for (const leaf of routeLeavesOf(command.toJSON())) leaves.add(leaf.route);\n }\n return leaves;\n}\n","import { SlashCommandBuilder } from '@discordjs/builders';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { formatFilePath, importFiles } from '@seedcord/utils';\nimport { botFolder } from '@seedcord/utils/internal';\nimport { Envapter } from 'envapt';\n\nimport { getDevChannel } from '#hmr/devChannel';\nimport { HmrModuleHandler } from '#hmr/HmrModuleHandler';\nimport { CommandInjector } from '#src/commands/CommandInjector';\nimport { contextMenuLeaves } from '#src/commands/contextMenuLeaves';\nimport { DeployReporter } from '#src/commands/DeployReporter';\nimport { resolveDeployTarget } from '#src/commands/deployTarget';\nimport { isCommandClass } from '#src/commands/isCommandClass';\nimport { LastDeploy } from '#src/commands/LastDeploy';\nimport { planDeploy } from '#src/commands/planDeploy';\nimport { putCommands } from '#src/commands/putCommands';\nimport { readCommands } from '#src/commands/readCommands';\nimport { GLOBAL_ID, scopeIdsOf } from '#src/commands/scopes';\nimport { slashRouteLeaves } from '#src/commands/slashRouteLeaves';\nimport { CommandMetadataKey } from '#src/metadataKeys';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { CommandMeta } from '#decorators/Command';\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { ContextMenuLeaves } from '#src/commands/contextMenuLeaves';\nimport type { DeployTarget } from '#src/commands/deployTarget';\nimport type { CommandCtor } from '#src/commands/isCommandClass';\nimport type { CommandBody, CommandScopes } from '#src/commands/putCommands';\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { CommandBuilder, DeployResult } from '#src/commands/types';\nimport type { Initializeable } from '#src/plugin/Plugin';\nimport type { AllSubscriptions } from '#subscribers/types/Subscriptions';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { APIApplicationCommand } from 'discord-api-types/v10';\n\ntype CommandArtifact = DeployTarget & { name: string };\n\n// gateway's and http's InteractionDispatcher both fit this\ninterface RouteWarnings {\n warnUnhandledRoutes(leaves: Iterable<string>): void;\n warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void;\n}\n\ninterface DeployHooks {\n routes?: RouteWarnings | undefined;\n check?: (commands: CommandBuilder[]) => void;\n}\n\n// discord's global command bucket allows 2 PUTs a minute\n// 75s covers a full wait plus the request itself\nexport const COMMAND_DEPLOY_TIMEOUT_MS = 75_000;\n\n/**\n * Scans the command directory, builds each command, and PUTs the global and guild scopes to Discord.\n *\n * @internal\n */\nexport class CommandRegistry implements Initializeable, HmrAware {\n private readonly logger = new Logger('Commands', { channel: 'commands' });\n private isInitialised = false;\n\n public readonly scopedCommands = new Map<ScopeId, CommandBuilder[]>();\n\n private readonly ctorToCommand = new Map<CommandCtor, CommandArtifact>();\n\n // batched during bulk load. A reload reports on the hmr channel instead\n private loading = false;\n private readonly loadedCommands: { name: string; from: string; kind: 'slash command' | 'context menu' }[] = [];\n\n private readonly hmrHandler?: HmrModuleHandler<CommandCtor, void, CommandArtifact | undefined>;\n private readonly pendingEvents = new Map<string, HmrUpdateEvent>();\n private readonly injector = new CommandInjector();\n private readonly lastDeploy = new LastDeploy();\n private readonly reporter = new DeployReporter(this.logger);\n\n private readonly dir: string;\n private readonly configuredGuilds: readonly string[];\n\n public constructor(private readonly core: CoreBase) {\n const { commands } = core.config.bot;\n if (!commands.path) {\n throw new SeedcordError(SeedcordErrorCode.CoreControllerPathMissing, ['CommandRegistry', 'commands']);\n }\n this.dir = commands.path;\n this.configuredGuilds = commands.guilds ?? [];\n\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n this.hmrHandler = new HmrModuleHandler<CommandCtor, void, CommandArtifact | undefined>({\n handlersDir: this.dir,\n isHandler: isCommandClass,\n registerHandler: this.registerCommand.bind(this),\n unregisterHandler: this.unregisterCommand.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: this.logger\n });\n }\n\n private getArtifacts(ctor: CommandCtor): CommandArtifact | undefined {\n return this.ctorToCommand.get(ctor);\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialised) return;\n this.isInitialised = true;\n\n this.loading = true;\n this.loadedCommands.length = 0;\n try {\n await this.loadCommands(this.dir);\n } finally {\n this.loading = false;\n }\n\n this.reportLoad();\n\n getDevChannel()?.on('seedcord:refresh-commands', (data) => {\n void this.refresh(data.shouldRefresh);\n });\n }\n\n private reportLoad(): void {\n const { utils } = this.logger;\n\n utils.summary(\n 'Loaded commands',\n {\n global: this.scopedCommands.get(GLOBAL_ID)?.length ?? 0,\n 'guild groups': [...this.scopedCommands.keys()].filter((scope) => scope !== GLOBAL_ID).length\n },\n 'debug'\n );\n\n let slash = 0;\n let menu = 0;\n for (const command of this.loadedCommands) {\n if (command.kind === 'slash command') slash++;\n else menu++;\n }\n\n utils.block(\n 'Loaded commands',\n [\n ...utils.entries(this.loadedCommands),\n ...utils.counts({ 'slash commands': slash, 'context menus': menu })\n ],\n 'debug'\n );\n }\n\n /** @internal */\n public async refresh(shouldRefresh = true): Promise<void> {\n if (!shouldRefresh) {\n this.logger.debug(paint.italic('Command refresh cancelled.'));\n this.pendingEvents.clear();\n return;\n }\n\n this.logger.debug(paint.italic('Refreshing commands...'));\n for (const event of this.pendingEvents.values()) {\n await this.hmrHandler?.handle(event);\n }\n this.pendingEvents.clear();\n await this.setCommands();\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n if (botFolder(this.dir).holds(event.file)) {\n this.pendingEvents.delete(event.file);\n this.pendingEvents.set(event.file, event);\n getDevChannel()?.send('seedcord:commands-update-prompt', {\n files: [...this.pendingEvents.keys()].map((f) => formatFilePath(f))\n });\n } else {\n await this.hmrHandler?.handle(event);\n }\n }\n\n private async loadCommands(dir: string): Promise<void> {\n for await (const { fullPath, relativePath, imported } of importFiles(dir)) {\n for (const exported of Object.values(imported)) {\n if (!isCommandClass(exported)) continue;\n this.registerCommand(exported, relativePath);\n this.hmrHandler?.trackHandler(fullPath, exported);\n }\n }\n }\n\n private registerCommand(Ctor: CommandCtor, rel: string): void {\n const meta = Reflect.getMetadata(CommandMetadataKey, Ctor) as CommandMeta | undefined;\n\n if (!meta) return;\n\n const instance = new Ctor();\n const comp = instance.component;\n const kind = comp instanceof SlashCommandBuilder ? 'slash command' : 'context menu';\n\n const target = resolveDeployTarget(meta, this.configuredGuilds, Ctor.name);\n\n for (const scope of scopeIdsOf(target)) {\n const inScope = this.scopedCommands.get(scope);\n if (inScope) inScope.push(comp);\n else this.scopedCommands.set(scope, [comp]);\n }\n\n this.ctorToCommand.set(Ctor, { name: comp.name, ...target });\n\n if (this.loading) {\n this.loadedCommands.push({ name: comp.name, from: formatFilePath(rel), kind });\n }\n }\n\n private unregisterCommand(ctor: CommandCtor, artifacts?: CommandArtifact): void {\n const info = artifacts ?? this.ctorToCommand.get(ctor);\n if (!info) return;\n\n for (const scope of scopeIdsOf(info)) {\n const inScope = this.scopedCommands.get(scope) ?? [];\n const idx = inScope.findIndex((c) => c.name === info.name);\n if (idx !== -1) inScope.splice(idx, 1);\n }\n this.ctorToCommand.delete(ctor);\n }\n\n /** @internal */\n public async deploy(signal: AbortSignal, { routes, check }: DeployHooks): Promise<void> {\n await this.init();\n check?.(this.allCommands());\n await this.setCommands(signal);\n routes?.warnUnhandledRoutes(this.routeLeaves());\n routes?.warnUnhandledContextMenuRoutes(this.contextMenuLeaves());\n }\n\n /** @internal */\n public async setCommands(signal?: AbortSignal): Promise<DeployResult> {\n // read here because gateway assigns core.rest after this registry is constructed\n const { rest, applicationId } = this.core;\n const last = await this.lastDeploy.read(applicationId, signal);\n const plan = await planDeploy(this.commandBodies(), last?.scopes, (unchanged) =>\n readCommands(unchanged, { rest, applicationId, signal })\n );\n await this.reporter.readGuildNames(rest, [...plan.toPut.keys(), ...plan.skipped], signal);\n if (plan.toPut.size > 0) this.lastDeploy.save({ applicationId, scopes: plan.skippedHashes });\n const put = await putCommands(plan.toPut, {\n rest,\n applicationId,\n logger: this.logger,\n signal,\n onDeployed: (scope: ScopeId, commands: readonly CommandBody[]) => this.reporter.deployed(scope, commands)\n });\n\n this.reporter.skipped(plan.skipped);\n const result: DeployResult = new Map([...plan.onDiscord, ...put]);\n this.lastDeploy.save({ applicationId, scopes: plan.hashes });\n\n this.injector.inject(result, this.allCommands());\n this.core.bus[PublishDefault]('commandsDeployed', deployedPayload(result));\n return result;\n }\n\n private commandBodies(): CommandScopes {\n // a guild-only start keeps the global commands another deploy put there\n const deployable = this.scopedCommands\n .entries()\n .filter(([scope, commands]) => scope !== GLOBAL_ID || commands.length > 0);\n return new Map(deployable.map(([scope, commands]) => [scope, commands.map((c) => c.toJSON())]));\n }\n\n private allCommands(): CommandBuilder[] {\n return [...this.scopedCommands.values()].flat();\n }\n\n private routeLeaves(): Set<string> {\n return slashRouteLeaves(this.allCommands());\n }\n\n private contextMenuLeaves(): ContextMenuLeaves {\n return contextMenuLeaves(this.allCommands().map((command) => command.toJSON()));\n }\n}\n\nfunction deployedPayload(result: DeployResult): AllSubscriptions['commandsDeployed'] {\n const guilds: Record<string, APIApplicationCommand[]> = {};\n for (const [scope, deployed] of result) {\n if (scope !== GLOBAL_ID) guilds[scope] = [...deployed.values()];\n }\n return { global: [...(result.get(GLOBAL_ID)?.values() ?? [])], guilds };\n}\n","import type { DevChannel } from '@seedcord/types/internal';\n\n// the minimal raw-hot shape, satisfied by both vite's `import.meta.hot` and the CLI's `NormalizedHotChannel`.\ninterface RawHot {\n send(event: string, data?: unknown): void;\n on(event: string, cb: (data: unknown) => void): void;\n off(event: string, cb: (data: unknown) => void): void;\n}\n\n/** @internal */\nexport function wrapHot<TSend, TRecv>(hot: RawHot): DevChannel<TSend, TRecv> {\n return {\n send: (event, data) => hot.send(event, data),\n // justified: RawHot types every payload as unknown, and the wire contract guarantees it matches TRecv[Key]\n on: (event, cb) => hot.on(event, cb as (data: unknown) => void),\n // justified: the same cast as on()\n off: (event, cb) => hot.off(event, cb as (data: unknown) => void)\n };\n}\n","// scoped here so import.meta.hot stays untyped in the rest of the package\n/// <reference types=\"vite/client\" />\nimport { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport { getDevChannel, setDevChannel } from './devChannel';\nimport { wrapHot } from './wrapHot';\n\nimport type { CliChannel } from './devChannel';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { SeedcordCliEvents, SeedcordFrameworkEvents } from '@seedcord/types/internal';\n\n// the only raw `import.meta.hot` read in the framework\nexport function connectDevChannel(): CliChannel | undefined {\n if (import.meta.hot) setDevChannel(wrapHot<SeedcordFrameworkEvents, SeedcordCliEvents>(import.meta.hot));\n return getDevChannel();\n}\n\nexport class HmrManager {\n private readonly logger = new Logger('HMR', { channel: 'hmr' });\n private readonly listeners = new Set<HmrAware>();\n\n public init(channel: CliChannel | undefined): void {\n if (!channel) return;\n\n if (!(Envapter.isDevelopment || Envapter.isTest)) {\n return;\n }\n\n this.logger.debug('Enabled');\n channel.on('seedcord:hmr', (payload) => void this.handleUpdate(payload));\n }\n\n public register(listener: HmrAware): void {\n this.listeners.add(listener);\n }\n\n private async handleUpdate(event: HmrUpdateEvent): Promise<void> {\n const promises = [...this.listeners].map(async (listener) => {\n try {\n await listener.onHmr(event);\n } catch (error) {\n this.logger.error(`Error handling HMR update in ${paint.sky.bold(listener.constructor.name)}`, error);\n }\n });\n\n await Promise.all(promises);\n }\n}\n","import { LEVEL_COLOR } from '@seedcord/logger';\nimport { filterCirculars } from '@seedcord/utils';\nimport { isPlainObject } from '@seedcord/utils/internal';\n\nimport type { LogLevel, LogRecord } from '@seedcord/types';\nimport type { WireError } from '@seedcord/types/internal';\n\n// each arg is either an Error or a JSON-safe value\ntype WireArg = { error: WireError } | { value: unknown };\n\ninterface WireLogRecord extends Omit<LogRecord, 'args'> {\n args?: WireArg[] | undefined;\n}\n\nexport function toWireError(thrown: unknown): WireError {\n return encodeError(thrown, new Set());\n}\n\nfunction encodeError(thrown: unknown, sent: Set<Error>): WireError {\n if (!Error.isError(thrown)) return { name: 'Error', message: String(thrown) };\n sent.add(thrown);\n const unsent = (value: unknown): boolean => !Error.isError(value) || !sent.has(value);\n const { cause } = thrown;\n return {\n name: thrown.name,\n message: thrown.message,\n stack: thrown.stack,\n code: Reflect.get(thrown, 'code'),\n cause: cause !== undefined && unsent(cause) ? encodeError(cause, sent) : undefined,\n errors:\n thrown instanceof AggregateError\n ? thrown.errors.filter(unsent).map((member) => encodeError(member, sent))\n : undefined\n };\n}\n\nexport function fromWireError({ name, message, stack, code, cause, errors }: WireError): Error {\n const options = cause && { cause: fromWireError(cause) };\n const error = errors\n ? new AggregateError(errors.map(fromWireError), message, options)\n : new Error(message, options);\n error.name = name;\n if (stack !== undefined) error.stack = stack;\n if (code !== undefined) Reflect.set(error, 'code', code);\n return error;\n}\n\n// node's console.log text for a function or a class\nfunction functionText(fn: { readonly name: string }): string {\n const parent: unknown = Object.getPrototypeOf(fn);\n if (Function.prototype.toString.call(fn).startsWith('class')) {\n const extended = typeof parent === 'function' && parent.name ? ` extends ${parent.name}` : '';\n return `[class ${fn.name || '(anonymous)'}${extended}]`;\n }\n const kind = parent === null ? 'Function (null prototype)' : fn.constructor.name;\n return fn.name ? `[${kind}: ${fn.name}]` : `[${kind} (anonymous)]`;\n}\n\nfunction toWireArg(arg: unknown): WireArg {\n if (Error.isError(arg)) return { error: toWireError(arg) };\n // JSON can't hold undefined, a symbol, a bigint or a function\n switch (typeof arg) {\n case 'undefined':\n case 'symbol': {\n return { value: String(arg) };\n }\n case 'bigint': {\n return { value: `${arg}n` };\n }\n case 'function': {\n return { value: functionText(arg) };\n }\n default: {\n return { value: filterCirculars(arg, { mode: 'json' }) };\n }\n }\n}\n\nfunction fromWireArg(arg: WireArg): unknown {\n return 'error' in arg ? fromWireError(arg.error) : arg.value;\n}\n\nfunction toWireLog({ args, ...record }: LogRecord): WireLogRecord {\n return { ...record, args: args?.map(toWireArg) };\n}\n\nfunction fromWireLog({ args, ...record }: WireLogRecord): LogRecord {\n return args ? { ...record, args: args.map(fromWireArg) } : record;\n}\n\n// seedcord dev's workerLogger reads these lines back out of the worker's console output\nconst DEV_LOG_MARKER = 'seedcord:log ';\n\nexport function toDevLogLine(record: LogRecord): string {\n return `${DEV_LOG_MARKER}${JSON.stringify(toWireLog(record))}`;\n}\n\nfunction isLogLevel(value: unknown): value is LogLevel {\n return typeof value === 'string' && Object.hasOwn(LEVEL_COLOR, value);\n}\n\nfunction isWireError(value: unknown): value is WireError {\n return (\n isPlainObject(value) &&\n typeof value.name === 'string' &&\n typeof value.message === 'string' &&\n (value.stack === undefined || typeof value.stack === 'string') &&\n (value.cause === undefined || isWireError(value.cause)) &&\n (value.errors === undefined || (Array.isArray(value.errors) && value.errors.every(isWireError)))\n );\n}\n\nfunction isWireArg(value: unknown): value is WireArg {\n if (!isPlainObject(value)) return false;\n return 'error' in value ? isWireError(value.error) : 'value' in value;\n}\n\nfunction isWireArgs(value: unknown): value is WireLogRecord['args'] {\n return value === undefined || (Array.isArray(value) && value.every(isWireArg));\n}\n\nfunction isWireLogRecord(value: unknown): value is WireLogRecord {\n return (\n isPlainObject(value) &&\n isLogLevel(value.level) &&\n typeof value.message === 'string' &&\n typeof value.label === 'string' &&\n typeof value.channel === 'string' &&\n typeof value.timestamp === 'number' &&\n isWireArgs(value.args)\n );\n}\n\n// the bot's own console output can start with the marker too\nexport function fromDevLogLine(line: string): LogRecord | undefined {\n if (!line.startsWith(DEV_LOG_MARKER)) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line.slice(DEV_LOG_MARKER.length));\n } catch {\n return undefined;\n }\n return isWireLogRecord(parsed) ? fromWireLog(parsed) : undefined;\n}\n","import { format } from 'node:util';\n\nimport { LoggerChannelRegistry } from '@seedcord/logger';\nimport { CONSOLE_LEVEL, consoleRecord, routeConsole, traceError } from '@seedcord/logger/internal';\n\nimport { toDevLogLine } from './wire';\n\nimport type { ConsoleMethod } from '@seedcord/logger/internal';\n\n// workerd's Console prints the arguments of these and nothing for group, count or time\nconst WORKER_ROUTED = [\n 'debug',\n 'dir',\n 'dirxml',\n 'error',\n 'info',\n 'log',\n 'table',\n 'trace',\n 'warn'\n] as const satisfies readonly ConsoleMethod[];\n\n// workaround: workerd cancels a hot.send made during a request. @cloudflare/vite-plugin's runner never awaits it.\n// move this back to the dev channel once the plugin gets fixed\nexport function forwardLogsToCli(): void {\n const registry = LoggerChannelRegistry.instance;\n // eslint-disable-next-line no-console -- console is the transport\n const print = console.log.bind(console);\n registry.installSink({ kind: 'capture', onLog: (record) => print(toDevLogLine(record)) }, { muteConsole: true });\n routeConsole(WORKER_ROUTED, (method, args, invoked) => {\n const level = CONSOLE_LEVEL[method];\n if (method === 'trace') {\n registry.dispatch(consoleRecord(level, '', [traceError(format(...args), invoked)]));\n return;\n }\n const [first, ...rest] = args;\n registry.dispatch(\n typeof first === 'string' ? consoleRecord(level, first, rest) : consoleRecord(level, '', args)\n );\n });\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { timestampFromSnowflake } from '@seedcord/utils';\n\nimport { Notice } from '#stops/Notice';\nimport { Silence } from '#stops/Silence';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { InteractionKind } from '#src/metadataKeys';\nimport type { Bus } from '#subscribers/Bus';\nimport type { DispatchOutcome } from '#subscribers/types/Subscriptions';\n\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatch', { channel: 'interactions' });\n return dispatchLogger;\n}\n\n/**\n * Classifies a caught stop for `interactionDispatched` so a gate and a\n * handler label the same stop identically.\n *\n * @internal\n */\nexport function outcomeFor(caught: unknown): Exclude<DispatchOutcome, 'handled'> {\n if (caught instanceof Silence) return 'refused';\n if (caught instanceof Notice) return caught.report ? 'failed' : 'refused';\n return 'failed';\n}\n\ninterface DispatchReport {\n readonly dispatchId: string;\n readonly routeId: string;\n readonly interactionId: string;\n readonly kind: `${InteractionKind}`;\n readonly outcome: DispatchOutcome;\n /** True when no route matched and the unhandled default ran. */\n readonly fallback: boolean;\n readonly userId: string | null;\n readonly guildId: string | null;\n /** `performance.now()` captured as the dispatch began. */\n readonly startedAt: number;\n /** {@link queuedMsFor} read at the same moment as `startedAt`. */\n readonly queuedMs: number;\n}\n\n/**\n * Discord's interaction creation to now, read from the snowflake. Called at dispatch entry, since\n * a later read would count the handler run into the queue time as well.\n *\n * @internal\n */\nexport function queuedMsFor(interactionId: string): number {\n // timestampFromSnowflake throws on a malformed id\n try {\n return Date.now() - timestampFromSnowflake(interactionId);\n } catch {\n return 0;\n }\n}\n\nexport function reportDispatch(bus: Bus, report: DispatchReport): void {\n const durationMs = performance.now() - report.startedAt;\n bus[PublishDefault]('interactionDispatched', {\n dispatchId: report.dispatchId,\n routeId: report.routeId,\n interactionId: report.interactionId,\n kind: report.kind,\n outcome: report.outcome,\n fallback: report.fallback,\n userId: report.userId,\n guildId: report.guildId,\n durationMs,\n queuedMs: report.queuedMs\n });\n\n // a throwing log sink must not stop the publish above\n logger().trace(\n `${paint.sky.bold(report.routeId)} ${report.outcome} ${paint.mute('in')} ${Math.round(durationMs)}ms`\n );\n}\n","import { asError } from '@seedcord/utils/internal';\n\nimport { outcomeFor } from './dispatchReport';\n\nimport type { DispatchResult, EventDispatchResult, HandlerResult } from './dispatchResult';\nimport type { Logger } from '@seedcord/logger';\n\ninterface Afterable<Result> {\n after?(result: Result): Promise<void>;\n}\n\n/** @internal */\nexport function resultFor(caught: unknown): DispatchResult {\n return { outcome: outcomeFor(caught), caught };\n}\n\n/**\n * Builds what an event middleware's `after()` receives. `handlers` is empty exactly when the chain\n * stopped the event.\n *\n * @internal\n */\nexport function eventResultFor(\n stopped: { caught: unknown } | null,\n handlers: readonly HandlerResult[]\n): EventDispatchResult {\n if (!stopped) return { outcome: 'handled', handlers };\n return { ...resultFor(stopped.caught), handlers: [] };\n}\n\n/**\n * Calls `after()` on each middleware that ran, newest first. A throw inside one is logged. Every\n * remaining middleware still gets its call.\n *\n * @internal\n */\nexport async function runAfter<Result>(\n ran: readonly Afterable<Result>[],\n result: Result,\n logger: Logger\n): Promise<void> {\n for (let index = ran.length - 1; index >= 0; index--) {\n const middleware = ran[index];\n try {\n await middleware?.after?.(result);\n } catch (caught) {\n logger.error(`${middleware?.constructor.name ?? 'A middleware'} threw from after()`, asError(caught));\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"internal.index.mjs","names":["logger"],"sources":["../src/subscribers/SubscriberLoader.ts","../src/decorators/brands.ts","../src/dispatch/MiddlewareRegistry.ts","../src/gates/runGates.ts","../src/gates/slowGate.ts","../src/miscellaneous/emojiConfig.ts","../src/miscellaneous/currentApplication.ts","../src/miscellaneous/readBotToken.ts","../src/miscellaneous/resolveEmojis.ts","../src/bakedIds.ts","../src/commands/guildSummaries.ts","../src/commands/DeployReporter.ts","../src/commands/deployTarget.ts","../src/commands/isCommandClass.ts","../src/commands/LastDeploy.ts","../src/commands/planDeploy.ts","../src/commands/putCommands.ts","../src/commands/readCommands.ts","../src/commands/slashRouteLeaves.ts","../src/commands/CommandRegistry.ts","../src/hmr/wrapHot.ts","../src/hmr/HmrManager.ts","../src/hmr/wire.ts","../src/hmr/forwardLogsToCli.ts","../src/dispatch/dispatchReport.ts","../src/dispatch/runAfter.ts"],"sourcesContent":["import { paint } from '@seedcord/errors';\nimport { importFiles } from '@seedcord/utils';\nimport { Envapter } from 'envapt';\n\nimport { HmrModuleHandler } from '#hmr/HmrModuleHandler';\nimport { SubscribeMetadataKey } from '#src/metadataKeys';\nimport { busLoggerOf } from '#subscribers/Bus';\nimport { registrationFor, Subscriber } from '#subscribers/index';\nimport {\n RegisterDefaults,\n RegisteredCount,\n RegisterSubscriber,\n UnregisterSubscriber,\n VerifyWebhooks\n} from '#subscribers/slots';\n\nimport type { Initializeable } from '#src/plugin/Plugin';\nimport type { SubscribeMetadataEntry } from '#subscribers/decorators/Subscribe';\nimport type { StoredSubscriberCtor, Bus } from '#subscribers/index';\nimport type { SubscriptionKey } from '#subscribers/types/Subscriptions';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\n\ntype SubscriberArtifact = SubscriptionKey[];\n\nexport class SubscriberLoader implements Initializeable, HmrAware {\n private isInitialized = false;\n private readonly hmrHandler?: HmrModuleHandler<StoredSubscriberCtor, void, SubscriberArtifact>;\n\n constructor(\n private readonly bus: Bus,\n private readonly directory: string | null\n ) {\n if (!this.directory) return;\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n\n this.hmrHandler = new HmrModuleHandler({\n handlersDir: this.directory,\n isHandler: this.isSubscriber.bind(this),\n registerHandler: this.registerSubscriber.bind(this),\n unregisterHandler: this.unregisterSubscriber.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: busLoggerOf(this.bus)\n });\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n\n this.bus[RegisterDefaults]();\n\n const { directory } = this;\n if (directory) {\n busLoggerOf(this.bus).debug(paint.mute(directory));\n await this.load(directory);\n busLoggerOf(this.bus).utils.list(\n [`${paint.iris.bold(this.bus[RegisteredCount])} subscribers`],\n paint.mint.bold('Loaded'),\n 'debug'\n );\n }\n\n if (!Envapter.isTest) await this.bus[VerifyWebhooks]();\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n if (this.hmrHandler) await this.hmrHandler.handle(event);\n }\n\n private async load(dir: string): Promise<void> {\n for await (const { fullPath, relativePath, imported } of importFiles(dir)) {\n for (const value of Object.values(imported)) {\n if (!this.isSubscriber(value)) continue;\n\n this.registerSubscriber(value);\n this.hmrHandler?.trackHandler(fullPath, value);\n busLoggerOf(this.bus).utils.registration(value.name, relativePath, undefined, 'trace');\n }\n }\n }\n\n private registerSubscriber(ctor: StoredSubscriberCtor): void {\n this.bus[RegisterSubscriber](registrationFor(ctor));\n }\n\n private unregisterSubscriber(ctor: StoredSubscriberCtor, artifacts?: SubscriberArtifact): void {\n this.bus[UnregisterSubscriber](ctor, artifacts);\n }\n\n private getArtifacts(ctor: StoredSubscriberCtor): SubscriberArtifact {\n const meta = Reflect.getMetadata(SubscribeMetadataKey, ctor) as SubscribeMetadataEntry;\n return [meta.subscriber];\n }\n\n private isSubscriber(value: unknown): value is StoredSubscriberCtor {\n if (typeof value !== 'function') return false;\n return value.prototype instanceof Subscriber && Reflect.hasMetadata(SubscribeMetadataKey, value);\n }\n}\n","// phantom, nothing assigns these at runtime\n\nexport const SlashRouteBrand: unique symbol = Symbol('seedcord:brand:slash-route');\nexport const AutocompleteRouteBrand: unique symbol = Symbol('seedcord:brand:autocomplete-route');\nexport const ContextMenuKindBrand: unique symbol = Symbol('seedcord:brand:context-menu-kind');\nexport const ContextMenuNamesBrand: unique symbol = Symbol('seedcord:brand:context-menu-names');\nexport const ComponentKindBrand: unique symbol = Symbol('seedcord:brand:component-kind');\nexport const ComponentDefsBrand: unique symbol = Symbol('seedcord:brand:component-defs');\nexport const MiddlewareKindsBrand: unique symbol = Symbol('seedcord:brand:middleware-kinds');\n","import { interactionMiddlewareMetaOf } from '#decorators/middleware';\n\nimport type { AnyMiddlewareCtor } from '#decorators/middleware';\nimport type { MiddlewareKind } from '#src/metadataKeys';\n\n/**\n * A decorated middleware reduced to what the registry orders and filters on. An absent `keys` puts the\n * middleware in every chain.\n *\n * @internal\n */\nexport interface MiddlewareRegistration<Key extends string> {\n readonly priority: number;\n readonly keys?: readonly Key[];\n}\n\n/**\n * Reads a transport's decorator metadata off a class. Returns `undefined` for an undecorated one.\n *\n * @internal\n */\nexport type MiddlewareRegistrationOf<Ctor, Key extends string> = (\n ctor: Ctor\n) => MiddlewareRegistration<Key> | undefined;\n\n/** @internal */\nexport function interactionMiddleware(ctor: AnyMiddlewareCtor): MiddlewareRegistration<MiddlewareKind> | undefined {\n const metadata = interactionMiddlewareMetaOf(ctor);\n if (!metadata) return undefined;\n return { priority: metadata.priority, ...(metadata.kinds && { keys: metadata.kinds }) };\n}\n\ninterface Entry<Ctor, Key extends string> {\n readonly ctor: Ctor;\n readonly priority: number;\n readonly keys?: ReadonlySet<Key>;\n}\n\n/**\n * Holds the middleware a transport loaded and keeps one chain per key. An interaction keys on its kind\n * and an event on its name. Priority and the key filter behave the same on each.\n *\n * @typeParam Ctor - The transport's middleware constructor type.\n * @typeParam Key - The chain lookup key.\n *\n * @internal\n */\nexport class MiddlewareRegistry<Ctor extends AnyMiddlewareCtor, Key extends string = MiddlewareKind> {\n private readonly entries: Entry<Ctor, Key>[] = [];\n // an in-flight dispatch keeps walking the array chainFor gave it. never mutate one in place.\n private chains = new Map<Key, readonly Ctor[]>();\n\n public constructor(private readonly registrationOf: MiddlewareRegistrationOf<Ctor, Key>) {}\n\n /**\n * Adds a decorated middleware class and returns what it registered with. Returns `undefined` for an\n * undecorated class or one already registered.\n */\n public register(ctor: Ctor): MiddlewareRegistration<Key> | undefined {\n const registration = this.registrationOf(ctor);\n if (!registration) return undefined;\n\n // a double import or an hmr re-scan registers the same class twice\n if (this.entries.some((entry) => entry.ctor === ctor)) return undefined;\n\n this.entries.push({\n ctor,\n priority: registration.priority,\n ...(registration.keys && { keys: new Set(registration.keys) })\n });\n this.rebuild();\n return registration;\n }\n\n public unregister(ctor: Ctor): void {\n const index = this.entries.findIndex((entry) => entry.ctor === ctor);\n if (index === -1) return;\n this.entries.splice(index, 1);\n this.rebuild();\n }\n\n public chainFor(key: Key): readonly Ctor[] {\n const built = this.chains.get(key);\n if (built) return built;\n\n const chain = this.entries.filter((entry) => !entry.keys || entry.keys.has(key)).map((entry) => entry.ctor);\n this.chains.set(key, chain);\n return chain;\n }\n\n private rebuild(): void {\n // a stable sort keeps ties in registration order\n this.entries.sort((a, b) => a.priority - b.priority);\n this.chains = new Map();\n }\n}\n","import 'reflect-metadata';\n\nimport { GatedMetadataKey } from '#src/metadataKeys';\n\nimport { discardCommits, runCheck, runCommits } from './effects';\n\nimport type { Gate, GateContextBase } from './Gate';\n\n// a combinator (`and`/`or`) reports once under its joined name.\nexport type GateObserver = (gateName: string, elapsedMs: number) => void;\n\nasync function timedCheck(gate: Gate<GateContextBase>, ctx: GateContextBase, observe: GateObserver): Promise<void> {\n const start = performance.now();\n // finally so a refusing gate still reports\n try {\n await runCheck(gate, ctx);\n } finally {\n observe(gate.name, performance.now() - start);\n }\n}\n\n// the first refusal throws out to the dispatcher boundary. an effect gate's commit runs once the\n// whole set passes, and observe never times the commit phase.\nexport async function runGates(\n gates: readonly Gate<GateContextBase>[],\n ctx: GateContextBase,\n observe?: GateObserver\n): Promise<void> {\n try {\n for (const gate of gates) {\n await (observe ? timedCheck(gate, ctx, observe) : runCheck(gate, ctx));\n }\n await runCommits(ctx);\n } finally {\n discardCommits(ctx);\n }\n}\n\n// dispatchers call this before execute, inside the boundary, so a refusal renders or drops\nexport async function runHandlerGates(\n handlerCtor: object,\n ctx: GateContextBase,\n declaredRoute?: string,\n observe?: GateObserver\n): Promise<void> {\n // justified: getMetadata returns any, and this key only ever stores the @Gated gate array\n const gates = Reflect.getMetadata(GatedMetadataKey, handlerCtor) as readonly Gate<GateContextBase>[] | undefined;\n if (!gates) return;\n await runGates(gates, { ...ctx, declaredRoute: declaredRoute ?? null }, observe);\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport type { GateObserver } from './runGates';\n\n// combined check time slower than this cuts into the 3s interaction-ack budget\nconst SLOW_GATE_MS = 750;\n\n// lazy because the logger reads the environment, which binds after this module loads\nlet gateLogger: Logger | undefined;\nfunction logger(): Logger {\n gateLogger ??= new Logger('Gates', { channel: 'gates' });\n return gateLogger;\n}\n\n/**\n * collects each gate check's elapsed time for one dispatch. `report` warns once when the checks sum\n * past the threshold, naming the total, the route, and each gate's share.\n */\nexport interface SlowGateMonitor {\n readonly observe: GateObserver;\n report(declaredRoute: string | null): void;\n}\n\n// undefined in production, so timedCheck skips the clock there. read per call, the environment binds late\nexport function slowGateMonitor(): SlowGateMonitor | undefined {\n if (Envapter.isProduction) return undefined;\n const readings: { name: string; ms: number }[] = [];\n return {\n observe: (name, ms) => readings.push({ name, ms }),\n report(declaredRoute) {\n const total = readings.reduce((sum, reading) => sum + reading.ms, 0);\n if (total < SLOW_GATE_MS) return;\n const shares = readings\n .toSorted((a, b) => b.ms - a.ms)\n .map((reading) => `${paint.sky.bold(reading.name)} ${paint.mute(`${Math.round(reading.ms)}ms`)}`);\n const route = declaredRoute === null ? '' : ` for ${paint.sky.bold(declaredRoute)}`;\n const headline = `gates${route} took ${paint.amber(`${Math.round(total)}ms`)} of the 3s ack budget`;\n logger().utils.block(headline, shares, 'warn', (text) => text);\n }\n };\n}\n","// narrows a configured emoji value to the [name, guildId] form\nexport function isEmojiTuple(value: unknown): value is readonly [string, string] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string';\n}\n","import { Routes } from 'discord-api-types/v10';\n\nimport type { REST } from '@discordjs/rest';\nimport type { RESTGetCurrentApplicationResult } from 'discord-api-types/v10';\n\nexport async function currentApplication(rest: Pick<REST, 'get'>): Promise<RESTGetCurrentApplicationResult> {\n // justified: the discord api contract for this route\n return (await rest.get(Routes.currentApplication())) as RESTGetCurrentApplicationResult;\n}\n","import { validateDiscordToken } from '@seedcord/errors/internal';\nimport { Envapter } from 'envapt';\n\nimport type { BotToken } from '@seedcord/errors/internal';\n\nexport function readBotToken(): BotToken {\n return validateDiscordToken(Envapter.get('DISCORD_BOT_TOKEN'));\n}\n","import { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError, SeedcordTypeError, throwSingleOrAggregate } from '@seedcord/errors/internal';\nimport { asError } from '@seedcord/utils/internal';\nimport { Routes } from 'discord-api-types/v10';\n\nimport { isEmojiTuple } from '#src/miscellaneous/emojiConfig';\nimport { accessorStore } from '#src/miscellaneous/guarded';\nimport { ResolvedEmoji } from '#src/miscellaneous/ResolvedEmoji';\n\nimport type { REST } from '@discordjs/rest';\nimport type { APIEmoji, RESTGetAPIApplicationEmojisResult, RESTGetAPIGuildEmojisResult } from 'discord-api-types/v10';\n\ntype EmojiRest = Pick<REST, 'get'>;\n\ntype EmojisByName = Map<string, APIEmoji>;\n\nfunction byName(list: readonly APIEmoji[]): EmojisByName {\n const map: EmojisByName = new Map();\n for (const entry of list) if (entry.name) map.set(entry.name, entry);\n return map;\n}\n\nclass EmojiResolver {\n private readonly resolved = accessorStore<ResolvedEmoji>();\n private readonly problems: Error[] = [];\n private readonly guilds = new Map<string, EmojisByName | Error>();\n private application?: EmojisByName | Error;\n\n constructor(\n private readonly rest: EmojiRest,\n private readonly applicationId: string\n ) {}\n\n public async resolve(configured: Readonly<Record<string, unknown>>): Promise<Record<string, ResolvedEmoji>> {\n for (const [key, value] of Object.entries(configured)) {\n if (isEmojiTuple(value)) await this.fromGuild(key, value);\n else if (typeof value === 'string') await this.fromApplication(key, value);\n else this.problems.push(new SeedcordTypeError(SeedcordErrorCode.ConfigEmojiInvalid, [key]));\n }\n\n throwSingleOrAggregate(this.problems, SeedcordErrorCode.ConfigEmojiUnresolved);\n return this.resolved;\n }\n\n private async fromApplication(key: string, name: string): Promise<void> {\n this.application ??= await this.applicationEmojis();\n\n if (Error.isError(this.application)) {\n this.unreadable(\n key,\n name,\n 'the application',\n this.application,\n `Check that ${paint.bold('DISCORD_BOT_TOKEN')} is still valid.`\n );\n return;\n }\n this.pick(key, name, this.application, 'the application');\n }\n\n private async applicationEmojis(): Promise<EmojisByName | Error> {\n try {\n // justified: the discord api contract for this route\n const listed = (await this.rest.get(\n Routes.applicationEmojis(this.applicationId)\n )) as RESTGetAPIApplicationEmojisResult;\n return byName(listed.items);\n } catch (error) {\n return asError(error);\n }\n }\n\n private async fromGuild(key: string, [name, guildId]: readonly [string, string]): Promise<void> {\n let guild = this.guilds.get(guildId);\n if (!guild) {\n guild = await this.guildEmojis(guildId);\n this.guilds.set(guildId, guild);\n }\n\n if (Error.isError(guild)) {\n this.unreadable(key, name, `guild ${guildId}`, guild, 'Check that the bot is in that guild.');\n return;\n }\n this.pick(key, name, guild, `guild ${guildId}`);\n }\n\n private async guildEmojis(guildId: string): Promise<EmojisByName | Error> {\n try {\n // justified: the discord api contract for this route\n return byName((await this.rest.get(Routes.guildEmojis(guildId))) as RESTGetAPIGuildEmojisResult);\n } catch (error) {\n return asError(error);\n }\n }\n\n private pick(key: string, name: string, source: EmojisByName, where: string): void {\n const found = source.get(name);\n if (!found?.id) {\n this.problems.push(new SeedcordError(SeedcordErrorCode.ConfigEmojiNotFound, [key, name, where]));\n return;\n }\n this.resolved[key] = new ResolvedEmoji(name, found.id, found.animated ?? false);\n }\n\n private unreadable(key: string, name: string, where: string, cause: Error, fix: string): void {\n this.problems.push(\n new SeedcordError(SeedcordErrorCode.ConfigEmojiSourceUnreadable, [key, name, where, cause.message, fix], {\n cause\n })\n );\n }\n}\n\nexport function resolveEmojis(\n rest: EmojiRest,\n applicationId: string,\n configured: Readonly<Record<string, unknown>>\n): Promise<Record<string, ResolvedEmoji>> {\n return new EmojiResolver(rest, applicationId).resolve(configured);\n}\n","import type { LinkedCommands } from '#src/commands/linkCommands';\nimport type { ResolvedEmoji } from '#src/miscellaneous/ResolvedEmoji';\n\nexport const BAKED_IDS_KEY = 'seedcord:core:baked-ids';\n\nexport interface BakedIds {\n readonly publicKey: string;\n readonly emojis: Readonly<Record<string, Pick<ResolvedEmoji, 'name' | 'id' | 'animated'>>>;\n readonly commands: LinkedCommands['commands'];\n readonly contextMenus: LinkedCommands['contextMenus'];\n}\n","import { Routes } from 'discord-api-types/v10';\n\nimport type { REST } from '@discordjs/rest';\nimport type { RESTGetAPIGuildResult } from 'discord-api-types/v10';\n\nexport interface GuildSummary {\n id: string;\n name: string;\n}\n\ntype ReadRest = Pick<REST, 'get'>;\n\n// the name is only a label. a guild the bot cannot read keeps its id as one\nasync function guildName(rest: ReadRest, guildId: string): Promise<string> {\n try {\n // justified: the discord api contract for the guild route\n return ((await rest.get(Routes.guild(guildId))) as RESTGetAPIGuildResult).name;\n } catch {\n return guildId;\n }\n}\n\nexport function guildSummaries(\n rest: ReadRest,\n guildIds: Iterable<string>,\n knownNames: ReadonlyMap<string, string> = new Map()\n): Promise<GuildSummary[]> {\n return Promise.all(\n Iterator.from(guildIds).map(async (id) => ({ id, name: knownNames.get(id) ?? (await guildName(rest, id)) }))\n );\n}\n","import { paint } from '@seedcord/errors';\nimport { pluralize, unlessAborted } from '@seedcord/utils/internal';\n\nimport { getDevChannel } from '#hmr/devChannel';\nimport { guildSummaries } from '#src/commands/guildSummaries';\nimport { GLOBAL_ID, skippedNote } from '#src/commands/scopes';\n\nimport type { CommandBody } from '#src/commands/putCommands';\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { REST } from '@discordjs/rest';\nimport type { Logger } from '@seedcord/logger';\n\nexport class DeployReporter {\n private readonly guildNames = new Map<string, string>();\n\n constructor(private readonly logger: Logger) {}\n\n // only seedcord dev reads guild names\n public async readGuildNames(\n rest: Pick<REST, 'get'>,\n scopes: readonly ScopeId[],\n signal: AbortSignal | undefined\n ): Promise<void> {\n if (!getDevChannel()) return;\n const unread = scopes.filter((scope) => scope !== GLOBAL_ID && !this.guildNames.has(scope));\n for (const { id, name } of await unlessAborted(guildSummaries(rest, unread), signal)) {\n this.guildNames.set(id, name);\n }\n }\n\n public skipped(scopes: readonly ScopeId[]): void {\n if (scopes.length > 0) this.logger.info(skippedNote(scopes, (guildId) => this.guildLabel(guildId)));\n }\n\n public deployed(scope: ScopeId, commands: readonly CommandBody[]): void {\n const { utils } = this.logger;\n const count = paint.iris.bold(commands.length);\n const tag = pluralize(commands.length, 'command');\n const title =\n scope === GLOBAL_ID\n ? `Deployed ${count} global ${tag}`\n : `Deployed ${count} ${tag} to ${this.guildLabel(scope)}`;\n\n utils.block(title, utils.wrap(commands.map((command) => command.name)), 'info');\n }\n\n private guildLabel(guildId: string): string {\n return paint.sky.bold(this.guildNames.get(guildId) ?? guildId);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport { ConfigGuilds } from '#decorators/Command';\n\nimport type { CommandMeta } from '#decorators/Command';\n\nexport type DeployTarget = Exclude<CommandMeta, { scope: 'config' }>;\n\nexport function resolveDeployTarget(\n meta: CommandMeta,\n configured: readonly string[],\n commandName: string\n): DeployTarget {\n if (meta.scope === 'global') return { scope: 'global' };\n\n if (meta.scope === 'config') {\n return configured.length > 0 ? guildTarget(configured) : { scope: 'global' };\n }\n\n if (configured.length === 0 && meta.guilds.includes(ConfigGuilds)) {\n throw new SeedcordTypeError(SeedcordErrorCode.CoreCommandGuildsEmpty, [commandName]);\n }\n\n return guildTarget(meta.guilds.flatMap((target) => (target === ConfigGuilds ? configured : target)));\n}\n\n// a repeated id would push the same builder into one guild twice, and discord rejects a duplicate name in the put\nfunction guildTarget(guilds: readonly string[]): DeployTarget {\n return { scope: 'guild', guilds: [...new Set(guilds)] };\n}\n","import { isBuilderComponentClass } from '#components/Component';\nimport { CommandMetadataKey } from '#src/metadataKeys';\n\nimport type { BuilderComponent } from '#components/Component';\nimport type { Constructor } from 'type-fest';\n\n/** @internal */\nexport type CommandCtor = Constructor<BuilderComponent<'command' | 'context_menu'>, never[]>;\n\n// the cli's codegen scan reads this too\n/** @internal */\nexport function isCommandClass(value: unknown): value is CommandCtor {\n return isBuilderComponentClass(value) && Reflect.hasMetadata(CommandMetadataKey, value);\n}\n","import { unlessAborted } from '@seedcord/utils/internal';\n\nimport { getDevChannel } from '#hmr/devChannel';\n\nimport type { DeployedHashes, SeedcordCliEvents } from '@seedcord/types/internal';\n\n// seedcord dev answers from one small file\n// something has really gone wrong if this times out\nconst ANSWER_TIMEOUT_MS = 5000;\n\nexport class LastDeploy {\n private saved?: Promise<DeployedHashes | undefined>;\n\n public read(applicationId: string, signal: AbortSignal | undefined): Promise<DeployedHashes | undefined> {\n this.saved ??= askSeedcordDev(applicationId);\n return unlessAborted(this.saved, signal);\n }\n\n public save(deployed: DeployedHashes): void {\n this.saved = Promise.resolve(deployed);\n getDevChannel()?.send('seedcord:commands-deployed', deployed);\n }\n}\n\nfunction askSeedcordDev(applicationId: string): Promise<DeployedHashes | undefined> {\n const cli = getDevChannel();\n if (!cli) return Promise.resolve(undefined);\n const { promise, resolve } = Promise.withResolvers<DeployedHashes | undefined>();\n const settle = (deployed: DeployedHashes | undefined): void => {\n clearTimeout(timer);\n cli.off('seedcord:last-deploy', onAnswer);\n resolve(deployed);\n };\n const onAnswer = ({ deployed }: SeedcordCliEvents['seedcord:last-deploy']): void => settle(deployed);\n const timer = setTimeout(() => settle(undefined), ANSWER_TIMEOUT_MS);\n cli.on('seedcord:last-deploy', onAnswer);\n cli.send('seedcord:read-deployed', { applicationId });\n return promise;\n}\n","import { commandKey } from '#src/commands/linkCommands';\nimport { globalFirst } from '#src/commands/scopes';\n\nimport type { CommandBody, CommandScopes } from '#src/commands/putCommands';\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { DeployResult } from '#src/commands/types';\nimport type { DeployedHashes } from '@seedcord/types/internal';\nimport type { APIApplicationCommand } from 'discord-api-types/v10';\n\ntype ScopeHashes = DeployedHashes['scopes'];\n\ninterface DeployPlan {\n hashes: ScopeHashes;\n // saved before the PUTs in case one fails\n skippedHashes: ScopeHashes;\n toPut: CommandScopes;\n skipped: ScopeId[];\n onDiscord: DeployResult;\n}\n\nconst HEX = 16;\n\nfunction byCodePoint(a: string, b: string): number {\n return a < b ? -1 : Number(a > b);\n}\n\nasync function hashOf(commands: readonly CommandBody[]): Promise<string> {\n // load order differs between the CLI scan and the registry\n const sorted = commands.toSorted((a, b) => byCodePoint(commandKey(a), commandKey(b)));\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(sorted)));\n return [...new Uint8Array(digest)].map((byte) => byte.toString(HEX).padStart(2, '0')).join('');\n}\n\nasync function scopeHashes(scopes: CommandScopes): Promise<ScopeHashes> {\n const hashed = [...scopes].map(async ([scope, commands]) => [scope, await hashOf(commands)] as const);\n return Object.fromEntries(await Promise.all(hashed));\n}\n\nfunction sameCommandNames(\n inCode: readonly CommandBody[],\n deployed: Map<string, APIApplicationCommand> | undefined\n): boolean {\n const onDiscord = new Set(deployed?.values().map(commandKey));\n return new Set(inCode.map(commandKey)).symmetricDifference(onDiscord).size === 0;\n}\n\nexport async function planDeploy(\n scopes: CommandScopes,\n last: ScopeHashes | undefined,\n readUnchanged: (scopes: ScopeId[]) => Promise<DeployResult>\n): Promise<DeployPlan> {\n const hashes = await scopeHashes(scopes);\n const unchanged = globalFirst(scopes).filter(([scope]) => hashes[scope] === last?.[scope]);\n const onDiscord = await readUnchanged(unchanged.map(([scope]) => scope));\n const skipped = new Set(\n unchanged\n .filter(([scope, commands]) => sameCommandNames(commands, onDiscord.get(scope)))\n .map(([scope]) => scope)\n );\n return {\n hashes,\n skippedHashes: Object.fromEntries(Object.entries(hashes).filter(([scope]) => skipped.has(scope))),\n toPut: new Map(scopes.entries().filter(([scope]) => !skipped.has(scope))),\n skipped: [...skipped],\n onDiscord\n };\n}\n","import { DiscordAPIError, HTTPError, RESTEvents } from '@discordjs/rest';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { asError, unlessAborted } from '@seedcord/utils/internal';\n\nimport { indexById } from '#src/commands/linkCommands';\nimport { commandsRoute, GLOBAL_ID, globalFirst } from '#src/commands/scopes';\n\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { DeployResult } from '#src/commands/types';\nimport type { RateLimitData, REST } from '@discordjs/rest';\nimport type { ILogger } from '@seedcord/types';\nimport type { APIApplicationCommand, RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';\n\nexport type CommandBody = RESTPostAPIApplicationCommandsJSONBody;\n\n// a scope left out keeps what discord has. an empty list clears it\nexport type CommandScopes = ReadonlyMap<ScopeId, readonly CommandBody[]>;\n\ninterface PutCommandsOptions {\n readonly rest: Pick<REST, 'put' | 'on' | 'off'>;\n readonly applicationId: string;\n readonly logger: Pick<ILogger, 'warn'>;\n readonly signal?: AbortSignal | undefined;\n readonly onDeployed?: (scope: ScopeId, commands: readonly CommandBody[]) => void;\n}\n\nconst MS_PER_SECOND = 1000;\n\n// discord replaces every command in a scope with the PUT body\nexport async function putCommands(scopes: CommandScopes, options: PutCommandsOptions): Promise<DeployResult> {\n const result: DeployResult = new Map();\n for (const [scope, commands] of globalFirst(scopes)) {\n result.set(scope, indexById(await putScope(scope, commands, options)));\n options.onDeployed?.(scope, commands);\n }\n return result;\n}\n\nasync function putScope(\n scope: ScopeId,\n commands: readonly CommandBody[],\n options: PutCommandsOptions\n): Promise<APIApplicationCommand[]> {\n try {\n return await put(commandsRoute(options.applicationId, scope), commands, options);\n } catch (error) {\n if (options.signal?.aborted) throw error;\n const refusal: [string, number | undefined] = [asError(error).message, statusOf(error)];\n throw scope === GLOBAL_ID\n ? new SeedcordError(SeedcordErrorCode.CoreCommandGlobalDeployFailed, refusal, { cause: error })\n : new SeedcordError(SeedcordErrorCode.CoreCommandGuildDeployFailed, [scope, ...refusal], { cause: error });\n }\n}\n\nfunction statusOf(error: unknown): number | undefined {\n return error instanceof DiscordAPIError || error instanceof HTTPError ? error.status : undefined;\n}\n\nasync function put(\n route: `/${string}`,\n body: readonly CommandBody[],\n { rest, logger, signal }: PutCommandsOptions\n): Promise<APIApplicationCommand[]> {\n const reportWait = (info: RateLimitData): void => {\n if (info.method !== 'PUT' || !info.url.endsWith(route)) return;\n const seconds = Math.ceil(info.timeToReset / MS_PER_SECOND);\n logger.warn(`Discord rate-limited the command deploy. It continues in ${paint.amber(`${seconds}s`)}.`);\n };\n\n rest.on(RESTEvents.RateLimited, reportWait);\n try {\n // @discordjs/rest checks the signal before it sends a request. Its rate-limit sleep ignores it\n // justified: the bulk-overwrite routes return the deployed command array\n return (await unlessAborted(rest.put(route, { body, signal }), signal)) as APIApplicationCommand[];\n } finally {\n rest.off(RESTEvents.RateLimited, reportWait);\n }\n}\n","import { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { asError } from '@seedcord/utils/internal';\n\nimport { indexById } from '#src/commands/linkCommands';\nimport { commandsRoute, GLOBAL_ID } from '#src/commands/scopes';\n\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { DeployResult } from '#src/commands/types';\nimport type { REST } from '@discordjs/rest';\nimport type { APIApplicationCommand, RESTGetAPIApplicationCommandsResult } from 'discord-api-types/v10';\n\ninterface ReadCommandsOptions {\n readonly rest: Pick<REST, 'get'>;\n readonly applicationId: string;\n readonly signal?: AbortSignal | undefined;\n}\n\nexport async function readCommandsAt(\n scope: ScopeId,\n { rest, applicationId, signal }: ReadCommandsOptions\n): Promise<Map<string, APIApplicationCommand>> {\n const route = commandsRoute(applicationId, scope);\n try {\n // justified: the discord api contract for both command list routes\n return indexById((await rest.get(route, { signal })) as RESTGetAPIApplicationCommandsResult);\n } catch (error) {\n if (signal?.aborted) throw error;\n const reason = asError(error).message;\n throw new SeedcordError(\n SeedcordErrorCode.CoreDiscordReadFailed,\n scope === GLOBAL_ID\n ? ['the commands deployed globally', reason]\n : [\n `the commands deployed to guild ${paint.sky(scope)}`,\n reason,\n `Check that ${paint.bold('DISCORD_BOT_TOKEN')} is still valid and that the bot is in that guild.`\n ],\n { cause: error }\n );\n }\n}\n\nexport async function readCommands(scopes: Iterable<ScopeId>, options: ReadCommandsOptions): Promise<DeployResult> {\n const read = Iterator.from(scopes).map(async (scope) => [scope, await readCommandsAt(scope, options)] as const);\n return new Map(await Promise.all(read));\n}\n","import { SlashCommandBuilder } from '@discordjs/builders';\nimport { routeLeavesOf } from '@seedcord/utils/internal';\n\nimport type { ContextMenuCommandBuilder } from '@discordjs/builders';\n\n/**\n * dedupes route keys since the same builder appears once per guild.\n * these come from the same {@link routeLeavesOf} that `seedcord codegen` reads, so the boot-time\n * check compares against the generated registry's keys.\n *\n * @internal\n */\nexport function slashRouteLeaves(commands: Iterable<SlashCommandBuilder | ContextMenuCommandBuilder>): Set<string> {\n const leaves = new Set<string>();\n for (const command of commands) {\n if (!(command instanceof SlashCommandBuilder)) continue;\n for (const leaf of routeLeavesOf(command.toJSON())) leaves.add(leaf.route);\n }\n return leaves;\n}\n","import { SlashCommandBuilder } from '@discordjs/builders';\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { formatFilePath, importFiles } from '@seedcord/utils';\nimport { botFolder } from '@seedcord/utils/internal';\nimport { Envapter } from 'envapt';\n\nimport { getDevChannel } from '#hmr/devChannel';\nimport { HmrModuleHandler } from '#hmr/HmrModuleHandler';\nimport { CommandInjector } from '#src/commands/CommandInjector';\nimport { contextMenuLeaves } from '#src/commands/contextMenuLeaves';\nimport { DeployReporter } from '#src/commands/DeployReporter';\nimport { resolveDeployTarget } from '#src/commands/deployTarget';\nimport { isCommandClass } from '#src/commands/isCommandClass';\nimport { LastDeploy } from '#src/commands/LastDeploy';\nimport { planDeploy } from '#src/commands/planDeploy';\nimport { putCommands } from '#src/commands/putCommands';\nimport { readCommands } from '#src/commands/readCommands';\nimport { GLOBAL_ID, scopeIdsOf } from '#src/commands/scopes';\nimport { slashRouteLeaves } from '#src/commands/slashRouteLeaves';\nimport { CommandMetadataKey } from '#src/metadataKeys';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { CommandMeta } from '#decorators/Command';\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { ContextMenuLeaves } from '#src/commands/contextMenuLeaves';\nimport type { DeployTarget } from '#src/commands/deployTarget';\nimport type { CommandCtor } from '#src/commands/isCommandClass';\nimport type { CommandBody, CommandScopes } from '#src/commands/putCommands';\nimport type { ScopeId } from '#src/commands/scopes';\nimport type { CommandBuilder, DeployResult } from '#src/commands/types';\nimport type { Initializeable } from '#src/plugin/Plugin';\nimport type { AllSubscriptions } from '#subscribers/types/Subscriptions';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { APIApplicationCommand } from 'discord-api-types/v10';\n\ntype CommandArtifact = DeployTarget & { name: string };\n\n// gateway's and http's InteractionDispatcher both fit this\ninterface RouteWarnings {\n warnUnhandledRoutes(leaves: Iterable<string>): void;\n warnUnhandledContextMenuRoutes(leaves: ContextMenuLeaves): void;\n}\n\ninterface DeployHooks {\n routes?: RouteWarnings | undefined;\n check?: (commands: CommandBuilder[]) => void;\n}\n\n// discord's global command bucket allows 2 PUTs a minute\n// 75s covers a full wait plus the request itself\nexport const COMMAND_DEPLOY_TIMEOUT_MS = 75_000;\n\n/**\n * Scans the command directory, builds each command, and PUTs the global and guild scopes to Discord.\n *\n * @internal\n */\nexport class CommandRegistry implements Initializeable, HmrAware {\n private readonly logger = new Logger('Commands', { channel: 'commands' });\n private isInitialised = false;\n\n public readonly scopedCommands = new Map<ScopeId, CommandBuilder[]>();\n\n private readonly ctorToCommand = new Map<CommandCtor, CommandArtifact>();\n\n // batched during bulk load. A reload reports on the hmr channel instead\n private loading = false;\n private readonly loadedCommands: { name: string; from: string; kind: 'slash command' | 'context menu' }[] = [];\n\n private readonly hmrHandler?: HmrModuleHandler<CommandCtor, void, CommandArtifact | undefined>;\n private readonly pendingEvents = new Map<string, HmrUpdateEvent>();\n private readonly injector = new CommandInjector();\n private readonly lastDeploy = new LastDeploy();\n private readonly reporter = new DeployReporter(this.logger);\n\n private readonly dir: string;\n private readonly configuredGuilds: readonly string[];\n\n public constructor(private readonly core: CoreBase) {\n const { commands } = core.config.bot;\n if (!commands.path) {\n throw new SeedcordError(SeedcordErrorCode.CoreControllerPathMissing, ['CommandRegistry', 'commands']);\n }\n this.dir = commands.path;\n this.configuredGuilds = commands.guilds ?? [];\n\n if (!Envapter.isDevelopment && !Envapter.isTest) return;\n this.hmrHandler = new HmrModuleHandler<CommandCtor, void, CommandArtifact | undefined>({\n handlersDir: this.dir,\n isHandler: isCommandClass,\n registerHandler: this.registerCommand.bind(this),\n unregisterHandler: this.unregisterCommand.bind(this),\n getArtifacts: this.getArtifacts.bind(this),\n logger: this.logger\n });\n }\n\n private getArtifacts(ctor: CommandCtor): CommandArtifact | undefined {\n return this.ctorToCommand.get(ctor);\n }\n\n /** @internal */\n public async init(): Promise<void> {\n if (this.isInitialised) return;\n this.isInitialised = true;\n\n this.loading = true;\n this.loadedCommands.length = 0;\n try {\n await this.loadCommands(this.dir);\n } finally {\n this.loading = false;\n }\n\n this.reportLoad();\n\n getDevChannel()?.on('seedcord:refresh-commands', (data) => {\n void this.refresh(data.shouldRefresh);\n });\n }\n\n private reportLoad(): void {\n const { utils } = this.logger;\n\n utils.summary(\n 'Loaded commands',\n {\n global: this.scopedCommands.get(GLOBAL_ID)?.length ?? 0,\n 'guild groups': [...this.scopedCommands.keys()].filter((scope) => scope !== GLOBAL_ID).length\n },\n 'debug'\n );\n\n let slash = 0;\n let menu = 0;\n for (const command of this.loadedCommands) {\n if (command.kind === 'slash command') slash++;\n else menu++;\n }\n\n utils.block(\n 'Loaded commands',\n [\n ...utils.entries(this.loadedCommands),\n ...utils.counts({ 'slash commands': slash, 'context menus': menu })\n ],\n 'debug'\n );\n }\n\n /** @internal */\n public async refresh(shouldRefresh = true): Promise<void> {\n if (!shouldRefresh) {\n this.logger.debug(paint.italic('Command refresh cancelled.'));\n this.pendingEvents.clear();\n return;\n }\n\n this.logger.debug(paint.italic('Refreshing commands...'));\n for (const event of this.pendingEvents.values()) {\n await this.hmrHandler?.handle(event);\n }\n this.pendingEvents.clear();\n await this.setCommands();\n }\n\n /** @internal */\n public async onHmr(event: HmrUpdateEvent): Promise<void> {\n if (botFolder(this.dir).holds(event.file)) {\n this.pendingEvents.delete(event.file);\n this.pendingEvents.set(event.file, event);\n getDevChannel()?.send('seedcord:commands-update-prompt', {\n files: [...this.pendingEvents.keys()].map((f) => formatFilePath(f))\n });\n } else {\n await this.hmrHandler?.handle(event);\n }\n }\n\n private async loadCommands(dir: string): Promise<void> {\n for await (const { fullPath, relativePath, imported } of importFiles(dir)) {\n for (const exported of Object.values(imported)) {\n if (!isCommandClass(exported)) continue;\n this.registerCommand(exported, relativePath);\n this.hmrHandler?.trackHandler(fullPath, exported);\n }\n }\n }\n\n private registerCommand(Ctor: CommandCtor, rel: string): void {\n const meta = Reflect.getMetadata(CommandMetadataKey, Ctor) as CommandMeta | undefined;\n\n if (!meta) return;\n\n const instance = new Ctor();\n const comp = instance.component;\n const kind = comp instanceof SlashCommandBuilder ? 'slash command' : 'context menu';\n\n const target = resolveDeployTarget(meta, this.configuredGuilds, Ctor.name);\n\n for (const scope of scopeIdsOf(target)) {\n const inScope = this.scopedCommands.get(scope);\n if (inScope) inScope.push(comp);\n else this.scopedCommands.set(scope, [comp]);\n }\n\n this.ctorToCommand.set(Ctor, { name: comp.name, ...target });\n\n if (this.loading) {\n this.loadedCommands.push({ name: comp.name, from: formatFilePath(rel), kind });\n }\n }\n\n private unregisterCommand(ctor: CommandCtor, artifacts?: CommandArtifact): void {\n const info = artifacts ?? this.ctorToCommand.get(ctor);\n if (!info) return;\n\n for (const scope of scopeIdsOf(info)) {\n const inScope = this.scopedCommands.get(scope) ?? [];\n const idx = inScope.findIndex((c) => c.name === info.name);\n if (idx !== -1) inScope.splice(idx, 1);\n }\n this.ctorToCommand.delete(ctor);\n }\n\n /** @internal */\n public async deploy(signal: AbortSignal, { routes, check }: DeployHooks): Promise<void> {\n await this.init();\n check?.(this.allCommands());\n await this.setCommands(signal);\n routes?.warnUnhandledRoutes(this.routeLeaves());\n routes?.warnUnhandledContextMenuRoutes(this.contextMenuLeaves());\n }\n\n /** @internal */\n public async setCommands(signal?: AbortSignal): Promise<DeployResult> {\n // read here because gateway assigns core.rest after this registry is constructed\n const { rest, applicationId } = this.core;\n const last = await this.lastDeploy.read(applicationId, signal);\n const plan = await planDeploy(this.commandBodies(), last?.scopes, (unchanged) =>\n readCommands(unchanged, { rest, applicationId, signal })\n );\n await this.reporter.readGuildNames(rest, [...plan.toPut.keys(), ...plan.skipped], signal);\n if (plan.toPut.size > 0) this.lastDeploy.save({ applicationId, scopes: plan.skippedHashes });\n const put = await putCommands(plan.toPut, {\n rest,\n applicationId,\n logger: this.logger,\n signal,\n onDeployed: (scope: ScopeId, commands: readonly CommandBody[]) => this.reporter.deployed(scope, commands)\n });\n\n this.reporter.skipped(plan.skipped);\n const result: DeployResult = new Map([...plan.onDiscord, ...put]);\n this.lastDeploy.save({ applicationId, scopes: plan.hashes });\n\n this.injector.inject(result, this.allCommands());\n this.core.bus[PublishDefault]('commandsDeployed', deployedPayload(result));\n return result;\n }\n\n private commandBodies(): CommandScopes {\n // a guild-only start keeps the global commands another deploy put there\n const deployable = this.scopedCommands\n .entries()\n .filter(([scope, commands]) => scope !== GLOBAL_ID || commands.length > 0);\n return new Map(deployable.map(([scope, commands]) => [scope, commands.map((c) => c.toJSON())]));\n }\n\n private allCommands(): CommandBuilder[] {\n return [...this.scopedCommands.values()].flat();\n }\n\n private routeLeaves(): Set<string> {\n return slashRouteLeaves(this.allCommands());\n }\n\n private contextMenuLeaves(): ContextMenuLeaves {\n return contextMenuLeaves(this.allCommands().map((command) => command.toJSON()));\n }\n}\n\nfunction deployedPayload(result: DeployResult): AllSubscriptions['commandsDeployed'] {\n const guilds: Record<string, APIApplicationCommand[]> = {};\n for (const [scope, deployed] of result) {\n if (scope !== GLOBAL_ID) guilds[scope] = [...deployed.values()];\n }\n return { global: [...(result.get(GLOBAL_ID)?.values() ?? [])], guilds };\n}\n","import type { DevChannel } from '@seedcord/types/internal';\n\n// the minimal raw-hot shape, satisfied by both vite's `import.meta.hot` and the CLI's `NormalizedHotChannel`.\ninterface RawHot {\n send(event: string, data?: unknown): void;\n on(event: string, cb: (data: unknown) => void): void;\n off(event: string, cb: (data: unknown) => void): void;\n}\n\n/** @internal */\nexport function wrapHot<TSend, TRecv>(hot: RawHot): DevChannel<TSend, TRecv> {\n return {\n send: (event, data) => hot.send(event, data),\n // justified: RawHot types every payload as unknown, and the wire contract guarantees it matches TRecv[Key]\n on: (event, cb) => hot.on(event, cb as (data: unknown) => void),\n // justified: the same cast as on()\n off: (event, cb) => hot.off(event, cb as (data: unknown) => void)\n };\n}\n","// scoped here so import.meta.hot stays untyped in the rest of the package\n/// <reference types=\"vite/client\" />\nimport { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { Envapter } from 'envapt';\n\nimport { getDevChannel, setDevChannel } from './devChannel';\nimport { wrapHot } from './wrapHot';\n\nimport type { CliChannel } from './devChannel';\nimport type { HmrAware, HmrUpdateEvent } from '@seedcord/types';\nimport type { SeedcordCliEvents, SeedcordFrameworkEvents } from '@seedcord/types/internal';\n\n// the only raw `import.meta.hot` read in the framework\nexport function connectDevChannel(): CliChannel | undefined {\n if (import.meta.hot) setDevChannel(wrapHot<SeedcordFrameworkEvents, SeedcordCliEvents>(import.meta.hot));\n return getDevChannel();\n}\n\nexport class HmrManager {\n private readonly logger = new Logger('HMR', { channel: 'hmr' });\n private readonly listeners = new Set<HmrAware>();\n\n public init(channel: CliChannel | undefined): void {\n if (!channel) return;\n\n if (!(Envapter.isDevelopment || Envapter.isTest)) {\n return;\n }\n\n this.logger.debug('Enabled');\n channel.on('seedcord:hmr', (payload) => void this.handleUpdate(payload));\n }\n\n public register(listener: HmrAware): void {\n this.listeners.add(listener);\n }\n\n private async handleUpdate(event: HmrUpdateEvent): Promise<void> {\n const promises = [...this.listeners].map(async (listener) => {\n try {\n await listener.onHmr(event);\n } catch (error) {\n this.logger.error(`Error handling HMR update in ${paint.sky.bold(listener.constructor.name)}`, error);\n }\n });\n\n await Promise.all(promises);\n }\n}\n","import { LEVEL_COLOR } from '@seedcord/logger';\nimport { filterCirculars } from '@seedcord/utils';\nimport { isPlainObject } from '@seedcord/utils/internal';\n\nimport type { LogLevel, LogRecord } from '@seedcord/types';\nimport type { WireError } from '@seedcord/types/internal';\n\n// each arg is either an Error or a JSON-safe value\ntype WireArg = { error: WireError } | { value: unknown };\n\ninterface WireLogRecord extends Omit<LogRecord, 'args'> {\n args?: WireArg[] | undefined;\n}\n\nexport function toWireError(thrown: unknown): WireError {\n return encodeError(thrown, new Set());\n}\n\nfunction encodeError(thrown: unknown, sent: Set<Error>): WireError {\n if (!Error.isError(thrown)) return { name: 'Error', message: String(thrown) };\n sent.add(thrown);\n const unsent = (value: unknown): boolean => !Error.isError(value) || !sent.has(value);\n const { cause } = thrown;\n return {\n name: thrown.name,\n message: thrown.message,\n stack: thrown.stack,\n code: Reflect.get(thrown, 'code'),\n cause: cause !== undefined && unsent(cause) ? encodeError(cause, sent) : undefined,\n errors:\n thrown instanceof AggregateError\n ? thrown.errors.filter(unsent).map((member) => encodeError(member, sent))\n : undefined\n };\n}\n\nexport function fromWireError({ name, message, stack, code, cause, errors }: WireError): Error {\n const options = cause && { cause: fromWireError(cause) };\n const error = errors\n ? new AggregateError(errors.map(fromWireError), message, options)\n : new Error(message, options);\n error.name = name;\n if (stack !== undefined) error.stack = stack;\n if (code !== undefined) Reflect.set(error, 'code', code);\n return error;\n}\n\n// node's console.log text for a function or a class\nfunction functionText(fn: { readonly name: string }): string {\n const parent: unknown = Object.getPrototypeOf(fn);\n if (Function.prototype.toString.call(fn).startsWith('class')) {\n const extended = typeof parent === 'function' && parent.name ? ` extends ${parent.name}` : '';\n return `[class ${fn.name || '(anonymous)'}${extended}]`;\n }\n const kind = parent === null ? 'Function (null prototype)' : fn.constructor.name;\n return fn.name ? `[${kind}: ${fn.name}]` : `[${kind} (anonymous)]`;\n}\n\nfunction toWireArg(arg: unknown): WireArg {\n if (Error.isError(arg)) return { error: toWireError(arg) };\n // JSON can't hold undefined, a symbol, a bigint or a function\n switch (typeof arg) {\n case 'undefined':\n case 'symbol': {\n return { value: String(arg) };\n }\n case 'bigint': {\n return { value: `${arg}n` };\n }\n case 'function': {\n return { value: functionText(arg) };\n }\n default: {\n return { value: filterCirculars(arg, { mode: 'json' }) };\n }\n }\n}\n\nfunction fromWireArg(arg: WireArg): unknown {\n return 'error' in arg ? fromWireError(arg.error) : arg.value;\n}\n\nfunction toWireLog({ args, ...record }: LogRecord): WireLogRecord {\n return { ...record, args: args?.map(toWireArg) };\n}\n\nfunction fromWireLog({ args, ...record }: WireLogRecord): LogRecord {\n return args ? { ...record, args: args.map(fromWireArg) } : record;\n}\n\n// seedcord dev's workerLogger reads these lines back out of the worker's console output\nconst DEV_LOG_MARKER = 'seedcord:log ';\n\nexport function toDevLogLine(record: LogRecord): string {\n return `${DEV_LOG_MARKER}${JSON.stringify(toWireLog(record))}`;\n}\n\nfunction isLogLevel(value: unknown): value is LogLevel {\n return typeof value === 'string' && Object.hasOwn(LEVEL_COLOR, value);\n}\n\nfunction isWireError(value: unknown): value is WireError {\n return (\n isPlainObject(value) &&\n typeof value.name === 'string' &&\n typeof value.message === 'string' &&\n (value.stack === undefined || typeof value.stack === 'string') &&\n (value.cause === undefined || isWireError(value.cause)) &&\n (value.errors === undefined || (Array.isArray(value.errors) && value.errors.every(isWireError)))\n );\n}\n\nfunction isWireArg(value: unknown): value is WireArg {\n if (!isPlainObject(value)) return false;\n return 'error' in value ? isWireError(value.error) : 'value' in value;\n}\n\nfunction isWireArgs(value: unknown): value is WireLogRecord['args'] {\n return value === undefined || (Array.isArray(value) && value.every(isWireArg));\n}\n\nfunction isWireLogRecord(value: unknown): value is WireLogRecord {\n return (\n isPlainObject(value) &&\n isLogLevel(value.level) &&\n typeof value.message === 'string' &&\n typeof value.label === 'string' &&\n typeof value.channel === 'string' &&\n typeof value.timestamp === 'number' &&\n isWireArgs(value.args)\n );\n}\n\n// the bot's own console output can start with the marker too\nexport function fromDevLogLine(line: string): LogRecord | undefined {\n if (!line.startsWith(DEV_LOG_MARKER)) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line.slice(DEV_LOG_MARKER.length));\n } catch {\n return undefined;\n }\n return isWireLogRecord(parsed) ? fromWireLog(parsed) : undefined;\n}\n","import { format } from 'node:util';\n\nimport { LoggerChannelRegistry } from '@seedcord/logger';\nimport { CONSOLE_LEVEL, consoleRecord, routeConsole, traceError } from '@seedcord/logger/internal';\n\nimport { toDevLogLine } from './wire';\n\nimport type { ConsoleMethod } from '@seedcord/logger/internal';\n\n// workerd's Console prints the arguments of these and nothing for group, count or time\nconst WORKER_ROUTED = [\n 'debug',\n 'dir',\n 'dirxml',\n 'error',\n 'info',\n 'log',\n 'table',\n 'trace',\n 'warn'\n] as const satisfies readonly ConsoleMethod[];\n\n// workaround: workerd cancels a hot.send made during a request. @cloudflare/vite-plugin's runner never awaits it.\n// move this back to the dev channel once the plugin gets fixed\nexport function forwardLogsToCli(): void {\n const registry = LoggerChannelRegistry.instance;\n // eslint-disable-next-line no-console -- console is the transport\n const print = console.log.bind(console);\n registry.installSink({ kind: 'capture', onLog: (record) => print(toDevLogLine(record)) }, { muteConsole: true });\n routeConsole(WORKER_ROUTED, (method, args, invoked) => {\n const level = CONSOLE_LEVEL[method];\n if (method === 'trace') {\n registry.dispatch(consoleRecord(level, '', [traceError(format(...args), invoked)]));\n return;\n }\n const [first, ...rest] = args;\n registry.dispatch(\n typeof first === 'string' ? consoleRecord(level, first, rest) : consoleRecord(level, '', args)\n );\n });\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { timestampFromSnowflake } from '@seedcord/utils';\n\nimport { Notice } from '#stops/Notice';\nimport { Silence } from '#stops/Silence';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { InteractionKind } from '#src/metadataKeys';\nimport type { Bus } from '#subscribers/Bus';\nimport type { DispatchOutcome } from '#subscribers/types/Subscriptions';\n\nlet dispatchLogger: Logger | undefined;\nfunction logger(): Logger {\n dispatchLogger ??= new Logger('Dispatch', { channel: 'interactions' });\n return dispatchLogger;\n}\n\n/**\n * Classifies a caught stop for `interactionDispatched` so a gate and a\n * handler label the same stop identically.\n *\n * @internal\n */\nexport function outcomeFor(caught: unknown): Exclude<DispatchOutcome, 'handled'> {\n if (caught instanceof Silence) return 'refused';\n if (caught instanceof Notice) return caught.report ? 'failed' : 'refused';\n return 'failed';\n}\n\ninterface DispatchReport {\n readonly dispatchId: string;\n readonly routeId: string;\n readonly interactionId: string;\n readonly kind: `${InteractionKind}`;\n readonly outcome: DispatchOutcome;\n /** True when no route matched and the unhandled default ran. */\n readonly fallback: boolean;\n readonly userId: string | null;\n readonly guildId: string | null;\n /** `performance.now()` captured as the dispatch began. */\n readonly startedAt: number;\n /** {@link queuedMsFor} read at the same moment as `startedAt`. */\n readonly queuedMs: number;\n}\n\n/**\n * Discord's interaction creation to now, read from the snowflake. Called at dispatch entry, since\n * a later read would count the handler run into the queue time as well.\n *\n * @internal\n */\nexport function queuedMsFor(interactionId: string): number {\n // timestampFromSnowflake throws on a malformed id\n try {\n return Date.now() - timestampFromSnowflake(interactionId);\n } catch {\n return 0;\n }\n}\n\nexport function reportDispatch(bus: Bus, report: DispatchReport): void {\n const durationMs = performance.now() - report.startedAt;\n bus[PublishDefault]('interactionDispatched', {\n dispatchId: report.dispatchId,\n routeId: report.routeId,\n interactionId: report.interactionId,\n kind: report.kind,\n outcome: report.outcome,\n fallback: report.fallback,\n userId: report.userId,\n guildId: report.guildId,\n durationMs,\n queuedMs: report.queuedMs\n });\n\n // a throwing log sink must not stop the publish above\n logger().trace(\n `${paint.sky.bold(report.routeId)} ${report.outcome} ${paint.mute('in')} ${Math.round(durationMs)}ms`\n );\n}\n","import { asError } from '@seedcord/utils/internal';\n\nimport { outcomeFor } from './dispatchReport';\n\nimport type { DispatchResult, EventDispatchResult, HandlerResult } from './dispatchResult';\nimport type { Logger } from '@seedcord/logger';\n\ninterface Afterable<Result> {\n after?(result: Result): Promise<void>;\n}\n\n/** @internal */\nexport function resultFor(caught: unknown): DispatchResult {\n return { outcome: outcomeFor(caught), caught };\n}\n\n/**\n * Builds what an event middleware's `after()` receives. `handlers` is empty exactly when the chain\n * stopped the event.\n *\n * @internal\n */\nexport function eventResultFor(\n stopped: { caught: unknown } | null,\n handlers: readonly HandlerResult[]\n): EventDispatchResult {\n if (!stopped) return { outcome: 'handled', handlers };\n return { ...resultFor(stopped.caught), handlers: [] };\n}\n\n/**\n * Calls `after()` on each middleware that ran, newest first. A throw inside one is logged. Every\n * remaining middleware still gets its call.\n *\n * @internal\n */\nexport async function runAfter<Result>(\n ran: readonly Afterable<Result>[],\n result: Result,\n logger: Logger\n): Promise<void> {\n for (let index = ran.length - 1; index >= 0; index--) {\n const middleware = ran[index];\n try {\n await middleware?.after?.(result);\n } catch (caught) {\n logger.error(`${middleware?.constructor.name ?? 'A middleware'} threw from after()`, asError(caught));\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,mBAAb,MAAkE;CAKzC;CACA;CALrB,AAAQ,gBAAgB;CACxB,AAAiB;CAEjB,YACI,AAAiB,KACjB,AAAiB,WACnB;EAFmB;EACA;EAEjB,IAAI,CAAC,KAAK,WAAW;EACrB,IAAI,CAAC,SAAS,iBAAiB,CAAC,SAAS,QAAQ;EAEjD,KAAK,aAAa,IAAI,iBAAiB;GACnC,aAAa,KAAK;GAClB,WAAW,KAAK,aAAa,KAAK,IAAI;GACtC,iBAAiB,KAAK,mBAAmB,KAAK,IAAI;GAClD,mBAAmB,KAAK,qBAAqB,KAAK,IAAI;GACtD,cAAc,KAAK,aAAa,KAAK,IAAI;GACzC,QAAQ,YAAY,KAAK,GAAG;EAChC,CAAC;CACL;;CAGA,MAAa,OAAsB;EAC/B,IAAI,KAAK,eAAe;EACxB,KAAK,gBAAgB;EAErB,KAAK,IAAI,iBAAiB,CAAC;EAE3B,MAAM,EAAE,cAAc;EACtB,IAAI,WAAW;GACX,YAAY,KAAK,GAAG,CAAC,CAAC,MAAM,MAAM,KAAK,SAAS,CAAC;GACjD,MAAM,KAAK,KAAK,SAAS;GACzB,YAAY,KAAK,GAAG,CAAC,CAAC,MAAM,KACxB,CAAC,GAAG,MAAM,KAAK,KAAK,KAAK,IAAI,gBAAgB,EAAE,aAAa,GAC5D,MAAM,KAAK,KAAK,QAAQ,GACxB,OACJ;EACJ;EAEA,IAAI,CAAC,SAAS,QAAQ,MAAM,KAAK,IAAI,eAAe,CAAC;CACzD;;CAGA,MAAa,MAAM,OAAsC;EACrD,IAAI,KAAK,YAAY,MAAM,KAAK,WAAW,OAAO,KAAK;CAC3D;CAEA,MAAc,KAAK,KAA4B;EAC3C,WAAW,MAAM,EAAE,UAAU,cAAc,cAAc,YAAY,GAAG,GACpE,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GAAG;GACzC,IAAI,CAAC,KAAK,aAAa,KAAK,GAAG;GAE/B,KAAK,mBAAmB,KAAK;GAC7B,KAAK,YAAY,aAAa,UAAU,KAAK;GAC7C,YAAY,KAAK,GAAG,CAAC,CAAC,MAAM,aAAa,MAAM,MAAM,cAAc,QAAW,OAAO;EACzF;CAER;CAEA,AAAQ,mBAAmB,MAAkC;EACzD,KAAK,IAAI,mBAAmB,CAAC,gBAAgB,IAAI,CAAC;CACtD;CAEA,AAAQ,qBAAqB,MAA4B,WAAsC;EAC3F,KAAK,IAAI,qBAAqB,CAAC,MAAM,SAAS;CAClD;CAEA,AAAQ,aAAa,MAAgD;EAEjE,OAAO,CADM,QAAQ,YAAY,sBAAsB,IAC5C,CAAC,CAAC,UAAU;CAC3B;CAEA,AAAQ,aAAa,OAA+C;EAChE,IAAI,OAAO,UAAU,YAAY,OAAO;EACxC,OAAO,MAAM,qBAAqB,cAAc,QAAQ,YAAY,sBAAsB,KAAK;CACnG;AACJ;;;;AClGA,MAAa,kBAAiC,OAAO,4BAA4B;AACjF,MAAa,yBAAwC,OAAO,mCAAmC;AAC/F,MAAa,uBAAsC,OAAO,kCAAkC;AAC5F,MAAa,wBAAuC,OAAO,mCAAmC;AAC9F,MAAa,qBAAoC,OAAO,+BAA+B;AACvF,MAAa,qBAAoC,OAAO,+BAA+B;AACvF,MAAa,uBAAsC,OAAO,iCAAiC;;;;;ACkB3F,SAAgB,sBAAsB,MAA6E;CAC/G,MAAM,WAAW,4BAA4B,IAAI;CACjD,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO;EAAE,UAAU,SAAS;EAAU,GAAI,SAAS,SAAS,EAAE,MAAM,SAAS,MAAM;CAAG;AAC1F;;;;;;;;;;AAiBA,IAAa,qBAAb,MAAqG;CAK7D;CAJpC,AAAiB,UAA8B,CAAC;CAEhD,AAAQ,yBAAS,IAAI,IAA0B;CAE/C,AAAO,YAAY,AAAiB,gBAAqD;EAArD;CAAsD;;;;;CAM1F,AAAO,SAAS,MAAqD;EACjE,MAAM,eAAe,KAAK,eAAe,IAAI;EAC7C,IAAI,CAAC,cAAc,OAAO;EAG1B,IAAI,KAAK,QAAQ,MAAM,UAAU,MAAM,SAAS,IAAI,GAAG,OAAO;EAE9D,KAAK,QAAQ,KAAK;GACd;GACA,UAAU,aAAa;GACvB,GAAI,aAAa,QAAQ,EAAE,MAAM,IAAI,IAAI,aAAa,IAAI,EAAE;EAChE,CAAC;EACD,KAAK,QAAQ;EACb,OAAO;CACX;CAEA,AAAO,WAAW,MAAkB;EAChC,MAAM,QAAQ,KAAK,QAAQ,WAAW,UAAU,MAAM,SAAS,IAAI;EACnE,IAAI,UAAU,IAAI;EAClB,KAAK,QAAQ,OAAO,OAAO,CAAC;EAC5B,KAAK,QAAQ;CACjB;CAEA,AAAO,SAAS,KAA2B;EACvC,MAAM,QAAQ,KAAK,OAAO,IAAI,GAAG;EACjC,IAAI,OAAO,OAAO;EAElB,MAAM,QAAQ,KAAK,QAAQ,QAAQ,UAAU,CAAC,MAAM,QAAQ,MAAM,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,UAAU,MAAM,IAAI;EAC1G,KAAK,OAAO,IAAI,KAAK,KAAK;EAC1B,OAAO;CACX;CAEA,AAAQ,UAAgB;EAEpB,KAAK,QAAQ,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;EACnD,KAAK,yBAAS,IAAI,IAAI;CAC1B;AACJ;;;;ACpFA,eAAe,WAAW,MAA6B,KAAsB,SAAsC;CAC/G,MAAM,QAAQ,YAAY,IAAI;CAE9B,IAAI;EACA,MAAM,SAAS,MAAM,GAAG;CAC5B,UAAU;EACN,QAAQ,KAAK,MAAM,YAAY,IAAI,IAAI,KAAK;CAChD;AACJ;AAIA,eAAsB,SAClB,OACA,KACA,SACa;CACb,IAAI;EACA,KAAK,MAAM,QAAQ,OACf,OAAO,UAAU,WAAW,MAAM,KAAK,OAAO,IAAI,SAAS,MAAM,GAAG;EAExE,MAAM,WAAW,GAAG;CACxB,UAAU;EACN,eAAe,GAAG;CACtB;AACJ;AAGA,eAAsB,gBAClB,aACA,KACA,eACA,SACa;CAEb,MAAM,QAAQ,QAAQ,YAAY,kBAAkB,WAAW;CAC/D,IAAI,CAAC,OAAO;CACZ,MAAM,SAAS,OAAO;EAAE,GAAG;EAAK,eAAe,iBAAiB;CAAK,GAAG,OAAO;AACnF;;;;AC1CA,MAAM,eAAe;AAGrB,IAAI;AACJ,SAASA,WAAiB;CACtB,eAAe,IAAI,OAAO,SAAS,EAAE,SAAS,QAAQ,CAAC;CACvD,OAAO;AACX;AAYA,SAAgB,kBAA+C;CAC3D,IAAI,SAAS,cAAc,OAAO;CAClC,MAAM,WAA2C,CAAC;CAClD,OAAO;EACH,UAAU,MAAM,OAAO,SAAS,KAAK;GAAE;GAAM;EAAG,CAAC;EACjD,OAAO,eAAe;GAClB,MAAM,QAAQ,SAAS,QAAQ,KAAK,YAAY,MAAM,QAAQ,IAAI,CAAC;GACnE,IAAI,QAAQ,cAAc;GAC1B,MAAM,SAAS,SACV,UAAU,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAC/B,KAAK,YAAY,GAAG,MAAM,IAAI,KAAK,QAAQ,IAAI,EAAE,GAAG,MAAM,KAAK,GAAG,KAAK,MAAM,QAAQ,EAAE,EAAE,GAAG,GAAG;GAEpG,MAAM,WAAW,QADH,kBAAkB,OAAO,KAAK,QAAQ,MAAM,IAAI,KAAK,aAAa,IACjD,QAAQ,MAAM,MAAM,GAAG,KAAK,MAAM,KAAK,EAAE,GAAG,EAAE;GAC7E,SAAO,CAAC,CAAC,MAAM,MAAM,UAAU,QAAQ,SAAS,SAAS,IAAI;EACjE;CACJ;AACJ;;;;ACzCA,SAAgB,aAAa,OAAoD;CAC7E,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,KAAK,OAAO,MAAM,OAAO,YAAY,OAAO,MAAM,OAAO;AAC7G;;;;ACEA,eAAsB,mBAAmB,MAAmE;CAExG,OAAQ,MAAM,KAAK,IAAI,OAAO,mBAAmB,CAAC;AACtD;;;;ACHA,SAAgB,eAAyB;CACrC,OAAO,qBAAqB,SAAS,IAAI,mBAAmB,CAAC;AACjE;;;;ACSA,SAAS,OAAO,MAAyC;CACrD,MAAM,sBAAoB,IAAI,IAAI;CAClC,KAAK,MAAM,SAAS,MAAM,IAAI,MAAM,MAAM,IAAI,IAAI,MAAM,MAAM,KAAK;CACnE,OAAO;AACX;AAEA,IAAM,gBAAN,MAAoB;CAOK;CACA;CAPrB,AAAiB,WAAW,cAA6B;CACzD,AAAiB,WAAoB,CAAC;CACtC,AAAiB,yBAAS,IAAI,IAAkC;CAChE,AAAQ;CAER,YACI,AAAiB,MACjB,AAAiB,eACnB;EAFmB;EACA;CAClB;CAEH,MAAa,QAAQ,YAAuF;EACxG,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAChD,IAAI,aAAa,KAAK,GAAG,MAAM,KAAK,UAAU,KAAK,KAAK;OACnD,IAAI,OAAO,UAAU,UAAU,MAAM,KAAK,gBAAgB,KAAK,KAAK;OACpE,KAAK,SAAS,KAAK,IAAI,kBAAkB,kBAAkB,oBAAoB,CAAC,GAAG,CAAC,CAAC;EAG9F,uBAAuB,KAAK,UAAU,kBAAkB,qBAAqB;EAC7E,OAAO,KAAK;CAChB;CAEA,MAAc,gBAAgB,KAAa,MAA6B;EACpE,KAAK,gBAAgB,MAAM,KAAK,kBAAkB;EAElD,IAAI,MAAM,QAAQ,KAAK,WAAW,GAAG;GACjC,KAAK,WACD,KACA,MACA,mBACA,KAAK,aACL,cAAc,MAAM,KAAK,mBAAmB,EAAE,iBAClD;GACA;EACJ;EACA,KAAK,KAAK,KAAK,MAAM,KAAK,aAAa,iBAAiB;CAC5D;CAEA,MAAc,oBAAmD;EAC7D,IAAI;GAKA,OAAO,QAAO,MAHQ,KAAK,KAAK,IAC5B,OAAO,kBAAkB,KAAK,aAAa,CAC/C,EACoB,CAAC,KAAK;EAC9B,SAAS,OAAO;GACZ,OAAO,QAAQ,KAAK;EACxB;CACJ;CAEA,MAAc,UAAU,KAAa,CAAC,MAAM,UAAoD;EAC5F,IAAI,QAAQ,KAAK,OAAO,IAAI,OAAO;EACnC,IAAI,CAAC,OAAO;GACR,QAAQ,MAAM,KAAK,YAAY,OAAO;GACtC,KAAK,OAAO,IAAI,SAAS,KAAK;EAClC;EAEA,IAAI,MAAM,QAAQ,KAAK,GAAG;GACtB,KAAK,WAAW,KAAK,MAAM,SAAS,WAAW,OAAO,sCAAsC;GAC5F;EACJ;EACA,KAAK,KAAK,KAAK,MAAM,OAAO,SAAS,SAAS;CAClD;CAEA,MAAc,YAAY,SAAgD;EACtE,IAAI;GAEA,OAAO,OAAQ,MAAM,KAAK,KAAK,IAAI,OAAO,YAAY,OAAO,CAAC,CAAiC;EACnG,SAAS,OAAO;GACZ,OAAO,QAAQ,KAAK;EACxB;CACJ;CAEA,AAAQ,KAAK,KAAa,MAAc,QAAsB,OAAqB;EAC/E,MAAM,QAAQ,OAAO,IAAI,IAAI;EAC7B,IAAI,CAAC,OAAO,IAAI;GACZ,KAAK,SAAS,KAAK,IAAI,cAAc,kBAAkB,qBAAqB;IAAC;IAAK;IAAM;GAAK,CAAC,CAAC;GAC/F;EACJ;EACA,KAAK,SAAS,OAAO,IAAI,cAAc,MAAM,MAAM,IAAI,MAAM,YAAY,KAAK;CAClF;CAEA,AAAQ,WAAW,KAAa,MAAc,OAAe,OAAc,KAAmB;EAC1F,KAAK,SAAS,KACV,IAAI,cAAc,kBAAkB,6BAA6B;GAAC;GAAK;GAAM;GAAO,MAAM;GAAS;EAAG,GAAG,EACrG,MACJ,CAAC,CACL;CACJ;AACJ;AAEA,SAAgB,cACZ,MACA,eACA,YACsC;CACtC,OAAO,IAAI,cAAc,MAAM,aAAa,CAAC,CAAC,QAAQ,UAAU;AACpE;;;;ACpHA,MAAa,gBAAgB;;;;ACU7B,eAAe,UAAU,MAAgB,SAAkC;CACvE,IAAI;EAEA,QAAS,MAAM,KAAK,IAAI,OAAO,MAAM,OAAO,CAAC,EAAC,CAA4B;CAC9E,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,SAAgB,eACZ,MACA,UACA,6BAA0C,IAAI,IAAI,GAC3B;CACvB,OAAO,QAAQ,IACX,SAAS,KAAK,QAAQ,CAAC,CAAC,IAAI,OAAO,QAAQ;EAAE;EAAI,MAAM,WAAW,IAAI,EAAE,KAAM,MAAM,UAAU,MAAM,EAAE;CAAG,EAAE,CAC/G;AACJ;;;;AClBA,IAAa,iBAAb,MAA4B;CAGK;CAF7B,AAAiB,6BAAa,IAAI,IAAoB;CAEtD,YAAY,AAAiB,QAAgB;EAAhB;CAAiB;CAG9C,MAAa,eACT,MACA,QACA,QACa;EACb,IAAI,CAAC,cAAc,GAAG;EACtB,MAAM,SAAS,OAAO,QAAQ,UAAU,sBAAuB,CAAC,KAAK,WAAW,IAAI,KAAK,CAAC;EAC1F,KAAK,MAAM,EAAE,IAAI,UAAU,MAAM,cAAc,eAAe,MAAM,MAAM,GAAG,MAAM,GAC/E,KAAK,WAAW,IAAI,IAAI,IAAI;CAEpC;CAEA,AAAO,QAAQ,QAAkC;EAC7C,IAAI,OAAO,SAAS,GAAG,KAAK,OAAO,KAAK,YAAY,SAAS,YAAY,KAAK,WAAW,OAAO,CAAC,CAAC;CACtG;CAEA,AAAO,SAAS,OAAgB,UAAwC;EACpE,MAAM,EAAE,UAAU,KAAK;EACvB,MAAM,QAAQ,MAAM,KAAK,KAAK,SAAS,MAAM;EAC7C,MAAM,MAAM,UAAU,SAAS,QAAQ,SAAS;EAChD,MAAM,QACF,qBACM,YAAY,MAAM,UAAU,QAC5B,YAAY,MAAM,GAAG,IAAI,MAAM,KAAK,WAAW,KAAK;EAE9D,MAAM,MAAM,OAAO,MAAM,KAAK,SAAS,KAAK,YAAY,QAAQ,IAAI,CAAC,GAAG,MAAM;CAClF;CAEA,AAAQ,WAAW,SAAyB;EACxC,OAAO,MAAM,IAAI,KAAK,KAAK,WAAW,IAAI,OAAO,KAAK,OAAO;CACjE;AACJ;;;;ACxCA,SAAgB,oBACZ,MACA,YACA,aACY;CACZ,IAAI,KAAK,UAAU,UAAU,OAAO,EAAE,OAAO,SAAS;CAEtD,IAAI,KAAK,UAAU,UACf,OAAO,WAAW,SAAS,IAAI,YAAY,UAAU,IAAI,EAAE,OAAO,SAAS;CAG/E,IAAI,WAAW,WAAW,KAAK,KAAK,OAAO,iBAAqB,GAC5D,MAAM,IAAI,kBAAkB,kBAAkB,wBAAwB,CAAC,WAAW,CAAC;CAGvF,OAAO,YAAY,KAAK,OAAO,SAAS,WAAY,sBAA0B,aAAa,MAAO,CAAC;AACvG;AAGA,SAAS,YAAY,QAAyC;CAC1D,OAAO;EAAE,OAAO;EAAS,QAAQ,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC;CAAE;AAC1D;;;;;ACnBA,SAAgB,eAAe,OAAsC;CACjE,OAAO,wBAAwB,KAAK,KAAK,QAAQ,YAAY,oBAAoB,KAAK;AAC1F;;;;ACLA,MAAM,oBAAoB;AAE1B,IAAa,aAAb,MAAwB;CACpB,AAAQ;CAER,AAAO,KAAK,eAAuB,QAAsE;EACrG,KAAK,UAAU,eAAe,aAAa;EAC3C,OAAO,cAAc,KAAK,OAAO,MAAM;CAC3C;CAEA,AAAO,KAAK,UAAgC;EACxC,KAAK,QAAQ,QAAQ,QAAQ,QAAQ;EACrC,cAAc,CAAC,EAAE,KAAK,8BAA8B,QAAQ;CAChE;AACJ;AAEA,SAAS,eAAe,eAA4D;CAChF,MAAM,MAAM,cAAc;CAC1B,IAAI,CAAC,KAAK,OAAO,QAAQ,QAAQ,MAAS;CAC1C,MAAM,EAAE,SAAS,YAAY,QAAQ,cAA0C;CAC/E,MAAM,UAAU,aAA+C;EAC3D,aAAa,KAAK;EAClB,IAAI,IAAI,wBAAwB,QAAQ;EACxC,QAAQ,QAAQ;CACpB;CACA,MAAM,YAAY,EAAE,eAAgE,OAAO,QAAQ;CACnG,MAAM,QAAQ,iBAAiB,OAAO,MAAS,GAAG,iBAAiB;CACnE,IAAI,GAAG,wBAAwB,QAAQ;CACvC,IAAI,KAAK,0BAA0B,EAAE,cAAc,CAAC;CACpD,OAAO;AACX;;;;AClBA,MAAM,MAAM;AAEZ,SAAS,YAAY,GAAW,GAAmB;CAC/C,OAAO,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC;AACpC;AAEA,eAAe,OAAO,UAAmD;CAErE,MAAM,SAAS,SAAS,UAAU,GAAG,MAAM,YAAY,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;CACpF,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC;CACrG,OAAO,CAAC,GAAG,IAAI,WAAW,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,KAAK,SAAS,GAAG,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;AACjG;AAEA,eAAe,YAAY,QAA6C;CACpE,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,cAAc,CAAC,OAAO,MAAM,OAAO,QAAQ,CAAC,CAAU;CACpG,OAAO,OAAO,YAAY,MAAM,QAAQ,IAAI,MAAM,CAAC;AACvD;AAEA,SAAS,iBACL,QACA,UACO;CACP,MAAM,YAAY,IAAI,IAAI,UAAU,OAAO,CAAC,CAAC,IAAI,UAAU,CAAC;CAC5D,OAAO,IAAI,IAAI,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,oBAAoB,SAAS,CAAC,CAAC,SAAS;AACnF;AAEA,eAAsB,WAClB,QACA,MACA,eACmB;CACnB,MAAM,SAAS,MAAM,YAAY,MAAM;CACvC,MAAM,YAAY,YAAY,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,OAAO,WAAW,OAAO,MAAM;CACzF,MAAM,YAAY,MAAM,cAAc,UAAU,KAAK,CAAC,WAAW,KAAK,CAAC;CACvE,MAAM,UAAU,IAAI,IAChB,UACK,QAAQ,CAAC,OAAO,cAAc,iBAAiB,UAAU,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,CAC/E,KAAK,CAAC,WAAW,KAAK,CAC/B;CACA,OAAO;EACH;EACA,eAAe,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,QAAQ,IAAI,KAAK,CAAC,CAAC;EAChG,OAAO,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;EACxE,SAAS,CAAC,GAAG,OAAO;EACpB;CACJ;AACJ;;;;ACvCA,MAAM,gBAAgB;AAGtB,eAAsB,YAAY,QAAuB,SAAoD;CACzG,MAAM,yBAAuB,IAAI,IAAI;CACrC,KAAK,MAAM,CAAC,OAAO,aAAa,YAAY,MAAM,GAAG;EACjD,OAAO,IAAI,OAAO,UAAU,MAAM,SAAS,OAAO,UAAU,OAAO,CAAC,CAAC;EACrE,QAAQ,aAAa,OAAO,QAAQ;CACxC;CACA,OAAO;AACX;AAEA,eAAe,SACX,OACA,UACA,SACgC;CAChC,IAAI;EACA,OAAO,MAAM,IAAI,cAAc,QAAQ,eAAe,KAAK,GAAG,UAAU,OAAO;CACnF,SAAS,OAAO;EACZ,IAAI,QAAQ,QAAQ,SAAS,MAAM;EACnC,MAAM,UAAwC,CAAC,QAAQ,KAAK,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC;EACtF,MAAM,qBACA,IAAI,cAAc,kBAAkB,+BAA+B,SAAS,EAAE,OAAO,MAAM,CAAC,IAC5F,IAAI,cAAc,kBAAkB,8BAA8B,CAAC,OAAO,GAAG,OAAO,GAAG,EAAE,OAAO,MAAM,CAAC;CACjH;AACJ;AAEA,SAAS,SAAS,OAAoC;CAClD,OAAO,iBAAiB,mBAAmB,iBAAiB,YAAY,MAAM,SAAS;AAC3F;AAEA,eAAe,IACX,OACA,MACA,EAAE,MAAM,QAAQ,UACgB;CAChC,MAAM,cAAc,SAA8B;EAC9C,IAAI,KAAK,WAAW,SAAS,CAAC,KAAK,IAAI,SAAS,KAAK,GAAG;EACxD,MAAM,UAAU,KAAK,KAAK,KAAK,cAAc,aAAa;EAC1D,OAAO,KAAK,4DAA4D,MAAM,MAAM,GAAG,QAAQ,EAAE,EAAE,EAAE;CACzG;CAEA,KAAK,GAAG,WAAW,aAAa,UAAU;CAC1C,IAAI;EAGA,OAAQ,MAAM,cAAc,KAAK,IAAI,OAAO;GAAE;GAAM;EAAO,CAAC,GAAG,MAAM;CACzE,UAAU;EACN,KAAK,IAAI,WAAW,aAAa,UAAU;CAC/C;AACJ;;;;AC5DA,eAAsB,eAClB,OACA,EAAE,MAAM,eAAe,UACoB;CAC3C,MAAM,QAAQ,cAAc,eAAe,KAAK;CAChD,IAAI;EAEA,OAAO,UAAW,MAAM,KAAK,IAAI,OAAO,EAAE,OAAO,CAAC,CAAyC;CAC/F,SAAS,OAAO;EACZ,IAAI,QAAQ,SAAS,MAAM;EAC3B,MAAM,SAAS,QAAQ,KAAK,CAAC,CAAC;EAC9B,MAAM,IAAI,cACN,kBAAkB,uBAClB,qBACM,CAAC,kCAAkC,MAAM,IACzC;GACI,kCAAkC,MAAM,IAAI,KAAK;GACjD;GACA,cAAc,MAAM,KAAK,mBAAmB,EAAE;EAClD,GACN,EAAE,OAAO,MAAM,CACnB;CACJ;AACJ;AAEA,eAAsB,aAAa,QAA2B,SAAqD;CAC/G,MAAM,OAAO,SAAS,KAAK,MAAM,CAAC,CAAC,IAAI,OAAO,UAAU,CAAC,OAAO,MAAM,eAAe,OAAO,OAAO,CAAC,CAAU;CAC9G,OAAO,IAAI,IAAI,MAAM,QAAQ,IAAI,IAAI,CAAC;AAC1C;;;;;;;;;;;AClCA,SAAgB,iBAAiB,UAAkF;CAC/G,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,WAAW,UAAU;EAC5B,IAAI,EAAE,mBAAmB,sBAAsB;EAC/C,KAAK,MAAM,QAAQ,cAAc,QAAQ,OAAO,CAAC,GAAG,OAAO,IAAI,KAAK,KAAK;CAC7E;CACA,OAAO;AACX;;;;ACiCA,MAAa,4BAA4B;;;;;;AAOzC,IAAa,kBAAb,MAAiE;CAqBzB;CApBpC,AAAiB,SAAS,IAAI,OAAO,YAAY,EAAE,SAAS,WAAW,CAAC;CACxE,AAAQ,gBAAgB;CAExB,AAAgB,iCAAiB,IAAI,IAA+B;CAEpE,AAAiB,gCAAgB,IAAI,IAAkC;CAGvE,AAAQ,UAAU;CAClB,AAAiB,iBAA2F,CAAC;CAE7G,AAAiB;CACjB,AAAiB,gCAAgB,IAAI,IAA4B;CACjE,AAAiB,WAAW,IAAI,gBAAgB;CAChD,AAAiB,aAAa,IAAI,WAAW;CAC7C,AAAiB,WAAW,IAAI,eAAe,KAAK,MAAM;CAE1D,AAAiB;CACjB,AAAiB;CAEjB,AAAO,YAAY,AAAiB,MAAgB;EAAhB;EAChC,MAAM,EAAE,aAAa,KAAK,OAAO;EACjC,IAAI,CAAC,SAAS,MACV,MAAM,IAAI,cAAc,kBAAkB,2BAA2B,CAAC,mBAAmB,UAAU,CAAC;EAExG,KAAK,MAAM,SAAS;EACpB,KAAK,mBAAmB,SAAS,UAAU,CAAC;EAE5C,IAAI,CAAC,SAAS,iBAAiB,CAAC,SAAS,QAAQ;EACjD,KAAK,aAAa,IAAI,iBAAiE;GACnF,aAAa,KAAK;GAClB,WAAW;GACX,iBAAiB,KAAK,gBAAgB,KAAK,IAAI;GAC/C,mBAAmB,KAAK,kBAAkB,KAAK,IAAI;GACnD,cAAc,KAAK,aAAa,KAAK,IAAI;GACzC,QAAQ,KAAK;EACjB,CAAC;CACL;CAEA,AAAQ,aAAa,MAAgD;EACjE,OAAO,KAAK,cAAc,IAAI,IAAI;CACtC;;CAGA,MAAa,OAAsB;EAC/B,IAAI,KAAK,eAAe;EACxB,KAAK,gBAAgB;EAErB,KAAK,UAAU;EACf,KAAK,eAAe,SAAS;EAC7B,IAAI;GACA,MAAM,KAAK,aAAa,KAAK,GAAG;EACpC,UAAU;GACN,KAAK,UAAU;EACnB;EAEA,KAAK,WAAW;EAEhB,cAAc,CAAC,EAAE,GAAG,8BAA8B,SAAS;GACvD,AAAK,KAAK,QAAQ,KAAK,aAAa;EACxC,CAAC;CACL;CAEA,AAAQ,aAAmB;EACvB,MAAM,EAAE,UAAU,KAAK;EAEvB,MAAM,QACF,mBACA;GACI,QAAQ,KAAK,eAAe,YAAa,CAAC,EAAE,UAAU;GACtD,gBAAgB,CAAC,GAAG,KAAK,eAAe,KAAK,CAAC,CAAC,CAAC,QAAQ,UAAU,UAAU,SAAS,CAAC,CAAC;EAC3F,GACA,OACJ;EAEA,IAAI,QAAQ;EACZ,IAAI,OAAO;EACX,KAAK,MAAM,WAAW,KAAK,gBACvB,IAAI,QAAQ,SAAS,iBAAiB;OACjC;EAGT,MAAM,MACF,mBACA,CACI,GAAG,MAAM,QAAQ,KAAK,cAAc,GACpC,GAAG,MAAM,OAAO;GAAE,kBAAkB;GAAO,iBAAiB;EAAK,CAAC,CACtE,GACA,OACJ;CACJ;;CAGA,MAAa,QAAQ,gBAAgB,MAAqB;EACtD,IAAI,CAAC,eAAe;GAChB,KAAK,OAAO,MAAM,MAAM,OAAO,4BAA4B,CAAC;GAC5D,KAAK,cAAc,MAAM;GACzB;EACJ;EAEA,KAAK,OAAO,MAAM,MAAM,OAAO,wBAAwB,CAAC;EACxD,KAAK,MAAM,SAAS,KAAK,cAAc,OAAO,GAC1C,MAAM,KAAK,YAAY,OAAO,KAAK;EAEvC,KAAK,cAAc,MAAM;EACzB,MAAM,KAAK,YAAY;CAC3B;;CAGA,MAAa,MAAM,OAAsC;EACrD,IAAI,UAAU,KAAK,GAAG,CAAC,CAAC,MAAM,MAAM,IAAI,GAAG;GACvC,KAAK,cAAc,OAAO,MAAM,IAAI;GACpC,KAAK,cAAc,IAAI,MAAM,MAAM,KAAK;GACxC,cAAc,CAAC,EAAE,KAAK,mCAAmC,EACrD,OAAO,CAAC,GAAG,KAAK,cAAc,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,eAAe,CAAC,CAAC,EACtE,CAAC;EACL,OACI,MAAM,KAAK,YAAY,OAAO,KAAK;CAE3C;CAEA,MAAc,aAAa,KAA4B;EACnD,WAAW,MAAM,EAAE,UAAU,cAAc,cAAc,YAAY,GAAG,GACpE,KAAK,MAAM,YAAY,OAAO,OAAO,QAAQ,GAAG;GAC5C,IAAI,CAAC,eAAe,QAAQ,GAAG;GAC/B,KAAK,gBAAgB,UAAU,YAAY;GAC3C,KAAK,YAAY,aAAa,UAAU,QAAQ;EACpD;CAER;CAEA,AAAQ,gBAAgB,MAAmB,KAAmB;EAC1D,MAAM,OAAO,QAAQ,YAAY,oBAAoB,IAAI;EAEzD,IAAI,CAAC,MAAM;EAGX,MAAM,OAAO,IADQ,KACD,CAAC,CAAC;EACtB,MAAM,OAAO,gBAAgB,sBAAsB,kBAAkB;EAErE,MAAM,SAAS,oBAAoB,MAAM,KAAK,kBAAkB,KAAK,IAAI;EAEzE,KAAK,MAAM,SAAS,WAAW,MAAM,GAAG;GACpC,MAAM,UAAU,KAAK,eAAe,IAAI,KAAK;GAC7C,IAAI,SAAS,QAAQ,KAAK,IAAI;QACzB,KAAK,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;EAC9C;EAEA,KAAK,cAAc,IAAI,MAAM;GAAE,MAAM,KAAK;GAAM,GAAG;EAAO,CAAC;EAE3D,IAAI,KAAK,SACL,KAAK,eAAe,KAAK;GAAE,MAAM,KAAK;GAAM,MAAM,eAAe,GAAG;GAAG;EAAK,CAAC;CAErF;CAEA,AAAQ,kBAAkB,MAAmB,WAAmC;EAC5E,MAAM,OAAO,aAAa,KAAK,cAAc,IAAI,IAAI;EACrD,IAAI,CAAC,MAAM;EAEX,KAAK,MAAM,SAAS,WAAW,IAAI,GAAG;GAClC,MAAM,UAAU,KAAK,eAAe,IAAI,KAAK,KAAK,CAAC;GACnD,MAAM,MAAM,QAAQ,WAAW,MAAM,EAAE,SAAS,KAAK,IAAI;GACzD,IAAI,QAAQ,IAAI,QAAQ,OAAO,KAAK,CAAC;EACzC;EACA,KAAK,cAAc,OAAO,IAAI;CAClC;;CAGA,MAAa,OAAO,QAAqB,EAAE,QAAQ,SAAqC;EACpF,MAAM,KAAK,KAAK;EAChB,QAAQ,KAAK,YAAY,CAAC;EAC1B,MAAM,KAAK,YAAY,MAAM;EAC7B,QAAQ,oBAAoB,KAAK,YAAY,CAAC;EAC9C,QAAQ,+BAA+B,KAAK,kBAAkB,CAAC;CACnE;;CAGA,MAAa,YAAY,QAA6C;EAElE,MAAM,EAAE,MAAM,kBAAkB,KAAK;EACrC,MAAM,OAAO,MAAM,KAAK,WAAW,KAAK,eAAe,MAAM;EAC7D,MAAM,OAAO,MAAM,WAAW,KAAK,cAAc,GAAG,MAAM,SAAS,cAC/D,aAAa,WAAW;GAAE;GAAM;GAAe;EAAO,CAAC,CAC3D;EACA,MAAM,KAAK,SAAS,eAAe,MAAM,CAAC,GAAG,KAAK,MAAM,KAAK,GAAG,GAAG,KAAK,OAAO,GAAG,MAAM;EACxF,IAAI,KAAK,MAAM,OAAO,GAAG,KAAK,WAAW,KAAK;GAAE;GAAe,QAAQ,KAAK;EAAc,CAAC;EAC3F,MAAM,MAAM,MAAM,YAAY,KAAK,OAAO;GACtC;GACA;GACA,QAAQ,KAAK;GACb;GACA,aAAa,OAAgB,aAAqC,KAAK,SAAS,SAAS,OAAO,QAAQ;EAC5G,CAAC;EAED,KAAK,SAAS,QAAQ,KAAK,OAAO;EAClC,MAAM,SAAuB,IAAI,IAAI,CAAC,GAAG,KAAK,WAAW,GAAG,GAAG,CAAC;EAChE,KAAK,WAAW,KAAK;GAAE;GAAe,QAAQ,KAAK;EAAO,CAAC;EAE3D,KAAK,SAAS,OAAO,QAAQ,KAAK,YAAY,CAAC;EAC/C,KAAK,KAAK,IAAI,eAAe,CAAC,oBAAoB,gBAAgB,MAAM,CAAC;EACzE,OAAO;CACX;CAEA,AAAQ,gBAA+B;EAEnC,MAAM,aAAa,KAAK,eACnB,QAAQ,CAAC,CACT,QAAQ,CAAC,OAAO,cAAc,sBAAuB,SAAS,SAAS,CAAC;EAC7E,OAAO,IAAI,IAAI,WAAW,KAAK,CAAC,OAAO,cAAc,CAAC,OAAO,SAAS,KAAK,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAClG;CAEA,AAAQ,cAAgC;EACpC,OAAO,CAAC,GAAG,KAAK,eAAe,OAAO,CAAC,CAAC,CAAC,KAAK;CAClD;CAEA,AAAQ,cAA2B;EAC/B,OAAO,iBAAiB,KAAK,YAAY,CAAC;CAC9C;CAEA,AAAQ,oBAAuC;EAC3C,OAAO,kBAAkB,KAAK,YAAY,CAAC,CAAC,KAAK,YAAY,QAAQ,OAAO,CAAC,CAAC;CAClF;AACJ;AAEA,SAAS,gBAAgB,QAA4D;CACjF,MAAM,SAAkD,CAAC;CACzD,KAAK,MAAM,CAAC,OAAO,aAAa,QAC5B,IAAI,oBAAqB,OAAO,SAAS,CAAC,GAAG,SAAS,OAAO,CAAC;CAElE,OAAO;EAAE,QAAQ,CAAC,GAAI,OAAO,YAAa,CAAC,EAAE,OAAO,KAAK,CAAC,CAAE;EAAG;CAAO;AAC1E;;;;;ACxRA,SAAgB,QAAsB,KAAuC;CACzE,OAAO;EACH,OAAO,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI;EAE3C,KAAK,OAAO,OAAO,IAAI,GAAG,OAAO,EAA6B;EAE9D,MAAM,OAAO,OAAO,IAAI,IAAI,OAAO,EAA6B;CACpE;AACJ;;;;ACJA,SAAgB,oBAA4C;CACxD,IAAI,YAAY,KAAK,cAAc,QAAoD,YAAY,GAAG,CAAC;CACvG,OAAO,cAAc;AACzB;AAEA,IAAa,aAAb,MAAwB;CACpB,AAAiB,SAAS,IAAI,OAAO,OAAO,EAAE,SAAS,MAAM,CAAC;CAC9D,AAAiB,4BAAY,IAAI,IAAc;CAE/C,AAAO,KAAK,SAAuC;EAC/C,IAAI,CAAC,SAAS;EAEd,IAAI,EAAE,SAAS,iBAAiB,SAAS,SACrC;EAGJ,KAAK,OAAO,MAAM,SAAS;EAC3B,QAAQ,GAAG,iBAAiB,YAAY,KAAK,KAAK,aAAa,OAAO,CAAC;CAC3E;CAEA,AAAO,SAAS,UAA0B;EACtC,KAAK,UAAU,IAAI,QAAQ;CAC/B;CAEA,MAAc,aAAa,OAAsC;EAC7D,MAAM,WAAW,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,IAAI,OAAO,aAAa;GACzD,IAAI;IACA,MAAM,SAAS,MAAM,KAAK;GAC9B,SAAS,OAAO;IACZ,KAAK,OAAO,MAAM,gCAAgC,MAAM,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,KAAK;GACxG;EACJ,CAAC;EAED,MAAM,QAAQ,IAAI,QAAQ;CAC9B;AACJ;;;;ACnCA,SAAgB,YAAY,QAA4B;CACpD,OAAO,YAAY,wBAAQ,IAAI,IAAI,CAAC;AACxC;AAEA,SAAS,YAAY,QAAiB,MAA6B;CAC/D,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,OAAO;EAAE,MAAM;EAAS,SAAS,OAAO,MAAM;CAAE;CAC5E,KAAK,IAAI,MAAM;CACf,MAAM,UAAU,UAA4B,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,KAAK,IAAI,KAAK;CACpF,MAAM,EAAE,UAAU;CAClB,OAAO;EACH,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,OAAO,OAAO;EACd,MAAM,QAAQ,IAAI,QAAQ,MAAM;EAChC,OAAO,UAAU,UAAa,OAAO,KAAK,IAAI,YAAY,OAAO,IAAI,IAAI;EACzE,QACI,kBAAkB,iBACZ,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,KAAK,WAAW,YAAY,QAAQ,IAAI,CAAC,IACtE;CACd;AACJ;AAEA,SAAgB,cAAc,EAAE,MAAM,SAAS,OAAO,MAAM,OAAO,UAA4B;CAC3F,MAAM,UAAU,SAAS,EAAE,OAAO,cAAc,KAAK,EAAE;CACvD,MAAM,QAAQ,SACR,IAAI,eAAe,OAAO,IAAI,aAAa,GAAG,SAAS,OAAO,IAC9D,IAAI,MAAM,SAAS,OAAO;CAChC,MAAM,OAAO;CACb,IAAI,UAAU,QAAW,MAAM,QAAQ;CACvC,IAAI,SAAS,QAAW,QAAQ,IAAI,OAAO,QAAQ,IAAI;CACvD,OAAO;AACX;AAGA,SAAS,aAAa,IAAuC;CACzD,MAAM,SAAkB,OAAO,eAAe,EAAE;CAChD,IAAI,SAAS,UAAU,SAAS,KAAK,EAAE,CAAC,CAAC,WAAW,OAAO,GAAG;EAC1D,MAAM,WAAW,OAAO,WAAW,cAAc,OAAO,OAAO,YAAY,OAAO,SAAS;EAC3F,OAAO,UAAU,GAAG,QAAQ,gBAAgB,SAAS;CACzD;CACA,MAAM,OAAO,WAAW,OAAO,8BAA8B,GAAG,YAAY;CAC5E,OAAO,GAAG,OAAO,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,KAAK;AACxD;AAEA,SAAS,UAAU,KAAuB;CACtC,IAAI,MAAM,QAAQ,GAAG,GAAG,OAAO,EAAE,OAAO,YAAY,GAAG,EAAE;CAEzD,QAAQ,OAAO,KAAf;EACI,KAAK;EACL,KAAK,UACD,OAAO,EAAE,OAAO,OAAO,GAAG,EAAE;EAEhC,KAAK,UACD,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG;EAE9B,KAAK,YACD,OAAO,EAAE,OAAO,aAAa,GAAG,EAAE;EAEtC,SACI,OAAO,EAAE,OAAO,gBAAgB,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;CAE/D;AACJ;AAEA,SAAS,YAAY,KAAuB;CACxC,OAAO,WAAW,MAAM,cAAc,IAAI,KAAK,IAAI,IAAI;AAC3D;AAEA,SAAS,UAAU,EAAE,MAAM,GAAG,UAAoC;CAC9D,OAAO;EAAE,GAAG;EAAQ,MAAM,MAAM,IAAI,SAAS;CAAE;AACnD;AAEA,SAAS,YAAY,EAAE,MAAM,GAAG,UAAoC;CAChE,OAAO,OAAO;EAAE,GAAG;EAAQ,MAAM,KAAK,IAAI,WAAW;CAAE,IAAI;AAC/D;AAGA,MAAM,iBAAiB;AAEvB,SAAgB,aAAa,QAA2B;CACpD,OAAO,GAAG,iBAAiB,KAAK,UAAU,UAAU,MAAM,CAAC;AAC/D;AAEA,SAAS,WAAW,OAAmC;CACnD,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,aAAa,KAAK;AACxE;AAEA,SAAS,YAAY,OAAoC;CACrD,OACI,cAAc,KAAK,KACnB,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,YAAY,aACxB,MAAM,UAAU,UAAa,OAAO,MAAM,UAAU,cACpD,MAAM,UAAU,UAAa,YAAY,MAAM,KAAK,OACpD,MAAM,WAAW,UAAc,MAAM,QAAQ,MAAM,MAAM,KAAK,MAAM,OAAO,MAAM,WAAW;AAErG;AAEA,SAAS,UAAU,OAAkC;CACjD,IAAI,CAAC,cAAc,KAAK,GAAG,OAAO;CAClC,OAAO,WAAW,QAAQ,YAAY,MAAM,KAAK,IAAI,WAAW;AACpE;AAEA,SAAS,WAAW,OAAgD;CAChE,OAAO,UAAU,UAAc,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,SAAS;AAChF;AAEA,SAAS,gBAAgB,OAAwC;CAC7D,OACI,cAAc,KAAK,KACnB,WAAW,MAAM,KAAK,KACtB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,cAAc,YAC3B,WAAW,MAAM,IAAI;AAE7B;AAGA,SAAgB,eAAe,MAAqC;CAChE,IAAI,CAAC,KAAK,WAAW,cAAc,GAAG,OAAO;CAC7C,IAAI;CACJ,IAAI;EACA,SAAS,KAAK,MAAM,KAAK,MAAM,EAAqB,CAAC;CACzD,QAAQ;EACJ;CACJ;CACA,OAAO,gBAAgB,MAAM,IAAI,YAAY,MAAM,IAAI;AAC3D;;;;ACrIA,MAAM,gBAAgB;CAClB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;AAIA,SAAgB,mBAAyB;CACrC,MAAM,WAAW,sBAAsB;CAEvC,MAAM,QAAQ,QAAQ,IAAI,KAAK,OAAO;CACtC,SAAS,YAAY;EAAE,MAAM;EAAW,QAAQ,WAAW,MAAM,aAAa,MAAM,CAAC;CAAE,GAAG,EAAE,aAAa,KAAK,CAAC;CAC/G,aAAa,gBAAgB,QAAQ,MAAM,YAAY;EACnD,MAAM,QAAQ,cAAc;EAC5B,IAAI,WAAW,SAAS;GACpB,SAAS,SAAS,cAAc,OAAO,IAAI,CAAC,WAAW,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;GAClF;EACJ;EACA,MAAM,CAAC,OAAO,GAAG,QAAQ;EACzB,SAAS,SACL,OAAO,UAAU,WAAW,cAAc,OAAO,OAAO,IAAI,IAAI,cAAc,OAAO,IAAI,IAAI,CACjG;CACJ,CAAC;AACL;;;;AC5BA,IAAI;AACJ,SAAS,SAAiB;CACtB,mBAAmB,IAAI,OAAO,YAAY,EAAE,SAAS,eAAe,CAAC;CACrE,OAAO;AACX;;;;;;;AAQA,SAAgB,WAAW,QAAsD;CAC7E,IAAI,kBAAkB,SAAS,OAAO;CACtC,IAAI,kBAAkB,QAAQ,OAAO,OAAO,SAAS,WAAW;CAChE,OAAO;AACX;;;;;;;AAwBA,SAAgB,YAAY,eAA+B;CAEvD,IAAI;EACA,OAAO,KAAK,IAAI,IAAI,uBAAuB,aAAa;CAC5D,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,SAAgB,eAAe,KAAU,QAA8B;CACnE,MAAM,aAAa,YAAY,IAAI,IAAI,OAAO;CAC9C,IAAI,eAAe,CAAC,yBAAyB;EACzC,YAAY,OAAO;EACnB,SAAS,OAAO;EAChB,eAAe,OAAO;EACtB,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB;EACA,UAAU,OAAO;CACrB,CAAC;CAGD,OAAO,CAAC,CAAC,MACL,GAAG,MAAM,IAAI,KAAK,OAAO,OAAO,EAAE,GAAG,OAAO,QAAQ,GAAG,MAAM,KAAK,IAAI,EAAE,GAAG,KAAK,MAAM,UAAU,EAAE,GACtG;AACJ;;;;;ACpEA,SAAgB,UAAU,QAAiC;CACvD,OAAO;EAAE,SAAS,WAAW,MAAM;EAAG;CAAO;AACjD;;;;;;;AAQA,SAAgB,eACZ,SACA,UACmB;CACnB,IAAI,CAAC,SAAS,OAAO;EAAE,SAAS;EAAW;CAAS;CACpD,OAAO;EAAE,GAAG,UAAU,QAAQ,MAAM;EAAG,UAAU,CAAC;CAAE;AACxD;;;;;;;AAQA,eAAsB,SAClB,KACA,QACA,QACa;CACb,KAAK,IAAI,QAAQ,IAAI,SAAS,GAAG,SAAS,GAAG,SAAS;EAClD,MAAM,aAAa,IAAI;EACvB,IAAI;GACA,MAAM,YAAY,QAAQ,MAAM;EACpC,SAAS,QAAQ;GACb,OAAO,MAAM,GAAG,YAAY,YAAY,QAAQ,eAAe,sBAAsB,QAAQ,MAAM,CAAC;EACxG;CACJ;AACJ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as StartupPhase, t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
|
|
2
2
|
import { n as settleWithin } from "./PluginLifecycle-AhQ46NIr.mjs";
|
|
3
|
-
import { n as shutdownOf, r as assertDeclaredRuntime } from "./ServerHost-
|
|
3
|
+
import { n as shutdownOf, r as assertDeclaredRuntime } from "./ServerHost-CtmAhEXn.mjs";
|
|
4
4
|
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
5
5
|
import { SeedcordError } from "@seedcord/errors/internal";
|
|
6
6
|
import { Logger } from "@seedcord/logger";
|
|
@@ -171,7 +171,7 @@ var HealthCheck = class HealthCheck {
|
|
|
171
171
|
function setEnvaptBaseDir() {
|
|
172
172
|
setBuiltFilesHook(() => {
|
|
173
173
|
const configDir = botConfigDir();
|
|
174
|
-
if (configDir !== void 0) Envapter.baseDir = configDir;
|
|
174
|
+
if (configDir !== void 0 && Envapter.source.supportsFiles) Envapter.baseDir = configDir;
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-internal.index.mjs","names":[],"sources":["../src/node/Lifecycle/drainWindow.ts","../src/node/Lifecycle/InFlight.ts","../src/node/HealthResponder.ts","../src/node/HealthCheck.ts","../src/node/envaptBaseDir.ts","../src/node-internal.index.ts"],"sourcesContent":["// gateway already used 5s. discord's 3s first-ack deadline is the floor.\nexport const DRAIN_WINDOW_MS = 5000;\n\nconst DRAIN_HEADROOM_MS = 1000;\nexport const DRAIN_TASK_TIMEOUT_MS = DRAIN_WINDOW_MS + DRAIN_HEADROOM_MS;\n","import { settleWithin } from '#src/lifecycle/withTimeout';\n\nimport type { Logger } from '@seedcord/logger';\n\nexport class InFlight {\n private readonly running = new Set<Promise<unknown>>();\n private isClosed = false;\n\n constructor(\n private readonly logger: Logger,\n private readonly label: string\n ) {}\n\n public get closed(): boolean {\n return this.isClosed;\n }\n\n public close(): void {\n this.isClosed = true;\n }\n\n public track(work: Promise<unknown>): void {\n this.running.add(work);\n const forget = (): void => {\n this.running.delete(work);\n };\n void work.then(forget, forget);\n }\n\n public async drain(timeoutMs: number): Promise<void> {\n await settleWithin(this.settleAll(), timeoutMs);\n // nothing cancels these before the process exits\n if (this.running.size > 0) {\n this.logger.warn(`${this.label}: ${String(this.running.size)} still running when the drain window closed`);\n }\n }\n\n // settling work can track more work\n private async settleAll(): Promise<void> {\n while (this.running.size > 0) await Promise.allSettled(this.running);\n }\n}\n","import type { HealthCheckConfig, HealthCheckOption } from '@seedcord/types';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nconst HTTP_OK = 200;\nconst HTTP_SERVICE_UNAVAILABLE = 503;\nconst DEFAULT_HEALTH_CHECK_PATH = '/health';\nconst JSON_HEADERS = { 'Content-Type': 'application/json' };\n\nexport function healthCheckConfig(option?: HealthCheckOption): HealthCheckConfig | undefined {\n if (option === false) return undefined;\n return typeof option === 'object' ? option : {};\n}\n\nexport class HealthResponder {\n public readonly path: string;\n\n constructor(path?: string) {\n this.path = path ?? DEFAULT_HEALTH_CHECK_PATH;\n }\n\n public static fromOption(option?: HealthCheckOption): HealthResponder | undefined {\n const config = healthCheckConfig(option);\n return config && new HealthResponder(config.path);\n }\n\n public tryRespond(req: IncomingMessage, res: ServerResponse): boolean {\n if (!this.matches(req.method, req.url)) return false;\n\n res.writeHead(HTTP_OK, JSON_HEADERS);\n res.end(this.body());\n return true;\n }\n\n public answer(request: Request, ready: boolean): Response | undefined {\n if (!this.matches(request.method, new URL(request.url).pathname)) return undefined;\n if (!ready) return new Response(null, { status: HTTP_SERVICE_UNAVAILABLE });\n return new Response(this.body(), { status: HTTP_OK, headers: JSON_HEADERS });\n }\n\n private matches(method: string | undefined, path: string | undefined): boolean {\n return method === 'GET' && path === this.path;\n }\n\n private body(): string {\n return JSON.stringify({ status: 'ok', timestamp: Date.now() });\n }\n}\n","import { createServer } from 'node:http';\n\nimport { paint, SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { isPortInUse } from '@seedcord/utils/internal';\n\nimport { ShutdownPhase } from '#src/lifecycle/phases';\n\nimport { healthCheckConfig, HealthResponder } from './HealthResponder';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig, HealthCheckOption } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\n\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\n\nexport class HealthCheck {\n public readonly logger = new Logger('HealthCheck', { channel: 'health' });\n\n private readonly _port: number;\n private readonly _host: string | undefined;\n\n private readonly responder: HealthResponder;\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this._port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this._host = options?.host;\n this.responder = new HealthResponder(options?.path);\n\n shutdown.addTask(ShutdownPhase.Drain, 'stop-healthcheck-server', () => this.stop());\n }\n\n public get port(): number {\n return this._port;\n }\n\n public get host(): string | undefined {\n return this._host;\n }\n\n public get path(): string {\n return this.responder.path;\n }\n\n public static fromOption(shutdown: CoordinatedShutdown, option?: HealthCheckOption): HealthCheck | undefined {\n const config = healthCheckConfig(option);\n return config && new HealthCheck(shutdown, config);\n }\n\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (this.responder.tryRespond(req, res)) return;\n\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n });\n this.server = server;\n\n const onListenError = (error: Error): void => {\n if (isPortInUse(error)) {\n reject(\n new SeedcordError(SeedcordErrorCode.PortInUse, [this._port, 'healthCheck.port'], {\n cause: error\n })\n );\n return;\n }\n reject(error);\n };\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // a late error would reject an already-settled promise, and node throws process-wide without a listener here\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n // binds all interfaces, so log an address a browser can open\n const address = this._host ?? 'localhost';\n this.logger.info(\n `${paint.mint.bold(paint.check)} Health check server listening on ${paint.sky.bold(`http://${address}:${this._port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this._host) {\n this.logger.debug(`Binding health check server to ${this._host}`);\n server.listen(this._port, this._host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this._port);\n }\n });\n }\n\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n // settle only in the callback, a 'close'-event resolve would swallow the callback's error\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(paint.coral.bold('Health check server stopped'));\n resolve();\n });\n });\n }\n}\n","import { botConfigDir, setBuiltFilesHook } from '@seedcord/utils/internal';\nimport { Envapter } from 'envapt';\n\nexport function setEnvaptBaseDir(): void {\n setBuiltFilesHook(() => {\n const configDir = botConfigDir();\n if (configDir !== undefined) Envapter.baseDir = configDir;\n });\n}\n","import { assertDeclaredRuntime } from '#node/assertRuntimeVersion';\n\n// the edge bundle never reaches this entry\nassertDeclaredRuntime();\n\nexport * from '#node/Lifecycle';\nexport * from '#node/HealthCheck';\nexport { HealthResponder } from '#node/HealthResponder';\nexport { settleWithin } from '#src/lifecycle/withTimeout';\nexport { shutdownOf } from '#node/ServerHost';\nexport { setEnvaptBaseDir } from '#node/envaptBaseDir';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node-internal.index.mjs","names":[],"sources":["../src/node/Lifecycle/drainWindow.ts","../src/node/Lifecycle/InFlight.ts","../src/node/HealthResponder.ts","../src/node/HealthCheck.ts","../src/node/envaptBaseDir.ts","../src/node-internal.index.ts"],"sourcesContent":["// gateway already used 5s. discord's 3s first-ack deadline is the floor.\nexport const DRAIN_WINDOW_MS = 5000;\n\nconst DRAIN_HEADROOM_MS = 1000;\nexport const DRAIN_TASK_TIMEOUT_MS = DRAIN_WINDOW_MS + DRAIN_HEADROOM_MS;\n","import { settleWithin } from '#src/lifecycle/withTimeout';\n\nimport type { Logger } from '@seedcord/logger';\n\nexport class InFlight {\n private readonly running = new Set<Promise<unknown>>();\n private isClosed = false;\n\n constructor(\n private readonly logger: Logger,\n private readonly label: string\n ) {}\n\n public get closed(): boolean {\n return this.isClosed;\n }\n\n public close(): void {\n this.isClosed = true;\n }\n\n public track(work: Promise<unknown>): void {\n this.running.add(work);\n const forget = (): void => {\n this.running.delete(work);\n };\n void work.then(forget, forget);\n }\n\n public async drain(timeoutMs: number): Promise<void> {\n await settleWithin(this.settleAll(), timeoutMs);\n // nothing cancels these before the process exits\n if (this.running.size > 0) {\n this.logger.warn(`${this.label}: ${String(this.running.size)} still running when the drain window closed`);\n }\n }\n\n // settling work can track more work\n private async settleAll(): Promise<void> {\n while (this.running.size > 0) await Promise.allSettled(this.running);\n }\n}\n","import type { HealthCheckConfig, HealthCheckOption } from '@seedcord/types';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nconst HTTP_OK = 200;\nconst HTTP_SERVICE_UNAVAILABLE = 503;\nconst DEFAULT_HEALTH_CHECK_PATH = '/health';\nconst JSON_HEADERS = { 'Content-Type': 'application/json' };\n\nexport function healthCheckConfig(option?: HealthCheckOption): HealthCheckConfig | undefined {\n if (option === false) return undefined;\n return typeof option === 'object' ? option : {};\n}\n\nexport class HealthResponder {\n public readonly path: string;\n\n constructor(path?: string) {\n this.path = path ?? DEFAULT_HEALTH_CHECK_PATH;\n }\n\n public static fromOption(option?: HealthCheckOption): HealthResponder | undefined {\n const config = healthCheckConfig(option);\n return config && new HealthResponder(config.path);\n }\n\n public tryRespond(req: IncomingMessage, res: ServerResponse): boolean {\n if (!this.matches(req.method, req.url)) return false;\n\n res.writeHead(HTTP_OK, JSON_HEADERS);\n res.end(this.body());\n return true;\n }\n\n public answer(request: Request, ready: boolean): Response | undefined {\n if (!this.matches(request.method, new URL(request.url).pathname)) return undefined;\n if (!ready) return new Response(null, { status: HTTP_SERVICE_UNAVAILABLE });\n return new Response(this.body(), { status: HTTP_OK, headers: JSON_HEADERS });\n }\n\n private matches(method: string | undefined, path: string | undefined): boolean {\n return method === 'GET' && path === this.path;\n }\n\n private body(): string {\n return JSON.stringify({ status: 'ok', timestamp: Date.now() });\n }\n}\n","import { createServer } from 'node:http';\n\nimport { paint, SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\nimport { isPortInUse } from '@seedcord/utils/internal';\n\nimport { ShutdownPhase } from '#src/lifecycle/phases';\n\nimport { healthCheckConfig, HealthResponder } from './HealthResponder';\n\nimport type { CoordinatedShutdown } from './Lifecycle/CoordinatedShutdown';\nimport type { HealthCheckConfig, HealthCheckOption } from '@seedcord/types';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\n\nconst HTTP_NOT_FOUND = 404;\n\nconst DEFAULT_HEALTH_CHECK_PORT = 6967;\n\nexport class HealthCheck {\n public readonly logger = new Logger('HealthCheck', { channel: 'health' });\n\n private readonly _port: number;\n private readonly _host: string | undefined;\n\n private readonly responder: HealthResponder;\n private server?: Server;\n\n constructor(shutdown: CoordinatedShutdown, options?: HealthCheckConfig) {\n this._port = options?.port ?? DEFAULT_HEALTH_CHECK_PORT;\n this._host = options?.host;\n this.responder = new HealthResponder(options?.path);\n\n shutdown.addTask(ShutdownPhase.Drain, 'stop-healthcheck-server', () => this.stop());\n }\n\n public get port(): number {\n return this._port;\n }\n\n public get host(): string | undefined {\n return this._host;\n }\n\n public get path(): string {\n return this.responder.path;\n }\n\n public static fromOption(shutdown: CoordinatedShutdown, option?: HealthCheckOption): HealthCheck | undefined {\n const config = healthCheckConfig(option);\n return config && new HealthCheck(shutdown, config);\n }\n\n public async init(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (this.responder.tryRespond(req, res)) return;\n\n res.writeHead(HTTP_NOT_FOUND, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'not found' }));\n });\n this.server = server;\n\n const onListenError = (error: Error): void => {\n if (isPortInUse(error)) {\n reject(\n new SeedcordError(SeedcordErrorCode.PortInUse, [this._port, 'healthCheck.port'], {\n cause: error\n })\n );\n return;\n }\n reject(error);\n };\n server.on('error', onListenError);\n\n server.once('listening', () => {\n // a late error would reject an already-settled promise, and node throws process-wide without a listener here\n server.removeListener('error', onListenError);\n server.on('error', (err) => this.logger.error('Health check server error', err));\n\n // binds all interfaces, so log an address a browser can open\n const address = this._host ?? 'localhost';\n this.logger.info(\n `${paint.mint.bold(paint.check)} Health check server listening on ${paint.sky.bold(`http://${address}:${this._port}${this.path}`)}`\n );\n resolve();\n });\n\n if (this._host) {\n this.logger.debug(`Binding health check server to ${this._host}`);\n server.listen(this._port, this._host);\n } else {\n this.logger.debug('Binding health check server to all interfaces');\n server.listen(this._port);\n }\n });\n }\n\n public stop(): Promise<void> {\n const server = this.server;\n // close() on a non-listening server invokes its callback with ERR_SERVER_NOT_RUNNING and\n // never fires 'close', so without this guard the promise would hang the shutdown phase.\n if (!server?.listening) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n // settle only in the callback, a 'close'-event resolve would swallow the callback's error\n server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n this.logger.info(paint.coral.bold('Health check server stopped'));\n resolve();\n });\n });\n }\n}\n","import { botConfigDir, setBuiltFilesHook } from '@seedcord/utils/internal';\nimport { Envapter } from 'envapt';\n\nexport function setEnvaptBaseDir(): void {\n setBuiltFilesHook(() => {\n const configDir = botConfigDir();\n if (configDir !== undefined && Envapter.source.supportsFiles) Envapter.baseDir = configDir;\n });\n}\n","import { assertDeclaredRuntime } from '#node/assertRuntimeVersion';\n\n// the edge bundle never reaches this entry\nassertDeclaredRuntime();\n\nexport * from '#node/Lifecycle';\nexport * from '#node/HealthCheck';\nexport { HealthResponder } from '#node/HealthResponder';\nexport { settleWithin } from '#src/lifecycle/withTimeout';\nexport { shutdownOf } from '#node/ServerHost';\nexport { setEnvaptBaseDir } from '#node/envaptBaseDir';\n"],"mappings":"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"}
|
package/dist/node.index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as ShutdownPhase } from "./phases-BauKJOTT.mjs";
|
|
2
2
|
import { n as settleWithin } from "./PluginLifecycle-AhQ46NIr.mjs";
|
|
3
3
|
import { n as CoordinatedLifecycle, t as CoordinatedStartup } from "./CoordinatedStartup-CGnJrqcl.mjs";
|
|
4
|
-
import { t as ServerHost } from "./ServerHost-
|
|
4
|
+
import { t as ServerHost } from "./ServerHost-CtmAhEXn.mjs";
|
|
5
5
|
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
6
6
|
import { SeedcordRangeError } from "@seedcord/errors/internal";
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seedcord/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.11.0
|
|
4
|
+
"version": "0.11.0",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": "
|
|
6
|
+
"node": "^24.22.1 || >=26.12.1",
|
|
7
7
|
"bun": ">=1.4.2"
|
|
8
8
|
},
|
|
9
9
|
"description": "Foundational code shared by both transports",
|
|
@@ -44,6 +44,12 @@
|
|
|
44
44
|
"default": "./dist/internal.index.mjs"
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
+
"./internal/env": {
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/env-internal.index.d.mts",
|
|
50
|
+
"default": "./dist/env-internal.index.mjs"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
47
53
|
"./node": {
|
|
48
54
|
"import": {
|
|
49
55
|
"types": "./dist/node.index.d.mts",
|
|
@@ -70,28 +76,29 @@
|
|
|
70
76
|
"LICENSE"
|
|
71
77
|
],
|
|
72
78
|
"peerDependencies": {
|
|
73
|
-
"envapt": "^9.
|
|
79
|
+
"envapt": "^9.2.0",
|
|
74
80
|
"typescript": "^5.9.3 || ^6.0.0 || ^7.0.0"
|
|
75
81
|
},
|
|
76
82
|
"dependencies": {
|
|
77
83
|
"@discordjs/builders": "^1.14.1",
|
|
78
84
|
"@discordjs/rest": "^2.6.3",
|
|
79
|
-
"@seedcord/custom-id": "^0.3.0
|
|
80
|
-
"@seedcord/errors": "^0.14.0
|
|
81
|
-
"@seedcord/event-emitter": "^0.
|
|
82
|
-
"@seedcord/logger": "^0.5.0
|
|
83
|
-
"@seedcord/types": "^0.15.0
|
|
84
|
-
"@seedcord/utils": "^0.9.0
|
|
85
|
+
"@seedcord/custom-id": "^0.3.0",
|
|
86
|
+
"@seedcord/errors": "^0.14.0",
|
|
87
|
+
"@seedcord/event-emitter": "^0.2.0",
|
|
88
|
+
"@seedcord/logger": "^0.5.0",
|
|
89
|
+
"@seedcord/types": "^0.15.0",
|
|
90
|
+
"@seedcord/utils": "^0.9.0",
|
|
85
91
|
"discord-api-types": "^0.38.52",
|
|
86
|
-
"reflect-metadata": "^0.2.2"
|
|
92
|
+
"reflect-metadata": "^0.2.2",
|
|
93
|
+
"verkit": "^0.5.0"
|
|
87
94
|
},
|
|
88
95
|
"devDependencies": {
|
|
89
|
-
"@seedcord/eslint-config": "^
|
|
90
|
-
"@seedcord/rate-limiter": "^0.
|
|
91
|
-
"@seedcord/tsconfig": "^
|
|
96
|
+
"@seedcord/eslint-config": "^3.0.0",
|
|
97
|
+
"@seedcord/rate-limiter": "^0.2.0",
|
|
98
|
+
"@seedcord/tsconfig": "^3.0.0",
|
|
92
99
|
"@seedcord/tsdown-config": "^2.0.1",
|
|
93
100
|
"@seedcord/vitest-config": "^0.0.1",
|
|
94
|
-
"envapt": "^9.
|
|
101
|
+
"envapt": "^9.2.0",
|
|
95
102
|
"type-fest": "^5.9.0",
|
|
96
103
|
"vite": "^8.3.4"
|
|
97
104
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ServerHost-CvGQr7oN.mjs","names":[],"sources":["../src/node/assertRuntimeVersion.ts","../src/node/processErrors.ts","../src/node/ServerHost.ts"],"sourcesContent":["import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// every seedcord package writes each engines range as `>=` and a version\nconst MINIMUM = '>=';\nconst SPACES = /\\s/g;\n\ninterface Version {\n parts: number[];\n prerelease: boolean;\n}\n\nfunction parseVersion(text: string): Version | undefined {\n const [withoutBuild = ''] = text.replace(/^v/, '').split('+');\n const [release = '', ...prerelease] = withoutBuild.split('-');\n const parts = release.split('.').map(Number);\n return parts.every(Number.isInteger) ? { parts, prerelease: prerelease.length > 0 } : undefined;\n}\n\n// semver ranks 1.4.2-canary.1 below 1.4.2\nfunction meetsMinimum(required: Version, running: Version): boolean {\n const length = Math.max(required.parts.length, running.parts.length);\n for (let index = 0; index < length; index++) {\n const wanted = required.parts[index] ?? 0;\n const got = running.parts[index] ?? 0;\n if (got !== wanted) return got > wanted;\n }\n return !running.prerelease;\n}\n\nexport function assertDeclaredRuntime(): void {\n const { bun, node } = process.versions;\n // tsdown-config bakes both ranges in from this package's engines\n const runtime =\n bun === undefined\n ? { name: 'Node', range: process.env.PACKAGE_NODE_RANGE ?? '', running: node }\n : { name: 'Bun', range: process.env.PACKAGE_BUN_RANGE ?? '', running: bun };\n\n const range = runtime.range.replaceAll(SPACES, '');\n if (!range.startsWith(MINIMUM)) return;\n\n const required = parseVersion(range.slice(MINIMUM.length));\n const running = parseVersion(runtime.running);\n if (!required || !running || meetsMinimum(required, running)) return;\n\n throw new SeedcordError(SeedcordErrorCode.UnsupportedRuntimeVersion, [\n runtime.name,\n runtime.range,\n runtime.running\n ]);\n}\n","import crypto from 'node:crypto';\n\nimport { Logger } from '@seedcord/logger';\nimport { asError } from '@seedcord/utils/internal';\n\nimport { logUnknownFault } from '#src/notices/logUnknownFault';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { CoreBase } from '#interfaces/CoreBase';\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\n\nconst logger = new Logger('Faults', { channel: 'errors' });\n\n/** @internal */\nexport function registerProcessErrors(core: CoreBase, shutdown: CoordinatedShutdown): () => void {\n function report(caught: unknown, origin: string): void {\n const error = asError(caught);\n const uuid = crypto.randomUUID();\n\n logUnknownFault(logger, uuid, error, core.config.errors);\n\n core.bus[PublishDefault]('unknownException', { uuid, dispatchId: null, error, origin });\n }\n\n const onRejection = (reason: unknown): void => {\n report(reason, 'process:unhandledRejection');\n };\n\n const onException = (caught: unknown): void => {\n report(caught, 'process:uncaughtException');\n // node does not exit on its own once a listener is registered\n void shutdown.run(1);\n };\n\n process.on('unhandledRejection', onRejection);\n process.on('uncaughtException', onException);\n\n return () => {\n process.off('unhandledRejection', onRejection);\n process.off('uncaughtException', onException);\n };\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { LoggerChannelRegistry } from '@seedcord/logger';\nimport { installNodeConsole, installNodeDefaults } from '@seedcord/logger/node';\nimport { HostShutdown, HostStartup } from '@seedcord/types/internal';\nimport { runsUnderSeedcordDev } from '@seedcord/utils/internal';\n\nimport { assertDeclaredRuntime } from '#node/assertRuntimeVersion';\nimport { PluginLifecycle } from '#src/lifecycle/PluginLifecycle';\nimport { attachmentsOf, PluginHost, sealAttachments } from '#src/plugin/PluginHost';\n\nimport { registerProcessErrors } from './processErrors';\n\nimport type { CoordinatedShutdown } from '#node/Lifecycle/CoordinatedShutdown';\nimport type { CoordinatedStartup } from '#src/lifecycle/CoordinatedStartup';\nimport type { Transport } from '#src/plugin/options';\nimport type { Promisable } from 'type-fest';\n\n/** Base class for a transport `Seedcord` class that runs as a long-lived node or bun process. */\nexport abstract class ServerHost<BotT extends Transport> extends PluginHost<BotT, 'server'> {\n /** @internal */\n readonly [HostShutdown]: CoordinatedShutdown;\n /** @internal */\n readonly [HostStartup]: CoordinatedStartup;\n\n /** Add a task that runs while the bot shuts down. */\n public readonly shutdown: Pick<CoordinatedShutdown, 'addTask'>;\n\n /** Add a task that runs while the bot starts. */\n public readonly startup: Pick<CoordinatedStartup, 'addTask'>;\n\n readonly #lifecycle: PluginLifecycle;\n #startFailed = false;\n #initPromise?: Promise<this> | undefined;\n\n static #isInstantiated = false;\n static #liveHost?: object | undefined;\n static #liveShutdown?: CoordinatedShutdown | undefined;\n static #liveProcessErrors?: (() => void) | undefined;\n\n constructor(transport: BotT, shutdown: CoordinatedShutdown, startup: CoordinatedStartup) {\n // a `sideEffects: false` build would drop the same call in the node entry\n assertDeclaredRuntime();\n super(transport, 'server');\n\n if (ServerHost.#isInstantiated) throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);\n\n // init() opens the log file\n installNodeConsole();\n\n ServerHost.#isInstantiated = true;\n ServerHost.#liveHost = this;\n ServerHost.#liveShutdown = shutdown;\n this[HostShutdown] = shutdown;\n this[HostStartup] = startup;\n // a getter returning the slot would expose run() and the signal handlers too\n this.shutdown = { addTask: shutdown.addTask.bind(shutdown) };\n this.startup = { addTask: startup.addTask.bind(startup) };\n this.#lifecycle = new PluginLifecycle(this.startup, this.shutdown);\n }\n\n /** @internal */\n protected init(): Promise<this> {\n // a retry after a rejection runs #runInit again and hits its restart guard\n this.#initPromise ??= this.#runInit().catch(async (caught: unknown) => {\n this.#initPromise = undefined;\n await this.#release(1);\n throw caught;\n });\n return this.#initPromise;\n }\n\n async #runInit(): Promise<this> {\n // a rerun after a failed startup would re-init the rolled-back plugins\n if (this.#startFailed) throw new SeedcordError(SeedcordErrorCode.LifecycleRestartAfterFailure);\n\n installNodeDefaults(this.config.logger);\n sealAttachments(this);\n this.#lifecycle.register(attachmentsOf(this));\n\n // codegen and the build construct the bot without starting it.\n // under seedcord dev the CLI handles SIGINT and SIGTERM\n if (!runsUnderSeedcordDev()) this[HostShutdown].registerSignalHandlers();\n if (this.config.errors?.catchProcessErrors ?? true) {\n ServerHost.#liveProcessErrors = registerProcessErrors(this, this[HostShutdown]);\n }\n\n const startupSettled: PromiseWithResolvers<void> = Promise.withResolvers();\n this[HostShutdown].gateOnStartup(startupSettled.promise);\n\n try {\n // nothing a plugin adds through startup.addTask runs after this\n await this[HostStartup].run(() => this.openHealth());\n } catch (caught) {\n this.#startFailed = true;\n await this.#lifecycle.rollback();\n throw caught;\n } finally {\n startupSettled.resolve();\n }\n\n return this;\n }\n\n /** @internal */\n protected openHealth(): Promisable<void> {}\n\n /**\n * Runs the coordinated shutdown without calling `process.exit`. A new `Seedcord` can be\n * constructed afterwards. `await using` calls this when its scope ends.\n *\n * @example\n * ```ts\n * // the bot shuts down when this block ends\n * await using bot = await new Seedcord(config).start();\n * console.log(await bot.rest.get(Routes.user('@me')));\n * ```\n */\n public async [Symbol.asyncDispose](): Promise<void> {\n await this.#release(0);\n }\n\n async #release(exitCode: number): Promise<void> {\n try {\n await this[HostShutdown].run(exitCode, false);\n } finally {\n ServerHost.reset(this);\n }\n }\n\n /** @internal */\n protected static reset(host?: object): boolean {\n if (host !== undefined && ServerHost.#liveHost !== host) return false;\n\n ServerHost.#liveShutdown?.removeSignalHandlers();\n ServerHost.#liveShutdown = undefined;\n ServerHost.#liveProcessErrors?.();\n ServerHost.#liveProcessErrors = undefined;\n ServerHost.#liveHost = undefined;\n ServerHost.#isInstantiated = false;\n // configure() keeps the dev TUI's capture sink\n LoggerChannelRegistry.instance.configure({});\n return true;\n }\n}\n\n// the public `shutdown` field carries addTask alone\nexport function shutdownOf(host: Pick<ServerHost<Transport>, typeof HostShutdown>): CoordinatedShutdown {\n return host[HostShutdown];\n}\n"],"mappings":";;;;;;;;;;;;AAIA,MAAM,UAAU;AAChB,MAAM,SAAS;AAOf,SAAS,aAAa,MAAmC;CACrD,MAAM,CAAC,eAAe,MAAM,KAAK,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG;CAC5D,MAAM,CAAC,UAAU,IAAI,GAAG,cAAc,aAAa,MAAM,GAAG;CAC5D,MAAM,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CAC3C,OAAO,MAAM,MAAM,OAAO,SAAS,IAAI;EAAE;EAAO,YAAY,WAAW,SAAS;CAAE,IAAI;AAC1F;AAGA,SAAS,aAAa,UAAmB,SAA2B;CAChE,MAAM,SAAS,KAAK,IAAI,SAAS,MAAM,QAAQ,QAAQ,MAAM,MAAM;CACnE,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS;EACzC,MAAM,SAAS,SAAS,MAAM,UAAU;EACxC,MAAM,MAAM,QAAQ,MAAM,UAAU;EACpC,IAAI,QAAQ,QAAQ,OAAO,MAAM;CACrC;CACA,OAAO,CAAC,QAAQ;AACpB;AAEA,SAAgB,wBAA8B;CAC1C,MAAM,EAAE,KAAK,SAAS,QAAQ;CAE9B,MAAM,UACF,QAAQ,SACF;EAAE,MAAM;EAAQ;EAA6C,SAAS;CAAK,IAC3E;EAAE,MAAM;EAAO;EAA4C,SAAS;CAAI;CAElF,MAAM,QAAQ,QAAQ,MAAM,WAAW,QAAQ,EAAE;CACjD,IAAI,CAAC,MAAM,WAAW,OAAO,GAAG;CAEhC,MAAM,WAAW,aAAa,MAAM,MAAM,CAAc,CAAC;CACzD,MAAM,UAAU,aAAa,QAAQ,OAAO;CAC5C,IAAI,CAAC,YAAY,CAAC,WAAW,aAAa,UAAU,OAAO,GAAG;CAE9D,MAAM,IAAI,cAAc,kBAAkB,2BAA2B;EACjE,QAAQ;EACR,QAAQ;EACR,QAAQ;CACZ,CAAC;AACL;;;;ACvCA,MAAM,SAAS,IAAI,OAAO,UAAU,EAAE,SAAS,SAAS,CAAC;;AAGzD,SAAgB,sBAAsB,MAAgB,UAA2C;CAC7F,SAAS,OAAO,QAAiB,QAAsB;EACnD,MAAM,QAAQ,QAAQ,MAAM;EAC5B,MAAM,OAAO,OAAO,WAAW;EAE/B,gBAAgB,QAAQ,MAAM,OAAO,KAAK,OAAO,MAAM;EAEvD,KAAK,IAAI,eAAe,CAAC,oBAAoB;GAAE;GAAM,YAAY;GAAM;GAAO;EAAO,CAAC;CAC1F;CAEA,MAAM,eAAe,WAA0B;EAC3C,OAAO,QAAQ,4BAA4B;CAC/C;CAEA,MAAM,eAAe,WAA0B;EAC3C,OAAO,QAAQ,2BAA2B;EAE1C,AAAK,SAAS,IAAI,CAAC;CACvB;CAEA,QAAQ,GAAG,sBAAsB,WAAW;CAC5C,QAAQ,GAAG,qBAAqB,WAAW;CAE3C,aAAa;EACT,QAAQ,IAAI,sBAAsB,WAAW;EAC7C,QAAQ,IAAI,qBAAqB,WAAW;CAChD;AACJ;;;;;ACtBA,IAAsB,aAAtB,MAAsB,mBAA2C,WAA2B;;CAExF,CAAU;;CAEV,CAAU;;CAGV,AAAgB;;CAGhB,AAAgB;CAEhB,AAAS;CACT,eAAe;CACf;CAEA,OAAO,kBAAkB;CACzB,OAAO;CACP,OAAO;CACP,OAAO;CAEP,YAAY,WAAiB,UAA+B,SAA6B;EAErF,sBAAsB;EACtB,MAAM,WAAW,QAAQ;EAEzB,IAAI,WAAW,iBAAiB,MAAM,IAAI,cAAc,kBAAkB,sBAAsB;EAGhG,mBAAmB;EAEnB,WAAW,kBAAkB;EAC7B,WAAW,YAAY;EACvB,WAAW,gBAAgB;EAC3B,KAAK,gBAAgB;EACrB,KAAK,eAAe;EAEpB,KAAK,WAAW,EAAE,SAAS,SAAS,QAAQ,KAAK,QAAQ,EAAE;EAC3D,KAAK,UAAU,EAAE,SAAS,QAAQ,QAAQ,KAAK,OAAO,EAAE;EACxD,KAAK,aAAa,IAAI,gBAAgB,KAAK,SAAS,KAAK,QAAQ;CACrE;;CAGA,AAAU,OAAsB;EAE5B,KAAK,iBAAiB,KAAK,SAAS,CAAC,CAAC,MAAM,OAAO,WAAoB;GACnE,KAAK,eAAe;GACpB,MAAM,KAAK,SAAS,CAAC;GACrB,MAAM;EACV,CAAC;EACD,OAAO,KAAK;CAChB;CAEA,MAAM,WAA0B;EAE5B,IAAI,KAAK,cAAc,MAAM,IAAI,cAAc,kBAAkB,4BAA4B;EAE7F,oBAAoB,KAAK,OAAO,MAAM;EACtC,gBAAgB,IAAI;EACpB,KAAK,WAAW,SAAS,cAAc,IAAI,CAAC;EAI5C,IAAI,CAAC,qBAAqB,GAAG,KAAK,aAAa,CAAC,uBAAuB;EACvE,IAAI,KAAK,OAAO,QAAQ,sBAAsB,MAC1C,WAAW,qBAAqB,sBAAsB,MAAM,KAAK,aAAa;EAGlF,MAAM,iBAA6C,QAAQ,cAAc;EACzE,KAAK,aAAa,CAAC,cAAc,eAAe,OAAO;EAEvD,IAAI;GAEA,MAAM,KAAK,YAAY,CAAC,UAAU,KAAK,WAAW,CAAC;EACvD,SAAS,QAAQ;GACb,KAAK,eAAe;GACpB,MAAM,KAAK,WAAW,SAAS;GAC/B,MAAM;EACV,UAAU;GACN,eAAe,QAAQ;EAC3B;EAEA,OAAO;CACX;;CAGA,AAAU,aAA+B,CAAC;;;;;;;;;;;;CAa1C,OAAc,OAAO,gBAA+B;EAChD,MAAM,KAAK,SAAS,CAAC;CACzB;CAEA,MAAM,SAAS,UAAiC;EAC5C,IAAI;GACA,MAAM,KAAK,aAAa,CAAC,IAAI,UAAU,KAAK;EAChD,UAAU;GACN,WAAW,MAAM,IAAI;EACzB;CACJ;;CAGA,OAAiB,MAAM,MAAwB;EAC3C,IAAI,SAAS,UAAa,WAAW,cAAc,MAAM,OAAO;EAEhE,WAAW,eAAe,qBAAqB;EAC/C,WAAW,gBAAgB;EAC3B,WAAW,qBAAqB;EAChC,WAAW,qBAAqB;EAChC,WAAW,YAAY;EACvB,WAAW,kBAAkB;EAE7B,sBAAsB,SAAS,UAAU,CAAC,CAAC;EAC3C,OAAO;CACX;AACJ;AAGA,SAAgB,WAAW,MAA6E;CACpG,OAAO,KAAK;AAChB"}
|