@seedcord/core 0.11.0 → 0.12.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/Bus-W1K-uMeO.mjs +930 -0
  2. package/dist/Bus-W1K-uMeO.mjs.map +1 -0
  3. package/dist/{CoordinatedShutdown-H5YjZn8A.d.mts → CoordinatedShutdown-BVtNNSsF.d.mts} +13 -4
  4. package/dist/{CoordinatedStartup-CGnJrqcl.mjs → CoordinatedStartup-DA3gW8op.mjs} +22 -20
  5. package/dist/CoordinatedStartup-DA3gW8op.mjs.map +1 -0
  6. package/dist/Generated-ByC53_A2.d.mts +26 -0
  7. package/dist/{HmrModuleHandler-8ZC-UuZh.mjs → HmrModuleHandler-BMD7dg8N.mjs} +2 -2
  8. package/dist/HmrModuleHandler-BMD7dg8N.mjs.map +1 -0
  9. package/dist/PluginHost-CYe-UVpp.mjs +552 -0
  10. package/dist/PluginHost-CYe-UVpp.mjs.map +1 -0
  11. package/dist/{PluginHost-DB4j8unu.d.mts → PluginHost-DWBgBS6Q.d.mts} +35 -8
  12. package/dist/{PluginLifecycle-AhQ46NIr.mjs → PluginLifecycle-DdwRzcDj.mjs} +3 -3
  13. package/dist/PluginLifecycle-DdwRzcDj.mjs.map +1 -0
  14. package/dist/{ServerHost-CtmAhEXn.mjs → ServerHost-DHg-Y2_q.mjs} +17 -6
  15. package/dist/{ServerHost-CtmAhEXn.mjs.map → ServerHost-DHg-Y2_q.mjs.map} +1 -1
  16. package/dist/{ServerHost-DYeh0KTk.d.mts → ServerHost-whpI0h4d.d.mts} +7 -6
  17. package/dist/bindViteEnv-NFzvdqlT.mjs +32 -0
  18. package/dist/{bindViteEnv-DWq5rftJ.mjs.map → bindViteEnv-NFzvdqlT.mjs.map} +1 -1
  19. package/dist/env-internal.index.mjs +1 -1
  20. package/dist/hmr.index.mjs +1 -1
  21. package/dist/{index-Du71Xx69.d.mts → index-Boo33hh3.d.mts} +10 -11
  22. package/dist/index.d.mts +108 -6
  23. package/dist/index.mjs +118 -12
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/internal.index.d.mts +25 -7
  26. package/dist/internal.index.mjs +8 -8
  27. package/dist/node-internal.index.d.mts +7 -4
  28. package/dist/node-internal.index.mjs +19 -6
  29. package/dist/node-internal.index.mjs.map +1 -1
  30. package/dist/node.index.d.mts +3 -3
  31. package/dist/node.index.mjs +22 -28
  32. package/dist/node.index.mjs.map +1 -1
  33. package/dist/{phases-FTM69J3a.d.mts → phases-Dojnr-ng.d.mts} +111 -12
  34. package/dist/plugin.index.d.mts +3 -2
  35. package/dist/plugin.index.mjs +1 -1
  36. package/dist/{subscribers-Ce3SWy23.mjs → subscribers-D4_HZcgw.mjs} +12 -829
  37. package/dist/subscribers-D4_HZcgw.mjs.map +1 -0
  38. package/dist/{bindViteEnv-DWq5rftJ.mjs → usingCtx-Cf2cZi96.mjs} +1 -31
  39. package/package.json +11 -9
  40. package/dist/CoordinatedStartup-CGnJrqcl.mjs.map +0 -1
  41. package/dist/HmrModuleHandler-8ZC-UuZh.mjs.map +0 -1
  42. package/dist/PluginHost-B8kxLOeb.mjs +0 -245
  43. package/dist/PluginHost-B8kxLOeb.mjs.map +0 -1
  44. package/dist/PluginLifecycle-AhQ46NIr.mjs.map +0 -1
  45. package/dist/phases-BauKJOTT.mjs +0 -31
  46. package/dist/phases-BauKJOTT.mjs.map +0 -1
  47. package/dist/publishDefault-D3dAyzZ9.mjs +0 -7
  48. package/dist/publishDefault-D3dAyzZ9.mjs.map +0 -1
  49. package/dist/subscribers-Ce3SWy23.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { i as Bus, n as StartupPhase, r as CoreBase, t as ShutdownPhase } from "./phases-FTM69J3a.mjs";
1
+ import { F as Kv, a as Bus, i as CoreBase, n as StartupPhase, r as TaskShutdownPhase } from "./phases-Dojnr-ng.mjs";
2
2
  import { Logger } from "@seedcord/logger";
3
3
  import { REST } from "@discordjs/rest";
4
4
  import { HostPluginKeys } from "@seedcord/types/internal";
@@ -35,7 +35,7 @@ interface PluginLifecycleSpec {
35
35
  };
36
36
  /** Defaults to `ShutdownPhase.Disconnect` and 10000ms. */
37
37
  dispose?: {
38
- phase?: ShutdownPhase;
38
+ phase?: TaskShutdownPhase;
39
39
  timeout?: number;
40
40
  };
41
41
  }
@@ -49,7 +49,7 @@ interface ResolvedPluginLifecycleSpec {
49
49
  readonly timeout: number;
50
50
  };
51
51
  readonly dispose: {
52
- readonly phase: ShutdownPhase;
52
+ readonly phase: TaskShutdownPhase;
53
53
  readonly timeout: number;
54
54
  };
55
55
  }
