@nectar-js/nectar 0.2.0 → 0.3.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/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as main } from "./cli-DJg1-t94.js";
2
+ import { t as main } from "./cli-Cv0mpiRy.js";
3
3
  //#region src/cli.ts
4
4
  process.exitCode = await main(process.argv.slice(2), process.cwd());
5
5
  //#endregion
@@ -1,4 +1,4 @@
1
- import { ButtonInteraction, ChannelSelectMenuInteraction, ChatInputCommandInteraction, Client, ClientEvents, ClientOptions, GatewayIntentBits, Interaction, MentionableSelectMenuInteraction, MessageContextMenuCommandInteraction, ModalSubmitInteraction, PermissionResolvable, RoleSelectMenuInteraction, StringSelectMenuInteraction, UserContextMenuCommandInteraction, UserSelectMenuInteraction } from "discord.js";
1
+ import { Attachment, ButtonInteraction, ChannelSelectMenuInteraction, ChatInputCommandInteraction, Client, ClientEvents, ClientOptions, CommandInteractionOption, GatewayIntentBits, Interaction, MentionableSelectMenuInteraction, MessageContextMenuCommandInteraction, ModalSubmitInteraction, PermissionResolvable, RoleSelectMenuInteraction, StringSelectMenuInteraction, User, UserContextMenuCommandInteraction, UserSelectMenuInteraction } from "discord.js";
2
2
  import { ApplicationCommandType, ApplicationIntegrationType, ChannelType, InteractionContextType, LocalizationMap, RESTPostAPIApplicationCommandsJSONBody } from "discord-api-types/v10";
3
3
  //#region src/version.d.ts
4
4
  /** From package.json, which sits one level up from both `src/` and `dist/`. */
@@ -69,7 +69,15 @@ interface CommandMeta extends TopLevelMeta {
69
69
  options?: CommandOption[];
70
70
  nameLocalizations?: LocalizationMap;
71
71
  descriptionLocalizations?: LocalizationMap;
72
+ /**
73
+ * Defer the reply before the handler runs, so a slow handler doesn't miss Discord's three
74
+ * second window. `"ephemeral"` defers with an ephemeral reply. The handler then answers with
75
+ * `editReply()`.
76
+ */
77
+ defer?: boolean | "ephemeral";
72
78
  }
79
+ /** How a command route defers, as the manifest records it. */
80
+ type DeferMode = "reply" | "ephemeral";
73
81
  /** `export const meta` in a `route.ts` under `commands/`. Describes a parent command or subcommand group. */
