@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.
- package/dist/Bus-W1K-uMeO.mjs +930 -0
- package/dist/Bus-W1K-uMeO.mjs.map +1 -0
- package/dist/{CoordinatedShutdown-H5YjZn8A.d.mts → CoordinatedShutdown-BVtNNSsF.d.mts} +13 -4
- package/dist/{CoordinatedStartup-CGnJrqcl.mjs → CoordinatedStartup-DA3gW8op.mjs} +22 -20
- package/dist/CoordinatedStartup-DA3gW8op.mjs.map +1 -0
- package/dist/Generated-ByC53_A2.d.mts +26 -0
- package/dist/{HmrModuleHandler-8ZC-UuZh.mjs → HmrModuleHandler-BMD7dg8N.mjs} +2 -2
- package/dist/HmrModuleHandler-BMD7dg8N.mjs.map +1 -0
- package/dist/PluginHost-CYe-UVpp.mjs +552 -0
- package/dist/PluginHost-CYe-UVpp.mjs.map +1 -0
- package/dist/{PluginHost-DB4j8unu.d.mts → PluginHost-DWBgBS6Q.d.mts} +35 -8
- package/dist/{PluginLifecycle-AhQ46NIr.mjs → PluginLifecycle-DdwRzcDj.mjs} +3 -3
- package/dist/PluginLifecycle-DdwRzcDj.mjs.map +1 -0
- package/dist/{ServerHost-CtmAhEXn.mjs → ServerHost-DHg-Y2_q.mjs} +17 -6
- package/dist/{ServerHost-CtmAhEXn.mjs.map → ServerHost-DHg-Y2_q.mjs.map} +1 -1
- package/dist/{ServerHost-DYeh0KTk.d.mts → ServerHost-whpI0h4d.d.mts} +7 -6
- package/dist/bindViteEnv-NFzvdqlT.mjs +32 -0
- package/dist/{bindViteEnv-DWq5rftJ.mjs.map → bindViteEnv-NFzvdqlT.mjs.map} +1 -1
- package/dist/env-internal.index.mjs +1 -1
- package/dist/hmr.index.mjs +1 -1
- package/dist/{index-Du71Xx69.d.mts → index-Boo33hh3.d.mts} +10 -11
- package/dist/index.d.mts +108 -6
- package/dist/index.mjs +118 -12
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +25 -7
- package/dist/internal.index.mjs +8 -8
- package/dist/node-internal.index.d.mts +7 -4
- package/dist/node-internal.index.mjs +19 -6
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.d.mts +3 -3
- package/dist/node.index.mjs +22 -28
- package/dist/node.index.mjs.map +1 -1
- package/dist/{phases-FTM69J3a.d.mts → phases-Dojnr-ng.d.mts} +111 -12
- package/dist/plugin.index.d.mts +3 -2
- package/dist/plugin.index.mjs +1 -1
- package/dist/{subscribers-Ce3SWy23.mjs → subscribers-D4_HZcgw.mjs} +12 -829
- package/dist/subscribers-D4_HZcgw.mjs.map +1 -0
- package/dist/{bindViteEnv-DWq5rftJ.mjs → usingCtx-Cf2cZi96.mjs} +1 -31
- package/package.json +11 -9
- package/dist/CoordinatedStartup-CGnJrqcl.mjs.map +0 -1
- package/dist/HmrModuleHandler-8ZC-UuZh.mjs.map +0 -1
- package/dist/PluginHost-B8kxLOeb.mjs +0 -245
- package/dist/PluginHost-B8kxLOeb.mjs.map +0 -1
- package/dist/PluginLifecycle-AhQ46NIr.mjs.map +0 -1
- package/dist/phases-BauKJOTT.mjs +0 -31
- package/dist/phases-BauKJOTT.mjs.map +0 -1
- package/dist/publishDefault-D3dAyzZ9.mjs +0 -7
- package/dist/publishDefault-D3dAyzZ9.mjs.map +0 -1
- package/dist/subscribers-Ce3SWy23.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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?:
|
|
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:
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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,
|
|
297
|
-
//# sourceMappingURL=PluginHost-
|
|
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 {
|
|
2
|
-
import {
|
|
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-
|
|
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 {
|
|
2
|
-
import { n as attachmentsOf, r as sealAttachments, t as PluginHost } from "./PluginHost-
|
|
3
|
-
import { i as logUnknownFault, t as PluginLifecycle } from "./PluginLifecycle-
|
|
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-
|
|
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 {
|
|
2
|
-
import { n as CoordinatedStartup, t as CoordinatedShutdown } from "./CoordinatedShutdown-
|
|
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:
|
|
15
|
+
readonly shutdown: ShutdownTasks;
|
|
15
16
|
/** Add a task that runs while the bot starts. */
|
|
16
|
-
readonly startup:
|
|
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-
|
|
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-
|
|
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"}
|
package/dist/hmr.index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { K as
|
|
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.
|
|
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.
|
|
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
|
|
574
|
-
*
|
|
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
|
|
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
|
-
|
|
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-
|
|
1039
|
+
//# sourceMappingURL=index-Boo33hh3.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
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-
|
|
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
|
|
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 |
|
|
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
|