@@ -167,6 +167,7 @@ declare const FixedScope: unique symbol;
167
167
  * ```
168
168
  */
169
169
  declare abstract class Plugin<Opts extends PluginOptions = {}, TCore extends CoreBase = CoreBase, Fixed extends ScopeAxis = never> implements Initializeable, HmrAware {
170
+ #private;
170
171
  private readonly host;
171
172
  /** @internal */
172
173
  protected static readonly [FixedScope]: Partial<DeclaredScope>;
@@ -185,6 +186,19 @@ declare abstract class Plugin<Opts extends PluginOptions = {}, TCore extends Cor
185
186
  constructor(host: CoreBase, ...[spec]: PluginSpecArgs<Opts, Fixed>);
186
187
  /** The host, typed to the transport whose `Plugin` base this class extends. */
187
188
  protected get core(): TCore;
189
+ /**
190
+ * This plugin's records in the bot's `config.kv`, stored under its attach key. Two attachments of
191
+ * one plugin class each get their own records. Renaming the attach key leaves the records under
192
+ * the old key behind.
193
+ *
194
+ * @throws A **SeedcordTypeError** when something built the plugin with `new` outside `attach()`.
195
+ * @example
196
+ * ```ts
197
+ * // works in a field initializer, the constructor, and every method
198
+ * private readonly jobs = this.kv.collection<Job>('job');
199
+ * ```
200
+ */
201
+ protected get kv(): Kv;
188
202
  /**
189
203
  * Runs in `StartupPhase.Configuration` by default.
190
204
  * Move it to a different phase with the {@link PluginLifecycleSpec}.
@@ -205,6 +219,15 @@ declare abstract class Plugin<Opts extends PluginOptions = {}, TCore extends Cor
205
219
  * shutdown phase. The process must still be alive for that call to happen.
206
220
  */
207
221
  dispose?(): Promise<void>;
222
+ /**
223
+ * Returns data that `seedcord codegen` writes into the bot's `seedcord-gen.ts` as a type.
224
+ * Read it back with `Generated<typeof YourPlugin>`. Return plain data, or classes and objects
225
+ * that a project file or your plugin's package exports. The class needs a `static readonly id`.
226
+ *
227
+ * `seedcord codegen` calls this without running `init()`. Read only what the constructor stored
228
+ * and the bot's files.
229
+ */
230
+ codegen?(): Promise<unknown>;
208
231
  /**
209
232
  * Rejects the options this plugin got.
210
233
  *
@@ -254,9 +277,13 @@ declare abstract class PluginHost<BotT extends Transport, BotRt extends Runtime>
254
277
  abstract readonly config: Config;
255
278
  abstract readonly rest: REST;
256
279
  abstract readonly applicationId: string;
257
- abstract readonly rateLimiter: IRateLimiter;
258
- abstract readonly bus: Bus;
259
- constructor(transport: BotT, runtime: BotRt);
280
+ /** @see {@link Bus} */
281
+ readonly bus: Bus;
282
+ /** @see {@link Kv} */
283
+ readonly kv: Kv;
284
+ /** @see {@link IRateLimiter} */
285
+ readonly rateLimiter: IRateLimiter;
286
+ constructor(transport: BotT, runtime: BotRt, driver: Config['kv']);
260
287
  /** @internal */
261
288
  get [attachmentsSlot](): readonly Attachment[];
262
289
  /** @internal */
@@ -293,5 +320,5 @@ declare abstract class PluginHost<BotT extends Transport, BotRt extends Runtime>
293
320
  declare function attachmentsOf(host: Pick<PluginHost<Transport, Runtime>, typeof attachmentsSlot>): readonly Attachment[];
294
321
  declare function sealAttachments(host: Pick<PluginHost<Transport, Runtime>, typeof sealSlot>): void;
295
322
  //#endregion
296
- export { FixedScope as a, PluginArgs as c, Transport as d, PluginLifecycleSpec as f, sealAttachments as i, PluginCtor as l, PluginHost as n, Initializeable as o, attachmentsOf as r, Plugin as s, Attachment as t, PluginOptions as u };
297
- //# sourceMappingURL=PluginHost-DB4j8unu.d.mts.map
323
+ export { FixedScope as a, PluginArgs as c, Runtime as d, Transport as f, sealAttachments as i, PluginCtor as l, PluginHost as n, Initializeable as o, PluginLifecycleSpec as p, attachmentsOf as r, Plugin as s, Attachment as t, PluginOptions as u };
324
+ //# sourceMappingURL=PluginHost-DWBgBS6Q.d.mts.map
@@ -1,5 +1,5 @@
1
- import { n as StartupPhase } from "./phases-BauKJOTT.mjs";
2
- import { o as pluginLoggerOf } from "./PluginHost-B8kxLOeb.mjs";
1
+ import { y as StartupPhase } from "./Bus-W1K-uMeO.mjs";
2
+ import { s as pluginLoggerOf } from "./PluginHost-CYe-UVpp.mjs";
3
3
  import { SeedcordErrorCode, isSeedcordError } from "@seedcord/errors";
4
4
  import { SeedcordError, throwSingleOrAggregate } from "@seedcord/errors/internal";
5
5
  import { Logger } from "@seedcord/logger";
@@ -155,4 +155,4 @@ var PluginLifecycle = class {
155
155
 
156
156
  //#endregion
157
157
  export { logUnknownFault as i, settleWithin as n, withTimeout as r, PluginLifecycle as t };
158
- //# sourceMappingURL=PluginLifecycle-AhQ46NIr.mjs.map
158
+ //# sourceMappingURL=PluginLifecycle-DdwRzcDj.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginLifecycle-DdwRzcDj.mjs","names":[],"sources":["../src/notices/logUnknownFault.ts","../src/lifecycle/withTimeout.ts","../src/lifecycle/PluginLifecycle.ts"],"sourcesContent":["import type { Logger } from '@seedcord/logger';\nimport type { ErrorsConfig } from '@seedcord/types';\n\nfunction causeLine(error: Error): string {\n const { cause } = error;\n if (!Error.isError(cause)) return '';\n const [first] = cause.message.split('\\n');\n return `\\ncaused by ${cause.name}: ${first ?? cause.message}`;\n}\n\nexport function logUnknownFault(logger: Logger, uuid: string, error: Error, errors: ErrorsConfig | undefined): void {\n if (errors?.errorStack ?? true) logger.error(uuid, error);\n else logger.error(`${uuid} | ${error.message}${causeLine(error)}`);\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// calling start() in here turns its synchronous throw into a rejection\nasync function raceTimer(\n start: () => Promise<unknown>,\n timeoutMs: number,\n onTimeout: (resolve: () => void, reject: (error: unknown) => void) => void\n): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n start(),\n new Promise<void>((resolve, reject) => {\n // node's timer can go off before performance.now() reaches timeoutMs\n const expiresAt = performance.now() + timeoutMs;\n const tick = (): void => {\n const remaining = expiresAt - performance.now();\n if (remaining > 0) {\n timer = setTimeout(tick, remaining);\n return;\n }\n onTimeout(resolve, reject);\n };\n timer = setTimeout(tick, timeoutMs);\n })\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n// rejects with a LifecycleTaskTimeout when run outlasts the bound\nexport function withTimeout(name: string, run: () => Promise<void>, timeoutMs: number): Promise<void> {\n return raceTimer(run, timeoutMs, (_, reject) => {\n reject(new SeedcordError(SeedcordErrorCode.LifecycleTaskTimeout, [name, timeoutMs]));\n });\n}\n\n// the caller does not get work's rejection\nexport function settleWithin(work: Promise<unknown>, timeoutMs: number): Promise<void> {\n return raceTimer(\n () =>\n work.then(\n () => undefined,\n () => undefined\n ),\n timeoutMs,\n (resolve) => resolve()\n );\n}\n","import { SeedcordErrorCode, isSeedcordError } from '@seedcord/errors';\nimport { throwSingleOrAggregate } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { StartupPhase } from '#src/lifecycle/phases';\nimport { pluginLoggerOf } from '#src/plugin/Plugin';\n\nimport { withTimeout } from './withTimeout';\n\nimport type { ShutdownTasks } from '#node/Lifecycle/CoordinatedShutdown';\nimport type { TaskShutdownPhase } from '#src/lifecycle/phases';\nimport type { Attachment } from '#src/plugin/PluginHost';\nimport type { StartupTasks } from './CoordinatedStartup';\n\ninterface ReadyStep {\n readonly key: string;\n readonly run: () => Promise<void>;\n readonly timeout: number;\n}\n\nexport class PluginLifecycle {\n private readonly logger = new Logger('Plugins', { channel: 'plugins' });\n private readonly completedInits = new Set<Attachment>();\n private readonly disposePhases = new Set<TaskShutdownPhase>();\n private readonly timedOutInits = new Set<Promise<void>>();\n private attachments: readonly Attachment[] = [];\n private registered = false;\n\n constructor(\n private readonly startup: StartupTasks,\n private readonly shutdown: ShutdownTasks\n ) {}\n\n // one combined task per phase keeps plugin inits sequential while the phase's other tasks run concurrently\n public register(attachments: readonly Attachment[]): void {\n if (this.registered) return;\n this.registered = true;\n this.attachments = [...attachments];\n\n const groups = Map.groupBy(this.attachments, (attachment) => attachment.spec.init.phase);\n\n for (const [phase, group] of groups) {\n // Ready inits run in the combined Ready task, before the ready hooks\n if (phase === StartupPhase.Ready) continue;\n const budget = group.reduce((sum, a) => sum + a.spec.init.timeout, 0);\n this.startup.addTask(phase, 'plugins-init', () => this.runInits(group), budget);\n }\n\n this.registerReadyTask(groups.get(StartupPhase.Ready) ?? []);\n }\n\n public async rollback(): Promise<void> {\n // log rollback dispose failures so they do not hide the startup error\n await this.disposeCompleted(undefined, (caught) => this.logger.warn('rollback dispose failed', caught));\n // prevents re-dispose when shutdown runs after rollback\n this.completedInits.clear();\n }\n\n public afterTimedOutInits(run: () => void): void {\n if (this.timedOutInits.size === 0) {\n run();\n return;\n }\n void Promise.allSettled(this.timedOutInits).then(run);\n }\n\n private async runInits(group: readonly Attachment[]): Promise<void> {\n for (const attachment of group) {\n const { key, instance, spec } = attachment;\n\n pluginLoggerOf(instance).utils.initialization(key, 'start');\n const running = instance.init();\n try {\n await withTimeout(`Plugin (${key})`, () => running, spec.init.timeout);\n } catch (caught) {\n if (isSeedcordError(caught, undefined, SeedcordErrorCode.LifecycleTaskTimeout)) {\n this.disposeWhenInitResolves(attachment, running);\n }\n throw caught;\n }\n pluginLoggerOf(instance).utils.initialization(key, 'end');\n\n this.completedInits.add(attachment);\n if (instance.dispose) this.registerDisposeTask(spec.dispose.phase);\n }\n }\n\n // disposeCompleted skips a plugin missing from completedInits\n private disposeWhenInitResolves({ key, instance, spec }: Attachment, running: Promise<void>): void {\n const dispose = instance.dispose?.bind(instance);\n\n const settled = running.then(\n async () => {\n if (!dispose) return;\n await withTimeout(`Plugin:${key}:dispose`, dispose, spec.dispose.timeout).catch((caught: unknown) =>\n this.logger.warn(`${key} dispose failed after a timed-out init`, caught)\n );\n },\n (caught: unknown) => this.logger.warn(`${key} init failed after its timeout`, caught)\n );\n this.timedOutInits.add(settled);\n void settled.finally(() => this.timedOutInits.delete(settled));\n }\n\n private registerReadyTask(readyInits: readonly Attachment[]): void {\n const steps = this.attachments.reduce<ReadyStep[]>((acc, { key, instance, spec }) => {\n const ready = instance.ready?.bind(instance);\n if (ready) acc.push({ key, run: ready, timeout: spec.ready.timeout });\n return acc;\n }, []);\n if (readyInits.length === 0 && steps.length === 0) return;\n\n const budget =\n readyInits.reduce((sum, a) => sum + a.spec.init.timeout, 0) +\n steps.reduce((sum, step) => sum + step.timeout, 0);\n this.startup.addTask(\n StartupPhase.Ready,\n 'plugins-ready',\n async () => {\n await this.runInits(readyInits);\n for (const step of steps) {\n await withTimeout(`Plugin:${step.key}:ready`, step.run, step.timeout);\n }\n },\n budget\n );\n }\n\n private registerDisposeTask(phase: TaskShutdownPhase): void {\n if (this.disposePhases.has(phase)) return;\n this.disposePhases.add(phase);\n\n // the budget covers every dispose in this phase because they all run in this one task\n const budget = this.attachments.reduce(\n (sum, a) => (a.instance.dispose && a.spec.dispose.phase === phase ? sum + a.spec.dispose.timeout : sum),\n 0\n );\n this.shutdown.addTask(phase, 'plugins-dispose', () => this.runDisposals(phase), budget);\n }\n\n private async runDisposals(phase: TaskShutdownPhase): Promise<void> {\n const failures: unknown[] = [];\n await this.disposeCompleted(phase, (caught) => failures.push(caught));\n throwSingleOrAggregate(failures, SeedcordErrorCode.PluginDisposeFailures);\n }\n\n private async disposeCompleted(\n phase: TaskShutdownPhase | undefined,\n onError: (caught: unknown) => void\n ): Promise<void> {\n for (const attachment of [...this.attachments].reverse()) {\n if (!this.completedInits.has(attachment)) continue;\n const { key, instance, spec } = attachment;\n const dispose = instance.dispose?.bind(instance);\n if (!dispose) continue;\n if (phase !== undefined && spec.dispose.phase !== phase) continue;\n\n try {\n await withTimeout(`Plugin:${key}:dispose`, dispose, spec.dispose.timeout);\n } catch (caught) {\n onError(caught);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { t as PublishDefault } from "./publishDefault-D3dAyzZ9.mjs";
2
- import { n as attachmentsOf, r as sealAttachments, t as PluginHost } from "./PluginHost-B8kxLOeb.mjs";
3
- import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-AhQ46NIr.mjs";
1
+ import { b as PublishDefault, v as ShutdownPhase } from "./Bus-W1K-uMeO.mjs";
2
+ import { n as attachmentsOf, r as sealAttachments, t as PluginHost } from "./PluginHost-CYe-UVpp.mjs";
3
+ import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-DdwRzcDj.mjs";
4
+ import { t as _usingCtx } from "./usingCtx-Cf2cZi96.mjs";
4
5
  import { SeedcordErrorCode } from "@seedcord/errors";
5
6
  import { SeedcordError } from "@seedcord/errors/internal";
6
7
  import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
@@ -80,9 +81,9 @@ var ServerHost = class ServerHost extends PluginHost {
80
81
  static #liveHost;
81
82
  static #liveShutdown;
82
83
  static #liveProcessErrors;
83
- constructor(transport, shutdown, startup) {
84
+ constructor(transport, shutdown, startup, driver) {
84
85
  assertDeclaredRuntime();
85
- super(transport, "server");
86
+ super(transport, "server", driver);
86
87
  if (ServerHost.#isInstantiated) throw new SeedcordError(SeedcordErrorCode.CoreSingletonViolation);
87
88
  installNodeConsole();
88
89
  ServerHost.#isInstantiated = true;
@@ -93,6 +94,16 @@ var ServerHost = class ServerHost extends PluginHost {
93
94
  this.shutdown = { addTask: shutdown.addTask.bind(shutdown) };
94
95
  this.startup = { addTask: startup.addTask.bind(startup) };
95
96
  this.#lifecycle = new PluginLifecycle(this.startup, this.shutdown);
97
+ shutdown.addTask(5, "close-kv", async () => {
98
+ try {
99
+ var _usingCtx$1 = _usingCtx();
100
+ _usingCtx$1.a(this.kv.driver);
101
+ } catch (_) {
102
+ _usingCtx$1.e = _;
103
+ } finally {
104
+ await _usingCtx$1.d();
105
+ }
106
+ });
96
107
  }
97
108
  /** @internal */
98
109
  init() {
@@ -165,4 +176,4 @@ function shutdownOf(host) {
165
176
 
166
177
  //#endregion
167
178
  export { shutdownOf as n, assertDeclaredRuntime as r, ServerHost as t };
168
- //# sourceMappingURL=ServerHost-CtmAhEXn.mjs.map
179
+ //# sourceMappingURL=ServerHost-DHg-Y2_q.mjs.map
@@ -1 +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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"}
1
+ {"version":3,"file":"ServerHost-DHg-Y2_q.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 { ShutdownPhase } from '#src/lifecycle/phases';\nimport { PluginLifecycle } from '#src/lifecycle/PluginLifecycle';\nimport { attachmentsOf, PluginHost, sealAttachments } from '#src/plugin/PluginHost';\n\nimport { registerProcessErrors } from './processErrors';\n\nimport type { CoordinatedShutdown, ShutdownTasks } from '#node/Lifecycle/CoordinatedShutdown';\nimport type { CoordinatedStartup, StartupTasks } from '#src/lifecycle/CoordinatedStartup';\nimport type { Transport } from '#src/plugin/options';\nimport type { Config } from '@seedcord/types';\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: ShutdownTasks;\n\n /** Add a task that runs while the bot starts. */\n public readonly startup: StartupTasks;\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, driver: Config['kv']) {\n // a `sideEffects: false` build would drop the same call in the node entry\n assertDeclaredRuntime();\n super(transport, 'server', driver);\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 shutdown.addTask(ShutdownPhase.Release, 'close-kv', async () => {\n await using _driver = this.kv.driver;\n });\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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"}
@@ -1,6 +1,7 @@
1
- import { d as Transport, n as PluginHost } from "./PluginHost-DB4j8unu.mjs";
2
- import { n as CoordinatedStartup, t as CoordinatedShutdown } from "./CoordinatedShutdown-H5YjZn8A.mjs";
1
+ import { f as Transport, n as PluginHost } from "./PluginHost-DWBgBS6Q.mjs";
2
+ import { i as StartupTasks, n as ShutdownTasks, r as CoordinatedStartup, t as CoordinatedShutdown } from "./CoordinatedShutdown-BVtNNSsF.mjs";
3
3
  import { HostShutdown, HostStartup } from "@seedcord/types/internal";
4
+ import { Config } from "@seedcord/types";
4
5
  import { Promisable } from "type-fest";
5
6
  //#region src/node/ServerHost.d.ts
6
7
  /** Base class for a transport `Seedcord` class that runs as a long-lived node or bun process. */
@@ -11,10 +12,10 @@ declare abstract class ServerHost<BotT extends Transport> extends PluginHost<Bot
11
12
  /** @internal */
12
13
  readonly [HostStartup]: CoordinatedStartup;
13
14
  /** Add a task that runs while the bot shuts down. */
14
- readonly shutdown: Pick<CoordinatedShutdown, 'addTask'>;
15
+ readonly shutdown: ShutdownTasks;
15
16
  /** Add a task that runs while the bot starts. */
16
- readonly startup: Pick<CoordinatedStartup, 'addTask'>;
17
- constructor(transport: BotT, shutdown: CoordinatedShutdown, startup: CoordinatedStartup);
17
+ readonly startup: StartupTasks;
18
+ constructor(transport: BotT, shutdown: CoordinatedShutdown, startup: CoordinatedStartup, driver: Config['kv']);
18
19
  /** @internal */
19
20
  protected init(): Promise<this>;
20
21
  /** @internal */
@@ -37,4 +38,4 @@ declare abstract class ServerHost<BotT extends Transport> extends PluginHost<Bot
37
38
  declare function shutdownOf(host: Pick<ServerHost<Transport>, typeof HostShutdown>): CoordinatedShutdown;
38
39
  //#endregion
39
40
  export { shutdownOf as n, ServerHost as t };
40
- //# sourceMappingURL=ServerHost-DYeh0KTk.d.mts.map
41
+ //# sourceMappingURL=ServerHost-whpI0h4d.d.mts.map
@@ -0,0 +1,32 @@
1
+ import { t as _usingCtx } from "./usingCtx-Cf2cZi96.mjs";
2
+ import { PortableSource, merge } from "envapt";
3
+
4
+ //#region src/miscellaneous/bindViteEnv.ts
5
+ function bindEnv(envapter, source, directory) {
6
+ try {
7
+ var _usingCtx$1 = _usingCtx();
8
+ const previousSource = envapter.source;
9
+ const previousBaseDir = previousSource.supportsFiles ? envapter.baseDir : void 0;
10
+ const cleanup = _usingCtx$1.u(new DisposableStack());
11
+ cleanup.defer(() => {
12
+ envapter.useSource(previousSource);
13
+ if (previousSource.supportsFiles) envapter.baseDir = previousBaseDir;
14
+ });
15
+ if (previousSource.supportsFiles && directory !== void 0) envapter.baseDir = directory;
16
+ envapter.useSource(source);
17
+ return cleanup.move();
18
+ } catch (_) {
19
+ _usingCtx$1.e = _;
20
+ } finally {
21
+ _usingCtx$1.d();
22
+ }
23
+ }
24
+ function bindViteEnv(envapter, vite, { bindings, directory } = {}) {
25
+ const { MODE: mode, BASE_URL: _baseUrl, DEV: _dev, PROD: _prod, SSR: _ssr, ...values } = vite;
26
+ const source = bindings === void 0 ? envapter.source : merge((key) => key === "MODE" && typeof mode === "string" ? mode : void 0, new PortableSource(bindings));
27
+ return bindEnv(envapter, merge(source, new PortableSource(values)), directory);
28
+ }
29
+
30
+ //#endregion
31
+ export { bindViteEnv as n, bindEnv as t };
32
+ //# sourceMappingURL=bindViteEnv-NFzvdqlT.mjs.map
@@ -1 +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"}
1
+ {"version":3,"file":"bindViteEnv-NFzvdqlT.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"}
@@ -1,3 +1,3 @@
1
- import { n as bindViteEnv } from "./bindViteEnv-DWq5rftJ.mjs";
1
+ import { n as bindViteEnv } from "./bindViteEnv-NFzvdqlT.mjs";
2
2
 
3
3
  export { bindViteEnv };
@@ -1,3 +1,3 @@
1
- import { t as HmrModuleHandler } from "./HmrModuleHandler-8ZC-UuZh.mjs";
1
+ import { t as HmrModuleHandler } from "./HmrModuleHandler-BMD7dg8N.mjs";
2
2
 
3
3
  export { HmrModuleHandler };
@@ -1,10 +1,10 @@
1
- import { K as Notice, P as DispatchContext, U as MiddlewareKind, l as Subscriber, r as CoreBase, y as SubscriptionKey } from "./phases-FTM69J3a.mjs";
1
+ import { K as MiddlewareKind, L as DispatchContext, Y as Notice, b as SubscriptionKey, i as CoreBase, u as Subscriber } from "./phases-Dojnr-ng.mjs";
2
2
  import "reflect-metadata";
3
3
  import { CustomId, CustomIdField } from "@seedcord/custom-id";
4
4
  import { ActionRowBuilder, ButtonBuilder, ChannelSelectMenuBuilder, CheckboxBuilder, CheckboxGroupBuilder, CheckboxGroupOptionBuilder, ContainerBuilder, ContextMenuCommandBuilder, EmbedBuilder, FileBuilder, FileUploadBuilder, LabelBuilder, MediaGalleryBuilder, MentionableSelectMenuBuilder, ModalBuilder, RadioGroupBuilder, RadioGroupOptionBuilder, RoleSelectMenuBuilder, SectionBuilder, SeparatorBuilder, SlashCommandBuilder, SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextDisplayBuilder, TextInputBuilder, UserSelectMenuBuilder } from "@discordjs/builders";
5
5
  import { APIApplicationCommand, APIMessageComponentEmoji, APIMessageTopLevelComponent, ApplicationCommandType, ChannelType, RESTPostAPIApplicationCommandsJSONBody, Snowflake } from "discord-api-types/v10";
6
6
  import { EventFrequency, ILogger, ReplyFile, ReplyResponse } from "@seedcord/types";
7
- import { Constructor, IsNever, LiteralUnion, NonEmptyTuple } from "type-fest";
7
+ import { Constructor, IsNever, LiteralUnion, NonEmptyTuple, Simplify } from "type-fest";
8
8
  //#region src/registries/ContextMenuRegistry.d.ts
9
9
  /** The two context-menu command kinds Discord defines. */
10
10
  type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Message;
@@ -19,7 +19,7 @@ type ContextMenuKind = ApplicationCommandType.User | ApplicationCommandType.Mess
19
19
  *
20
20
  * @example
21
21
  * ```ts