74
82
  interface CommandRouteMeta extends TopLevelMeta {
75
83
  name?: string;
@@ -276,11 +284,18 @@ interface Trace {
276
284
  elapsed(): number;
277
285
  }
278
286
  type Params = Record<string, string | string[]>;
279
- interface InteractionContext<I = Interaction, P = Params> {
287
+ /** Command option values by name. Untyped until the route is known. */
288
+ type Options = Record<string, unknown>;
289
+ interface InteractionContext<I = Interaction, P = Params, O = Options> {
280
290
  interaction: I;
281
291
  client: Client;
282
292
  route: RouteInfo;
283
293
  params: P;
294
+ /**
295
+ * The command's options by name, resolved through discord.js. Options the user left out
296
+ * are `null`. Empty for components and autocomplete.
297
+ */
298
+ options: O;
284
299
  env: Env;
285
300
  trace: Trace;
286
301
  /** What plugins provide. Empty without plugins. */
@@ -380,6 +395,8 @@ interface ManifestRouteBase {
380
395
  }
381
396
  interface ManifestCommandRoute extends ManifestRouteBase {
382
397
  kind: "command";
398
+ /** The reply to defer before the handler runs, from `meta.defer`. */
399
+ defer: DeferMode | null;
383
400
  }
384
401
  interface ManifestAutocompleteRoute extends ManifestRouteBase {
385
402
  kind: "autocomplete";
@@ -575,6 +592,11 @@ type Empty = Record<never, never>;
575
592
  type Declared<K extends string> = NectarRoutes extends Record<K, infer V> ? V : never;
576
593
  type Fallback<T, F> = [T] extends [never] ? F : T;
577
594
  type ComponentKindName = "button" | "modal" | `select:${SelectKind}`;
595
+ /** One command option as the generated types describe it. */
596
+ interface OptionSpecType {
597
+ type: OptionType;
598
+ required: boolean;
599
+ }
578
600
  /** Component routes by path. Until types are generated, any string is accepted. */
579
601
  type ComponentRoutes = Fallback<Declared<"components">, Record<string, {
580
602
  kind: ComponentKindName;
@@ -584,7 +606,7 @@ type ComponentRoutes = Fallback<Declared<"components">, Record<string, {
584
606
  /** Command routes by path. Until types are generated, any string is accepted. */
585
607
  type CommandRoutes = Fallback<Declared<"commands">, Record<string, {
586
608
  type: CommandType;
587
- options: Record<string, OptionType>;
609
+ options: Record<string, OptionSpecType>;
588
610
  context: Empty;
589
611
  }>>;
590
612
  type ComponentPath = keyof ComponentRoutes & string;
@@ -593,9 +615,16 @@ type Route<Routes, P extends string> = P extends keyof Routes ? Routes[P] : neve
593
615
  type ComponentInteraction<K> = K extends "button" ? ButtonInteraction : K extends "modal" ? ModalSubmitInteraction : K extends "select:string" ? StringSelectMenuInteraction : K extends "select:user" ? UserSelectMenuInteraction : K extends "select:role" ? RoleSelectMenuInteraction : K extends "select:channel" ? ChannelSelectMenuInteraction : K extends "select:mentionable" ? MentionableSelectMenuInteraction : never;
594
616
  type CommandInteraction<T> = T extends "chatInput" ? ChatInputCommandInteraction : T extends "user" ? UserContextMenuCommandInteraction : T extends "message" ? MessageContextMenuCommandInteraction : never;
595
617
  type Field<R, K extends string, F> = R extends Record<K, infer V> ? V : F;
618
+ /** What `ctx.options` holds for each option type, as discord.js resolves it. */
619
+ type OptionValue<T> = T extends "string" ? string : T extends "integer" | "number" ? number : T extends "boolean" ? boolean : T extends "user" ? User : T extends "channel" ? NonNullable<CommandInteractionOption["channel"]> : T extends "role" ? NonNullable<CommandInteractionOption["role"]> : T extends "mentionable" ? NonNullable<CommandInteractionOption["member" | "role" | "user"]> : T extends "attachment" ? Attachment : never;
620
+ /** `ctx.options` for a command route: required options as their value, the rest `| null`. */
621
+ type OptionValues<P extends CommandPath> = { [K in keyof Field<Route<CommandRoutes, P>, "options", Empty>]: Field<Route<CommandRoutes, P>, "options", Empty>[K] extends {
622
+ type: infer T;
623
+ required: infer R;
624
+ } ? R extends true ? OptionValue<T> : OptionValue<T> | null : never; };
596
625
  type ComponentParams<P extends ComponentPath> = Field<Route<ComponentRoutes, P>, "params", Params>;
597
- type ComponentContext<P extends ComponentPath> = InteractionContext<ComponentInteraction<Field<Route<ComponentRoutes, P>, "kind", ComponentKindName>>, ComponentParams<P>> & Field<Route<ComponentRoutes, P>, "context", Empty>;
598
- type CommandContext<P extends CommandPath> = InteractionContext<CommandInteraction<Field<Route<CommandRoutes, P>, "type", CommandType>>, Empty> & Field<Route<CommandRoutes, P>, "context", Empty>;
626
+ type ComponentContext<P extends ComponentPath> = InteractionContext<ComponentInteraction<Field<Route<ComponentRoutes, P>, "kind", ComponentKindName>>, ComponentParams<P>, Empty> & Field<Route<ComponentRoutes, P>, "context", Empty>;
627
+ type CommandContext<P extends CommandPath> = InteractionContext<CommandInteraction<Field<Route<CommandRoutes, P>, "type", CommandType>>, Empty, OptionValues<P>> & Field<Route<CommandRoutes, P>, "context", Empty>;
599
628
  /** Every field is optional when the route has no parameters, so `customId("confirm")` works. */
600
629
  type ParamsArg<P extends ComponentPath> = Empty extends ComponentParams<P> ? [params?: ComponentParams<P>] : [params: ComponentParams<P>];
601
630
  /**
@@ -661,6 +690,21 @@ interface RoleOptions extends PolicyOptions {
661
690
  }
662
691
  /** Passes only when the invoking member holds the listed role IDs. */
663
692
  declare function requireRoles(roles: string | readonly string[], options?: RoleOptions): Middleware;
693
+ interface CooldownOptions {
694
+ /**
695
+ * Who shares a cooldown: each user (default), everyone in a guild, or everyone. In a DM,
696
+ * `"guild"` falls back to the user.
697
+ */
698
+ scope?: "user" | "guild" | "global";
699
+ /** What the user sees while waiting, given the seconds left. */
700
+ message?: string | ((seconds: number) => string);
701
+ }
702
+ /**
703
+ * Passes once per `seconds` for each route and subject. The cooldown starts when the
704
+ * interaction passes, so a handler that throws still counts. Autocomplete is never held back.
705
+ * Cooldowns live in memory, per process, and reset on restart.
706
+ */
707
+ declare function cooldown(seconds: number, options?: CooldownOptions): Middleware;
664
708
  //#endregion
665
709
  //#region src/registration/diff.d.ts
666
710
  interface CommandDiff {
@@ -755,5 +799,5 @@ interface NectarRoutes {}
755
799
  */
756
800
  interface NectarServices {}
757
801
  //#endregion
758
- export { InteractionContext as $, defineComponent as A, OptionChoice as At, PluginChange as B, ComponentOptions as C, ChannelOption as Ct, Routed as D, CommandType as Dt, ComponentRoutes as E, CommandRouteMeta as Et, NectarConfig as F, version as Ft, definePlugin as G, PluginCommandContext as H, defineConfig as I, ContextExtension as J, EventMeta as K, validateConfig as L, defineEvent as M, SimpleOption as Mt, defineMiddleware as N, StringOption as Nt, customId as O, IntegerOption as Ot, ConfigError as P, TopLevelMeta as Pt, Extended as Q, NectarPlugin as R, ComponentKindName as S, SelectKind as St, ComponentPath as T, CommandOption as Tt, PluginError as U, PluginCommand as V, PluginGraph as W, ErrorHandler as X, Env as Y, EventContext as Z, requiredIntents as _, LoggerOptions as _t, SyncResult as a, RouteInfo as at, CommandRoutes as b, CustomIdTooLongError as bt, RegistrationError as c, RejectReason as ct, CommandDiff as d, SignalEmitter as dt, Logger as et, PolicyOptions as f, SignalType as ft, requireRoles as g, LogSink as gt, requirePermissions as h, LogRecord as ht, SyncOptions as i, Params as it, defineError as j, OptionType as jt, defineCommand as k, NumberOption as kt, RegistrationProblem as l, Signal as lt, guildOnly as m, LogLevel as mt, NectarServices as n, MiddlewareExtension as nt, UnsafeSyncError as o, Trace as ot, RoleOptions as p, LogFields as pt, EventMode as q, ScopeSync as r, Next as rt, syncCommands as s, InteractionMeta as st, NectarRoutes as t, Middleware as tt, Scope as u, SignalData as ut, CommandContext as v, ParamValidator as vt, ComponentParams as w, CommandMeta as wt, ComponentContext as x, MAX_CUSTOM_ID_LENGTH as xt, CommandPath as y, StandardSchemaLike as yt, PluginApp as z };
759
- //# sourceMappingURL=index-DNXcZ4Ff.d.ts.map
802
+ export { Env as $, OptionValues as A, CommandOption as At, defineConfig as B, version as Bt, ComponentContext as C, ParamValidator as Ct, ComponentPath as D, SelectKind as Dt, ComponentParams as E, MAX_CUSTOM_ID_LENGTH as Et, defineError as F, OptionChoice as Ft, PluginCommand as G, NectarPlugin as H, defineEvent as I, OptionType as It, PluginGraph as J, PluginCommandContext as K, defineMiddleware as L, SimpleOption as Lt, customId as M, CommandType as Mt, defineCommand as N, IntegerOption as Nt, ComponentRoutes as O, ChannelOption as Ot, defineComponent as P, NumberOption as Pt, ContextExtension as Q, ConfigError as R, StringOption as Rt, CommandRoutes as S, LoggerOptions as St, ComponentOptions as T, CustomIdTooLongError as Tt, PluginApp as U, validateConfig as V, PluginChange as W, EventMeta as X, definePlugin as Y, EventMode as Z, requirePermissions as _, SignalType as _t, SyncResult as a, Middleware as at, CommandContext as b, LogRecord as bt, RegistrationError as c, Options as ct, CommandDiff as d, Trace as dt, ErrorHandler as et, CooldownOptions as f, InteractionMeta as ft, guildOnly as g, SignalEmitter as gt, cooldown as h, SignalData as ht, SyncOptions as i, Logger as it, Routed as j, CommandRouteMeta as jt, OptionSpecType as k, CommandMeta as kt, RegistrationProblem as l, Params as lt, RoleOptions as m, Signal as mt, NectarServices as n, Extended as nt, UnsafeSyncError as o, MiddlewareExtension as ot, PolicyOptions as p, RejectReason as pt, PluginError as q, ScopeSync as r, InteractionContext as rt, syncCommands as s, Next as st, NectarRoutes as t, EventContext as tt, Scope as u, RouteInfo as ut, requireRoles as v, LogFields as vt, ComponentKindName as w, StandardSchemaLike as wt, CommandPath as x, LogSink as xt, requiredIntents as y, LogLevel as yt, NectarConfig as z, TopLevelMeta as zt };
803
+ //# sourceMappingURL=index-IqoTGJFM.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as InteractionContext, A as defineComponent, At as OptionChoice, B as PluginChange, C as ComponentOptions, Ct as ChannelOption, D as Routed, Dt as CommandType, E as ComponentRoutes, Et as CommandRouteMeta, F as NectarConfig, Ft as version, G as definePlugin, H as PluginCommandContext, I as defineConfig, J as ContextExtension, K as EventMeta, L as validateConfig, M as defineEvent, Mt as SimpleOption, N as defineMiddleware, Nt as StringOption, O as customId, Ot as IntegerOption, P as ConfigError, Pt as TopLevelMeta, Q as Extended, R as NectarPlugin, S as ComponentKindName, St as SelectKind, T as ComponentPath, Tt as CommandOption, U as PluginError, V as PluginCommand, W as PluginGraph, X as ErrorHandler, Y as Env, Z as EventContext, _ as requiredIntents, _t as LoggerOptions, a as SyncResult, at as RouteInfo, b as CommandRoutes, bt as CustomIdTooLongError, c as RegistrationError, ct as RejectReason, d as CommandDiff, dt as SignalEmitter, et as Logger, f as PolicyOptions, ft as SignalType, g as requireRoles, gt as LogSink, h as requirePermissions, ht as LogRecord, i as SyncOptions, it as Params, j as defineError, jt as OptionType, k as defineCommand, kt as NumberOption, l as RegistrationProblem, lt as Signal, m as guildOnly, mt as LogLevel, n as NectarServices, nt as MiddlewareExtension, o as UnsafeSyncError, ot as Trace, p as RoleOptions, pt as LogFields, q as EventMode, r as ScopeSync, rt as Next, s as syncCommands, st as InteractionMeta, t as NectarRoutes, tt as Middleware, u as Scope, ut as SignalData, v as CommandContext, vt as ParamValidator, w as ComponentParams, wt as CommandMeta, x as ComponentContext, xt as MAX_CUSTOM_ID_LENGTH, y as CommandPath, yt as StandardSchemaLike, z as PluginApp } from "./index-DNXcZ4Ff.js";
2
- export { type ChannelOption, type CommandContext, type CommandDiff, type CommandMeta, type CommandOption, type CommandPath, type CommandRouteMeta, type CommandRoutes, type CommandType, type ComponentContext, type ComponentKindName, type ComponentOptions, type ComponentParams, type ComponentPath, type ComponentRoutes, ConfigError, type ContextExtension, CustomIdTooLongError, type Env, type ErrorHandler, type EventContext, type EventMeta, type EventMode, type Extended, type IntegerOption, type InteractionContext, type InteractionMeta, type LogFields, type LogLevel, type LogRecord, type LogSink, type Logger, type LoggerOptions, MAX_CUSTOM_ID_LENGTH, type Middleware, type MiddlewareExtension, type NectarConfig, type NectarPlugin, NectarRoutes, NectarServices, type Next, type NumberOption, type OptionChoice, type OptionType, type ParamValidator, type Params, type PluginApp, type PluginChange, type PluginCommand, type PluginCommandContext, PluginError, type PluginGraph, type PolicyOptions, RegistrationError, type RegistrationProblem, type RejectReason, type RoleOptions, type RouteInfo, type Routed, type Scope, type ScopeSync, type SelectKind, type Signal, type SignalData, type SignalEmitter, type SignalType, type SimpleOption, type StandardSchemaLike, type StringOption, type SyncOptions, type SyncResult, type TopLevelMeta, type Trace, UnsafeSyncError, customId, defineCommand, defineComponent, defineConfig, defineError, defineEvent, defineMiddleware, definePlugin, guildOnly, requirePermissions, requireRoles, requiredIntents, syncCommands, validateConfig, version };
1
+ import { $ as Env, A as OptionValues, At as CommandOption, B as defineConfig, Bt as version, C as ComponentContext, Ct as ParamValidator, D as ComponentPath, Dt as SelectKind, E as ComponentParams, Et as MAX_CUSTOM_ID_LENGTH, F as defineError, Ft as OptionChoice, G as PluginCommand, H as NectarPlugin, I as defineEvent, It as OptionType, J as PluginGraph, K as PluginCommandContext, L as defineMiddleware, Lt as SimpleOption, M as customId, Mt as CommandType, N as defineCommand, Nt as IntegerOption, O as ComponentRoutes, Ot as ChannelOption, P as defineComponent, Pt as NumberOption, Q as ContextExtension, R as ConfigError, Rt as StringOption, S as CommandRoutes, St as LoggerOptions, T as ComponentOptions, Tt as CustomIdTooLongError, U as PluginApp, V as validateConfig, W as PluginChange, X as EventMeta, Y as definePlugin, Z as EventMode, _ as requirePermissions, _t as SignalType, a as SyncResult, at as Middleware, b as CommandContext, bt as LogRecord, c as RegistrationError, ct as Options, d as CommandDiff, dt as Trace, et as ErrorHandler, f as CooldownOptions, ft as InteractionMeta, g as guildOnly, gt as SignalEmitter, h as cooldown, ht as SignalData, i as SyncOptions, it as Logger, j as Routed, jt as CommandRouteMeta, k as OptionSpecType, kt as CommandMeta, l as RegistrationProblem, lt as Params, m as RoleOptions, mt as Signal, n as NectarServices, nt as Extended, o as UnsafeSyncError, ot as MiddlewareExtension, p as PolicyOptions, pt as RejectReason, q as PluginError, r as ScopeSync, rt as InteractionContext, s as syncCommands, st as Next, t as NectarRoutes, tt as EventContext, u as Scope, ut as RouteInfo, v as requireRoles, vt as LogFields, w as ComponentKindName, wt as StandardSchemaLike, x as CommandPath, xt as LogSink, y as requiredIntents, yt as LogLevel, z as NectarConfig, zt as TopLevelMeta } from "./index-IqoTGJFM.js";
2
+ export { type ChannelOption, type CommandContext, type CommandDiff, type CommandMeta, type CommandOption, type CommandPath, type CommandRouteMeta, type CommandRoutes, type CommandType, type ComponentContext, type ComponentKindName, type ComponentOptions, type ComponentParams, type ComponentPath, type ComponentRoutes, ConfigError, type ContextExtension, type CooldownOptions, CustomIdTooLongError, type Env, type ErrorHandler, type EventContext, type EventMeta, type EventMode, type Extended, type IntegerOption, type InteractionContext, type InteractionMeta, type LogFields, type LogLevel, type LogRecord, type LogSink, type Logger, type LoggerOptions, MAX_CUSTOM_ID_LENGTH, type Middleware, type MiddlewareExtension, type NectarConfig, type NectarPlugin, NectarRoutes, NectarServices, type Next, type NumberOption, type OptionChoice, type OptionSpecType, type OptionType, type OptionValues, type Options, type ParamValidator, type Params, type PluginApp, type PluginChange, type PluginCommand, type PluginCommandContext, PluginError, type PluginGraph, type PolicyOptions, RegistrationError, type RegistrationProblem, type RejectReason, type RoleOptions, type RouteInfo, type Routed, type Scope, type ScopeSync, type SelectKind, type Signal, type SignalData, type SignalEmitter, type SignalType, type SimpleOption, type StandardSchemaLike, type StringOption, type SyncOptions, type SyncResult, type TopLevelMeta, type Trace, UnsafeSyncError, cooldown, customId, defineCommand, defineComponent, defineConfig, defineError, defineEvent, defineMiddleware, definePlugin, guildOnly, requirePermissions, requireRoles, requiredIntents, syncCommands, validateConfig, version };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { D as version, T as MAX_CUSTOM_ID_LENGTH, l as encodeComponentRoute, n as definePlugin, t as PluginError, w as CustomIdTooLongError } from "./plugins-BCYwtuo-.js";
2
- import { l as defineConfig, n as syncCommands, o as requiredIntents, r as RegistrationError, s as ConfigError, t as UnsafeSyncError, u as validateConfig } from "./registration-BN3P3MDg.js";
1
+ import { D as version, T as MAX_CUSTOM_ID_LENGTH, l as encodeComponentRoute, n as definePlugin, t as PluginError, w as CustomIdTooLongError } from "./plugins-C2uwN3-D.js";
2
+ import { l as defineConfig, n as syncCommands, o as requiredIntents, r as RegistrationError, s as ConfigError, t as UnsafeSyncError, u as validateConfig } from "./registration-mUZJidnF.js";
3
3
  import { MessageFlags } from "discord-api-types/v10";
4
4
  //#region src/define.ts
5
5
  /**
@@ -72,6 +72,40 @@ function requireRoles(roles, options = {}) {
72
72
  return next();
73
73
  };
74
74
  }
75
+ /**
76
+ * Passes once per `seconds` for each route and subject. The cooldown starts when the
77
+ * interaction passes, so a handler that throws still counts. Autocomplete is never held back.
78
+ * Cooldowns live in memory, per process, and reset on restart.
79
+ */
80
+ function cooldown(seconds, options = {}) {
81
+ if (!(seconds > 0)) throw new RangeError(`cooldown() needs a positive number of seconds.`);
82
+ const scope = options.scope ?? "user";
83
+ const message = options.message ?? ((left) => `Try again in ${left}s.`);
84
+ const until = /* @__PURE__ */ new Map();
85
+ return async (ctx, next) => {
86
+ const i = ctx.interaction;
87
+ if (typeof i.respond === "function") return next();
88
+ const now = Date.now();
89
+ const key = `${ctx.route.id}\n${subject(i, scope)}`;
90
+ const expires = until.get(key);
91
+ if (expires !== void 0 && expires > now) {
92
+ const left = Math.ceil((expires - now) / 1e3);
93
+ return deny(ctx, typeof message === "string" ? message : message(left));
94
+ }
95
+ if (until.size >= SWEEP_AT) {
96
+ for (const [k, t] of until) if (t <= now) until.delete(k);
97
+ }
98
+ until.set(key, now + seconds * 1e3);
99
+ return next();
100
+ };
101
+ }
102
+ /** Expired entries are dropped once the map reaches this size, so it stays bounded. */
103
+ const SWEEP_AT = 1e3;
104
+ function subject(i, scope) {
105
+ if (scope === "global") return "";
106
+ if (scope === "guild" && typeof i.guildId === "string") return `g:${i.guildId}`;
107
+ return `u:${i.user?.id ?? ""}`;
108
+ }
75
109
  function inGuild(ctx) {
76
110
  const i = ctx.interaction;
77
111
  return typeof i.inGuild === "function" ? i.inGuild() : typeof i.guildId === "string";
@@ -100,6 +134,6 @@ async function deny(ctx, message) {
100
134
  });
101
135
  }
102
136
  //#endregion
103
- export { ConfigError, CustomIdTooLongError, MAX_CUSTOM_ID_LENGTH, PluginError, RegistrationError, UnsafeSyncError, customId, defineCommand, defineComponent, defineConfig, defineError, defineEvent, defineMiddleware, definePlugin, guildOnly, requirePermissions, requireRoles, requiredIntents, syncCommands, validateConfig, version };
137
+ export { ConfigError, CustomIdTooLongError, MAX_CUSTOM_ID_LENGTH, PluginError, RegistrationError, UnsafeSyncError, cooldown, customId, defineCommand, defineComponent, defineConfig, defineError, defineEvent, defineMiddleware, definePlugin, guildOnly, requirePermissions, requireRoles, requiredIntents, syncCommands, validateConfig, version };
104
138
 
105
139
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/define.ts","../src/policy.ts"],"sourcesContent":["import type {\n ButtonInteraction,\n ChannelSelectMenuInteraction,\n ChatInputCommandInteraction,\n ClientEvents,\n MentionableSelectMenuInteraction,\n MessageContextMenuCommandInteraction,\n ModalSubmitInteraction,\n RoleSelectMenuInteraction,\n StringSelectMenuInteraction,\n UserContextMenuCommandInteraction,\n UserSelectMenuInteraction,\n} from \"discord.js\";\nimport type { CommandType, OptionType } from \"./commands/meta.js\";\nimport type { SelectKind } from \"./components/compile.js\";\nimport type { ParamValidator } from \"./components/params.js\";\nimport { encodeComponentRoute } from \"./components/registry.js\";\nimport type { NectarRoutes } from \"./index.js\";\nimport type {\n ContextExtension,\n ErrorHandler,\n EventContext,\n Extended,\n InteractionContext,\n Middleware,\n Next,\n Params,\n} from \"./runtime/types.js\";\n\ntype Empty = Record<never, never>;\n\n/** `NectarRoutes[K]` when the generated types declare it, otherwise `never`. */\ntype Declared<K extends string> = NectarRoutes extends Record<K, infer V> ? V : never;\ntype Fallback<T, F> = [T] extends [never] ? F : T;\n\nexport type ComponentKindName = \"button\" | \"modal\" | `select:${SelectKind}`;\n\nexport interface ComponentRouteType {\n kind: ComponentKindName;\n params: Params;\n context: object;\n}\n\nexport interface CommandRouteType {\n type: CommandType;\n options: Record<string, OptionType>;\n context: object;\n}\n\n/** Component routes by path. Until types are generated, any string is accepted. */\nexport type ComponentRoutes = Fallback<\n Declared<\"components\">,\n Record<string, { kind: ComponentKindName; params: Params; context: Empty }>\n>;\n\n/** Command routes by path. Until types are generated, any string is accepted. */\nexport type CommandRoutes = Fallback<\n Declared<\"commands\">,\n Record<string, { type: CommandType; options: Record<string, OptionType>; context: Empty }>\n>;\n\nexport type ComponentPath = keyof ComponentRoutes & string;\nexport type CommandPath = keyof CommandRoutes & string;\n\ntype Route<Routes, P extends string> = P extends keyof Routes ? Routes[P] : never;\n\ntype ComponentInteraction<K> = K extends \"button\"\n ? ButtonInteraction\n : K extends \"modal\"\n ? ModalSubmitInteraction\n : K extends \"select:string\"\n ? StringSelectMenuInteraction\n : K extends \"select:user\"\n ? UserSelectMenuInteraction\n : K extends \"select:role\"\n ? RoleSelectMenuInteraction\n : K extends \"select:channel\"\n ? ChannelSelectMenuInteraction\n : K extends \"select:mentionable\"\n ? MentionableSelectMenuInteraction\n : never;\n\ntype CommandInteraction<T> = T extends \"chatInput\"\n ? ChatInputCommandInteraction\n : T extends \"user\"\n ? UserContextMenuCommandInteraction\n : T extends \"message\"\n ? MessageContextMenuCommandInteraction\n : never;\n\ntype Field<R, K extends string, F> = R extends Record<K, infer V> ? V : F;\n\nexport type ComponentParams<P extends ComponentPath> = Field<\n Route<ComponentRoutes, P>,\n \"params\",\n Params\n>;\n\nexport type ComponentContext<P extends ComponentPath> = InteractionContext<\n ComponentInteraction<Field<Route<ComponentRoutes, P>, \"kind\", ComponentKindName>>,\n ComponentParams<P>\n> &\n Field<Route<ComponentRoutes, P>, \"context\", Empty>;\n\nexport type CommandContext<P extends CommandPath> = InteractionContext<\n CommandInteraction<Field<Route<CommandRoutes, P>, \"type\", CommandType>>,\n Empty\n> &\n Field<Route<CommandRoutes, P>, \"context\", Empty>;\n\n/** Every field is optional when the route has no parameters, so `customId(\"confirm\")` works. */\ntype ParamsArg<P extends ComponentPath> =\n Empty extends ComponentParams<P> ? [params?: ComponentParams<P>] : [params: ComponentParams<P>];\n\n/**\n * The custom ID for a component route, ready for a discord.js builder.\n *\n * Typed against the generated route map: the path must exist and the parameters must match\n * the dynamic segments. Throws when a value is missing or the ID would exceed Discord's limit.\n */\nexport function customId<P extends ComponentPath>(route: P, ...args: ParamsArg<P>): string {\n return encodeComponentRoute(route, (args[0] ?? {}) as Params);\n}\n\n/** A handler that remembers which route it was written for, so the compiler can check the file location. */\nexport type Routed<F> = F & { route: string };\n\nexport function defineCommand<P extends CommandPath>(\n route: P,\n handler: (ctx: CommandContext<P>) => unknown,\n): Routed<typeof handler> {\n return routed(\"defineCommand\", route, handler);\n}\n\nexport interface ComponentOptions<P extends ComponentPath> {\n /**\n * Validators for the route's parameters, run on every incoming custom ID before middleware.\n * A parameter without one accepts any string. When a validator fails the interaction is\n * dropped and reported; the handler never sees it.\n */\n params?: { [K in keyof ComponentParams<P>]?: ParamValidator<ComponentParams<P>[K]> };\n}\n\nexport function defineComponent<P extends ComponentPath>(\n route: P,\n handler: (ctx: ComponentContext<P>) => unknown,\n options: ComponentOptions<P> = {},\n): Routed<typeof handler> {\n const defined = routed(\"defineComponent\", route, handler);\n if (options.params !== undefined) Object.assign(defined, { params: options.params });\n return defined;\n}\n\nexport function defineEvent<Name extends keyof ClientEvents>(\n event: Name,\n handler: (...args: [...ClientEvents[Name], EventContext]) => unknown,\n): Routed<typeof handler> {\n return routed(\"defineEvent\", event, handler);\n}\n\n/**\n * `return next({ member })` types `member` onto every downstream context. Middleware that\n * calls `next()` without returning it adds nothing; pass the extension type explicitly if\n * you need both.\n */\nexport function defineMiddleware<E extends ContextExtension = Empty>(\n middleware: (\n ctx: InteractionContext,\n next: Next,\n // biome-ignore lint/suspicious/noConfusingVoidType: handlers that only call next() return void\n ) => Promise<Extended<E> | undefined | void> | Extended<E> | undefined | void,\n): Middleware<E> {\n if (typeof middleware !== \"function\") {\n throw new TypeError(`defineMiddleware() expects a function, got ${typeof middleware}.`);\n }\n return middleware as Middleware<E>;\n}\n\nexport function defineError(handler: ErrorHandler): ErrorHandler {\n if (typeof handler !== \"function\") {\n throw new TypeError(`defineError() expects a function, got ${typeof handler}.`);\n }\n return handler;\n}\n\nfunction routed<F>(name: string, route: string, handler: F): Routed<F> {\n if (typeof route !== \"string\" || route === \"\") {\n throw new TypeError(`${name}() expects the route path as its first argument.`);\n }\n if (typeof handler !== \"function\") {\n throw new TypeError(`${name}(\"${route}\") expects a handler function, got ${typeof handler}.`);\n }\n return Object.assign(handler, { route });\n}\n","import type { PermissionResolvable } from \"discord.js\";\nimport { MessageFlags } from \"discord-api-types/v10\";\nimport type { InteractionContext, Middleware } from \"./runtime/types.js\";\n\n/**\n * Opt-in policy middleware. Each returns a middleware that stops the chain and answers the\n * user with a short ephemeral message when the check fails. Registration-time permissions\n * (`meta.defaultMemberPermissions`) are a separate concept: Discord enforces those before the\n * interaction reaches the bot, and server admins can override them. These checks run in the\n * bot and cannot be overridden.\n *\n * Use them from a `middleware.ts`:\n *\n * export default requirePermissions(\"BanMembers\");\n *\n * Or compose them with your own middleware by calling them inside it.\n */\n\nexport interface PolicyOptions {\n /** What the user sees when the check fails. */\n message?: string;\n}\n\n/** Passes only interactions that come from a guild. */\nexport function guildOnly(options: PolicyOptions = {}): Middleware {\n const message = options.message ?? \"This only works in a server.\";\n return async (ctx, next) => {\n if (!inGuild(ctx)) return deny(ctx, message);\n return next();\n };\n}\n\n/** Passes only when the invoking member has every listed permission in the current channel. */\nexport function requirePermissions(\n permissions: PermissionResolvable,\n options: PolicyOptions = {},\n): Middleware {\n const message = options.message ?? \"You do not have permission to do that.\";\n return async (ctx, next) => {\n if (!inGuild(ctx)) return deny(ctx, message);\n const held = (ctx.interaction as GuildInteractionLike).memberPermissions;\n if (held === null || held === undefined || !held.has(permissions)) return deny(ctx, message);\n return next();\n };\n}\n\nexport interface RoleOptions extends PolicyOptions {\n /** `\"any\"` (default) passes with one matching role; `\"all\"` needs every listed role. */\n mode?: \"any\" | \"all\";\n}\n\n/** Passes only when the invoking member holds the listed role IDs. */\nexport function requireRoles(\n roles: string | readonly string[],\n options: RoleOptions = {},\n): Middleware {\n const wanted = typeof roles === \"string\" ? [roles] : [...roles];\n const message = options.message ?? \"You do not have the role for that.\";\n const mode = options.mode ?? \"any\";\n return async (ctx, next) => {\n if (!inGuild(ctx)) return deny(ctx, message);\n const held = memberRoles(ctx.interaction as GuildInteractionLike);\n const ok = mode === \"all\" ? wanted.every((r) => held.has(r)) : wanted.some((r) => held.has(r));\n if (!ok) return deny(ctx, message);\n return next();\n };\n}\n\nfunction inGuild(ctx: InteractionContext): boolean {\n const i = ctx.interaction as GuildInteractionLike;\n return typeof i.inGuild === \"function\" ? i.inGuild() : typeof i.guildId === \"string\";\n}\n\n/**\n * Role IDs of the invoking member. discord.js gives a `GuildMember` with a role cache when the\n * guild is cached and a raw API member with an ID array otherwise.\n */\nfunction memberRoles(interaction: GuildInteractionLike): Set<string> {\n const roles = interaction.member?.roles;\n if (roles === undefined || roles === null) return new Set();\n if (Array.isArray(roles)) return new Set(roles);\n return new Set((roles as { cache: Map<string, unknown> }).cache.keys());\n}\n\n/** Answers the user, when the interaction can still be answered, and ends the chain. */\nasync function deny(ctx: InteractionContext, message: string): Promise<void> {\n const i = ctx.interaction as RepliableLike;\n if (typeof i.respond === \"function\") {\n // Autocomplete cannot show a message. An empty list is the only quiet answer.\n if (!i.responded) await i.respond([]);\n return;\n }\n if (typeof i.reply !== \"function\" || i.replied || i.deferred) return;\n await i.reply({ content: message, flags: MessageFlags.Ephemeral });\n}\n\ninterface GuildInteractionLike {\n inGuild?: () => boolean;\n guildId?: string | null;\n memberPermissions?: { has(permission: PermissionResolvable): boolean } | null;\n member?: { roles: readonly string[] | { cache: Map<string, unknown> } } | null;\n}\n\ninterface RepliableLike {\n replied?: boolean;\n deferred?: boolean;\n responded?: boolean;\n reply?: (options: { content: string; flags: MessageFlags }) => Promise<unknown>;\n respond?: (choices: never[]) => Promise<unknown>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/define.ts","../src/policy.ts"],"sourcesContent":["import type {\n Attachment,\n ButtonInteraction,\n ChannelSelectMenuInteraction,\n ChatInputCommandInteraction,\n ClientEvents,\n CommandInteractionOption,\n MentionableSelectMenuInteraction,\n MessageContextMenuCommandInteraction,\n ModalSubmitInteraction,\n RoleSelectMenuInteraction,\n StringSelectMenuInteraction,\n User,\n UserContextMenuCommandInteraction,\n UserSelectMenuInteraction,\n} from \"discord.js\";\nimport type { CommandType, OptionType } from \"./commands/meta.js\";\nimport type { SelectKind } from \"./components/compile.js\";\nimport type { ParamValidator } from \"./components/params.js\";\nimport { encodeComponentRoute } from \"./components/registry.js\";\nimport type { NectarRoutes } from \"./index.js\";\nimport type {\n ContextExtension,\n ErrorHandler,\n EventContext,\n Extended,\n InteractionContext,\n Middleware,\n Next,\n Params,\n} from \"./runtime/types.js\";\n\ntype Empty = Record<never, never>;\n\n/** `NectarRoutes[K]` when the generated types declare it, otherwise `never`. */\ntype Declared<K extends string> = NectarRoutes extends Record<K, infer V> ? V : never;\ntype Fallback<T, F> = [T] extends [never] ? F : T;\n\nexport type ComponentKindName = \"button\" | \"modal\" | `select:${SelectKind}`;\n\nexport interface ComponentRouteType {\n kind: ComponentKindName;\n params: Params;\n context: object;\n}\n\n/** One command option as the generated types describe it. */\nexport interface OptionSpecType {\n type: OptionType;\n required: boolean;\n}\n\nexport interface CommandRouteType {\n type: CommandType;\n options: Record<string, OptionSpecType>;\n context: object;\n}\n\n/** Component routes by path. Until types are generated, any string is accepted. */\nexport type ComponentRoutes = Fallback<\n Declared<\"components\">,\n Record<string, { kind: ComponentKindName; params: Params; context: Empty }>\n>;\n\n/** Command routes by path. Until types are generated, any string is accepted. */\nexport type CommandRoutes = Fallback<\n Declared<\"commands\">,\n Record<string, { type: CommandType; options: Record<string, OptionSpecType>; context: Empty }>\n>;\n\nexport type ComponentPath = keyof ComponentRoutes & string;\nexport type CommandPath = keyof CommandRoutes & string;\n\ntype Route<Routes, P extends string> = P extends keyof Routes ? Routes[P] : never;\n\ntype ComponentInteraction<K> = K extends \"button\"\n ? ButtonInteraction\n : K extends \"modal\"\n ? ModalSubmitInteraction\n : K extends \"select:string\"\n ? StringSelectMenuInteraction\n : K extends \"select:user\"\n ? UserSelectMenuInteraction\n : K extends \"select:role\"\n ? RoleSelectMenuInteraction\n : K extends \"select:channel\"\n ? ChannelSelectMenuInteraction\n : K extends \"select:mentionable\"\n ? MentionableSelectMenuInteraction\n : never;\n\ntype CommandInteraction<T> = T extends \"chatInput\"\n ? ChatInputCommandInteraction\n : T extends \"user\"\n ? UserContextMenuCommandInteraction\n : T extends \"message\"\n ? MessageContextMenuCommandInteraction\n : never;\n\ntype Field<R, K extends string, F> = R extends Record<K, infer V> ? V : F;\n\n/** What `ctx.options` holds for each option type, as discord.js resolves it. */\ntype OptionValue<T> = T extends \"string\"\n ? string\n : T extends \"integer\" | \"number\"\n ? number\n : T extends \"boolean\"\n ? boolean\n : T extends \"user\"\n ? User\n : T extends \"channel\"\n ? NonNullable<CommandInteractionOption[\"channel\"]>\n : T extends \"role\"\n ? NonNullable<CommandInteractionOption[\"role\"]>\n : T extends \"mentionable\"\n ? NonNullable<CommandInteractionOption[\"member\" | \"role\" | \"user\"]>\n : T extends \"attachment\"\n ? Attachment\n : never;\n\n/** `ctx.options` for a command route: required options as their value, the rest `| null`. */\nexport type OptionValues<P extends CommandPath> = {\n [K in keyof Field<Route<CommandRoutes, P>, \"options\", Empty>]: Field<\n Route<CommandRoutes, P>,\n \"options\",\n Empty\n >[K] extends { type: infer T; required: infer R }\n ? R extends true\n ? OptionValue<T>\n : OptionValue<T> | null\n : never;\n};\n\nexport type ComponentParams<P extends ComponentPath> = Field<\n Route<ComponentRoutes, P>,\n \"params\",\n Params\n>;\n\nexport type ComponentContext<P extends ComponentPath> = InteractionContext<\n ComponentInteraction<Field<Route<ComponentRoutes, P>, \"kind\", ComponentKindName>>,\n ComponentParams<P>,\n Empty\n> &\n Field<Route<ComponentRoutes, P>, \"context\", Empty>;\n\nexport type CommandContext<P extends CommandPath> = InteractionContext<\n CommandInteraction<Field<Route<CommandRoutes, P>, \"type\", CommandType>>,\n Empty,\n OptionValues<P>\n> &\n Field<Route<CommandRoutes, P>, \"context\", Empty>;\n\n/** Every field is optional when the route has no parameters, so `customId(\"confirm\")` works. */\ntype ParamsArg<P extends ComponentPath> =\n Empty extends ComponentParams<P> ? [params?: ComponentParams<P>] : [params: ComponentParams<P>];\n\n/**\n * The custom ID for a component route, ready for a discord.js builder.\n *\n * Typed against the generated route map: the path must exist and the parameters must match\n * the dynamic segments. Throws when a value is missing or the ID would exceed Discord's limit.\n */\nexport function customId<P extends ComponentPath>(route: P, ...args: ParamsArg<P>): string {\n return encodeComponentRoute(route, (args[0] ?? {}) as Params);\n}\n\n/** A handler that remembers which route it was written for, so the compiler can check the file location. */\nexport type Routed<F> = F & { route: string };\n\nexport function defineCommand<P extends CommandPath>(\n route: P,\n handler: (ctx: CommandContext<P>) => unknown,\n): Routed<typeof handler> {\n return routed(\"defineCommand\", route, handler);\n}\n\nexport interface ComponentOptions<P extends ComponentPath> {\n /**\n * Validators for the route's parameters, run on every incoming custom ID before middleware.\n * A parameter without one accepts any string. When a validator fails the interaction is\n * dropped and reported; the handler never sees it.\n */\n params?: { [K in keyof ComponentParams<P>]?: ParamValidator<ComponentParams<P>[K]> };\n}\n\nexport function defineComponent<P extends ComponentPath>(\n route: P,\n handler: (ctx: ComponentContext<P>) => unknown,\n options: ComponentOptions<P> = {},\n): Routed<typeof handler> {\n const defined = routed(\"defineComponent\", route, handler);\n if (options.params !== undefined) Object.assign(defined, { params: options.params });\n return defined;\n}\n\nexport function defineEvent<Name extends keyof ClientEvents>(\n event: Name,\n handler: (...args: [...ClientEvents[Name], EventContext]) => unknown,\n): Routed<typeof handler> {\n return routed(\"defineEvent\", event, handler);\n}\n\n/**\n * `return next({ member })` types `member` onto every downstream context. Middleware that\n * calls `next()` without returning it adds nothing; pass the extension type explicitly if\n * you need both.\n */\nexport function defineMiddleware<E extends ContextExtension = Empty>(\n middleware: (\n ctx: InteractionContext,\n next: Next,\n // biome-ignore lint/suspicious/noConfusingVoidType: handlers that only call next() return void\n ) => Promise<Extended<E> | undefined | void> | Extended<E> | undefined | void,\n): Middleware<E> {\n if (typeof middleware !== \"function\") {\n throw new TypeError(`defineMiddleware() expects a function, got ${typeof middleware}.`);\n }\n return middleware as Middleware<E>;\n}\n\nexport function defineError(handler: ErrorHandler): ErrorHandler {\n if (typeof handler !== \"function\") {\n throw new TypeError(`defineError() expects a function, got ${typeof handler}.`);\n }\n return handler;\n}\n\nfunction routed<F>(name: string, route: string, handler: F): Routed<F> {\n if (typeof route !== \"string\" || route === \"\") {\n throw new TypeError(`${name}() expects the route path as its first argument.`);\n }\n if (typeof handler !== \"function\") {\n throw new TypeError(`${name}(\"${route}\") expects a handler function, got ${typeof handler}.`);\n }\n return Object.assign(handler, { route });\n}\n","import type { PermissionResolvable } from \"discord.js\";\nimport { MessageFlags } from \"discord-api-types/v10\";\nimport type { InteractionContext, Middleware } from \"./runtime/types.js\";\n\n/**\n * Opt-in policy middleware. Each returns a middleware that stops the chain and answers the\n * user with a short ephemeral message when the check fails. Registration-time permissions\n * (`meta.defaultMemberPermissions`) are a separate concept: Discord enforces those before the\n * interaction reaches the bot, and server admins can override them. These checks run in the\n * bot and cannot be overridden.\n *\n * Use them from a `middleware.ts`:\n *\n * export default requirePermissions(\"BanMembers\");\n *\n * Or compose them with your own middleware by calling them inside it.\n */\n\nexport interface PolicyOptions {\n /** What the user sees when the check fails. */\n message?: string;\n}\n\n/** Passes only interactions that come from a guild. */\nexport function guildOnly(options: PolicyOptions = {}): Middleware {\n const message = options.message ?? \"This only works in a server.\";\n return async (ctx, next) => {\n if (!inGuild(ctx)) return deny(ctx, message);\n return next();\n };\n}\n\n/** Passes only when the invoking member has every listed permission in the current channel. */\nexport function requirePermissions(\n permissions: PermissionResolvable,\n options: PolicyOptions = {},\n): Middleware {\n const message = options.message ?? \"You do not have permission to do that.\";\n return async (ctx, next) => {\n if (!inGuild(ctx)) return deny(ctx, message);\n const held = (ctx.interaction as GuildInteractionLike).memberPermissions;\n if (held === null || held === undefined || !held.has(permissions)) return deny(ctx, message);\n return next();\n };\n}\n\nexport interface RoleOptions extends PolicyOptions {\n /** `\"any\"` (default) passes with one matching role; `\"all\"` needs every listed role. */\n mode?: \"any\" | \"all\";\n}\n\n/** Passes only when the invoking member holds the listed role IDs. */\nexport function requireRoles(\n roles: string | readonly string[],\n options: RoleOptions = {},\n): Middleware {\n const wanted = typeof roles === \"string\" ? [roles] : [...roles];\n const message = options.message ?? \"You do not have the role for that.\";\n const mode = options.mode ?? \"any\";\n return async (ctx, next) => {\n if (!inGuild(ctx)) return deny(ctx, message);\n const held = memberRoles(ctx.interaction as GuildInteractionLike);\n const ok = mode === \"all\" ? wanted.every((r) => held.has(r)) : wanted.some((r) => held.has(r));\n if (!ok) return deny(ctx, message);\n return next();\n };\n}\n\nexport interface CooldownOptions {\n /**\n * Who shares a cooldown: each user (default), everyone in a guild, or everyone. In a DM,\n * `\"guild\"` falls back to the user.\n */\n scope?: \"user\" | \"guild\" | \"global\";\n /** What the user sees while waiting, given the seconds left. */\n message?: string | ((seconds: number) => string);\n}\n\n/**\n * Passes once per `seconds` for each route and subject. The cooldown starts when the\n * interaction passes, so a handler that throws still counts. Autocomplete is never held back.\n * Cooldowns live in memory, per process, and reset on restart.\n */\nexport function cooldown(seconds: number, options: CooldownOptions = {}): Middleware {\n if (!(seconds > 0)) throw new RangeError(`cooldown() needs a positive number of seconds.`);\n const scope = options.scope ?? \"user\";\n const message = options.message ?? ((left: number) => `Try again in ${left}s.`);\n const until = new Map<string, number>();\n return async (ctx, next) => {\n const i = ctx.interaction as SubjectLike;\n if (typeof i.respond === \"function\") return next();\n const now = Date.now();\n const key = `${ctx.route.id}\\n${subject(i, scope)}`;\n const expires = until.get(key);\n if (expires !== undefined && expires > now) {\n const left = Math.ceil((expires - now) / 1000);\n return deny(ctx, typeof message === \"string\" ? message : message(left));\n }\n if (until.size >= SWEEP_AT) {\n for (const [k, t] of until) if (t <= now) until.delete(k);\n }\n until.set(key, now + seconds * 1000);\n return next();\n };\n}\n\n/** Expired entries are dropped once the map reaches this size, so it stays bounded. */\nconst SWEEP_AT = 1000;\n\nfunction subject(i: SubjectLike, scope: NonNullable<CooldownOptions[\"scope\"]>): string {\n if (scope === \"global\") return \"\";\n if (scope === \"guild\" && typeof i.guildId === \"string\") return `g:${i.guildId}`;\n return `u:${i.user?.id ?? \"\"}`;\n}\n\ninterface SubjectLike {\n guildId?: string | null;\n user?: { id: string } | null;\n respond?: unknown;\n}\n\nfunction inGuild(ctx: InteractionContext): boolean {\n const i = ctx.interaction as GuildInteractionLike;\n return typeof i.inGuild === \"function\" ? i.inGuild() : typeof i.guildId === \"string\";\n}\n\n/**\n * Role IDs of the invoking member. discord.js gives a `GuildMember` with a role cache when the\n * guild is cached and a raw API member with an ID array otherwise.\n */\nfunction memberRoles(interaction: GuildInteractionLike): Set<string> {\n const roles = interaction.member?.roles;\n if (roles === undefined || roles === null) return new Set();\n if (Array.isArray(roles)) return new Set(roles);\n return new Set((roles as { cache: Map<string, unknown> }).cache.keys());\n}\n\n/** Answers the user, when the interaction can still be answered, and ends the chain. */\nasync function deny(ctx: InteractionContext, message: string): Promise<void> {\n const i = ctx.interaction as RepliableLike;\n if (typeof i.respond === \"function\") {\n // Autocomplete cannot show a message. An empty list is the only quiet answer.\n if (!i.responded) await i.respond([]);\n return;\n }\n if (typeof i.reply !== \"function\" || i.replied || i.deferred) return;\n await i.reply({ content: message, flags: MessageFlags.Ephemeral });\n}\n\ninterface GuildInteractionLike {\n inGuild?: () => boolean;\n guildId?: string | null;\n memberPermissions?: { has(permission: PermissionResolvable): boolean } | null;\n member?: { roles: readonly string[] | { cache: Map<string, unknown> } } | null;\n}\n\ninterface RepliableLike {\n replied?: boolean;\n deferred?: boolean;\n responded?: boolean;\n reply?: (options: { content: string; flags: MessageFlags }) => Promise<unknown>;\n respond?: (choices: never[]) => Promise<unknown>;\n}\n"],"mappings":"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"}
@@ -558,9 +558,10 @@ function toManifest(graph, outDir) {
558
558
  };
559
559
  };
560
560
  const routes = [];
561
- for (const command of graph.commands) for (const route of Object.values(command.handlers)) routes.push({
561
+ for (const command of graph.commands) for (const [key, route] of Object.entries(command.handlers)) routes.push({
562
562
  ...base(route),
563
- kind: "command"
563
+ kind: "command",
564
+ defer: command.defer[key] ?? null
564
565
  });
565
566
  for (const entry of graph.autocomplete) routes.push({
566
567
  ...base(entry.route),
@@ -735,4 +736,4 @@ var PluginError = class extends Error {
735
736
  //#endregion
736
737
  export { typeOf as C, version as D, decodeCustomId as E, docsUrl as S, MAX_CUSTOM_ID_LENGTH as T, parseSegment as _, MANIFEST_FILE as a, loadModule as b, writeManifest as c, checkHandler as d, compileComponents as f, formatSegment as g, paramValidatorsOf as h, pluginGraph as i, encodeComponentRoute as l, findInvalidParam as m, definePlugin as n, stableStringify as o, customIdFor as p, applyPlugins as r, toManifest as s, PluginError as t, registerComponentRoutes as u, enableModuleReloading as v, CustomIdTooLongError as w, Diagnostics as x, invalidateModuleGraph as y };
737
738
 
738
- //# sourceMappingURL=plugins-BCYwtuo-.js.map
739
+ //# sourceMappingURL=plugins-C2uwN3-D.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugins-BCYwtuo-.js","names":["describe"],"sources":["../src/version.ts","../src/components/customId.ts","../src/compiler/diagnostics.ts","../src/compiler/load.ts","../src/compiler/segments.ts","../src/components/params.ts","../src/components/compile.ts","../src/components/registry.ts","../src/manifest/emit.ts","../src/plugins/transform.ts","../src/plugins/index.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\n/** From package.json, which sits one level up from both `src/` and `dist/`. */\nexport const { version } = createRequire(import.meta.url)(\"../package.json\") as {\n version: string;\n};\n","/** Discord rejects custom IDs longer than this. */\nexport const MAX_CUSTOM_ID_LENGTH = 100;\n\nconst PREFIX = \"n:\";\nconst SHORT_ID_LENGTH = 6;\n\n/** Characters a route with no parameters uses: the prefix and the short ID. */\nexport const BASE_OVERHEAD = PREFIX.length + SHORT_ID_LENGTH;\n\nexport class CustomIdTooLongError extends Error {\n constructor(\n readonly customId: string,\n readonly routeId: string,\n ) {\n super(\n `Custom ID for ${routeId} is ${customId.length} characters, Discord allows ${MAX_CUSTOM_ID_LENGTH}. Encode a shorter identifier instead of the full value.`,\n );\n this.name = \"CustomIdTooLongError\";\n }\n}\n\n/**\n * Builds `n:<shortId>:<v1>:<v2>...`. Values are escaped so they may contain `:` and `\\`.\n * Throws when the result is longer than Discord allows; it never truncates.\n */\nexport function encodeCustomId(shortId: string, values: readonly string[], routeId = shortId) {\n let out = PREFIX + shortId;\n for (const value of values) out += `:${escapeValue(value)}`;\n if (out.length > MAX_CUSTOM_ID_LENGTH) throw new CustomIdTooLongError(out, routeId);\n return out;\n}\n\nexport type DecodedCustomId =\n | { ok: true; shortId: string; values: string[] }\n | { ok: false; reason: \"not-nectar\" | \"malformed\" };\n\n/**\n * Splits a raw custom ID back into its short ID and positional values.\n * IDs without the Nectar prefix are reported as `not-nectar` so hand-built components pass through.\n */\nexport function decodeCustomId(raw: string): DecodedCustomId {\n if (!raw.startsWith(PREFIX)) return { ok: false, reason: \"not-nectar\" };\n\n const shortId = raw.slice(PREFIX.length, PREFIX.length + SHORT_ID_LENGTH);\n if (!/^[0-9a-z]{6}$/.test(shortId)) return { ok: false, reason: \"malformed\" };\n\n const values: string[] = [];\n let index = PREFIX.length + SHORT_ID_LENGTH;\n if (index === raw.length) return { ok: true, shortId, values };\n if (raw[index] !== \":\") return { ok: false, reason: \"malformed\" };\n index++;\n\n let current = \"\";\n while (index < raw.length) {\n const char = raw[index] as string;\n if (char === \"\\\\\") {\n const next = raw[index + 1];\n if (next !== \"\\\\\" && next !== \":\") return { ok: false, reason: \"malformed\" };\n current += next;\n index += 2;\n continue;\n }\n if (char === \":\") {\n values.push(current);\n current = \"\";\n index++;\n continue;\n }\n current += char;\n index++;\n }\n values.push(current);\n return { ok: true, shortId, values };\n}\n\nfunction escapeValue(value: string): string {\n return value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\":\", \"\\\\:\");\n}\n","export type Severity = \"error\" | \"warning\";\n\nexport interface Diagnostic {\n code: string;\n severity: Severity;\n message: string;\n /** Absolute path of the file or directory that caused the diagnostic. */\n file?: string;\n /** Canonical route identity, when the diagnostic is about a specific route. */\n route?: string;\n}\n\n/** Every code the compiler reports. Each has an entry in the diagnostics reference. */\nexport const DIAGNOSTIC_CODES = [\n \"file-outside-category\",\n \"unknown-category\",\n \"file-in-wrong-category\",\n \"invalid-segment\",\n \"route-without-path\",\n \"dynamic-segment-not-allowed\",\n \"duplicate-param\",\n \"catch-all-not-last\",\n \"duplicate-route\",\n \"module-load-failed\",\n \"missing-handler\",\n \"route-mismatch\",\n \"missing-meta\",\n \"invalid-meta\",\n \"invalid-name\",\n \"invalid-description\",\n \"invalid-option\",\n \"missing-route-meta\",\n \"route-meta-without-path\",\n \"unused-route-meta\",\n \"mixed-command-and-subcommands\",\n \"mixed-subcommand-and-group\",\n \"command-too-deep\",\n \"too-many-subcommands\",\n \"context-menu-nested\",\n \"top-level-field-on-group\",\n \"top-level-field-on-subcommand\",\n \"duplicate-command-name\",\n \"too-many-commands\",\n \"autocomplete-without-command\",\n \"autocomplete-export-not-function\",\n \"autocomplete-unknown-option\",\n \"autocomplete-missing-handler\",\n \"autocomplete-missing-file\",\n \"missing-select-kind\",\n \"invalid-select-kind\",\n \"invalid-param-validator\",\n \"catch-all-route\",\n \"short-id-collision\",\n \"duplicate-component-pattern\",\n \"unknown-event\",\n \"event-nested-path\",\n \"event-mode-conflict\",\n \"missing-intent\",\n \"plugin-failed\",\n \"plugin-invalid-change\",\n \"plugin-unknown-route\",\n \"plugin-missing-file\",\n] as const;\n\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[number];\n\nconst REFERENCE = \"https://nectar-js.github.io/nectar/reference/diagnostics\";\n\n/** The reference entry for a compiler code. Codes from plugins have none. */\nexport function docsUrl(code: string): string | undefined {\n return (DIAGNOSTIC_CODES as readonly string[]).includes(code)\n ? `${REFERENCE}#${code}`\n : undefined;\n}\n\n/** A value's type as a message puts it: `missing`, `a number`, `an array`. */\nexport function typeOf(value: unknown): string {\n if (value === undefined) return \"missing\";\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n return typeof value === \"object\" ? \"an object\" : `a ${typeof value}`;\n}\n\ninterface DiagnosticLocation {\n file?: string;\n route?: string;\n}\n\nexport class Diagnostics {\n readonly items: Diagnostic[] = [];\n\n error(code: DiagnosticCode, message: string, location: DiagnosticLocation = {}): void {\n this.push(\"error\", code, message, location);\n }\n\n warn(code: DiagnosticCode, message: string, location: DiagnosticLocation = {}): void {\n this.push(\"warning\", code, message, location);\n }\n\n get hasErrors(): boolean {\n return this.items.some((d) => d.severity === \"error\");\n }\n\n private push(\n severity: Severity,\n code: string,\n message: string,\n location: DiagnosticLocation,\n ): void {\n const item: Diagnostic = { code, severity, message };\n if (location.file !== undefined) item.file = location.file;\n if (location.route !== undefined) item.route = location.route;\n this.items.push(item);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport { registerHooks } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\n/**\n * Imports an application module by absolute path.\n *\n * Relies on Node's native TypeScript type stripping (unflagged since 22.18), so handler\n * files must use erasable syntax only: no enums, namespaces, or parameter properties.\n *\n * With reloading enabled (see `enableModuleReloading`) the URL carries a version query, so a\n * changed file evaluates again on the next import instead of coming back from the ESM cache.\n */\nexport async function loadModule(file: string): Promise<Record<string, unknown>> {\n const url = pathToFileURL(file).href;\n return (await import(reloading === null ? url : versioned(url))) as Record<string, unknown>;\n}\n\ninterface Reloading {\n /** Project root; only files under it (outside `node_modules`) are versioned. */\n root: string;\n /** Bumped by `invalidateModuleGraph` so every project module evaluates again. */\n generation: number;\n}\n\nlet reloading: Reloading | null = null;\n\n/**\n * Turns on cache busting for project files. Used by `nectar dev` only.\n *\n * Every import of a file under `root` gets `?nectar=<content hash>-<generation>` appended, the\n * direct ones here and the transitive ones through a resolve hook. A handler whose content\n * changed therefore gets a new URL and a fresh evaluation; its unchanged imports keep their\n * URL and are shared. Old instances stay in the ESM cache until the process exits.\n */\nexport function enableModuleReloading(root: string): void {\n if (reloading !== null) return;\n reloading = { root: path.resolve(root), generation: 0 };\n registerHooks({\n resolve(specifier, context, next) {\n const result = next(specifier, context);\n return { ...result, url: versioned(result.url) };\n },\n });\n}\n\n/**\n * Makes every project module evaluate again on its next import. For changes to files the\n * compiler does not track (helpers a handler imports), since nothing knows who imports them.\n */\nexport function invalidateModuleGraph(): void {\n if (reloading !== null) reloading.generation += 1;\n}\n\nfunction versioned(url: string): string {\n if (reloading === null || !url.startsWith(\"file:\") || url.includes(\"?\") || url.includes(\"#\")) {\n return url;\n }\n const file = fileURLToPath(url);\n const inside = !path.relative(reloading.root, file).startsWith(\"..\");\n if (!inside || file.split(path.sep).includes(\"node_modules\")) return url;\n let hash: string;\n try {\n hash = createHash(\"sha1\").update(readFileSync(file)).digest(\"base64url\").slice(0, 10);\n } catch {\n return url;\n }\n return `${url}?nectar=${hash}-${reloading.generation}`;\n}\n","export type Segment =\n | { type: \"static\"; name: string }\n | { type: \"dynamic\"; name: string }\n | { type: \"catchAll\"; name: string }\n | { type: \"group\"; name: string };\n\nexport type SegmentParseResult = { ok: true; segment: Segment } | { ok: false; reason: string };\n\nconst STATIC_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;\nconst PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Parses one directory name into a route segment.\n *\n * `name` static\n * `[name]` dynamic\n * `[...name]` catch-all\n * `(name)` group, organizational only\n */\nexport function parseSegment(dirName: string): SegmentParseResult {\n if (dirName.startsWith(\"[\") || dirName.endsWith(\"]\")) {\n if (!dirName.startsWith(\"[\") || !dirName.endsWith(\"]\")) {\n return fail(`\"${dirName}\" has an unmatched bracket. Parameters look like [name].`);\n }\n const inner = dirName.slice(1, -1);\n const isCatchAll = inner.startsWith(\"...\");\n const name = isCatchAll ? inner.slice(3) : inner;\n if (!PARAM_NAME.test(name)) {\n return fail(\n `\"${dirName}\" has an invalid parameter name. Parameters become keys of ctx.params, so use letters, digits, and underscores, and don't start with a digit.`,\n );\n }\n return ok({ type: isCatchAll ? \"catchAll\" : \"dynamic\", name });\n }\n\n if (dirName.startsWith(\"(\") || dirName.endsWith(\")\")) {\n if (!dirName.startsWith(\"(\") || !dirName.endsWith(\")\")) {\n return fail(`\"${dirName}\" has an unmatched parenthesis. Route groups look like (name).`);\n }\n const name = dirName.slice(1, -1);\n if (!STATIC_NAME.test(name)) {\n return fail(\n `\"${dirName}\" isn't a valid group name. Use letters, digits, hyphens, and underscores.`,\n );\n }\n return ok({ type: \"group\", name });\n }\n\n if (!STATIC_NAME.test(dirName)) {\n return fail(\n `\"${dirName}\" can't be part of a route path. Use letters, digits, hyphens, and underscores, and start with a letter or digit.`,\n );\n }\n return ok({ type: \"static\", name: dirName });\n}\n\n/** Renders a segment back into its directory form. Groups render as their directory name. */\nexport function formatSegment(segment: Segment): string {\n switch (segment.type) {\n case \"static\":\n return segment.name;\n case \"dynamic\":\n return `[${segment.name}]`;\n case \"catchAll\":\n return `[...${segment.name}]`;\n case \"group\":\n return `(${segment.name})`;\n }\n}\n\nfunction ok(segment: Segment): SegmentParseResult {\n return { ok: true, segment };\n}\n\nfunction fail(reason: string): SegmentParseResult {\n return { ok: false, reason };\n}\n","import { typeOf } from \"../compiler/diagnostics.js\";\n\n/**\n * A Standard Schema (https://standardschema.dev) validator, which zod, valibot, and arktype\n * all produce. Only the result's `issues` are looked at: a schema that transforms the value\n * does not change what the handler receives.\n */\nexport interface StandardSchemaLike<V = unknown> {\n \"~standard\": {\n validate(\n value: V,\n ):\n | { issues?: ReadonlyArray<unknown> | undefined }\n | Promise<{ issues?: ReadonlyArray<unknown> | undefined }>;\n };\n}\n\n/**\n * Checks one custom ID parameter. A function passes by returning anything but `false` and\n * fails by returning `false` or throwing. A schema fails by reporting issues.\n */\nexport type ParamValidator<V = string | string[]> = ((value: V) => unknown) | StandardSchemaLike<V>;\n\nexport type ParamValidators = Record<string, ParamValidator>;\n\n/**\n * Reads the validators `defineComponent` attached to a handler and checks them against the\n * route's parameters. Throws with a developer-facing message when the shape is wrong; the\n * compiler reports it as a diagnostic and the runtime as a load error.\n */\nexport function paramValidatorsOf(\n handler: unknown,\n route: { params: readonly string[] },\n): ParamValidators {\n const declared = (handler as { params?: unknown }).params;\n if (declared === undefined) return {};\n if (typeof declared !== \"object\" || declared === null || Array.isArray(declared)) {\n throw new Error(`params is ${typeOf(declared)}, not an object of validators.`);\n }\n const validators: ParamValidators = {};\n for (const [name, validator] of Object.entries(declared)) {\n if (!route.params.includes(name)) {\n throw new Error(\n `params validates \"${name}\", which is not a parameter of this route. ${\n route.params.length === 0 ? \"It has none.\" : `It has: ${route.params.join(\", \")}.`\n }`,\n );\n }\n if (!isValidator(validator)) {\n throw new Error(\n `params.${name} is ${typeOf(validator)}. A validator is a function or a Standard Schema.`,\n );\n }\n validators[name] = validator;\n }\n return validators;\n}\n\nfunction isValidator(value: unknown): value is ParamValidator {\n if (typeof value === \"function\") return true;\n if (typeof value !== \"object\" || value === null) return false;\n const standard = (value as Record<string, unknown>)[\"~standard\"];\n return (\n typeof standard === \"object\" &&\n standard !== null &&\n typeof (standard as Record<string, unknown>).validate === \"function\"\n );\n}\n\n/**\n * Runs every validator against the decoded parameters. Resolves to the first parameter that\n * failed, or `null` when all passed. A validator that throws counts as a failure; the\n * caller decides what to log, so the value never leaves this function.\n */\nexport async function findInvalidParam(\n validators: ParamValidators,\n params: Record<string, string | string[]>,\n): Promise<string | null> {\n for (const [name, validator] of Object.entries(validators)) {\n const value = params[name];\n if (value === undefined) return name;\n try {\n if (typeof validator === \"function\") {\n if ((await validator(value)) === false) return name;\n continue;\n }\n const result = await validator[\"~standard\"].validate(value);\n if (result.issues !== undefined && result.issues.length > 0) return name;\n } catch {\n return name;\n }\n }\n return null;\n}\n","import path from \"node:path\";\nimport { Diagnostics, typeOf } from \"../compiler/diagnostics.js\";\nimport { loadModule } from \"../compiler/load.js\";\nimport type { Route, RouteTable } from \"../compiler/routes.js\";\nimport { formatSegment } from \"../compiler/segments.js\";\nimport { BASE_OVERHEAD, encodeCustomId, MAX_CUSTOM_ID_LENGTH } from \"./customId.js\";\nimport { paramValidatorsOf } from \"./params.js\";\n\nexport type ComponentKind = \"button\" | \"select\" | \"modal\";\n\nexport type SelectKind = \"string\" | \"user\" | \"role\" | \"channel\" | \"mentionable\";\n\nconst SELECT_KINDS: ReadonlySet<string> = new Set([\n \"string\",\n \"user\",\n \"role\",\n \"channel\",\n \"mentionable\",\n]);\n\nexport interface ComponentRoute extends Route {\n category: \"component\";\n kind: ComponentKind;\n /** The `kind` export of a `select.ts`. `null` for buttons and modals. */\n selectKind: SelectKind | null;\n /** Name of the trailing catch-all parameter, if the route has one. */\n catchAll: string | null;\n /** Characters of the encoded custom ID taken by the prefix, short ID, and separators. */\n overhead: number;\n}\n\nexport interface CompiledComponents {\n routes: ComponentRoute[];\n diagnostics: Diagnostics;\n}\n\n/** Values for one route's parameters. A catch-all parameter takes an array. */\nexport type ComponentParams = Record<string, string | readonly string[]>;\n\n/** Validates the component routes of a route table and resolves their select kinds. */\nexport async function compileComponents(table: RouteTable): Promise<CompiledComponents> {\n const diagnostics = new Diagnostics();\n const candidates = table.routes.filter((r) => r.category === \"component\");\n\n // Each route reports into its own list, so diagnostics come out in route order rather than\n // in whatever order the imports finish.\n const results = await Promise.all(\n candidates.map(async (route) => {\n const own = new Diagnostics();\n return { route: await compileRoute(route, own), diagnostics: own };\n }),\n );\n const routes: ComponentRoute[] = [];\n for (const result of results) {\n diagnostics.items.push(...result.diagnostics.items);\n if (result.route !== null) routes.push(result.route);\n }\n\n detectShortIdCollisions(routes, diagnostics);\n detectDuplicatePatterns(routes, diagnostics);\n\n return { routes, diagnostics };\n}\n\n/** What the encoder needs from a route. Compiled, manifest, and registered routes all satisfy it. */\nexport interface EncodableRoute {\n id: string;\n shortId: string;\n params: string[];\n catchAll: string | null;\n}\n\n/**\n * Encodes a custom ID for a compiled route. Throws when a parameter is missing, a value is not\n * a string, or the result exceeds Discord's limit.\n */\nexport function customIdFor(route: EncodableRoute, params: ComponentParams = {}): string {\n const values: string[] = [];\n for (const name of route.params) {\n const value = params[name];\n if (name === route.catchAll) {\n if (value === undefined) continue;\n if (typeof value === \"string\") {\n values.push(value);\n continue;\n }\n values.push(...value);\n continue;\n }\n if (typeof value !== \"string\") {\n throw new TypeError(\n `Route ${route.id} needs a string for parameter \"${name}\", got ${describe(value)}.`,\n );\n }\n values.push(value);\n }\n for (const name of Object.keys(params)) {\n if (!route.params.includes(name)) {\n throw new TypeError(\n `Route ${route.id} has no parameter \"${name}\". ${route.params.length === 0 ? \"It takes none.\" : `It takes: ${route.params.join(\", \")}.`}`,\n );\n }\n }\n return encodeCustomId(route.shortId, values, route.id);\n}\n\nasync function compileRoute(\n route: Route,\n diagnostics: Diagnostics,\n): Promise<ComponentRoute | null> {\n const kind = route.kind as ComponentKind;\n const last = route.segments.at(-1);\n const catchAll = last?.type === \"catchAll\" ? last.name : null;\n const overhead = BASE_OVERHEAD + route.params.length;\n\n if (catchAll !== null) {\n diagnostics.warn(\n \"catch-all-route\",\n `${formatSegment(last as NonNullable<typeof last>)} takes any number of values. They all count toward Discord's ${MAX_CUSTOM_ID_LENGTH} character limit on custom IDs, and customId() throws when an ID goes over.`,\n { file: route.file, route: route.id },\n );\n }\n\n let module: Record<string, unknown>;\n try {\n module = await loadModule(route.file);\n } catch (error) {\n diagnostics.error(\n \"module-load-failed\",\n `The compiler imports every route file to read its exports, and this one threw: ${error instanceof Error ? error.message : String(error)}`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n\n if (!checkHandler(module, route, diagnostics)) return null;\n try {\n paramValidatorsOf(module.default, route);\n } catch (error) {\n diagnostics.error(\n \"invalid-param-validator\",\n `${error instanceof Error ? error.message : String(error)} Validators go in defineComponent's third argument, like { params: { id: (value) => ... } }.`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n\n let selectKind: SelectKind | null = null;\n if (kind === \"select\") {\n selectKind = validateSelectKind(module, route, diagnostics);\n if (selectKind === null) return null;\n }\n\n return { ...route, category: \"component\", kind, selectKind, catchAll, overhead };\n}\n\n/**\n * The default export is the handler Nectar calls, and one made with `defineComponent(path, ...)`\n * or the like must name the route its file sits in.\n */\nexport function checkHandler(\n module: Record<string, unknown>,\n route: Route,\n diagnostics: Diagnostics,\n expected = route.path,\n): boolean {\n const handler = module.default;\n if (typeof handler !== \"function\") {\n const define =\n route.kind === \"command\"\n ? \"defineCommand\"\n : route.kind === \"event\"\n ? \"defineEvent\"\n : \"defineComponent\";\n diagnostics.error(\n \"missing-handler\",\n `This ${path.basename(route.file)} ${handler === undefined ? \"has no default export\" : `exports ${typeOf(handler)} as its default`}. Nectar calls the default export when the route runs, so export the handler, like export default ${define}(\"${expected}\", handler).`,\n { file: route.file, route: route.id },\n );\n return false;\n }\n const declared = (handler as { route?: unknown }).route;\n if (declared === undefined || declared === expected) return true;\n diagnostics.error(\n \"route-mismatch\",\n `This file's route is \"${expected}\", but its handler says \"${String(declared)}\". The string types the handler, so it has to match where the file is. Change it to \"${expected}\", or move the file.`,\n { file: route.file, route: route.id },\n );\n return false;\n}\n\nfunction validateSelectKind(\n module: Record<string, unknown>,\n route: Route,\n diagnostics: Diagnostics,\n): SelectKind | null {\n const kind = module.kind;\n if (kind === undefined) {\n diagnostics.error(\n \"missing-select-kind\",\n 'This select.ts doesn\\'t export kind, which says what the select menu picks from: \"string\", \"user\", \"role\", \"channel\", or \"mentionable\". Add one, like export const kind = \"string\".',\n { file: route.file, route: route.id },\n );\n return null;\n }\n if (typeof kind !== \"string\" || !SELECT_KINDS.has(kind)) {\n diagnostics.error(\n \"invalid-select-kind\",\n `kind is ${describe(kind)}. Use \"string\", \"user\", \"role\", \"channel\", or \"mentionable\".`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n return kind as SelectKind;\n}\n\nfunction detectShortIdCollisions(routes: ComponentRoute[], diagnostics: Diagnostics): void {\n const seen = new Map<string, ComponentRoute>();\n for (const route of routes) {\n const existing = seen.get(route.shortId);\n if (existing === undefined || existing.id === route.id) {\n seen.set(route.shortId, route);\n continue;\n }\n diagnostics.error(\n \"short-id-collision\",\n `${route.id} and ${existing.id} hash to the same short ID, \"${route.shortId}\", so Nectar can't tell their custom IDs apart. Rename a directory in one of them.`,\n { file: route.file, route: route.id },\n );\n }\n}\n\n/**\n * Two routes of the same kind whose paths differ only in parameter names, like\n * `tickets/[id]/close` and `tickets/[ticketId]/close`. Their hashes differ, but they take the\n * same values in the same places, so they are one route split in two.\n */\nfunction detectDuplicatePatterns(routes: ComponentRoute[], diagnostics: Diagnostics): void {\n const seen = new Map<string, ComponentRoute>();\n for (const route of routes) {\n const shape = route.segments\n .filter((s) => s.type !== \"group\")\n .map((s) => (s.type === \"static\" ? s.name : s.type === \"dynamic\" ? \"[]\" : \"[...]\"))\n .join(\"/\");\n const key = `${route.kind}#${shape}`;\n const existing = seen.get(key);\n if (existing === undefined) {\n seen.set(key, route);\n continue;\n }\n if (existing.id === route.id) continue;\n diagnostics.error(\n \"duplicate-component-pattern\",\n `${route.id} is the same path as ${existing.id} in ${relative(existing.file)}, with a different parameter name. Parameter names don't make routes distinct. Merge the two and keep one name.`,\n { file: route.file, route: route.id },\n );\n }\n}\n\nfunction describe(value: unknown): string {\n return typeof value === \"string\" ? JSON.stringify(value) : typeof value;\n}\n\nfunction relative(file: string): string {\n return path.relative(process.cwd(), file).split(path.sep).join(\"/\");\n}\n","import { type ComponentParams, customIdFor, type EncodableRoute } from \"./compile.js\";\n\nexport interface RegisteredComponentRoute extends EncodableRoute {\n path: string;\n}\n\n/**\n * Component routes the running app knows about, keyed by path. The runtime fills this from\n * the manifest before any handler runs, so `customId()` never needs the manifest itself.\n */\nconst routes = new Map<string, RegisteredComponentRoute>();\n\nexport function registerComponentRoutes(list: Iterable<RegisteredComponentRoute>): void {\n routes.clear();\n for (const route of list) routes.set(route.path, route);\n}\n\nexport function encodeComponentRoute(path: string, params: ComponentParams): string {\n const route = routes.get(path);\n if (route === undefined) {\n throw new Error(\n routes.size === 0\n ? `customId(\"${path}\") was called before the runtime registered any routes. Call it from a handler, or from code that runs after start().`\n : `No component route \"${path}\". Check the directory name under components/.`,\n );\n }\n return customIdFor(route, params);\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { RouteGraph } from \"../compiler/graph.js\";\nimport type { Route } from \"../compiler/routes.js\";\nimport { version } from \"../version.js\";\nimport { MANIFEST_VERSION, type Manifest, type ManifestRoute } from \"./schema.js\";\n\nexport const MANIFEST_FILE = \"manifest.json\";\n\n/** Serializes a route graph. `outDir` is where the manifest will live; paths are made relative to it. */\nexport function toManifest(graph: RouteGraph, outDir: string): Manifest {\n const rel = (file: string) => posix(path.relative(graph.appDir, file));\n const base = (route: Route) => {\n const chains = graph.chains.get(route.file) ?? { middleware: [], errors: [] };\n return {\n id: route.id,\n category: route.category,\n path: route.path,\n file: rel(route.file),\n middleware: chains.middleware.map(rel),\n errors: chains.errors.map(rel),\n plugins: graph.plugins.get(route.file) ?? [],\n };\n };\n\n const routes: ManifestRoute[] = [];\n for (const command of graph.commands) {\n for (const route of Object.values(command.handlers))\n routes.push({ ...base(route), kind: \"command\" });\n }\n for (const entry of graph.autocomplete) {\n routes.push({ ...base(entry.route), kind: \"autocomplete\", options: entry.options });\n }\n for (const route of graph.components) {\n routes.push({\n ...base(route),\n kind: route.kind,\n shortId: route.shortId,\n params: route.params,\n catchAll: route.catchAll,\n selectKind: route.selectKind,\n overhead: route.overhead,\n });\n }\n for (const event of graph.events) {\n for (const handler of event.handlers) {\n routes.push({\n ...base(handler.route),\n kind: \"event\",\n event: event.name,\n once: handler.once,\n order: handler.order,\n });\n }\n }\n routes.sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id));\n\n return {\n version: MANIFEST_VERSION,\n nectar: version,\n appDir: posix(path.relative(path.resolve(outDir), graph.appDir)),\n routes,\n commands: graph.commands.map((c) => ({\n name: c.name,\n type: c.type,\n payload: c.payload,\n handlers: Object.fromEntries(Object.entries(c.handlers).map(([k, r]) => [k, r.id])),\n })),\n events: graph.events.map((e) => ({\n name: e.name,\n mode: e.mode,\n handlers: e.handlers.map((h) => h.route.id),\n })),\n };\n}\n\n/** Writes `manifest.json` into `outDir` with sorted keys, so identical graphs give identical bytes. */\nexport function writeManifest(manifest: Manifest, outDir: string): string {\n mkdirSync(outDir, { recursive: true });\n const file = path.join(outDir, MANIFEST_FILE);\n writeFileSync(file, `${stableStringify(manifest)}\\n`);\n return file;\n}\n\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(value, (_key, v: unknown) => (isPlainObject(v) ? sortKeys(v) : v), 2);\n}\n\nfunction sortKeys(object: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(object).sort()) out[key] = object[key];\n return out;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction posix(file: string): string {\n return file.split(path.sep).join(\"/\");\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { RouteGraph } from \"../compiler/graph.js\";\nimport { toManifest } from \"../manifest/emit.js\";\nimport type { NectarPlugin, PluginChange, PluginGraph } from \"./index.js\";\n\n/** A frozen copy of the graph in manifest shape, with absolute file paths. */\nexport function pluginGraph(graph: RouteGraph): PluginGraph {\n const manifest = toManifest(graph, graph.appDir);\n const absolute = (file: string) => path.join(graph.appDir, ...file.split(\"/\"));\n // Cloned because the manifest shares arrays and payloads with the graph itself.\n return deepFreeze(\n structuredClone({\n appDir: graph.appDir,\n routes: manifest.routes.map((route) => ({\n ...route,\n file: absolute(route.file),\n middleware: route.middleware.map(absolute),\n errors: route.errors.map(absolute),\n })),\n commands: manifest.commands,\n events: manifest.events,\n }),\n );\n}\n\n/**\n * Runs every plugin's `transform` in config order and applies the returned changes to the\n * graph. Problems become diagnostics; a plugin never mutates the graph directly.\n */\nexport async function applyPlugins(\n graph: RouteGraph,\n plugins: readonly NectarPlugin[],\n): Promise<void> {\n for (const plugin of plugins) {\n if (plugin.transform === undefined) continue;\n let changes: PluginChange[];\n try {\n changes = (await plugin.transform(pluginGraph(graph))) ?? [];\n } catch (error) {\n graph.diagnostics.error(\n \"plugin-failed\",\n `Plugin \"${plugin.name}\" threw while transforming routes: ${describe(error)}`,\n );\n continue;\n }\n if (!Array.isArray(changes)) {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin.name}\" returned ${typeof changes} from transform. Return an array of changes, or nothing.`,\n );\n continue;\n }\n for (const change of changes) apply(graph, plugin.name, change);\n }\n}\n\nfunction apply(graph: RouteGraph, plugin: string, change: PluginChange): void {\n const type: unknown = isRecord(change) ? change.type : undefined;\n if (type !== \"middleware\" && type !== \"diagnostic\") {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin}\" returned a change with type ${JSON.stringify(type)}. A change's type is \"middleware\" or \"diagnostic\".`,\n );\n return;\n }\n if (change.type === \"diagnostic\") {\n const { severity, code, message, file, route } = change;\n graph.diagnostics.items.push({\n code,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n message,\n ...(file === undefined ? {} : { file }),\n ...(route === undefined ? {} : { route }),\n });\n return;\n }\n\n const routes = graph.routes.filter(\n (r) => r.id === change.route && (change.kind === undefined || r.kind === change.kind),\n );\n const target = change.kind === undefined ? change.route : `${change.route} (${change.kind})`;\n if (routes.length === 0) {\n graph.diagnostics.error(\n \"plugin-unknown-route\",\n `Plugin \"${plugin}\" adds middleware to route \"${target}\", which does not exist. Route IDs look like \"command:moderation/ban\".`,\n );\n return;\n }\n if (routes.some((r) => r.category === \"event\")) {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin}\" adds middleware to route \"${target}\", but event handlers don't run middleware.`,\n { route: change.route },\n );\n return;\n }\n if (\n typeof change.file !== \"string\" ||\n !path.isAbsolute(change.file) ||\n !existsSync(change.file)\n ) {\n graph.diagnostics.error(\n \"plugin-missing-file\",\n `Plugin \"${plugin}\" adds middleware from ${JSON.stringify(change.file)}, which is not an absolute path to an existing file.`,\n { route: change.route },\n );\n return;\n }\n const file = path.normalize(change.file);\n for (const route of routes) {\n const chains = graph.chains.get(route.file);\n if (chains === undefined || chains.middleware.includes(file)) continue;\n if (change.position === \"inner\") chains.middleware.push(file);\n else chains.middleware.unshift(file);\n const touched = graph.plugins.get(route.file) ?? [];\n if (!touched.includes(plugin)) graph.plugins.set(route.file, [...touched, plugin]);\n }\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const inner of Object.values(value)) deepFreeze(inner);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import type { Client } from \"discord.js\";\nimport type { Project } from \"../cli/project.js\";\nimport type { Severity } from \"../compiler/diagnostics.js\";\nimport type { RouteKind } from \"../compiler/routes.js\";\nimport type { NectarServices } from \"../index.js\";\nimport type {\n Manifest,\n ManifestCommand,\n ManifestEvent,\n ManifestRoute,\n} from \"../manifest/schema.js\";\nimport type { SignalEmitter } from \"../runtime/signals.js\";\nimport type { Env, Logger } from \"../runtime/types.js\";\n\n/**\n * A plugin takes part in compilation and the runtime lifecycle. A library that only exports\n * functions for handlers to call does not need to be one.\n */\nexport interface NectarPlugin {\n /** Unique among the configured plugins. Named in diagnostics and in the manifest. */\n name: string;\n version?: string;\n /**\n * Runs after the route graph is validated and before the manifest is written. The graph is\n * frozen; return changes and the compiler applies and checks them. Plugins run in config\n * order, each seeing the changes of the ones before it.\n */\n transform?(graph: PluginGraph): Maybe<PluginChange[]> | Promise<Maybe<PluginChange[]>>;\n /** Declarations appended to `.nectar/types.d.ts`. */\n types?(graph: PluginGraph): Maybe<string>;\n /** Extra `nectar <name>` commands. */\n commands?: PluginCommand[];\n /**\n * Runs when the runtime starts, before any handler is imported and before login. A sharded\n * bot runs it in every process. Returned services land on `ctx.services` for every handler\n * and middleware.\n */\n start?(app: PluginApp): Maybe<Partial<NectarServices>> | Promise<Maybe<Partial<NectarServices>>>;\n /** Runs on shutdown, after in-flight interactions drain and before the client is destroyed. */\n stop?(app: PluginApp): void | Promise<void>;\n /**\n * Runs once per application, in the process that runs shard 0, after every `start`. For work\n * that must not repeat per shard process: a scheduled job, a web server, posting stats.\n */\n startGlobal?(app: PluginApp): void | Promise<void>;\n /** Runs on shutdown in the process that ran `startGlobal`, before any `stop`. */\n stopGlobal?(app: PluginApp): void | Promise<void>;\n}\n\n/** A hook may return nothing, so a body without `return` type-checks. */\n// biome-ignore lint/suspicious/noConfusingVoidType: that is the point\ntype Maybe<T> = T | undefined | void;\n\nexport type PluginChange =\n | {\n type: \"middleware\";\n /** Route ID, `<category>:<path>`. */\n route: string;\n /**\n * Only the route of this kind. A command and its autocomplete share an ID; without\n * `kind`, both get the middleware, as they would from a `middleware.ts`.\n */\n kind?: RouteKind;\n /** Absolute path of a module whose default export is a middleware. */\n file: string;\n /** `outer` (default) runs before the app's own middleware, `inner` right before the handler. */\n position?: \"outer\" | \"inner\";\n }\n | {\n type: \"diagnostic\";\n severity: Severity;\n code: string;\n message: string;\n file?: string;\n route?: string;\n };\n\ntype DeepReadonly<T> = T extends (infer U)[]\n ? readonly DeepReadonly<U>[]\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\n/** The compiled app as a plugin sees it: the manifest shape with absolute file paths, frozen. */\nexport interface PluginGraph {\n readonly appDir: string;\n readonly routes: DeepReadonly<ManifestRoute[]>;\n readonly commands: DeepReadonly<ManifestCommand[]>;\n readonly events: DeepReadonly<ManifestEvent[]>;\n}\n\nexport interface PluginApp {\n readonly client: Client;\n readonly env: Env;\n readonly logger: Logger;\n readonly signals: SignalEmitter;\n readonly manifest: Manifest;\n}\n\nexport interface PluginCommand {\n name: string;\n description: string;\n options?: Record<string, { type: \"boolean\" | \"string\"; description: string }>;\n /** Returns the exit code. */\n run(ctx: PluginCommandContext): number | Promise<number>;\n}\n\nexport interface PluginCommandContext {\n project: Project;\n flags: Record<string, string | boolean | undefined>;\n out(line: string): void;\n err(line: string): void;\n}\n\nexport function definePlugin(plugin: NectarPlugin): NectarPlugin {\n return plugin;\n}\n\n/** A plugin misbehaved: threw from a hook, or provided something that clashes. */\nexport class PluginError extends Error {\n constructor(\n readonly plugin: string,\n readonly detail: string,\n ) {\n super(`Plugin \"${plugin}\": ${detail}`);\n this.name = \"PluginError\";\n }\n}\n\nexport { applyPlugins, pluginGraph } from \"./transform.js\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugins-C2uwN3-D.js","names":["describe"],"sources":["../src/version.ts","../src/components/customId.ts","../src/compiler/diagnostics.ts","../src/compiler/load.ts","../src/compiler/segments.ts","../src/components/params.ts","../src/components/compile.ts","../src/components/registry.ts","../src/manifest/emit.ts","../src/plugins/transform.ts","../src/plugins/index.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\n/** From package.json, which sits one level up from both `src/` and `dist/`. */\nexport const { version } = createRequire(import.meta.url)(\"../package.json\") as {\n version: string;\n};\n","/** Discord rejects custom IDs longer than this. */\nexport const MAX_CUSTOM_ID_LENGTH = 100;\n\nconst PREFIX = \"n:\";\nconst SHORT_ID_LENGTH = 6;\n\n/** Characters a route with no parameters uses: the prefix and the short ID. */\nexport const BASE_OVERHEAD = PREFIX.length + SHORT_ID_LENGTH;\n\nexport class CustomIdTooLongError extends Error {\n constructor(\n readonly customId: string,\n readonly routeId: string,\n ) {\n super(\n `Custom ID for ${routeId} is ${customId.length} characters, Discord allows ${MAX_CUSTOM_ID_LENGTH}. Encode a shorter identifier instead of the full value.`,\n );\n this.name = \"CustomIdTooLongError\";\n }\n}\n\n/**\n * Builds `n:<shortId>:<v1>:<v2>...`. Values are escaped so they may contain `:` and `\\`.\n * Throws when the result is longer than Discord allows; it never truncates.\n */\nexport function encodeCustomId(shortId: string, values: readonly string[], routeId = shortId) {\n let out = PREFIX + shortId;\n for (const value of values) out += `:${escapeValue(value)}`;\n if (out.length > MAX_CUSTOM_ID_LENGTH) throw new CustomIdTooLongError(out, routeId);\n return out;\n}\n\nexport type DecodedCustomId =\n | { ok: true; shortId: string; values: string[] }\n | { ok: false; reason: \"not-nectar\" | \"malformed\" };\n\n/**\n * Splits a raw custom ID back into its short ID and positional values.\n * IDs without the Nectar prefix are reported as `not-nectar` so hand-built components pass through.\n */\nexport function decodeCustomId(raw: string): DecodedCustomId {\n if (!raw.startsWith(PREFIX)) return { ok: false, reason: \"not-nectar\" };\n\n const shortId = raw.slice(PREFIX.length, PREFIX.length + SHORT_ID_LENGTH);\n if (!/^[0-9a-z]{6}$/.test(shortId)) return { ok: false, reason: \"malformed\" };\n\n const values: string[] = [];\n let index = PREFIX.length + SHORT_ID_LENGTH;\n if (index === raw.length) return { ok: true, shortId, values };\n if (raw[index] !== \":\") return { ok: false, reason: \"malformed\" };\n index++;\n\n let current = \"\";\n while (index < raw.length) {\n const char = raw[index] as string;\n if (char === \"\\\\\") {\n const next = raw[index + 1];\n if (next !== \"\\\\\" && next !== \":\") return { ok: false, reason: \"malformed\" };\n current += next;\n index += 2;\n continue;\n }\n if (char === \":\") {\n values.push(current);\n current = \"\";\n index++;\n continue;\n }\n current += char;\n index++;\n }\n values.push(current);\n return { ok: true, shortId, values };\n}\n\nfunction escapeValue(value: string): string {\n return value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\":\", \"\\\\:\");\n}\n","export type Severity = \"error\" | \"warning\";\n\nexport interface Diagnostic {\n code: string;\n severity: Severity;\n message: string;\n /** Absolute path of the file or directory that caused the diagnostic. */\n file?: string;\n /** Canonical route identity, when the diagnostic is about a specific route. */\n route?: string;\n}\n\n/** Every code the compiler reports. Each has an entry in the diagnostics reference. */\nexport const DIAGNOSTIC_CODES = [\n \"file-outside-category\",\n \"unknown-category\",\n \"file-in-wrong-category\",\n \"invalid-segment\",\n \"route-without-path\",\n \"dynamic-segment-not-allowed\",\n \"duplicate-param\",\n \"catch-all-not-last\",\n \"duplicate-route\",\n \"module-load-failed\",\n \"missing-handler\",\n \"route-mismatch\",\n \"missing-meta\",\n \"invalid-meta\",\n \"invalid-name\",\n \"invalid-description\",\n \"invalid-option\",\n \"missing-route-meta\",\n \"route-meta-without-path\",\n \"unused-route-meta\",\n \"mixed-command-and-subcommands\",\n \"mixed-subcommand-and-group\",\n \"command-too-deep\",\n \"too-many-subcommands\",\n \"context-menu-nested\",\n \"top-level-field-on-group\",\n \"top-level-field-on-subcommand\",\n \"duplicate-command-name\",\n \"too-many-commands\",\n \"autocomplete-without-command\",\n \"autocomplete-export-not-function\",\n \"autocomplete-unknown-option\",\n \"autocomplete-missing-handler\",\n \"autocomplete-missing-file\",\n \"missing-select-kind\",\n \"invalid-select-kind\",\n \"invalid-param-validator\",\n \"catch-all-route\",\n \"short-id-collision\",\n \"duplicate-component-pattern\",\n \"unknown-event\",\n \"event-nested-path\",\n \"event-mode-conflict\",\n \"missing-intent\",\n \"plugin-failed\",\n \"plugin-invalid-change\",\n \"plugin-unknown-route\",\n \"plugin-missing-file\",\n] as const;\n\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[number];\n\nconst REFERENCE = \"https://nectar-js.github.io/nectar/reference/diagnostics\";\n\n/** The reference entry for a compiler code. Codes from plugins have none. */\nexport function docsUrl(code: string): string | undefined {\n return (DIAGNOSTIC_CODES as readonly string[]).includes(code)\n ? `${REFERENCE}#${code}`\n : undefined;\n}\n\n/** A value's type as a message puts it: `missing`, `a number`, `an array`. */\nexport function typeOf(value: unknown): string {\n if (value === undefined) return \"missing\";\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n return typeof value === \"object\" ? \"an object\" : `a ${typeof value}`;\n}\n\ninterface DiagnosticLocation {\n file?: string;\n route?: string;\n}\n\nexport class Diagnostics {\n readonly items: Diagnostic[] = [];\n\n error(code: DiagnosticCode, message: string, location: DiagnosticLocation = {}): void {\n this.push(\"error\", code, message, location);\n }\n\n warn(code: DiagnosticCode, message: string, location: DiagnosticLocation = {}): void {\n this.push(\"warning\", code, message, location);\n }\n\n get hasErrors(): boolean {\n return this.items.some((d) => d.severity === \"error\");\n }\n\n private push(\n severity: Severity,\n code: string,\n message: string,\n location: DiagnosticLocation,\n ): void {\n const item: Diagnostic = { code, severity, message };\n if (location.file !== undefined) item.file = location.file;\n if (location.route !== undefined) item.route = location.route;\n this.items.push(item);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport { registerHooks } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\n/**\n * Imports an application module by absolute path.\n *\n * Relies on Node's native TypeScript type stripping (unflagged since 22.18), so handler\n * files must use erasable syntax only: no enums, namespaces, or parameter properties.\n *\n * With reloading enabled (see `enableModuleReloading`) the URL carries a version query, so a\n * changed file evaluates again on the next import instead of coming back from the ESM cache.\n */\nexport async function loadModule(file: string): Promise<Record<string, unknown>> {\n const url = pathToFileURL(file).href;\n return (await import(reloading === null ? url : versioned(url))) as Record<string, unknown>;\n}\n\ninterface Reloading {\n /** Project root; only files under it (outside `node_modules`) are versioned. */\n root: string;\n /** Bumped by `invalidateModuleGraph` so every project module evaluates again. */\n generation: number;\n}\n\nlet reloading: Reloading | null = null;\n\n/**\n * Turns on cache busting for project files. Used by `nectar dev` only.\n *\n * Every import of a file under `root` gets `?nectar=<content hash>-<generation>` appended, the\n * direct ones here and the transitive ones through a resolve hook. A handler whose content\n * changed therefore gets a new URL and a fresh evaluation; its unchanged imports keep their\n * URL and are shared. Old instances stay in the ESM cache until the process exits.\n */\nexport function enableModuleReloading(root: string): void {\n if (reloading !== null) return;\n reloading = { root: path.resolve(root), generation: 0 };\n registerHooks({\n resolve(specifier, context, next) {\n const result = next(specifier, context);\n return { ...result, url: versioned(result.url) };\n },\n });\n}\n\n/**\n * Makes every project module evaluate again on its next import. For changes to files the\n * compiler does not track (helpers a handler imports), since nothing knows who imports them.\n */\nexport function invalidateModuleGraph(): void {\n if (reloading !== null) reloading.generation += 1;\n}\n\nfunction versioned(url: string): string {\n if (reloading === null || !url.startsWith(\"file:\") || url.includes(\"?\") || url.includes(\"#\")) {\n return url;\n }\n const file = fileURLToPath(url);\n const inside = !path.relative(reloading.root, file).startsWith(\"..\");\n if (!inside || file.split(path.sep).includes(\"node_modules\")) return url;\n let hash: string;\n try {\n hash = createHash(\"sha1\").update(readFileSync(file)).digest(\"base64url\").slice(0, 10);\n } catch {\n return url;\n }\n return `${url}?nectar=${hash}-${reloading.generation}`;\n}\n","export type Segment =\n | { type: \"static\"; name: string }\n | { type: \"dynamic\"; name: string }\n | { type: \"catchAll\"; name: string }\n | { type: \"group\"; name: string };\n\nexport type SegmentParseResult = { ok: true; segment: Segment } | { ok: false; reason: string };\n\nconst STATIC_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;\nconst PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Parses one directory name into a route segment.\n *\n * `name` static\n * `[name]` dynamic\n * `[...name]` catch-all\n * `(name)` group, organizational only\n */\nexport function parseSegment(dirName: string): SegmentParseResult {\n if (dirName.startsWith(\"[\") || dirName.endsWith(\"]\")) {\n if (!dirName.startsWith(\"[\") || !dirName.endsWith(\"]\")) {\n return fail(`\"${dirName}\" has an unmatched bracket. Parameters look like [name].`);\n }\n const inner = dirName.slice(1, -1);\n const isCatchAll = inner.startsWith(\"...\");\n const name = isCatchAll ? inner.slice(3) : inner;\n if (!PARAM_NAME.test(name)) {\n return fail(\n `\"${dirName}\" has an invalid parameter name. Parameters become keys of ctx.params, so use letters, digits, and underscores, and don't start with a digit.`,\n );\n }\n return ok({ type: isCatchAll ? \"catchAll\" : \"dynamic\", name });\n }\n\n if (dirName.startsWith(\"(\") || dirName.endsWith(\")\")) {\n if (!dirName.startsWith(\"(\") || !dirName.endsWith(\")\")) {\n return fail(`\"${dirName}\" has an unmatched parenthesis. Route groups look like (name).`);\n }\n const name = dirName.slice(1, -1);\n if (!STATIC_NAME.test(name)) {\n return fail(\n `\"${dirName}\" isn't a valid group name. Use letters, digits, hyphens, and underscores.`,\n );\n }\n return ok({ type: \"group\", name });\n }\n\n if (!STATIC_NAME.test(dirName)) {\n return fail(\n `\"${dirName}\" can't be part of a route path. Use letters, digits, hyphens, and underscores, and start with a letter or digit.`,\n );\n }\n return ok({ type: \"static\", name: dirName });\n}\n\n/** Renders a segment back into its directory form. Groups render as their directory name. */\nexport function formatSegment(segment: Segment): string {\n switch (segment.type) {\n case \"static\":\n return segment.name;\n case \"dynamic\":\n return `[${segment.name}]`;\n case \"catchAll\":\n return `[...${segment.name}]`;\n case \"group\":\n return `(${segment.name})`;\n }\n}\n\nfunction ok(segment: Segment): SegmentParseResult {\n return { ok: true, segment };\n}\n\nfunction fail(reason: string): SegmentParseResult {\n return { ok: false, reason };\n}\n","import { typeOf } from \"../compiler/diagnostics.js\";\n\n/**\n * A Standard Schema (https://standardschema.dev) validator, which zod, valibot, and arktype\n * all produce. Only the result's `issues` are looked at: a schema that transforms the value\n * does not change what the handler receives.\n */\nexport interface StandardSchemaLike<V = unknown> {\n \"~standard\": {\n validate(\n value: V,\n ):\n | { issues?: ReadonlyArray<unknown> | undefined }\n | Promise<{ issues?: ReadonlyArray<unknown> | undefined }>;\n };\n}\n\n/**\n * Checks one custom ID parameter. A function passes by returning anything but `false` and\n * fails by returning `false` or throwing. A schema fails by reporting issues.\n */\nexport type ParamValidator<V = string | string[]> = ((value: V) => unknown) | StandardSchemaLike<V>;\n\nexport type ParamValidators = Record<string, ParamValidator>;\n\n/**\n * Reads the validators `defineComponent` attached to a handler and checks them against the\n * route's parameters. Throws with a developer-facing message when the shape is wrong; the\n * compiler reports it as a diagnostic and the runtime as a load error.\n */\nexport function paramValidatorsOf(\n handler: unknown,\n route: { params: readonly string[] },\n): ParamValidators {\n const declared = (handler as { params?: unknown }).params;\n if (declared === undefined) return {};\n if (typeof declared !== \"object\" || declared === null || Array.isArray(declared)) {\n throw new Error(`params is ${typeOf(declared)}, not an object of validators.`);\n }\n const validators: ParamValidators = {};\n for (const [name, validator] of Object.entries(declared)) {\n if (!route.params.includes(name)) {\n throw new Error(\n `params validates \"${name}\", which is not a parameter of this route. ${\n route.params.length === 0 ? \"It has none.\" : `It has: ${route.params.join(\", \")}.`\n }`,\n );\n }\n if (!isValidator(validator)) {\n throw new Error(\n `params.${name} is ${typeOf(validator)}. A validator is a function or a Standard Schema.`,\n );\n }\n validators[name] = validator;\n }\n return validators;\n}\n\nfunction isValidator(value: unknown): value is ParamValidator {\n if (typeof value === \"function\") return true;\n if (typeof value !== \"object\" || value === null) return false;\n const standard = (value as Record<string, unknown>)[\"~standard\"];\n return (\n typeof standard === \"object\" &&\n standard !== null &&\n typeof (standard as Record<string, unknown>).validate === \"function\"\n );\n}\n\n/**\n * Runs every validator against the decoded parameters. Resolves to the first parameter that\n * failed, or `null` when all passed. A validator that throws counts as a failure; the\n * caller decides what to log, so the value never leaves this function.\n */\nexport async function findInvalidParam(\n validators: ParamValidators,\n params: Record<string, string | string[]>,\n): Promise<string | null> {\n for (const [name, validator] of Object.entries(validators)) {\n const value = params[name];\n if (value === undefined) return name;\n try {\n if (typeof validator === \"function\") {\n if ((await validator(value)) === false) return name;\n continue;\n }\n const result = await validator[\"~standard\"].validate(value);\n if (result.issues !== undefined && result.issues.length > 0) return name;\n } catch {\n return name;\n }\n }\n return null;\n}\n","import path from \"node:path\";\nimport { Diagnostics, typeOf } from \"../compiler/diagnostics.js\";\nimport { loadModule } from \"../compiler/load.js\";\nimport type { Route, RouteTable } from \"../compiler/routes.js\";\nimport { formatSegment } from \"../compiler/segments.js\";\nimport { BASE_OVERHEAD, encodeCustomId, MAX_CUSTOM_ID_LENGTH } from \"./customId.js\";\nimport { paramValidatorsOf } from \"./params.js\";\n\nexport type ComponentKind = \"button\" | \"select\" | \"modal\";\n\nexport type SelectKind = \"string\" | \"user\" | \"role\" | \"channel\" | \"mentionable\";\n\nconst SELECT_KINDS: ReadonlySet<string> = new Set([\n \"string\",\n \"user\",\n \"role\",\n \"channel\",\n \"mentionable\",\n]);\n\nexport interface ComponentRoute extends Route {\n category: \"component\";\n kind: ComponentKind;\n /** The `kind` export of a `select.ts`. `null` for buttons and modals. */\n selectKind: SelectKind | null;\n /** Name of the trailing catch-all parameter, if the route has one. */\n catchAll: string | null;\n /** Characters of the encoded custom ID taken by the prefix, short ID, and separators. */\n overhead: number;\n}\n\nexport interface CompiledComponents {\n routes: ComponentRoute[];\n diagnostics: Diagnostics;\n}\n\n/** Values for one route's parameters. A catch-all parameter takes an array. */\nexport type ComponentParams = Record<string, string | readonly string[]>;\n\n/** Validates the component routes of a route table and resolves their select kinds. */\nexport async function compileComponents(table: RouteTable): Promise<CompiledComponents> {\n const diagnostics = new Diagnostics();\n const candidates = table.routes.filter((r) => r.category === \"component\");\n\n // Each route reports into its own list, so diagnostics come out in route order rather than\n // in whatever order the imports finish.\n const results = await Promise.all(\n candidates.map(async (route) => {\n const own = new Diagnostics();\n return { route: await compileRoute(route, own), diagnostics: own };\n }),\n );\n const routes: ComponentRoute[] = [];\n for (const result of results) {\n diagnostics.items.push(...result.diagnostics.items);\n if (result.route !== null) routes.push(result.route);\n }\n\n detectShortIdCollisions(routes, diagnostics);\n detectDuplicatePatterns(routes, diagnostics);\n\n return { routes, diagnostics };\n}\n\n/** What the encoder needs from a route. Compiled, manifest, and registered routes all satisfy it. */\nexport interface EncodableRoute {\n id: string;\n shortId: string;\n params: string[];\n catchAll: string | null;\n}\n\n/**\n * Encodes a custom ID for a compiled route. Throws when a parameter is missing, a value is not\n * a string, or the result exceeds Discord's limit.\n */\nexport function customIdFor(route: EncodableRoute, params: ComponentParams = {}): string {\n const values: string[] = [];\n for (const name of route.params) {\n const value = params[name];\n if (name === route.catchAll) {\n if (value === undefined) continue;\n if (typeof value === \"string\") {\n values.push(value);\n continue;\n }\n values.push(...value);\n continue;\n }\n if (typeof value !== \"string\") {\n throw new TypeError(\n `Route ${route.id} needs a string for parameter \"${name}\", got ${describe(value)}.`,\n );\n }\n values.push(value);\n }\n for (const name of Object.keys(params)) {\n if (!route.params.includes(name)) {\n throw new TypeError(\n `Route ${route.id} has no parameter \"${name}\". ${route.params.length === 0 ? \"It takes none.\" : `It takes: ${route.params.join(\", \")}.`}`,\n );\n }\n }\n return encodeCustomId(route.shortId, values, route.id);\n}\n\nasync function compileRoute(\n route: Route,\n diagnostics: Diagnostics,\n): Promise<ComponentRoute | null> {\n const kind = route.kind as ComponentKind;\n const last = route.segments.at(-1);\n const catchAll = last?.type === \"catchAll\" ? last.name : null;\n const overhead = BASE_OVERHEAD + route.params.length;\n\n if (catchAll !== null) {\n diagnostics.warn(\n \"catch-all-route\",\n `${formatSegment(last as NonNullable<typeof last>)} takes any number of values. They all count toward Discord's ${MAX_CUSTOM_ID_LENGTH} character limit on custom IDs, and customId() throws when an ID goes over.`,\n { file: route.file, route: route.id },\n );\n }\n\n let module: Record<string, unknown>;\n try {\n module = await loadModule(route.file);\n } catch (error) {\n diagnostics.error(\n \"module-load-failed\",\n `The compiler imports every route file to read its exports, and this one threw: ${error instanceof Error ? error.message : String(error)}`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n\n if (!checkHandler(module, route, diagnostics)) return null;\n try {\n paramValidatorsOf(module.default, route);\n } catch (error) {\n diagnostics.error(\n \"invalid-param-validator\",\n `${error instanceof Error ? error.message : String(error)} Validators go in defineComponent's third argument, like { params: { id: (value) => ... } }.`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n\n let selectKind: SelectKind | null = null;\n if (kind === \"select\") {\n selectKind = validateSelectKind(module, route, diagnostics);\n if (selectKind === null) return null;\n }\n\n return { ...route, category: \"component\", kind, selectKind, catchAll, overhead };\n}\n\n/**\n * The default export is the handler Nectar calls, and one made with `defineComponent(path, ...)`\n * or the like must name the route its file sits in.\n */\nexport function checkHandler(\n module: Record<string, unknown>,\n route: Route,\n diagnostics: Diagnostics,\n expected = route.path,\n): boolean {\n const handler = module.default;\n if (typeof handler !== \"function\") {\n const define =\n route.kind === \"command\"\n ? \"defineCommand\"\n : route.kind === \"event\"\n ? \"defineEvent\"\n : \"defineComponent\";\n diagnostics.error(\n \"missing-handler\",\n `This ${path.basename(route.file)} ${handler === undefined ? \"has no default export\" : `exports ${typeOf(handler)} as its default`}. Nectar calls the default export when the route runs, so export the handler, like export default ${define}(\"${expected}\", handler).`,\n { file: route.file, route: route.id },\n );\n return false;\n }\n const declared = (handler as { route?: unknown }).route;\n if (declared === undefined || declared === expected) return true;\n diagnostics.error(\n \"route-mismatch\",\n `This file's route is \"${expected}\", but its handler says \"${String(declared)}\". The string types the handler, so it has to match where the file is. Change it to \"${expected}\", or move the file.`,\n { file: route.file, route: route.id },\n );\n return false;\n}\n\nfunction validateSelectKind(\n module: Record<string, unknown>,\n route: Route,\n diagnostics: Diagnostics,\n): SelectKind | null {\n const kind = module.kind;\n if (kind === undefined) {\n diagnostics.error(\n \"missing-select-kind\",\n 'This select.ts doesn\\'t export kind, which says what the select menu picks from: \"string\", \"user\", \"role\", \"channel\", or \"mentionable\". Add one, like export const kind = \"string\".',\n { file: route.file, route: route.id },\n );\n return null;\n }\n if (typeof kind !== \"string\" || !SELECT_KINDS.has(kind)) {\n diagnostics.error(\n \"invalid-select-kind\",\n `kind is ${describe(kind)}. Use \"string\", \"user\", \"role\", \"channel\", or \"mentionable\".`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n return kind as SelectKind;\n}\n\nfunction detectShortIdCollisions(routes: ComponentRoute[], diagnostics: Diagnostics): void {\n const seen = new Map<string, ComponentRoute>();\n for (const route of routes) {\n const existing = seen.get(route.shortId);\n if (existing === undefined || existing.id === route.id) {\n seen.set(route.shortId, route);\n continue;\n }\n diagnostics.error(\n \"short-id-collision\",\n `${route.id} and ${existing.id} hash to the same short ID, \"${route.shortId}\", so Nectar can't tell their custom IDs apart. Rename a directory in one of them.`,\n { file: route.file, route: route.id },\n );\n }\n}\n\n/**\n * Two routes of the same kind whose paths differ only in parameter names, like\n * `tickets/[id]/close` and `tickets/[ticketId]/close`. Their hashes differ, but they take the\n * same values in the same places, so they are one route split in two.\n */\nfunction detectDuplicatePatterns(routes: ComponentRoute[], diagnostics: Diagnostics): void {\n const seen = new Map<string, ComponentRoute>();\n for (const route of routes) {\n const shape = route.segments\n .filter((s) => s.type !== \"group\")\n .map((s) => (s.type === \"static\" ? s.name : s.type === \"dynamic\" ? \"[]\" : \"[...]\"))\n .join(\"/\");\n const key = `${route.kind}#${shape}`;\n const existing = seen.get(key);\n if (existing === undefined) {\n seen.set(key, route);\n continue;\n }\n if (existing.id === route.id) continue;\n diagnostics.error(\n \"duplicate-component-pattern\",\n `${route.id} is the same path as ${existing.id} in ${relative(existing.file)}, with a different parameter name. Parameter names don't make routes distinct. Merge the two and keep one name.`,\n { file: route.file, route: route.id },\n );\n }\n}\n\nfunction describe(value: unknown): string {\n return typeof value === \"string\" ? JSON.stringify(value) : typeof value;\n}\n\nfunction relative(file: string): string {\n return path.relative(process.cwd(), file).split(path.sep).join(\"/\");\n}\n","import { type ComponentParams, customIdFor, type EncodableRoute } from \"./compile.js\";\n\nexport interface RegisteredComponentRoute extends EncodableRoute {\n path: string;\n}\n\n/**\n * Component routes the running app knows about, keyed by path. The runtime fills this from\n * the manifest before any handler runs, so `customId()` never needs the manifest itself.\n */\nconst routes = new Map<string, RegisteredComponentRoute>();\n\nexport function registerComponentRoutes(list: Iterable<RegisteredComponentRoute>): void {\n routes.clear();\n for (const route of list) routes.set(route.path, route);\n}\n\nexport function encodeComponentRoute(path: string, params: ComponentParams): string {\n const route = routes.get(path);\n if (route === undefined) {\n throw new Error(\n routes.size === 0\n ? `customId(\"${path}\") was called before the runtime registered any routes. Call it from a handler, or from code that runs after start().`\n : `No component route \"${path}\". Check the directory name under components/.`,\n );\n }\n return customIdFor(route, params);\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { RouteGraph } from \"../compiler/graph.js\";\nimport type { Route } from \"../compiler/routes.js\";\nimport { version } from \"../version.js\";\nimport { MANIFEST_VERSION, type Manifest, type ManifestRoute } from \"./schema.js\";\n\nexport const MANIFEST_FILE = \"manifest.json\";\n\n/** Serializes a route graph. `outDir` is where the manifest will live; paths are made relative to it. */\nexport function toManifest(graph: RouteGraph, outDir: string): Manifest {\n const rel = (file: string) => posix(path.relative(graph.appDir, file));\n const base = (route: Route) => {\n const chains = graph.chains.get(route.file) ?? { middleware: [], errors: [] };\n return {\n id: route.id,\n category: route.category,\n path: route.path,\n file: rel(route.file),\n middleware: chains.middleware.map(rel),\n errors: chains.errors.map(rel),\n plugins: graph.plugins.get(route.file) ?? [],\n };\n };\n\n const routes: ManifestRoute[] = [];\n for (const command of graph.commands) {\n for (const [key, route] of Object.entries(command.handlers)) {\n routes.push({ ...base(route), kind: \"command\", defer: command.defer[key] ?? null });\n }\n }\n for (const entry of graph.autocomplete) {\n routes.push({ ...base(entry.route), kind: \"autocomplete\", options: entry.options });\n }\n for (const route of graph.components) {\n routes.push({\n ...base(route),\n kind: route.kind,\n shortId: route.shortId,\n params: route.params,\n catchAll: route.catchAll,\n selectKind: route.selectKind,\n overhead: route.overhead,\n });\n }\n for (const event of graph.events) {\n for (const handler of event.handlers) {\n routes.push({\n ...base(handler.route),\n kind: \"event\",\n event: event.name,\n once: handler.once,\n order: handler.order,\n });\n }\n }\n routes.sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id));\n\n return {\n version: MANIFEST_VERSION,\n nectar: version,\n appDir: posix(path.relative(path.resolve(outDir), graph.appDir)),\n routes,\n commands: graph.commands.map((c) => ({\n name: c.name,\n type: c.type,\n payload: c.payload,\n handlers: Object.fromEntries(Object.entries(c.handlers).map(([k, r]) => [k, r.id])),\n })),\n events: graph.events.map((e) => ({\n name: e.name,\n mode: e.mode,\n handlers: e.handlers.map((h) => h.route.id),\n })),\n };\n}\n\n/** Writes `manifest.json` into `outDir` with sorted keys, so identical graphs give identical bytes. */\nexport function writeManifest(manifest: Manifest, outDir: string): string {\n mkdirSync(outDir, { recursive: true });\n const file = path.join(outDir, MANIFEST_FILE);\n writeFileSync(file, `${stableStringify(manifest)}\\n`);\n return file;\n}\n\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(value, (_key, v: unknown) => (isPlainObject(v) ? sortKeys(v) : v), 2);\n}\n\nfunction sortKeys(object: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(object).sort()) out[key] = object[key];\n return out;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction posix(file: string): string {\n return file.split(path.sep).join(\"/\");\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { RouteGraph } from \"../compiler/graph.js\";\nimport { toManifest } from \"../manifest/emit.js\";\nimport type { NectarPlugin, PluginChange, PluginGraph } from \"./index.js\";\n\n/** A frozen copy of the graph in manifest shape, with absolute file paths. */\nexport function pluginGraph(graph: RouteGraph): PluginGraph {\n const manifest = toManifest(graph, graph.appDir);\n const absolute = (file: string) => path.join(graph.appDir, ...file.split(\"/\"));\n // Cloned because the manifest shares arrays and payloads with the graph itself.\n return deepFreeze(\n structuredClone({\n appDir: graph.appDir,\n routes: manifest.routes.map((route) => ({\n ...route,\n file: absolute(route.file),\n middleware: route.middleware.map(absolute),\n errors: route.errors.map(absolute),\n })),\n commands: manifest.commands,\n events: manifest.events,\n }),\n );\n}\n\n/**\n * Runs every plugin's `transform` in config order and applies the returned changes to the\n * graph. Problems become diagnostics; a plugin never mutates the graph directly.\n */\nexport async function applyPlugins(\n graph: RouteGraph,\n plugins: readonly NectarPlugin[],\n): Promise<void> {\n for (const plugin of plugins) {\n if (plugin.transform === undefined) continue;\n let changes: PluginChange[];\n try {\n changes = (await plugin.transform(pluginGraph(graph))) ?? [];\n } catch (error) {\n graph.diagnostics.error(\n \"plugin-failed\",\n `Plugin \"${plugin.name}\" threw while transforming routes: ${describe(error)}`,\n );\n continue;\n }\n if (!Array.isArray(changes)) {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin.name}\" returned ${typeof changes} from transform. Return an array of changes, or nothing.`,\n );\n continue;\n }\n for (const change of changes) apply(graph, plugin.name, change);\n }\n}\n\nfunction apply(graph: RouteGraph, plugin: string, change: PluginChange): void {\n const type: unknown = isRecord(change) ? change.type : undefined;\n if (type !== \"middleware\" && type !== \"diagnostic\") {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin}\" returned a change with type ${JSON.stringify(type)}. A change's type is \"middleware\" or \"diagnostic\".`,\n );\n return;\n }\n if (change.type === \"diagnostic\") {\n const { severity, code, message, file, route } = change;\n graph.diagnostics.items.push({\n code,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n message,\n ...(file === undefined ? {} : { file }),\n ...(route === undefined ? {} : { route }),\n });\n return;\n }\n\n const routes = graph.routes.filter(\n (r) => r.id === change.route && (change.kind === undefined || r.kind === change.kind),\n );\n const target = change.kind === undefined ? change.route : `${change.route} (${change.kind})`;\n if (routes.length === 0) {\n graph.diagnostics.error(\n \"plugin-unknown-route\",\n `Plugin \"${plugin}\" adds middleware to route \"${target}\", which does not exist. Route IDs look like \"command:moderation/ban\".`,\n );\n return;\n }\n if (routes.some((r) => r.category === \"event\")) {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin}\" adds middleware to route \"${target}\", but event handlers don't run middleware.`,\n { route: change.route },\n );\n return;\n }\n if (\n typeof change.file !== \"string\" ||\n !path.isAbsolute(change.file) ||\n !existsSync(change.file)\n ) {\n graph.diagnostics.error(\n \"plugin-missing-file\",\n `Plugin \"${plugin}\" adds middleware from ${JSON.stringify(change.file)}, which is not an absolute path to an existing file.`,\n { route: change.route },\n );\n return;\n }\n const file = path.normalize(change.file);\n for (const route of routes) {\n const chains = graph.chains.get(route.file);\n if (chains === undefined || chains.middleware.includes(file)) continue;\n if (change.position === \"inner\") chains.middleware.push(file);\n else chains.middleware.unshift(file);\n const touched = graph.plugins.get(route.file) ?? [];\n if (!touched.includes(plugin)) graph.plugins.set(route.file, [...touched, plugin]);\n }\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const inner of Object.values(value)) deepFreeze(inner);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import type { Client } from \"discord.js\";\nimport type { Project } from \"../cli/project.js\";\nimport type { Severity } from \"../compiler/diagnostics.js\";\nimport type { RouteKind } from \"../compiler/routes.js\";\nimport type { NectarServices } from \"../index.js\";\nimport type {\n Manifest,\n ManifestCommand,\n ManifestEvent,\n ManifestRoute,\n} from \"../manifest/schema.js\";\nimport type { SignalEmitter } from \"../runtime/signals.js\";\nimport type { Env, Logger } from \"../runtime/types.js\";\n\n/**\n * A plugin takes part in compilation and the runtime lifecycle. A library that only exports\n * functions for handlers to call does not need to be one.\n */\nexport interface NectarPlugin {\n /** Unique among the configured plugins. Named in diagnostics and in the manifest. */\n name: string;\n version?: string;\n /**\n * Runs after the route graph is validated and before the manifest is written. The graph is\n * frozen; return changes and the compiler applies and checks them. Plugins run in config\n * order, each seeing the changes of the ones before it.\n */\n transform?(graph: PluginGraph): Maybe<PluginChange[]> | Promise<Maybe<PluginChange[]>>;\n /** Declarations appended to `.nectar/types.d.ts`. */\n types?(graph: PluginGraph): Maybe<string>;\n /** Extra `nectar <name>` commands. */\n commands?: PluginCommand[];\n /**\n * Runs when the runtime starts, before any handler is imported and before login. A sharded\n * bot runs it in every process. Returned services land on `ctx.services` for every handler\n * and middleware.\n */\n start?(app: PluginApp): Maybe<Partial<NectarServices>> | Promise<Maybe<Partial<NectarServices>>>;\n /** Runs on shutdown, after in-flight interactions drain and before the client is destroyed. */\n stop?(app: PluginApp): void | Promise<void>;\n /**\n * Runs once per application, in the process that runs shard 0, after every `start`. For work\n * that must not repeat per shard process: a scheduled job, a web server, posting stats.\n */\n startGlobal?(app: PluginApp): void | Promise<void>;\n /** Runs on shutdown in the process that ran `startGlobal`, before any `stop`. */\n stopGlobal?(app: PluginApp): void | Promise<void>;\n}\n\n/** A hook may return nothing, so a body without `return` type-checks. */\n// biome-ignore lint/suspicious/noConfusingVoidType: that is the point\ntype Maybe<T> = T | undefined | void;\n\nexport type PluginChange =\n | {\n type: \"middleware\";\n /** Route ID, `<category>:<path>`. */\n route: string;\n /**\n * Only the route of this kind. A command and its autocomplete share an ID; without\n * `kind`, both get the middleware, as they would from a `middleware.ts`.\n */\n kind?: RouteKind;\n /** Absolute path of a module whose default export is a middleware. */\n file: string;\n /** `outer` (default) runs before the app's own middleware, `inner` right before the handler. */\n position?: \"outer\" | \"inner\";\n }\n | {\n type: \"diagnostic\";\n severity: Severity;\n code: string;\n message: string;\n file?: string;\n route?: string;\n };\n\ntype DeepReadonly<T> = T extends (infer U)[]\n ? readonly DeepReadonly<U>[]\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\n/** The compiled app as a plugin sees it: the manifest shape with absolute file paths, frozen. */\nexport interface PluginGraph {\n readonly appDir: string;\n readonly routes: DeepReadonly<ManifestRoute[]>;\n readonly commands: DeepReadonly<ManifestCommand[]>;\n readonly events: DeepReadonly<ManifestEvent[]>;\n}\n\nexport interface PluginApp {\n readonly client: Client;\n readonly env: Env;\n readonly logger: Logger;\n readonly signals: SignalEmitter;\n readonly manifest: Manifest;\n}\n\nexport interface PluginCommand {\n name: string;\n description: string;\n options?: Record<string, { type: \"boolean\" | \"string\"; description: string }>;\n /** Returns the exit code. */\n run(ctx: PluginCommandContext): number | Promise<number>;\n}\n\nexport interface PluginCommandContext {\n project: Project;\n flags: Record<string, string | boolean | undefined>;\n out(line: string): void;\n err(line: string): void;\n}\n\nexport function definePlugin(plugin: NectarPlugin): NectarPlugin {\n return plugin;\n}\n\n/** A plugin misbehaved: threw from a hook, or provided something that clashes. */\nexport class PluginError extends Error {\n constructor(\n readonly plugin: string,\n readonly detail: string,\n ) {\n super(`Plugin \"${plugin}\": ${detail}`);\n this.name = \"PluginError\";\n }\n}\n\nexport { applyPlugins, pluginGraph } from \"./transform.js\";\n"],"mappings":"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"}