22
- * // seedcord-gen.d.ts (emitted, committed)
22
+ * // seedcord-gen.ts (emitted, committed)
23
23
  * declare module '@seedcord/gateway' {
24
24
  * interface UserContextMenuRegistry {
25
25
  * 'View Profile': { cache: 'cached' };
@@ -77,7 +77,7 @@ interface SlashRouteEntry {
77
77
  *
78
78
  * @example
79
79
  * ```ts
80
- * // seedcord-gen.d.ts (emitted, committed)
80
+ * // seedcord-gen.ts (emitted, committed)
81
81
  * declare module '@seedcord/gateway' {
82
82
  * interface SlashRegistry {
83
83
  * ban: { options: { target: { kind: 'user'; required: true } }; cache: 'cached' };
@@ -570,13 +570,12 @@ interface Gate<Ctx extends GateContextBase = GateContextBase, Name extends strin
570
570
  readonly [GateBrand]: true;
571
571
  }
572
572
  /**
573
- * A gate that carries a side effect, split so the effect fires only once the gate is the one that
574
- * let the request through. `check` peeks and refuses, while `commit` applies the effect after the whole
575
- * gate set passes. {@link Cooldown} is the catalog gate that returns one. Build one with
573
+ * A gate whose `check` method can refuse a request without applying its side effect. `commit` applies
574
+ * the effect after every gate passes. {@link Cooldown} is the catalog gate that returns one. Build one with
576
575
  * {@link defineEffectGate}.
577
576
  *
578
577
  * @typeParam Ctx - The context both `check` and `commit` require. A field missing from it fails to compile.
579
- * @typeParam Name - The gate's literal name, shown when a mismatch error names the gate.
578
+ * @typeParam Name - The literal name a mismatch error prints.
580
579
  *
581
580
  * @example
582
581
  * ```ts
@@ -871,10 +870,10 @@ type ChoiceValueOf<Entry> = Entry extends {
871
870
  kind: 'integer' | 'number';
872
871
  } ? number : never;
873
872
  /** the focused option. `value` is the raw partial discord delivers mid-type, always a string regardless of kind. */
874
- interface FocusedField<Route extends keyof SlashRegistry> {
873
+ type FocusedField<Route extends keyof SlashRegistry> = Simplify<{
875
874
  name: AutocompletableNames<Route>;
876
875
  value: string;
877
- }
876
+ }>;
878
877
  type NamesOfKind<Route extends keyof SlashRegistry, Kind extends OptionKind> = Route extends unknown ? { [Name in keyof Row<Route>]: Row<Route>[Name] extends {
879
878
  kind: Kind;
880
879
  } ? Name : never; }[keyof Row<Route>] : never;
@@ -1037,4 +1036,4 @@ declare abstract class WebhookLog<KeyOfSubscribers extends SubscriptionKey, TCor
1037
1036
  declare function WebhookUrl(envKey: string): <HandlerCtor extends Constructor<WebhookLog<SubscriptionKey, CoreBase>>>(constructor: HandlerCtor) => void;
1038
1037
  //#endregion
1039
1038
  export { SlashRouteBrand as $, Gate as A, CacheFor as At, HandlerOutcome as B, UserContextMenuRegistry as Bt, GateNoticeOptions as C, CommandMeta as Ct, JoinNames as D, RowComponent as Dt, IntersectRequired as E, BuilderComponent as Et, defineGate as F, SlashRouteEntry as Ft, RegisterInteractionMiddleware as G, AnyMiddlewareCtor as H, SerializedReply as I, ContextMenuKind as It, ComponentDefsBrand as J, interactionMiddlewareMetaOf as K, serializeReply as L, MenuCacheFor as Lt, GuildPermissionsContext as M, RouteCache as Mt, RequiredOf as N, SlashOption as Nt, TwoOrMore as O, BuilderType as Ot, defineEffectGate as P, SlashRegistry as Pt, MiddlewareKindsBrand as Q, DispatchResult as R, MessageContextMenuRegistry as Rt, PermissionScope as S, resolveDeployTarget as St, GateFitsWith as T, RegisterCommand as Tt, InteractionMiddlewareMetadata as U, HandlerResult as V, InteractionMiddlewareOptions as W, ContextMenuKindBrand as X, ComponentKindBrand as Y, ContextMenuNamesBrand as Z, RequirePermissions as _, ScopeId as _t, Subscribe as a, InjectedContextMenuMap as at, RequireRoleOptions as b, scopeIdsOf as bt, PageCursor as c, CommandRef as ct, AutocompleteOptions as d, commandRefOf as dt, CommandInfo as et, ChoiceValueOf as f, indexById as ft, RequireBotPermissions as g, GLOBAL_ID as gt, ResolvedEmoji as h, DeployResult as ht, WebhookFile as i, InjectedCommandMap as it, GateContextBase as j, OptionKind as jt, EffectGate as k, RowType as kt, pageCursor as l, LinkedCommands as lt, FocusedField as m, CommandBuilder as mt, WebhookLog as n, ContextMenuInfo as nt, SubscribeOptions as o, installLinks as ot, EntryFor as p, linkCommands as pt, AutocompleteRouteBrand as q, WebhookReport as r, ContextMenus as rt, PAGE_MAX as s, warnLinks as st, WebhookUrl as t, Commands as tt, AutocompletableNames as u, commandKey as ut, RequirePermissionsOptions as v, commandsRoute as vt, pickNotice as w, GuildTarget as wt, PermissionNames as x, skippedNote as xt, RequireRole as y, globalFirst as yt, EventDispatchResult as z, NamesFor as zt };
1040
- //# sourceMappingURL=index-Du71Xx69.d.mts.map
1039
+ //# sourceMappingURL=index-Boo33hh3.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,12 +1,13 @@
1
- import { C as AckState, K as Notice, P as DispatchContext, U as MiddlewareKind, _ as ResponseSent, b as Subscriptions, f as DispatchOutcome, g as ResponseOutcome, h as ResponseFailed, i as Bus, l as Subscriber, m as ResponseAttempt, n as StartupPhase, p as FaultSource, r as CoreBase, t as ShutdownPhase, u as AttemptedWrite, v as SubscriptionData, x as ReplyTelemetry, y as SubscriptionKey, z as InteractionKind } from "./phases-FTM69J3a.mjs";
2
- import { $ as SlashRouteBrand, A as Gate, B as HandlerOutcome, Bt as UserContextMenuRegistry, C as GateNoticeOptions, D as JoinNames, Dt as RowComponent, E as IntersectRequired, Et as BuilderComponent, F as defineGate, G as RegisterInteractionMiddleware, I as SerializedReply, It as ContextMenuKind, J as ComponentDefsBrand, M as GuildPermissionsContext, N as RequiredOf, O as TwoOrMore, Ot as BuilderType, P as defineEffectGate, Pt as SlashRegistry, R as DispatchResult, Rt as MessageContextMenuRegistry, S as PermissionScope, Tt as RegisterCommand, V as HandlerResult, W as InteractionMiddlewareOptions, X as ContextMenuKindBrand, Y as ComponentKindBrand, Z as ContextMenuNamesBrand, _ as RequirePermissions, a as Subscribe, at as InjectedContextMenuMap, b as RequireRoleOptions, c as PageCursor, d as AutocompleteOptions, et as CommandInfo, g as RequireBotPermissions, h as ResolvedEmoji, i as WebhookFile, it as InjectedCommandMap, j as GateContextBase, k as EffectGate, kt as RowType, n as WebhookLog, nt as ContextMenuInfo, o as SubscribeOptions, q as AutocompleteRouteBrand, r as WebhookReport, rt as ContextMenus, t as WebhookUrl, tt as Commands, v as RequirePermissionsOptions, wt as GuildTarget, y as RequireRole, z as EventDispatchResult, zt as NamesFor } from "./index-Du71Xx69.mjs";
1
+ import { F as Kv, H as InteractionKind, I as KvCollection, K as MiddlewareKind, L as DispatchContext, S as ReplyTelemetry, Y as Notice, _ as ResponseOutcome, a as Bus, b as SubscriptionKey, d as AttemptedWrite, g as ResponseFailed, h as ResponseAttempt, i as CoreBase, m as FaultSource, n as StartupPhase, p as DispatchOutcome, t as ShutdownPhase, u as Subscriber, v as ResponseSent, w as AckState, x as Subscriptions, y as SubscriptionData } from "./phases-Dojnr-ng.mjs";
2
+ import { $ as SlashRouteBrand, A as Gate, B as HandlerOutcome, Bt as UserContextMenuRegistry, C as GateNoticeOptions, D as JoinNames, Dt as RowComponent, E as IntersectRequired, Et as BuilderComponent, F as defineGate, G as RegisterInteractionMiddleware, I as SerializedReply, It as ContextMenuKind, J as ComponentDefsBrand, M as GuildPermissionsContext, N as RequiredOf, O as TwoOrMore, Ot as BuilderType, P as defineEffectGate, Pt as SlashRegistry, R as DispatchResult, Rt as MessageContextMenuRegistry, S as PermissionScope, Tt as RegisterCommand, V as HandlerResult, W as InteractionMiddlewareOptions, X as ContextMenuKindBrand, Y as ComponentKindBrand, Z as ContextMenuNamesBrand, _ as RequirePermissions, a as Subscribe, at as InjectedContextMenuMap, b as RequireRoleOptions, c as PageCursor, d as AutocompleteOptions, et as CommandInfo, g as RequireBotPermissions, h as ResolvedEmoji, i as WebhookFile, it as InjectedCommandMap, j as GateContextBase, k as EffectGate, kt as RowType, n as WebhookLog, nt as ContextMenuInfo, o as SubscribeOptions, q as AutocompleteRouteBrand, r as WebhookReport, rt as ContextMenus, t as WebhookUrl, tt as Commands, v as RequirePermissionsOptions, wt as GuildTarget, y as RequireRole, z as EventDispatchResult, zt as NamesFor } from "./index-Boo33hh3.mjs";
3
+ import { n as PluginTypes, t as Generated } from "./Generated-ByC53_A2.mjs";
3
4
  import "reflect-metadata";
4
5
  import { AnyCustomId, AnyCustomId as AnyCustomId$1, CustomId, CustomIdField, CustomIdShape, DecodedParams, DecodedRoute, FieldOptions, decodeFor, prefixOf, setCustomIdErrors } from "@seedcord/custom-id";
5
6
  import { ActionRowBuilder, ButtonBuilder } from "@discordjs/builders";
6
7
  import { APIMessageComponentEmoji, APIModalInteractionResponseCallbackData, ApplicationCommandType, ButtonStyle } from "discord-api-types/v10";
7
8
  import { Logger } from "@seedcord/logger";
8
9
  import { ValidDuration } from "@seedcord/utils";
9
- import { DeferOpts, EpochMs, LoggerChannelId, RenderContext, ReplyResponse, SendOpts, TypedExtract, V2Component } from "@seedcord/types";
10
+ import { DeferOpts, EpochMs, KvDriver, KvEntry, LoggerChannelId, RenderContext, ReplyResponse, SendOpts, TypedExtract } from "@seedcord/types";
10
11
  import { Constructor, Promisable } from "type-fest";
11
12
  //#region src/decorators/routes.d.ts
12
13
  type ComponentBrand = TypedExtract<InteractionKind, InteractionKind.Button | InteractionKind.Modal | InteractionKind.StringMenu | InteractionKind.UserMenu | InteractionKind.RoleMenu | InteractionKind.ChannelMenu | InteractionKind.MentionableMenu>;
@@ -332,7 +333,6 @@ export declare abstract class RepliableHandler<Event, TCore extends CoreBase, TM
332
333
  }, TNative, TSender extends BaseReplySender<TMessage, TNative>> extends BaseHandler<Event, TCore> {
333
334
  /** The reply surface for this interaction. Pass the handler to reply from outside its class. */
334
335
  readonly sender: TSender;
335
- protected readonly routeId: string;
336
336
  protected constructor(event: Event, core: TCore, dispatch: DispatchContext, sender?: TSender);
337
337
  protected abstract buildSender(event: Event, core: TCore, dispatch: DispatchContext): TSender;
338
338
  /** Send the initial response, exactly once. */
@@ -804,7 +804,7 @@ interface PaginatorControls {
804
804
  //#region src/pagination/render.d.ts
805
805
  /** What `renderItem` returns for one item, a text line or a section. */
806
806
  type ItemRender<Item> = (item: Item, index: number) => string | BuilderComponent<'section'>;
807
- type Renderable = string | V2Component[] | ReplyResponse;
807
+ type Renderable = string | ReplyResponse['components'] | ReplyResponse;
808
808
  /** A full-page render override. */
809
809
  type PageRender<Item> = (view: PageView<Item>, controls: PaginatorControls) => Renderable;
810
810
  //#endregion
@@ -895,9 +895,111 @@ export declare abstract class PaginatorBase<Item, Prefix extends string, Ctx> {
895
895
  protected buildPage(ctx: Ctx, n: number): Promise<ReplyResponse>;
896
896
  }
897
897
  //#endregion
898
+ //#region src/kv/KeyvDriver.d.ts
899
+ interface KeyvLike {
900
+ readonly store: {
901
+ readonly capabilities?: {
902
+ readonly store: string;
903
+ readonly methods: {
904
+ readonly iterator: {
905
+ readonly exists: boolean;
906
+ };
907
+ };
908
+ };
909
+ };
910
+ get(key: string): Promise<unknown>;
911
+ set(key: string, value: string, ttl?: number): Promise<boolean>;
912
+ delete(key: string): Promise<boolean>;
913
+ iterator(): AsyncGenerator<[string, unknown], void>;
914
+ disconnect(): Promise<void>;
915
+ }
916
+ /**
917
+ * Wraps a Keyv instance for `config.kv`.
918
+ *
919
+ * @example
920
+ * ```ts
921
+ * new Seedcord({ ...config, kv: new KeyvDriver(new Keyv(new KeyvSqlite('sqlite://./kv.sqlite'))) });
922
+ * ```
923
+ */
924
+ export declare class KeyvDriver<TKeyv extends KeyvLike> implements KvDriver, AsyncDisposable {
925
+ #private;
926
+ /** The Keyv instance you passed, with its full type. */
927
+ readonly keyv: TKeyv;
928
+ constructor(
929
+ /** The Keyv instance you passed, with its full type. */
930
+ keyv: TKeyv);
931
+ get(key: string): Promise<string | undefined>;
932
+ set(key: string, value: string, ttlMs?: number): Promise<void>;
933
+ delete(key: string): Promise<void>;
934
+ /** @throws A **SeedcordError** when the Keyv store can't list its keys, like `@keyv/memcache`. */
935
+ entries(prefix: string): AsyncIterable<KvEntry>;
936
+ [Symbol.asyncDispose](): Promise<void>;
937
+ }
938
+ //#endregion
939
+ //#region src/kv/MemoryDriver.d.ts
940
+ /**
941
+ * The default `config.kv`. It keeps every key in a `Map` and loses them on restart. On serverless,
942
+ * each running copy keeps its own.
943
+ */
944
+ export declare class MemoryDriver implements KvDriver, Disposable {
945
+ #private;
946
+ get(key: string): string | undefined;
947
+ set(key: string, value: string, ttlMs?: number): void;
948
+ delete(key: string): void;
949
+ entries(prefix: string): Iterable<KvEntry>;
950
+ [Symbol.dispose](): void;
951
+ }
952
+ //#endregion
953
+ //#region src/kv/UnstorageDriver.d.ts
954
+ type Bytes = NonNullable<Parameters<TextDecoder['decode']>[0]>;
955
+ interface StorageLike {
956
+ getItemRaw(key: string): Promise<string | Bytes | null>;
957
+ setItemRaw(key: string, value: string, opts?: {
958
+ ttl?: number;
959
+ }): Promise<void>;
960
+ removeItem(key: string): Promise<void>;
961
+ getKeys(): Promise<string[]>;
962
+ dispose(): Promise<void>;
963
+ }
964
+ /**
965
+ * Wraps an unstorage `Storage` for `config.kv`.
966
+ *
967
+ * @example
968
+ * ```ts
969
+ * new Seedcord({ ...config, kv: new UnstorageDriver(createStorage({ driver: fsDriver({ base: './.kv' }) })) });
970
+ * ```
971
+ */
972
+ export declare class UnstorageDriver<TStorage extends StorageLike> implements KvDriver, AsyncDisposable {
973
+ /** The storage you passed, with its full type. */
974
+ readonly storage: TStorage;
975
+ constructor(
976
+ /** The storage you passed, with its full type. */
977
+ storage: TStorage);
978
+ get(key: string): Promise<string | undefined>;
979
+ set(key: string, value: string, ttlMs?: number): Promise<void>;
980
+ delete(key: string): Promise<void>;
981
+ entries(prefix: string): AsyncIterable<KvEntry>;
982
+ [Symbol.asyncDispose](): Promise<void>;
983
+ }
984
+ //#endregion
985
+ //#region src/rateLimiter/buildKey.d.ts
986
+ /**
987
+ * Builds a rate-limit key from a prefix and scope parts, joined with `:`. A null or undefined part
988
+ * becomes `'global'`. A key built from a DM's guild id reads `<prefix>:global` for every DM caller.
989
+ *
990
+ * @param prefix - The bucket family, e.g. `'cooldown'`.
991
+ * @param parts - Scope values, e.g. a user or guild id.
992
+ *
993
+ * @example
994
+ * ```ts
995
+ * buildKey('report', interaction.guildId); // 'report:1234...' or 'report:global' in a DM
996
+ * ```
997
+ */
998
+ export declare function buildKey(prefix: string, ...parts: readonly (string | null | undefined)[]): string;
999
+ //#endregion
898
1000
  //#region src/index.d.ts
899
1001
  /** Package version */
900
1002
  export declare const version: string;
901
1003
  //#endregion
902
- export { type AnyCustomId, type AttemptedWrite, type AutocompleteOptions, BuilderComponent, type BuilderType, Bus, type CommandInfo, Commands, type ContextMenuInfo, ContextMenus, type ControlCosmetics, type ControlKey, type CooldownOptions, type CoreBase, CustomId, type CustomIdField, type CustomIdShape, type DecodedParams, type DecodedRoute, DispatchContext, type DispatchOutcome, type DispatchResult, type EffectGate, type EventDispatchResult, type FaultSource, type FieldOptions, type Gate, type GateContextBase, type GateNoticeOptions, type GuildPermissionsContext, type GuildTarget, type HandlerOutcome, type HandlerResult, type InjectedCommandMap, type InjectedContextMenuMap, InteractionKind, type InteractionMiddlewareOptions, type ItemRender, type MessageContextMenuRegistry, type MiddlewareKind, type ModalLike, Notice, type PageRender, type PageSourceBase, type PageView, type PaginatorConfig, type PaginatorControls, type PermissionAssertion, type PermissionNoticeOverrides, type PermissionScope, RegisterCommand, RegisterInteractionMiddleware, RequireBotPermissions, RequirePermissions, type RequirePermissionsOptions, RequireRole, type RequireRoleOptions, type RequiredOf, ResolvedEmoji, type ResponseAttempt, type ResponseFailed, type ResponseOutcome, type ResponseSent, RowComponent, type RowType, ShutdownPhase, type SlashRegistry, StartupPhase, Subscribe, type SubscribeOptions, Subscriber, type SubscriptionData, type SubscriptionKey, type Subscriptions, type UserContextMenuRegistry, type WebhookFile, WebhookLog, type WebhookReport, WebhookUrl, decodeFor, defineEffectGate, defineGate, prefixOf, setCustomIdErrors };
1004
+ export { type AnyCustomId, type AttemptedWrite, type AutocompleteOptions, BuilderComponent, type BuilderType, Bus, type CommandInfo, Commands, type ContextMenuInfo, ContextMenus, type ControlCosmetics, type ControlKey, type CooldownOptions, type CoreBase, CustomId, type CustomIdField, type CustomIdShape, type DecodedParams, type DecodedRoute, DispatchContext, type DispatchOutcome, type DispatchResult, type EffectGate, type EventDispatchResult, type FaultSource, type FieldOptions, type Gate, type GateContextBase, type GateNoticeOptions, type Generated, type GuildPermissionsContext, type GuildTarget, type HandlerOutcome, type HandlerResult, type InjectedCommandMap, type InjectedContextMenuMap, InteractionKind, type InteractionMiddlewareOptions, type ItemRender, type Kv, type KvCollection, type MessageContextMenuRegistry, type MiddlewareKind, type ModalLike, Notice, type PageRender, type PageSourceBase, type PageView, type PaginatorConfig, type PaginatorControls, type PermissionAssertion, type PermissionNoticeOverrides, type PermissionScope, type PluginTypes, RegisterCommand, RegisterInteractionMiddleware, RequireBotPermissions, RequirePermissions, type RequirePermissionsOptions, RequireRole, type RequireRoleOptions, type RequiredOf, ResolvedEmoji, type ResponseAttempt, type ResponseFailed, type ResponseOutcome, type ResponseSent, RowComponent, type RowType, ShutdownPhase, type SlashRegistry, StartupPhase, Subscribe, type SubscribeOptions, Subscriber, type SubscriptionData, type SubscriptionKey, type Subscriptions, type UserContextMenuRegistry, type WebhookFile, WebhookLog, type WebhookReport, WebhookUrl, decodeFor, defineEffectGate, defineGate, prefixOf, setCustomIdErrors };
903
1005
  //# sourceMappingURL=index.d.mts.map