@nectar-js/nectar 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration-BN3P3MDg.js","names":[],"sources":["../src/config.ts","../src/events/intents.ts","../src/registration/normalize.ts","../src/registration/diff.ts","../src/registration/remote.ts","../src/registration/errors.ts","../src/registration/sync.ts"],"sourcesContent":["import type { ClientOptions } from \"discord.js\";\nimport type { NectarPlugin } from \"./plugins/index.js\";\nimport type { LoggerOptions } from \"./runtime/logger.js\";\nimport type { Signal } from \"./runtime/signals.js\";\nimport type { Env } from \"./runtime/types.js\";\n\n/** `nectar.config.ts`: `export default defineConfig({ ... })`. */\nexport interface NectarConfig {\n /**\n * Bot token. Usually `process.env.DISCORD_TOKEN`; the CLI falls back to that variable when\n * this is omitted or empty.\n */\n token?: string | undefined;\n /** Application ID, for command registration. Falls back to `DISCORD_APPLICATION_ID`. */\n applicationId?: string | undefined;\n intents: ClientOptions[\"intents\"];\n partials?: ClientOptions[\"partials\"];\n /** Extra discord.js client options. `intents` and `partials` above take precedence. */\n client?: Partial<ClientOptions>;\n /** Import every handler at startup. Defaults to `true` in production, `false` otherwise. */\n eager?: boolean;\n /** Overrides `NODE_ENV`. */\n env?: Env;\n /**\n * Framework log level and sink. The default sink prints to the console; pass `sink` to hand\n * records to your own logger. Handlers are free to log however they like.\n */\n logger?: LoggerOptions;\n /** Called with every framework signal: interaction lifecycle, failures, gateway state, shutdown. */\n observe?: (signal: Signal) => void;\n /** Plugins, in the order their hooks run. This is the only way to register one. */\n plugins?: NectarPlugin[];\n /** Route directory, relative to the project root. Defaults to `app`. */\n appDir?: string;\n /** Build output, relative to the project root. Defaults to `.nectar`. */\n outDir?: string;\n dev?: {\n /** Guilds that receive commands instantly while developing. */\n guilds?: string[];\n };\n commands?: {\n /**\n * Where commands are registered outside development: everywhere, or only in the listed\n * guilds. Defaults to `\"global\"`. Development always uses `dev.guilds`.\n */\n target?: \"global\" | string[];\n };\n /**\n * Overrides for one environment, applied once the environment is known. Each key replaces\n * the value above it; nested objects are not merged.\n */\n environments?: Partial<Record<Env, Partial<Omit<NectarConfig, \"env\" | \"environments\">>>>;\n}\n\nexport function defineConfig(config: NectarConfig): NectarConfig {\n return config;\n}\n\n/** The config one environment runs with: `environments[env]` over the rest. */\nexport function configFor(config: NectarConfig, env: Env): NectarConfig {\n const { environments, ...base } = config;\n return { ...base, ...environments?.[env] };\n}\n\nexport class ConfigError extends Error {\n constructor(\n readonly file: string,\n readonly detail: string,\n ) {\n super(`${file}: ${detail}`);\n this.name = \"ConfigError\";\n }\n}\n\nconst ENVS = new Set<string>([\"development\", \"test\", \"production\"]);\nconst LEVELS = new Set<string>([\"debug\", \"info\", \"warn\", \"error\"]);\n\n/** Every option, so a misspelled or removed one fails instead of being ignored. */\nconst OPTIONS: Record<keyof NectarConfig, true> = {\n token: true,\n applicationId: true,\n intents: true,\n partials: true,\n client: true,\n eager: true,\n env: true,\n logger: true,\n observe: true,\n plugins: true,\n appDir: true,\n outDir: true,\n dev: true,\n commands: true,\n environments: true,\n};\nconst DEV_OPTIONS: Record<keyof NonNullable<NectarConfig[\"dev\"]>, true> = { guilds: true };\nconst COMMANDS_OPTIONS: Record<keyof NonNullable<NectarConfig[\"commands\"]>, true> = {\n target: true,\n};\nconst LOGGER_OPTIONS: Record<keyof LoggerOptions, true> = { level: true, sink: true };\n\n/** Checks a loaded config's shape. Discord validates intent and partial values itself at login. */\nexport function validateConfig(value: unknown, file: string): NectarConfig {\n const fail = (detail: string): never => {\n throw new ConfigError(file, detail);\n };\n if (!isRecord(value)) fail(\"the default export must be an object. Use defineConfig({ ... }).\");\n const config = value as Record<string, unknown>;\n checkKeys(config, OPTIONS, \"\", fail);\n for (const [key, options] of [\n [\"dev\", DEV_OPTIONS],\n [\"commands\", COMMANDS_OPTIONS],\n [\"logger\", LOGGER_OPTIONS],\n ] as const) {\n if (isRecord(config[key])) checkKeys(config[key], options, `${key}.`, fail);\n }\n\n for (const key of [\"token\", \"applicationId\"] as const) {\n if (config[key] !== undefined && typeof config[key] !== \"string\") {\n fail(`\\`${key}\\` must be a string, usually read from process.env.`);\n }\n }\n if (config.intents === undefined) fail(\"`intents` is required. Use [] for none.\");\n if (!isBitfield(config.intents)) {\n fail(\"`intents` must be an array of intent names or bits, a single bit, or a bigint.\");\n }\n if (config.partials !== undefined && !Array.isArray(config.partials)) {\n fail(\"`partials` must be an array.\");\n }\n if (config.client !== undefined && !isRecord(config.client)) fail(\"`client` must be an object.\");\n if (config.eager !== undefined && typeof config.eager !== \"boolean\") {\n fail(\"`eager` must be a boolean.\");\n }\n if (config.env !== undefined && (typeof config.env !== \"string\" || !ENVS.has(config.env))) {\n fail('`env` must be \"development\", \"test\", or \"production\".');\n }\n if (config.logger !== undefined) {\n if (!isRecord(config.logger)) fail(\"`logger` must be an object.\");\n const { level, sink } = config.logger as Record<string, unknown>;\n if (level !== undefined && (typeof level !== \"string\" || !LEVELS.has(level))) {\n fail('`logger.level` must be \"debug\", \"info\", \"warn\", or \"error\".');\n }\n if (sink !== undefined && typeof sink !== \"function\") {\n fail(\"`logger.sink` must be a function that receives log records.\");\n }\n }\n if (config.observe !== undefined && typeof config.observe !== \"function\") {\n fail(\"`observe` must be a function that receives signals.\");\n }\n if (config.plugins !== undefined) validatePlugins(config.plugins, fail);\n for (const key of [\"appDir\", \"outDir\"] as const) {\n const dir = config[key];\n if (dir !== undefined && (typeof dir !== \"string\" || dir === \"\")) {\n fail(`\\`${key}\\` must be a non-empty string.`);\n }\n }\n if (config.dev !== undefined) {\n if (!isRecord(config.dev)) fail(\"`dev` must be an object.\");\n const guilds = (config.dev as Record<string, unknown>).guilds;\n if (guilds !== undefined && !isGuildList(guilds)) {\n fail(\"`dev.guilds` must be an array of guild ID strings.\");\n }\n }\n if (config.commands !== undefined) {\n if (!isRecord(config.commands)) fail(\"`commands` must be an object.\");\n const target = (config.commands as Record<string, unknown>).target;\n if (target !== undefined && target !== \"global\" && !isGuildList(target)) {\n fail('`commands.target` must be \"global\" or an array of guild ID strings.');\n }\n }\n if (config.environments !== undefined) validateEnvironments(config, file, fail);\n return config as unknown as NectarConfig;\n}\n\n/** Each override must name an environment, and the config it produces must be valid. */\nfunction validateEnvironments(\n config: Record<string, unknown>,\n file: string,\n fail: (detail: string) => never,\n): void {\n if (!isRecord(config.environments)) fail(\"`environments` must be an object.\");\n for (const [name, override] of Object.entries(config.environments as Record<string, unknown>)) {\n const where = `\\`environments.${name}\\``;\n if (!ENVS.has(name)) fail(`${where}: use \"development\", \"test\", or \"production\" as the key.`);\n if (!isRecord(override)) fail(`${where} must be an object.`);\n for (const key of [\"env\", \"environments\"]) {\n if (key in (override as Record<string, unknown>)) fail(`${where} cannot set \\`${key}\\`.`);\n }\n try {\n validateConfig(configFor(config as unknown as NectarConfig, name as Env), file);\n } catch (error) {\n if (error instanceof ConfigError) fail(`${where}: ${error.detail}`);\n throw error;\n }\n }\n}\n\n/** Names `nectar` already answers to. A plugin command cannot take one. */\nconst BUILTIN_COMMANDS = new Set([\n \"dev\",\n \"build\",\n \"check\",\n \"routes\",\n \"manifest\",\n \"sync\",\n \"start\",\n \"clean\",\n \"info\",\n \"help\",\n]);\n\nfunction validatePlugins(value: unknown, fail: (detail: string) => never): void {\n if (!Array.isArray(value)) fail(\"`plugins` must be an array of plugins.\");\n const names = new Set<string>();\n const commands = new Map<string, string>();\n value.forEach((plugin: unknown, index) => {\n if (!isRecord(plugin) || typeof plugin.name !== \"string\" || plugin.name === \"\") {\n fail(\n `\\`plugins[${index}]\\` must be an object with a non-empty \\`name\\`. Use definePlugin({ ... }).`,\n );\n }\n const name = plugin.name as string;\n if (names.has(name)) fail(`Plugin \"${name}\" is listed twice.`);\n names.add(name);\n for (const hook of [\"transform\", \"types\", \"start\", \"stop\", \"startGlobal\", \"stopGlobal\"]) {\n if (plugin[hook] !== undefined && typeof plugin[hook] !== \"function\") {\n fail(`Plugin \"${name}\": \\`${hook}\\` must be a function.`);\n }\n }\n if (plugin.commands === undefined) return;\n if (!Array.isArray(plugin.commands)) fail(`Plugin \"${name}\": \\`commands\\` must be an array.`);\n for (const command of plugin.commands as unknown[]) {\n if (\n !isRecord(command) ||\n typeof command.name !== \"string\" ||\n !/^[a-z][a-z0-9-]*$/.test(command.name) ||\n typeof command.description !== \"string\" ||\n typeof command.run !== \"function\"\n ) {\n fail(\n `Plugin \"${name}\": every command needs a lowercase \\`name\\`, a \\`description\\`, and a \\`run\\` function.`,\n );\n }\n const commandName = command.name as string;\n if (BUILTIN_COMMANDS.has(commandName)) {\n fail(`Plugin \"${name}\": command \"${commandName}\" is built into nectar. Pick another name.`);\n }\n const owner = commands.get(commandName);\n if (owner !== undefined && owner !== name) {\n fail(`Plugins \"${owner}\" and \"${name}\" both define the command \"${commandName}\".`);\n }\n commands.set(commandName, name);\n }\n });\n}\n\n/** Fails on the first key `options` doesn't have, naming a likely intended one when there is one. */\nfunction checkKeys(\n value: Record<string, unknown>,\n options: Record<string, true>,\n prefix: string,\n fail: (detail: string) => never,\n): void {\n for (const key of Object.keys(value)) {\n if (Object.hasOwn(options, key)) continue;\n const near = Object.keys(options).find(\n (option) =>\n option.toLowerCase() === key.toLowerCase() || option === `${key}s` || `${option}s` === key,\n );\n fail(\n `\\`${prefix}${key}\\` isn't a config option.${\n near === undefined\n ? \" The options are listed at https://nectar-js.github.io/nectar/reference/config.\"\n : ` Did you mean \\`${prefix}${near}\\`?`\n }`,\n );\n }\n}\n\nfunction isGuildList(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((g) => typeof g === \"string\" && /^\\d+$/.test(g));\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isBitfield(value: unknown): boolean {\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"string\") {\n return true;\n }\n if (Array.isArray(value)) {\n return value.every(\n (v) => typeof v === \"number\" || typeof v === \"string\" || typeof v === \"bigint\",\n );\n }\n return isRecord(value) && \"bitfield\" in value;\n}\n","import { type BitFieldResolvable, GatewayIntentBits, IntentsBitField } from \"discord.js\";\nimport type { Diagnostic, DiagnosticCode } from \"../compiler/diagnostics.js\";\nimport type { CompiledEvent } from \"./compile.js\";\n\ntype Intent = keyof typeof GatewayIntentBits;\n\nconst PRIVILEGED: ReadonlySet<Intent> = new Set([\n \"GuildMembers\",\n \"GuildPresences\",\n \"MessageContent\",\n]);\n\nconst guildOrDm = (guild: Intent, dm: Intent): Intent[] => [guild, dm];\n\n/**\n * Which intent a gateway event needs. A list means any one of them is enough: message events\n * arrive with either the guild or the direct message intent. Events missing here need none.\n */\nconst REQUIRED: Record<string, Intent[]> = {\n guildCreate: [\"Guilds\"],\n guildUpdate: [\"Guilds\"],\n guildDelete: [\"Guilds\"],\n guildAvailable: [\"Guilds\"],\n guildUnavailable: [\"Guilds\"],\n channelCreate: [\"Guilds\"],\n channelUpdate: [\"Guilds\"],\n channelDelete: [\"Guilds\"],\n channelPinsUpdate: [\"Guilds\"],\n threadCreate: [\"Guilds\"],\n threadUpdate: [\"Guilds\"],\n threadDelete: [\"Guilds\"],\n threadListSync: [\"Guilds\"],\n threadMemberUpdate: [\"Guilds\"],\n threadMembersUpdate: [\"GuildMembers\"],\n stageInstanceCreate: [\"Guilds\"],\n stageInstanceUpdate: [\"Guilds\"],\n stageInstanceDelete: [\"Guilds\"],\n roleCreate: [\"Guilds\"],\n roleUpdate: [\"Guilds\"],\n roleDelete: [\"Guilds\"],\n guildMemberAdd: [\"GuildMembers\"],\n guildMemberUpdate: [\"GuildMembers\"],\n guildMemberRemove: [\"GuildMembers\"],\n guildMemberAvailable: [\"GuildMembers\"],\n guildMembersChunk: [\"GuildMembers\"],\n userUpdate: [\"GuildMembers\"],\n guildBanAdd: [\"GuildModeration\"],\n guildBanRemove: [\"GuildModeration\"],\n guildAuditLogEntryCreate: [\"GuildModeration\"],\n emojiCreate: [\"GuildExpressions\"],\n emojiUpdate: [\"GuildExpressions\"],\n emojiDelete: [\"GuildExpressions\"],\n stickerCreate: [\"GuildExpressions\"],\n stickerUpdate: [\"GuildExpressions\"],\n stickerDelete: [\"GuildExpressions\"],\n guildSoundboardSoundCreate: [\"GuildExpressions\"],\n guildSoundboardSoundUpdate: [\"GuildExpressions\"],\n guildSoundboardSoundDelete: [\"GuildExpressions\"],\n guildSoundboardSoundsUpdate: [\"GuildExpressions\"],\n guildIntegrationsUpdate: [\"GuildIntegrations\"],\n webhooksUpdate: [\"GuildWebhooks\"],\n inviteCreate: [\"GuildInvites\"],\n inviteDelete: [\"GuildInvites\"],\n voiceStateUpdate: [\"GuildVoiceStates\"],\n voiceChannelEffectSend: [\"GuildVoiceStates\"],\n presenceUpdate: [\"GuildPresences\"],\n messageCreate: guildOrDm(\"GuildMessages\", \"DirectMessages\"),\n messageUpdate: guildOrDm(\"GuildMessages\", \"DirectMessages\"),\n messageDelete: guildOrDm(\"GuildMessages\", \"DirectMessages\"),\n messageDeleteBulk: [\"GuildMessages\"],\n messageReactionAdd: guildOrDm(\"GuildMessageReactions\", \"DirectMessageReactions\"),\n messageReactionRemove: guildOrDm(\"GuildMessageReactions\", \"DirectMessageReactions\"),\n messageReactionRemoveAll: guildOrDm(\"GuildMessageReactions\", \"DirectMessageReactions\"),\n messageReactionRemoveEmoji: guildOrDm(\"GuildMessageReactions\", \"DirectMessageReactions\"),\n typingStart: guildOrDm(\"GuildMessageTyping\", \"DirectMessageTyping\"),\n messagePollVoteAdd: guildOrDm(\"GuildMessagePolls\", \"DirectMessagePolls\"),\n messagePollVoteRemove: guildOrDm(\"GuildMessagePolls\", \"DirectMessagePolls\"),\n guildScheduledEventCreate: [\"GuildScheduledEvents\"],\n guildScheduledEventUpdate: [\"GuildScheduledEvents\"],\n guildScheduledEventDelete: [\"GuildScheduledEvents\"],\n guildScheduledEventUserAdd: [\"GuildScheduledEvents\"],\n guildScheduledEventUserRemove: [\"GuildScheduledEvents\"],\n autoModerationRuleCreate: [\"AutoModerationConfiguration\"],\n autoModerationRuleUpdate: [\"AutoModerationConfiguration\"],\n autoModerationRuleDelete: [\"AutoModerationConfiguration\"],\n autoModerationActionExecution: [\"AutoModerationExecution\"],\n};\n\n/** The intents an event route depends on, for tooling. Empty when it needs none. */\nexport function requiredIntents(event: string): readonly Intent[] {\n return REQUIRED[event] ?? [];\n}\n\n/**\n * One warning per event whose handlers can never fire with the configured intents. Never\n * changes the config: privileged intents in particular must be a deliberate choice, made here\n * and in the developer portal.\n */\nexport function checkIntents(\n events: readonly CompiledEvent[],\n intents: BitFieldResolvable<Intent, number>,\n configFile: string,\n): Diagnostic[] {\n const enabled = new IntentsBitField(IntentsBitField.resolve(intents));\n const diagnostics: Diagnostic[] = [];\n for (const event of events) {\n const needed = requiredIntents(event.name);\n if (needed.length === 0 || needed.some((intent) => enabled.has(GatewayIntentBits[intent]))) {\n continue;\n }\n const first = event.handlers[0];\n if (first === undefined) continue;\n const names = needed.map((i) => `\"${i}\"`).join(\" or \");\n const privileged = needed.filter((i) => PRIVILEGED.has(i));\n diagnostics.push({\n code: \"missing-intent\" satisfies DiagnosticCode,\n severity: \"warning\",\n message: `\"${event.name}\" never fires without the ${names} intent. Add it to intents in ${configFile}.${\n privileged.length === 0\n ? \"\"\n : ` ${privileged.map((i) => `\"${i}\"`).join(\" and \")} is privileged, so also turn it on under Bot in the Discord Developer Portal.`\n }`,\n file: first.route.file,\n route: first.route.id,\n });\n }\n return diagnostics;\n}\n","import type {\n APIApplicationCommand,\n APIApplicationCommandOption,\n RESTPostAPIApplicationCommandsJSONBody,\n} from \"discord-api-types/v10\";\nimport { ApplicationCommandType, ApplicationIntegrationType } from \"discord-api-types/v10\";\n\n/**\n * A command reduced to the fields that decide whether Discord would consider it changed.\n *\n * Discord fills in defaults (`nsfw: false`, `integration_types: [0]`, `required: false`, empty\n * localization maps as `null`) and adds bookkeeping (`id`, `version`, `application_id`,\n * `dm_permission`) when it echoes a command back. Both sides pass through here so a no-op sync\n * produces an empty diff.\n */\nexport type NormalizedCommand = Record<string, unknown>;\n\nexport type AnyCommand = RESTPostAPIApplicationCommandsJSONBody | APIApplicationCommand;\n\n/** `<type>:<name>`. Discord allows the same name across command types. */\nexport function commandKey(command: { type?: number | undefined; name: string }): string {\n return `${command.type ?? ApplicationCommandType.ChatInput}:${command.name}`;\n}\n\nexport function normalizeCommand(command: AnyCommand): NormalizedCommand {\n const c = command as unknown as Record<string, unknown>;\n const type = (c.type as number | undefined) ?? ApplicationCommandType.ChatInput;\n return compact({\n type,\n name: c.name,\n name_localizations: localizations(c.name_localizations),\n description: type === ApplicationCommandType.ChatInput ? (c.description ?? \"\") : \"\",\n description_localizations: localizations(c.description_localizations),\n options: options(c.options),\n default_member_permissions:\n c.default_member_permissions == null ? undefined : String(c.default_member_permissions),\n nsfw: c.nsfw === true ? true : undefined,\n contexts: numbers(c.contexts),\n integration_types: numbers(c.integration_types) ?? [ApplicationIntegrationType.GuildInstall],\n });\n}\n\nfunction options(value: unknown): NormalizedCommand[] | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n return (value as APIApplicationCommandOption[]).map((option) => {\n const o = option as unknown as Record<string, unknown>;\n return compact({\n type: o.type,\n name: o.name,\n name_localizations: localizations(o.name_localizations),\n description: o.description,\n description_localizations: localizations(o.description_localizations),\n required: o.required === true ? true : undefined,\n autocomplete: o.autocomplete === true ? true : undefined,\n choices: choices(o.choices),\n options: options(o.options),\n channel_types: numbers(o.channel_types),\n min_value: number(o.min_value),\n max_value: number(o.max_value),\n min_length: number(o.min_length),\n max_length: number(o.max_length),\n });\n });\n}\n\nfunction choices(value: unknown): NormalizedCommand[] | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n return value.map((choice: Record<string, unknown>) =>\n compact({\n name: choice.name,\n value: choice.value,\n name_localizations: localizations(choice.name_localizations),\n }),\n );\n}\n\nfunction localizations(value: unknown): Record<string, string> | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const entries = Object.entries(value as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n );\n if (entries.length === 0) return undefined;\n entries.sort(([a], [b]) => a.localeCompare(b));\n return Object.fromEntries(entries);\n}\n\n/** Sorted, deduplicated. Discord treats these as sets. */\nfunction numbers(value: unknown): number[] | undefined {\n if (!Array.isArray(value)) return undefined;\n return [...new Set(value as number[])].sort((a, b) => a - b);\n}\n\nfunction number(value: unknown): number | undefined {\n return typeof value === \"number\" ? value : undefined;\n}\n\nfunction compact(object: Record<string, unknown>): NormalizedCommand {\n const out: NormalizedCommand = {};\n for (const [key, value] of Object.entries(object)) {\n if (value !== undefined) out[key] = value;\n }\n return out;\n}\n","import { stableStringify } from \"../manifest/emit.js\";\nimport { type AnyCommand, commandKey, normalizeCommand } from \"./normalize.js\";\n\nexport interface CommandDiff {\n /** Command names, `type:name` when the type is not chat input. */\n added: string[];\n removed: string[];\n changed: string[];\n unchanged: string[];\n hasChanges: boolean;\n}\n\n/** Compares what the app wants registered with what Discord currently has. */\nexport function diffCommands(desired: AnyCommand[], remote: AnyCommand[]): CommandDiff {\n const want = new Map(desired.map((c) => [commandKey(c), stableStringify(normalizeCommand(c))]));\n const have = new Map(remote.map((c) => [commandKey(c), stableStringify(normalizeCommand(c))]));\n\n const diff: CommandDiff = {\n added: [],\n removed: [],\n changed: [],\n unchanged: [],\n hasChanges: false,\n };\n for (const [key, body] of want) {\n const current = have.get(key);\n if (current === undefined) diff.added.push(label(key));\n else if (current === body) diff.unchanged.push(label(key));\n else diff.changed.push(label(key));\n }\n for (const key of have.keys()) {\n if (!want.has(key)) diff.removed.push(label(key));\n }\n for (const list of [diff.added, diff.removed, diff.changed, diff.unchanged]) list.sort();\n diff.hasChanges = diff.added.length + diff.removed.length + diff.changed.length > 0;\n return diff;\n}\n\nfunction label(key: string): string {\n return key.startsWith(\"1:\") ? key.slice(2) : key;\n}\n","import type {\n APIApplicationCommand,\n RESTPostAPIApplicationCommandsJSONBody,\n} from \"discord-api-types/v10\";\nimport { Routes } from \"discord-api-types/v10\";\nimport { RegistrationError } from \"./errors.js\";\n\n/** The two calls registration needs. discord.js's `REST` satisfies this. */\nexport interface CommandRest {\n get(route: `/${string}`, options?: { query: URLSearchParams }): Promise<unknown>;\n put(route: `/${string}`, options: { body: unknown }): Promise<unknown>;\n}\n\nexport type Scope = \"global\" | { guild: string };\n\n/** `global` or `guild:<id>`. Used for cache keys and messages. */\nexport function scopeKey(scope: Scope): string {\n return scope === \"global\" ? \"global\" : `guild:${scope.guild}`;\n}\n\nfunction scopeRoute(applicationId: string, scope: Scope): `/${string}` {\n return scope === \"global\"\n ? Routes.applicationCommands(applicationId)\n : Routes.applicationGuildCommands(applicationId, scope.guild);\n}\n\n/** With full localization maps, which Discord leaves out unless asked. */\nexport async function fetchCommands(\n rest: CommandRest,\n applicationId: string,\n scope: Scope,\n): Promise<APIApplicationCommand[]> {\n return (await rest.get(scopeRoute(applicationId, scope), {\n query: new URLSearchParams({ with_localizations: \"true\" }),\n })) as APIApplicationCommand[];\n}\n\n/** Bulk overwrite: Discord replaces the scope's whole command set with `commands`. */\nexport async function putCommands(\n rest: CommandRest,\n applicationId: string,\n scope: Scope,\n commands: RESTPostAPIApplicationCommandsJSONBody[],\n): Promise<void> {\n try {\n await rest.put(scopeRoute(applicationId, scope), { body: commands });\n } catch (error) {\n throw RegistrationError.from(error, scope, commands) ?? error;\n }\n}\n","import type { RESTPostAPIApplicationCommandsJSONBody } from \"discord-api-types/v10\";\nimport { type Scope, scopeKey } from \"./remote.js\";\n\nexport interface RegistrationProblem {\n /** Command name, or `null` when Discord rejected the request as a whole. */\n command: string | null;\n /** Dotted path inside the command, with option and choice indices replaced by their names. */\n field: string;\n message: string;\n}\n\n/** Discord rejected a bulk overwrite. Wraps the `DiscordAPIError` with per-command detail. */\nexport class RegistrationError extends Error {\n constructor(\n readonly scope: Scope,\n readonly problems: RegistrationProblem[],\n override readonly cause: unknown,\n ) {\n super(\n `Discord rejected the ${scopeKey(scope)} command registration:\\n${problems\n .map(\n (p) =>\n ` ${p.command ?? \"(request)\"}${p.field === \"\" ? \"\" : ` ${p.field}`}: ${p.message}`,\n )\n .join(\"\\n\")}`,\n );\n this.name = \"RegistrationError\";\n }\n\n /** `null` when `error` is not a Discord API error. */\n static from(\n error: unknown,\n scope: Scope,\n commands: RESTPostAPIApplicationCommandsJSONBody[],\n ): RegistrationError | null {\n if (!isDiscordApiError(error)) return null;\n const problems: RegistrationProblem[] = [];\n if (error.rawError.errors !== undefined) {\n collect(error.rawError.errors, [], commands, problems);\n }\n if (problems.length === 0) {\n problems.push({ command: null, field: \"\", message: error.rawError.message });\n }\n return new RegistrationError(scope, problems, error);\n }\n}\n\ninterface DiscordApiErrorLike {\n rawError: { message: string; errors?: unknown };\n}\n\nfunction isDiscordApiError(error: unknown): error is DiscordApiErrorLike {\n if (typeof error !== \"object\" || error === null || !(\"rawError\" in error)) return false;\n const raw = (error as { rawError: unknown }).rawError;\n return (\n typeof raw === \"object\" &&\n raw !== null &&\n typeof (raw as { message?: unknown }).message === \"string\"\n );\n}\n\n/**\n * Discord nests errors by request path, `{ \"0\": { options: { \"1\": { description: { _errors } } } } }`.\n * The top-level index is the command in the bulk body; deeper indices are options and choices.\n */\nfunction collect(\n node: unknown,\n path: (string | number)[],\n commands: RESTPostAPIApplicationCommandsJSONBody[],\n out: RegistrationProblem[],\n) {\n if (typeof node === \"string\") {\n out.push(problem(path, commands, node));\n return;\n }\n if (typeof node !== \"object\" || node === null) return;\n for (const [key, value] of Object.entries(node)) {\n if (key === \"_errors\" && Array.isArray(value)) {\n for (const entry of value) {\n const message =\n typeof entry === \"object\" && entry !== null && \"message\" in entry\n ? String((entry as { message: unknown }).message)\n : String(entry);\n out.push(problem(path, commands, message));\n }\n } else {\n collect(value, [...path, /^\\d+$/.test(key) ? Number(key) : key], commands, out);\n }\n }\n}\n\nfunction problem(\n path: (string | number)[],\n commands: RESTPostAPIApplicationCommandsJSONBody[],\n message: string,\n): RegistrationProblem {\n const [first, ...rest] = path;\n const command = typeof first === \"number\" ? commands[first] : undefined;\n if (command === undefined) return { command: null, field: path.join(\".\"), message };\n\n const field: string[] = [];\n let cursor: unknown = command;\n for (const segment of rest) {\n if (typeof segment === \"number\" && Array.isArray(cursor)) {\n cursor = cursor[segment];\n const name = (cursor as { name?: unknown } | undefined)?.name;\n field.push(typeof name === \"string\" ? name : String(segment));\n } else {\n cursor = (cursor as Record<string, unknown> | undefined)?.[segment];\n field.push(String(segment));\n }\n }\n return { command: command.name, field: field.join(\".\"), message };\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n type APIApplicationCommand,\n ApplicationCommandType,\n type RESTPostAPIApplicationCommandsJSONBody,\n} from \"discord-api-types/v10\";\nimport { stableStringify } from \"../manifest/emit.js\";\nimport { type CommandDiff, diffCommands } from \"./diff.js\";\nimport { commandKey, normalizeCommand } from \"./normalize.js\";\nimport { type CommandRest, fetchCommands, putCommands, type Scope, scopeKey } from \"./remote.js\";\n\nexport const REGISTRATION_CACHE_FILE = \"registration.json\";\nconst CACHE_VERSION = 1;\n\n/**\n * The command types an app declares. Other commands in a scope, like the Entry Point command\n * Discord creates for Activities, go back unchanged in every overwrite: Discord rejects one\n * that drops them, with error 50240.\n */\nconst DECLARED_TYPES: ReadonlySet<number> = new Set([\n ApplicationCommandType.ChatInput,\n ApplicationCommandType.User,\n ApplicationCommandType.Message,\n]);\n\n/** What the last successful sync sent, so an unchanged app skips the remote read. */\ninterface RegistrationCache {\n version: typeof CACHE_VERSION;\n applicationId: string;\n /** Scope key to hash of the normalized payloads registered there. */\n scopes: Record<string, string>;\n}\n\nexport interface SyncOptions {\n rest: CommandRest;\n applicationId: string;\n commands: RESTPostAPIApplicationCommandsJSONBody[];\n scopes: Scope[];\n /** Directory holding `registration.json`. No cache when omitted. */\n cacheDir?: string;\n /** Compute diffs and report, but write nothing to Discord or the cache. */\n dryRun?: boolean;\n /** Proceed past the safety guard. */\n force?: boolean;\n}\n\nexport interface ScopeSync {\n scope: Scope;\n /** `null` when the cache proved nothing changed and Discord was not consulted. */\n diff: CommandDiff | null;\n /** A bulk overwrite was sent. */\n applied: boolean;\n}\n\nexport interface SyncResult {\n scopes: ScopeSync[];\n /** Why the guard would block this sync. Empty when it is safe. */\n unsafe: string[];\n}\n\n/** Thrown instead of applying when the guard trips and `force` is not set. */\nexport class UnsafeSyncError extends Error {\n constructor(readonly reasons: string[]) {\n super(\n `Refusing to register commands:\\n${reasons.map((r) => ` ${r}`).join(\"\\n\")}\\nPass force to do it anyway.`,\n );\n this.name = \"UnsafeSyncError\";\n }\n}\n\n/**\n * Reconciles every scope: read remote, diff, bulk overwrite only when something differs.\n * All scopes are read and checked before any is written, so a guard failure changes nothing.\n */\nexport async function syncCommands(options: SyncOptions): Promise<SyncResult> {\n const {\n rest,\n applicationId,\n commands,\n scopes,\n cacheDir,\n dryRun = false,\n force = false,\n } = options;\n const cache = cacheDir === undefined ? null : readCache(cacheDir);\n const hash = hashCommands(commands);\n const unsafe: string[] = [];\n\n if (cache !== null && cache.applicationId !== applicationId) {\n unsafe.push(\n `The application ID changed from ${cache.applicationId} to ${applicationId}. Commands registered under the old application are left as they are.`,\n );\n }\n const targets = new Set(scopes.map(scopeKey));\n for (const key of Object.keys(cache?.scopes ?? {})) {\n if (!targets.has(key)) {\n unsafe.push(\n `${key} received commands last time but is no longer a target. Its commands stay registered on Discord until removed.`,\n );\n }\n }\n\n const results: ScopeSync[] = [];\n /** Remote commands of other types, by scope key, to send back in the overwrite. */\n const kept = new Map<string, RESTPostAPIApplicationCommandsJSONBody[]>();\n for (const scope of scopes) {\n const key = scopeKey(scope);\n const cached =\n cache !== null && cache.applicationId === applicationId && cache.scopes[key] === hash;\n if (cached) {\n results.push({ scope, diff: null, applied: false });\n continue;\n }\n const remote = await fetchCommands(rest, applicationId, scope);\n const declared = remote.filter((c) => DECLARED_TYPES.has(c.type));\n kept.set(key, remote.filter((c) => !DECLARED_TYPES.has(c.type)).map(resubmit));\n const diff = diffCommands(commands, declared);\n if (commands.length === 0 && declared.length > 0) {\n unsafe.push(`${key} has ${declared.length} command(s) registered and the app declares none.`);\n }\n results.push({ scope, diff, applied: false });\n }\n\n if (dryRun) return { scopes: results, unsafe };\n if (unsafe.length > 0 && !force) throw new UnsafeSyncError(unsafe);\n\n const next: RegistrationCache = { version: CACHE_VERSION, applicationId, scopes: {} };\n for (const result of results) {\n if (result.diff?.hasChanges) {\n const body = [...commands, ...(kept.get(scopeKey(result.scope)) ?? [])];\n await putCommands(rest, applicationId, result.scope, body);\n result.applied = true;\n }\n next.scopes[scopeKey(result.scope)] = hash;\n // Persist after every scope so a failure halfway does not forget the ones already done.\n if (cacheDir !== undefined) writeCache(cacheDir, next);\n }\n return { scopes: results, unsafe };\n}\n\n/** A fetched command as an overwrite takes it, without the fields Discord fills in itself. */\nfunction resubmit(command: APIApplicationCommand): RESTPostAPIApplicationCommandsJSONBody {\n const {\n id: _id,\n application_id: _application,\n guild_id: _guild,\n version: _version,\n name_localized: _name,\n description_localized: _description,\n ...body\n } = command;\n // Its type is one Nectar doesn't declare, so the body is passed through as Discord gave it.\n return body as RESTPostAPIApplicationCommandsJSONBody;\n}\n\n/** Order-insensitive, like the diff: reordering commands is not a change. */\nfunction hashCommands(commands: RESTPostAPIApplicationCommandsJSONBody[]): string {\n const normalized = commands\n .map((c) => [commandKey(c), normalizeCommand(c)] as const)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([, c]) => c);\n return createHash(\"sha256\").update(stableStringify(normalized)).digest(\"hex\");\n}\n\nfunction readCache(dir: string): RegistrationCache | null {\n let text: string;\n try {\n text = readFileSync(path.join(dir, REGISTRATION_CACHE_FILE), \"utf8\");\n } catch {\n return null;\n }\n const parsed: unknown = JSON.parse(text);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as { version?: unknown }).version !== CACHE_VERSION\n ) {\n return null;\n }\n return parsed as RegistrationCache;\n}\n\nfunction writeCache(dir: string, cache: RegistrationCache) {\n mkdirSync(dir, { recursive: true });\n writeFileSync(path.join(dir, REGISTRATION_CACHE_FILE), `${stableStringify(cache)}\\n`);\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { b as loadModule, h as paramValidatorsOf, m as findInvalidParam, t as PluginError, u as registerComponentRoutes, w as decodeCustomId } from "./plugins-CGvM19v9.js";
1
+ import { E as decodeCustomId, b as loadModule, h as paramValidatorsOf, m as findInvalidParam, t as PluginError, u as registerComponentRoutes } from "./plugins-BCYwtuo-.js";
2
2
  import path from "node:path";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { Client, DiscordjsError, DiscordjsErrorCodes, Events } from "discord.js";
@@ -334,14 +334,26 @@ function routeInfo(state, route) {
334
334
  id: route.id,
335
335
  category: route.category,
336
336
  path: route.path,
337
- file: absolute(state, route.file)
337
+ file: paths(state, route).file
338
338
  };
339
339
  }
340
340
  function chains(state, route) {
341
- return {
342
- middleware: route.middleware.map((f) => absolute(state, f)),
343
- errors: route.errors.map((f) => absolute(state, f))
344
- };
341
+ return paths(state, route);
342
+ }
343
+ /** A route's absolute paths, joined on its first interaction instead of every one. */
344
+ const resolved = /* @__PURE__ */ new WeakMap();
345
+ function paths(state, route) {
346
+ let found = resolved.get(route);
347
+ if (found === void 0 || found.appDir !== state.appDir) {
348
+ found = {
349
+ appDir: state.appDir,
350
+ file: absolute(state, route.file),
351
+ middleware: route.middleware.map((f) => absolute(state, f)),
352
+ errors: route.errors.map((f) => absolute(state, f))
353
+ };
354
+ resolved.set(route, found);
355
+ }
356
+ return found;
345
357
  }
346
358
  //#endregion
347
359
  //#region src/runtime/dispatch.ts
@@ -888,4 +900,4 @@ function envFromProcess() {
888
900
  //#endregion
889
901
  export { bindEvents as a, ModuleRegistry as c, createLogger as i, createSignals as l, createRuntime as n, createInteractionDispatcher as o, manifestFiles as r, HandlerLoadError as s, LoginError as t, loadManifest as u };
890
902
 
891
- //# sourceMappingURL=runtime-CZJeZvSL.js.map
903
+ //# sourceMappingURL=runtime-B_bjrg1W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-B_bjrg1W.js","names":["describe"],"sources":["../src/components/matcher.ts","../src/manifest/load.ts","../src/runtime/signals.ts","../src/runtime/errors.ts","../src/runtime/middleware.ts","../src/runtime/modules.ts","../src/runtime/state.ts","../src/runtime/dispatch.ts","../src/runtime/events.ts","../src/runtime/logger.ts","../src/runtime/runtime.ts"],"sourcesContent":["import type { ComponentKind } from \"./compile.js\";\nimport { decodeCustomId } from \"./customId.js\";\n\n/** What the matcher needs from a route. Both compiled and manifest routes satisfy it. */\nexport interface MatchableRoute {\n kind: ComponentKind;\n shortId: string;\n params: string[];\n catchAll: string | null;\n}\n\nexport type MatchResult<Route extends MatchableRoute = MatchableRoute> =\n | { ok: true; route: Route; params: Record<string, string | string[]> }\n /** The custom ID is not Nectar's. Hand-built components should be left alone. */\n | { ok: false; reason: \"not-nectar\" }\n /** The ID carries the Nectar prefix but cannot be decoded, names no route, or has the wrong number of values. */\n | { ok: false; reason: \"malformed\" | \"unknown-route\" | \"param-count\" };\n\nexport interface ComponentMatcher<Route extends MatchableRoute = MatchableRoute> {\n match(kind: ComponentKind, customId: string): MatchResult<Route>;\n}\n\n/**\n * Resolves incoming custom IDs to routes.\n *\n * Custom IDs carry the route's short ID, so matching is a direct lookup rather than a pattern\n * scan. The interaction kind is part of the key, so a button's ID sent back as a modal\n * submission finds no route.\n */\nexport function createMatcher<Route extends MatchableRoute>(\n routes: readonly Route[],\n): ComponentMatcher<Route> {\n const byId = new Map<string, Route>();\n for (const route of routes) byId.set(`${route.kind}:${route.shortId}`, route);\n\n return {\n match(kind, customId) {\n const decoded = decodeCustomId(customId);\n if (!decoded.ok) return decoded;\n\n const route = byId.get(`${kind}:${decoded.shortId}`);\n if (route === undefined) return { ok: false, reason: \"unknown-route\" };\n\n const fixed = route.catchAll === null ? route.params.length : route.params.length - 1;\n if (\n route.catchAll === null ? decoded.values.length !== fixed : decoded.values.length < fixed\n ) {\n return { ok: false, reason: \"param-count\" };\n }\n\n const params: Record<string, string | string[]> = {};\n for (let i = 0; i < fixed; i++)\n params[route.params[i] as string] = decoded.values[i] as string;\n if (route.catchAll !== null) params[route.catchAll] = decoded.values.slice(fixed);\n return { ok: true, route, params };\n },\n };\n}\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { MANIFEST_VERSION, type Manifest } from \"./schema.js\";\n\nexport interface LoadedManifest {\n manifest: Manifest;\n /** Absolute path of the app directory the manifest was compiled from. */\n appDir: string;\n}\n\nexport class ManifestVersionError extends Error {\n constructor(\n readonly file: string,\n readonly found: unknown,\n ) {\n super(\n `${file} is manifest version ${String(found)}, this build of @nectar-js/nectar reads version ${MANIFEST_VERSION}. Run \\`nectar build\\` again.`,\n );\n this.name = \"ManifestVersionError\";\n }\n}\n\n/** Reads a manifest from disk and checks its version. Does not validate the rest of the shape. */\nexport function loadManifest(file: string): LoadedManifest {\n const absolute = path.resolve(file);\n const parsed: unknown = JSON.parse(readFileSync(absolute, \"utf8\"));\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new TypeError(`${absolute} is not a manifest object.`);\n }\n const manifest = parsed as Partial<Manifest>;\n if (manifest.version !== MANIFEST_VERSION) {\n throw new ManifestVersionError(absolute, manifest.version);\n }\n if (typeof manifest.appDir !== \"string\") {\n throw new TypeError(`${absolute} has no appDir.`);\n }\n return {\n manifest: manifest as Manifest,\n appDir: path.resolve(path.dirname(absolute), manifest.appDir),\n };\n}\n","import type { Interaction } from \"discord.js\";\nimport { ApplicationCommandType } from \"discord-api-types/v10\";\nimport type { Logger, RouteInfo } from \"./types.js\";\n\n/** Structured, non-sensitive description of an interaction for logs and signals. */\nexport interface InteractionMeta {\n type:\n | \"chatInput\"\n | \"userContextMenu\"\n | \"messageContextMenu\"\n | \"autocomplete\"\n | \"button\"\n | \"select\"\n | \"modal\"\n | \"unknown\";\n /** Full command name with subcommand group and subcommand, for commands and autocomplete. */\n command?: string;\n /** Custom ID with Nectar param values replaced by `*`. */\n customId?: string;\n guildId: string | null;\n channelId: string | null;\n userId: string | null;\n}\n\nexport interface RegistrationScopeResult {\n scope: string;\n /** A bulk overwrite was sent. */\n applied: boolean;\n}\n\n/** Everything the runtime reports about itself, without the timestamp. */\nexport type SignalData =\n | { type: \"interaction:start\"; trace: string; interaction: InteractionMeta }\n | { type: \"route:match\"; trace: string; interaction: InteractionMeta; route: RouteInfo }\n | {\n type: \"middleware:enter\";\n trace: string;\n interaction: InteractionMeta;\n route: RouteInfo;\n file: string;\n }\n | { type: \"handler:enter\"; trace: string; interaction: InteractionMeta; route: RouteInfo }\n | {\n type: \"handler:complete\";\n trace: string;\n interaction: InteractionMeta;\n route: RouteInfo;\n /** Milliseconds the handler took. */\n duration: number;\n }\n | {\n type: \"interaction:complete\";\n trace: string;\n interaction: InteractionMeta;\n route: RouteInfo;\n /** Milliseconds since the runtime received the interaction. */\n duration: number;\n /** `false` when a middleware stopped the chain before the handler. */\n handled: boolean;\n }\n | {\n type: \"interaction:fail\";\n trace: string;\n interaction: InteractionMeta;\n route: RouteInfo;\n error: unknown;\n /** The `error.ts` that handled it, or `null` for the default boundary. */\n boundary: string | null;\n }\n | {\n /** The interaction was refused before any application code ran. */\n type: \"interaction:reject\";\n trace: string;\n interaction: InteractionMeta;\n reason: RejectReason;\n /** Known when the route matched but a parameter failed validation. */\n route?: RouteInfo;\n /** The parameter that failed, for `invalid-param`. Its value is never included. */\n param?: string;\n }\n | { type: \"event:fail\"; event: string; route: RouteInfo; error: unknown; boundary: string | null }\n | { type: \"registration:start\"; scopes: string[] }\n | { type: \"registration:complete\"; scopes: RegistrationScopeResult[]; duration: number }\n | { type: \"gateway:connect\"; shard: number; resumed: boolean }\n | { type: \"gateway:disconnect\"; shard: number; code: number }\n | { type: \"shutdown\" };\n\nexport type RejectReason =\n /** A command, autocomplete option, or context menu Discord sent that no route serves. */\n | \"no-route\"\n /** An interaction type Nectar does not route at all. */\n | \"unknown-interaction\"\n /** A Nectar custom ID that cannot be decoded. */\n | \"malformed\"\n /** A decoded custom ID whose short ID names no route of that kind. */\n | \"unknown-route\"\n /** The wrong number of values for the route. */\n | \"param-count\"\n /** A route's own validator refused a value. */\n | \"invalid-param\";\n\nexport type SignalType = SignalData[\"type\"];\n\nexport type Signal = SignalData & {\n /** Epoch milliseconds. */\n at: number;\n};\n\nexport type SignalListener = (signal: Signal) => void;\n\nexport interface SignalEmitter {\n /** Delivers every signal to `listener`. Returns a function that unsubscribes. */\n on(listener: SignalListener): () => void;\n emit(data: SignalData): void;\n}\n\n/**\n * Fan-out for framework signals. Listeners run synchronously in subscription order; one that\n * throws is reported through the logger and does not affect the others or the interaction.\n */\nexport function createSignals(logger: Pick<Logger, \"error\">): SignalEmitter {\n const listeners = new Set<SignalListener>();\n return {\n on(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n emit(data) {\n if (listeners.size === 0) return;\n const signal: Signal = { ...data, at: Date.now() };\n for (const listener of listeners) {\n try {\n listener(signal);\n } catch (error) {\n logger.error(`A signal listener threw on ${data.type}.`, { error });\n }\n }\n },\n };\n}\n\n/** Reads the fields spec 31 asks for off a discord.js interaction, tolerating stubs. */\nexport function interactionMeta(interaction: Interaction): InteractionMeta {\n const i = interaction as unknown as InteractionLike;\n const guard = (name: GuardName) => typeof i[name] === \"function\" && i[name]?.() === true;\n const where = {\n guildId: i.guildId ?? null,\n channelId: i.channelId ?? null,\n userId: i.user?.id ?? null,\n };\n if (guard(\"isChatInputCommand\") || guard(\"isAutocomplete\")) {\n return {\n type: guard(\"isAutocomplete\") ? \"autocomplete\" : \"chatInput\",\n command: [\n i.commandName,\n i.options?.getSubcommandGroup(false) ?? null,\n i.options?.getSubcommand(false) ?? null,\n ]\n .filter((p): p is string => typeof p === \"string\")\n .join(\" \"),\n ...where,\n };\n }\n if (guard(\"isContextMenuCommand\")) {\n return {\n type:\n i.commandType === ApplicationCommandType.Message ? \"messageContextMenu\" : \"userContextMenu\",\n command: i.commandName ?? \"\",\n ...where,\n };\n }\n const kind = guard(\"isButton\")\n ? \"button\"\n : guard(\"isAnySelectMenu\")\n ? \"select\"\n : guard(\"isModalSubmit\")\n ? \"modal\"\n : null;\n if (kind !== null) return { type: kind, customId: redactCustomId(i.customId ?? \"\"), ...where };\n return { type: \"unknown\", ...where };\n}\n\n/**\n * Keeps the route part of a Nectar custom ID and hides the parameter values, which may carry\n * anything the application put there. Other custom IDs are not ours and pass through.\n */\nexport function redactCustomId(customId: string): string {\n if (!customId.startsWith(\"n:\")) return customId;\n const params = customId.indexOf(\":\", 2);\n return params === -1 ? customId : `${customId.slice(0, params)}:*`;\n}\n\ntype GuardName =\n | \"isChatInputCommand\"\n | \"isAutocomplete\"\n | \"isContextMenuCommand\"\n | \"isButton\"\n | \"isAnySelectMenu\"\n | \"isModalSubmit\";\n\ninterface InteractionLike extends Partial<Record<GuardName, () => boolean>> {\n commandName?: string;\n commandType?: number;\n customId?: string;\n guildId?: string | null;\n channelId?: string | null;\n user?: { id: string };\n options?: {\n getSubcommandGroup(required: false): string | null;\n getSubcommand(required: false): string | null;\n };\n}\n","import type { Interaction } from \"discord.js\";\nimport { MessageFlags } from \"discord-api-types/v10\";\nimport type { LogFields } from \"./logger.js\";\nimport type { ModuleRegistry } from \"./modules.js\";\nimport { type InteractionMeta, interactionMeta } from \"./signals.js\";\nimport type { ErrorHandler, EventContext, InteractionContext, Logger } from \"./types.js\";\n\n/** The reply the default boundary sends when an interaction is still unanswered. */\nexport const GENERIC_ERROR_REPLY = \"Something went wrong while handling that.\";\n\n/**\n * Passes an error through the route's boundaries, nearest first, then the default boundary.\n * A boundary handles the error by returning normally. Returning `\"unhandled\"` or throwing\n * hands it (or the newly thrown error) to the next one. Nothing is ever swallowed: the default\n * boundary always logs.\n *\n * `middleware` is the chain that ran before the handler; development output lists it.\n * Resolves to the boundary file that handled the error, or `null` for the default boundary.\n */\nexport async function handleError(\n error: unknown,\n ctx: InteractionContext | EventContext,\n boundaries: readonly string[],\n modules: ModuleRegistry,\n logger: Logger,\n middleware: readonly string[] = [],\n): Promise<string | null> {\n let current = error;\n for (const file of boundaries) {\n try {\n const boundary = await modules.loadDefault<ErrorHandler>(file, \"An error boundary\");\n const result = await boundary(current, ctx);\n if (result !== \"unhandled\") return file;\n } catch (thrown) {\n current = thrown;\n }\n }\n await defaultBoundary(current, ctx, logger, middleware);\n return null;\n}\n\n/** The structured metadata every framework log line about a route carries. */\nexport function logFields(\n ctx: InteractionContext | EventContext,\n meta?: InteractionMeta,\n): LogFields {\n const fields: LogFields = { route: ctx.route.id };\n if (!(\"interaction\" in ctx)) {\n fields.event = ctx.route.path.split(\"/\")[0];\n return fields;\n }\n const i = meta ?? interactionMeta(ctx.interaction);\n fields.trace = ctx.trace.id;\n fields.interaction = i.type;\n if (i.command !== undefined) fields.command = i.command;\n if (i.customId !== undefined) fields.customId = i.customId;\n fields.guild = i.guildId;\n fields.channel = i.channelId;\n fields.user = i.userId;\n return fields;\n}\n\nasync function defaultBoundary(\n error: unknown,\n ctx: InteractionContext | EventContext,\n logger: Logger,\n middleware: readonly string[],\n): Promise<void> {\n logger.error(\n ctx.env === \"development\"\n ? developmentReport(ctx, middleware)\n : `Unhandled error in ${ctx.route.id} (${ctx.route.file})`,\n { ...logFields(ctx), error },\n );\n\n if (!(\"interaction\" in ctx)) return;\n const interaction = ctx.interaction as RepliableLike;\n if (typeof interaction.isRepliable !== \"function\" || !interaction.isRepliable()) return;\n if (interaction.replied || interaction.deferred) return;\n\n try {\n await interaction.reply({ content: GENERIC_ERROR_REPLY, flags: MessageFlags.Ephemeral });\n } catch (replyError) {\n logger.error(`Could not send the error reply for ${ctx.route.id}`, {\n ...logFields(ctx),\n error: replyError,\n });\n }\n}\n\n/** Where the failure sits in the app, so the developer can go straight to the boundary. */\nfunction developmentReport(\n ctx: InteractionContext | EventContext,\n middleware: readonly string[],\n): string {\n const rows: [string, string][] = [[\"file\", ctx.route.file]];\n if (\"interaction\" in ctx) {\n rows.push([\"interaction\", describeInteraction(ctx.interaction)]);\n // Discord gives a handler 3000ms to respond. Far past that means a slow handler or a\n // second process on the same token that answered first.\n rows.push([\"elapsed\", `${ctx.trace.elapsed()}ms since Discord created it`]);\n rows.push([\n \"middleware\",\n middleware.length === 0 ? \"none\" : middleware.join(`\\n${\" \".repeat(15)}`),\n ]);\n }\n const width = Math.max(...rows.map(([key]) => key.length));\n return [\n `Unhandled error in ${ctx.route.id}`,\n ...rows.map(([key, value]) => ` ${key.padEnd(width)} ${value}`),\n ].join(\"\\n\");\n}\n\nfunction describeInteraction(interaction: Interaction): string {\n const meta = interactionMeta(interaction);\n let what: string;\n switch (meta.type) {\n case \"chatInput\":\n what = `/${meta.command}`;\n break;\n case \"autocomplete\":\n what = `autocomplete for /${meta.command}`;\n break;\n case \"userContextMenu\":\n case \"messageContextMenu\":\n what = `context menu \"${meta.command}\"`;\n break;\n case \"unknown\":\n what = \"unknown interaction\";\n break;\n default:\n what = `${meta.type} \"${meta.customId}\"`;\n }\n const where = [\n meta.guildId === null ? \"direct message\" : `guild ${meta.guildId}`,\n meta.channelId === null ? null : `channel ${meta.channelId}`,\n meta.userId === null ? null : `user ${meta.userId}`,\n ].filter((p): p is string => p !== null);\n return `${what} (${where.join(\", \")})`;\n}\n\ninterface RepliableLike {\n isRepliable?: () => boolean;\n replied?: boolean;\n deferred?: boolean;\n reply: (options: { content: string; flags: MessageFlags }) => Promise<unknown>;\n}\n","import type {\n ContextExtension,\n Extended,\n Handler,\n InteractionContext,\n Middleware,\n Next,\n} from \"./types.js\";\n\nexport interface ChainHooks {\n /** Called with the index of each middleware right before it runs. */\n middleware?: (index: number) => void;\n /** Called right before the handler runs. Skipped when a middleware stopped the chain. */\n handler?: () => void;\n}\n\n/**\n * Runs middleware outer to inner, then the handler.\n *\n * `next()` continues unchanged; `next({ member })` puts `member` on every downstream context.\n * Returning without calling `next` stops the chain. Throwing anywhere unwinds to the caller,\n * which hands it to the error boundaries. Code after `await next()` runs after the handler.\n */\nexport async function runChain(\n middleware: readonly Middleware[],\n ctx: InteractionContext,\n handler: Handler,\n hooks: ChainHooks = {},\n): Promise<void> {\n await step(0);\n\n async function step(index: number, current: InteractionContext = ctx): Promise<unknown> {\n const layer = middleware[index];\n if (layer === undefined) {\n hooks.handler?.();\n return handler(current);\n }\n hooks.middleware?.(index);\n\n let called = false;\n const next: Next = <E extends ContextExtension>(extension?: E) => {\n if (called) throw new Error(\"next() was called twice in the same middleware.\");\n called = true;\n const downstream = extension === undefined ? current : { ...current, ...extension };\n return step(index + 1, downstream) as Promise<Extended<E>>;\n };\n return layer(current, next);\n }\n}\n","import { loadModule } from \"../compiler/load.js\";\n\nexport type HandlerModule = Record<string, unknown>;\n\nexport class HandlerLoadError extends Error {\n constructor(\n readonly file: string,\n readonly detail: string,\n ) {\n super(`${file}: ${detail}`);\n this.name = \"HandlerLoadError\";\n }\n}\n\n/**\n * Imports handler modules once and caches them. Files are absolute paths taken from the\n * manifest, so nothing here touches the filesystem beyond `import()`.\n */\nexport class ModuleRegistry {\n private readonly cache = new Map<string, Promise<HandlerModule>>();\n\n load(file: string): Promise<HandlerModule> {\n let pending = this.cache.get(file);\n if (pending === undefined) {\n pending = loadModule(file).catch((error: unknown) => {\n this.cache.delete(file);\n throw new HandlerLoadError(file, error instanceof Error ? error.message : String(error));\n });\n this.cache.set(file, pending);\n }\n return pending;\n }\n\n /**\n * Forgets cached modules so the next load imports them again. With no argument, forgets\n * everything. Only useful together with `enableModuleReloading`; otherwise `import()` hands\n * back the same instance.\n */\n invalidate(files?: Iterable<string>): void {\n if (files === undefined) {\n this.cache.clear();\n return;\n }\n for (const file of files) this.cache.delete(file);\n }\n\n /** Imports every file up front so a bad module fails startup instead of the first interaction. */\n async preload(files: Iterable<string>): Promise<void> {\n await Promise.all([...new Set(files)].map((file) => this.load(file)));\n }\n\n /** The default export of a file, checked to be a function. */\n async loadDefault<T>(file: string, what: string): Promise<T> {\n const module = await this.load(file);\n const value = module.default;\n if (typeof value !== \"function\") {\n throw new HandlerLoadError(\n file,\n `${what} must be the default export and a function, got ${describe(value)}.`,\n );\n }\n return value as T;\n }\n\n /** A named export of a file, checked to be a function. */\n async loadNamed<T>(file: string, name: string, what: string): Promise<T> {\n const module = await this.load(file);\n const value = module[name];\n if (typeof value !== \"function\") {\n throw new HandlerLoadError(\n file,\n `${what} must be exported as \"${name}\" and be a function, got ${describe(value)}.`,\n );\n }\n return value as T;\n }\n}\n\nfunction describe(value: unknown): string {\n return value === undefined ? \"no export\" : typeof value;\n}\n","import path from \"node:path\";\nimport type { Client } from \"discord.js\";\nimport type { NectarServices } from \"../index.js\";\nimport type { Manifest, ManifestRoute } from \"../manifest/schema.js\";\nimport type { ModuleRegistry } from \"./modules.js\";\nimport type { SignalEmitter } from \"./signals.js\";\nimport type { Env, Logger, RouteInfo } from \"./types.js\";\n\n/** Everything dispatch needs, shared by interactions and events. */\nexport interface RuntimeState {\n manifest: Manifest;\n /** Absolute. */\n appDir: string;\n client: Client;\n modules: ModuleRegistry;\n env: Env;\n logger: Logger;\n signals: SignalEmitter;\n /** Filled by plugin `start` hooks. */\n services: NectarServices;\n}\n\nexport function absolute(state: RuntimeState, file: string): string {\n return path.join(state.appDir, ...file.split(\"/\"));\n}\n\nexport function routeInfo(state: RuntimeState, route: ManifestRoute): RouteInfo {\n return {\n id: route.id,\n category: route.category,\n path: route.path,\n file: paths(state, route).file,\n };\n}\n\nexport function chains(state: RuntimeState, route: ManifestRoute) {\n return paths(state, route);\n}\n\ninterface RoutePaths {\n appDir: string;\n file: string;\n middleware: string[];\n errors: string[];\n}\n\n/** A route's absolute paths, joined on its first interaction instead of every one. */\nconst resolved = new WeakMap<ManifestRoute, RoutePaths>();\n\nfunction paths(state: RuntimeState, route: ManifestRoute): RoutePaths {\n let found = resolved.get(route);\n if (found === undefined || found.appDir !== state.appDir) {\n found = {\n appDir: state.appDir,\n file: absolute(state, route.file),\n middleware: route.middleware.map((f) => absolute(state, f)),\n errors: route.errors.map((f) => absolute(state, f)),\n };\n resolved.set(route, found);\n }\n return found;\n}\n","import type { AutocompleteInteraction, Interaction } from \"discord.js\";\nimport { ApplicationCommandType } from \"discord-api-types/v10\";\nimport {\n type ComponentKind,\n createMatcher,\n findInvalidParam,\n type ParamValidators,\n paramValidatorsOf,\n} from \"../components/index.js\";\nimport type {\n ManifestAutocompleteRoute,\n ManifestCommand,\n ManifestCommandRoute,\n ManifestComponentRoute,\n ManifestRoute,\n} from \"../manifest/schema.js\";\nimport { handleError, logFields } from \"./errors.js\";\nimport { runChain } from \"./middleware.js\";\nimport { HandlerLoadError } from \"./modules.js\";\nimport { type InteractionMeta, interactionMeta } from \"./signals.js\";\nimport { chains, type RuntimeState, routeInfo } from \"./state.js\";\nimport type { Handler, InteractionContext, Middleware } from \"./types.js\";\n\n/** Routes one incoming interaction. Resolves to nothing; every error ends in a boundary. */\nexport type InteractionDispatcher = (interaction: Interaction) => Promise<void>;\n\nexport function createInteractionDispatcher(state: RuntimeState): InteractionDispatcher {\n const tables = buildTables(state.manifest.routes, state.manifest.commands);\n\n return async (interaction) => {\n const receivedAt = Date.now();\n const meta = interactionMeta(interaction);\n state.signals.emit({ type: \"interaction:start\", trace: interaction.id, interaction: meta });\n\n if (interaction.isChatInputCommand()) {\n const group = interaction.options.getSubcommandGroup(false);\n const sub = interaction.options.getSubcommand(false);\n const key = [group, sub].filter((p) => p !== null).join(\"/\");\n const route = tables.commands.get(\n commandKey(ApplicationCommandType.ChatInput, interaction.commandName, key),\n );\n if (route === undefined) {\n return unknown(state, interaction, meta, `chat input command /${meta.command}`);\n }\n return run(state, route, interaction, meta, {}, receivedAt);\n }\n\n if (interaction.isContextMenuCommand()) {\n const route = tables.commands.get(\n commandKey(interaction.commandType, interaction.commandName, \"\"),\n );\n if (route === undefined) {\n return unknown(state, interaction, meta, `context menu command \"${meta.command}\"`);\n }\n return run(state, route, interaction, meta, {}, receivedAt);\n }\n\n if (interaction.isAutocomplete()) {\n const group = interaction.options.getSubcommandGroup(false);\n const sub = interaction.options.getSubcommand(false);\n const key = [group, sub].filter((p) => p !== null).join(\"/\");\n const command = tables.commands.get(\n commandKey(ApplicationCommandType.ChatInput, interaction.commandName, key),\n );\n const route = command === undefined ? undefined : tables.autocomplete.get(command.id);\n const option = interaction.options.getFocused(true).name;\n if (route === undefined || !route.options.includes(option)) {\n return unknown(state, interaction, meta, `autocomplete for /${meta.command} \"${option}\"`);\n }\n return run(state, route, interaction, meta, {}, receivedAt, option);\n }\n\n const component: [ComponentKind, string] | null = interaction.isButton()\n ? [\"button\", interaction.customId]\n : interaction.isAnySelectMenu()\n ? [\"select\", interaction.customId]\n : interaction.isModalSubmit()\n ? [\"modal\", interaction.customId]\n : null;\n if (component === null) {\n // Not a type Nectar routes. The app may listen for it on the client itself.\n state.signals.emit({\n type: \"interaction:reject\",\n trace: interaction.id,\n interaction: meta,\n reason: \"unknown-interaction\",\n });\n return;\n }\n\n const [kind, customId] = component;\n const match = tables.matcher.match(kind, customId);\n if (!match.ok) {\n if (match.reason === \"not-nectar\") return;\n state.logger.warn(`Ignoring ${kind} with custom ID \"${meta.customId}\": ${match.reason}.`, {\n trace: interaction.id,\n interaction: kind,\n customId: meta.customId,\n });\n state.signals.emit({\n type: \"interaction:reject\",\n trace: interaction.id,\n interaction: meta,\n reason: match.reason,\n });\n return;\n }\n return run(state, match.route, interaction, meta, match.params, receivedAt);\n };\n}\n\nasync function run(\n state: RuntimeState,\n route: ManifestRoute,\n interaction: Interaction,\n meta: InteractionMeta,\n params: Record<string, string | string[]>,\n receivedAt: number,\n autocompleteOption?: string,\n): Promise<void> {\n const ctx: InteractionContext = {\n interaction,\n client: state.client,\n route: routeInfo(state, route),\n params,\n env: state.env,\n trace: {\n id: interaction.id,\n receivedAt,\n elapsed: () => Date.now() - interaction.createdTimestamp,\n },\n services: state.services,\n };\n const files = chains(state, route);\n const tag = { trace: interaction.id, interaction: meta, route: ctx.route };\n state.signals.emit({ type: \"route:match\", ...tag });\n\n let handlerStart = 0;\n try {\n const middleware = await Promise.all(\n files.middleware.map((file) => state.modules.loadDefault<Middleware>(file, \"Middleware\")),\n );\n const handler =\n autocompleteOption === undefined\n ? await state.modules.loadDefault<Handler>(ctx.route.file, \"The handler\")\n : await state.modules.loadNamed<Handler>(\n ctx.route.file,\n autocompleteOption,\n \"The autocomplete handler\",\n );\n if (route.kind === \"button\" || route.kind === \"select\" || route.kind === \"modal\") {\n const invalid = await findInvalidParam(validators(ctx.route.file, handler, route), params);\n if (invalid !== null) {\n state.logger.warn(\n `Rejected ${route.kind} for ${ctx.route.id}: \"${invalid}\" failed validation.`,\n {\n ...logFields(ctx, meta),\n param: invalid,\n },\n );\n state.signals.emit({\n type: \"interaction:reject\",\n trace: interaction.id,\n interaction: meta,\n reason: \"invalid-param\",\n route: ctx.route,\n param: invalid,\n });\n return;\n }\n }\n await runChain(middleware, ctx, handler, {\n middleware: (index) =>\n state.signals.emit({\n type: \"middleware:enter\",\n ...tag,\n file: files.middleware[index] ?? \"\",\n }),\n handler: () => {\n handlerStart = Date.now();\n state.signals.emit({ type: \"handler:enter\", ...tag });\n },\n });\n const handled = handlerStart !== 0;\n if (handled) {\n state.signals.emit({ type: \"handler:complete\", ...tag, duration: Date.now() - handlerStart });\n }\n const duration = Date.now() - receivedAt;\n state.signals.emit({ type: \"interaction:complete\", ...tag, duration, handled });\n state.logger.debug(\n handled ? `Handled ${ctx.route.id} in ${duration}ms.` : `Middleware stopped ${ctx.route.id}.`,\n logFields(ctx, meta),\n );\n } catch (error) {\n const boundary = await handleError(\n error,\n ctx,\n files.errors,\n state.modules,\n state.logger,\n files.middleware,\n );\n state.signals.emit({ type: \"interaction:fail\", ...tag, error, boundary });\n if (boundary !== null) {\n state.logger.debug(`${ctx.route.id} failed, handled by ${boundary}.`, {\n ...logFields(ctx, meta),\n boundary,\n error,\n });\n }\n if (autocompleteOption !== undefined)\n await closeAutocomplete(interaction as AutocompleteInteraction);\n }\n}\n\n/** Discord shows a spinner until autocomplete answers, so a failed handler answers with nothing. */\nasync function closeAutocomplete(interaction: AutocompleteInteraction): Promise<void> {\n if (interaction.responded) return;\n try {\n await interaction.respond([]);\n } catch {\n // Already answered or expired. Nothing left to do.\n }\n}\n\nfunction unknown(\n state: RuntimeState,\n interaction: Interaction,\n meta: InteractionMeta,\n what: string,\n): void {\n state.logger.warn(`No route for ${what}. Run \\`nectar sync\\` if commands changed.`, {\n trace: interaction.id,\n interaction: meta.type,\n command: meta.command,\n });\n state.signals.emit({\n type: \"interaction:reject\",\n trace: interaction.id,\n interaction: meta,\n reason: \"no-route\",\n });\n}\n\n/**\n * The route's parameter validators, checked once per handler instance. The compiler checked\n * the shape at build time; this repeats it so a JavaScript project or a hot-reloaded file\n * fails the same way instead of at the first click.\n */\nfunction validators(\n file: string,\n handler: Handler,\n route: ManifestComponentRoute,\n): ParamValidators {\n const cached = validatorCache.get(handler);\n if (cached !== undefined) return cached;\n let result: ParamValidators;\n try {\n result = paramValidatorsOf(handler, route);\n } catch (error) {\n throw new HandlerLoadError(file, error instanceof Error ? error.message : String(error));\n }\n validatorCache.set(handler, result);\n return result;\n}\n\nconst validatorCache = new WeakMap<Handler, ParamValidators>();\n\ninterface Tables {\n /** `${type}:${name}:${handlerKey}` to the handler route. */\n commands: Map<string, ManifestCommandRoute>;\n autocomplete: Map<string, ManifestAutocompleteRoute>;\n matcher: ReturnType<typeof createMatcher<ManifestComponentRoute>>;\n}\n\nfunction buildTables(routes: ManifestRoute[], commands: ManifestCommand[]): Tables {\n const commandRoutes = new Map<string, ManifestCommandRoute>();\n const autocomplete = new Map<string, ManifestAutocompleteRoute>();\n const components: ManifestComponentRoute[] = [];\n\n for (const route of routes) {\n if (route.kind === \"command\") commandRoutes.set(route.id, route);\n else if (route.kind === \"autocomplete\") autocomplete.set(route.id, route);\n else if (route.kind !== \"event\") components.push(route);\n }\n\n const table = new Map<string, ManifestCommandRoute>();\n for (const command of commands) {\n for (const [key, id] of Object.entries(command.handlers)) {\n const route = commandRoutes.get(id);\n if (route !== undefined) table.set(commandKey(command.type, command.name, key), route);\n }\n }\n\n return { commands: table, autocomplete, matcher: createMatcher(components) };\n}\n\nfunction commandKey(type: number, name: string, handlerKey: string): string {\n return `${type}:${name}:${handlerKey}`;\n}\n","import type { ManifestEvent, ManifestEventRoute } from \"../manifest/schema.js\";\nimport { handleError } from \"./errors.js\";\nimport { chains, type RuntimeState, routeInfo } from \"./state.js\";\nimport type { EventContext, EventHandler } from \"./types.js\";\n\nexport interface EventBinding {\n name: string;\n /** Resolves once every handler it fanned out to has finished. */\n listener: (...args: unknown[]) => Promise<void>;\n}\n\n/**\n * One discord.js listener per event. It fans out to the compiled handlers in manifest order,\n * sequentially or concurrently as the event's mode says. A `once` handler runs on the first\n * emission only; when every handler of an event is spent, the listener is removed.\n *\n * Handler errors go to the route's boundaries and never reach the client's `error` event.\n * Returns the bindings so the runtime can remove them on shutdown.\n */\nexport function bindEvents(state: RuntimeState): EventBinding[] {\n const routes = new Map<string, ManifestEventRoute>();\n for (const route of state.manifest.routes) {\n if (route.kind === \"event\") routes.set(route.id, route);\n }\n\n const bindings: EventBinding[] = [];\n for (const event of state.manifest.events) {\n const handlers = event.handlers\n .map((id) => routes.get(id))\n .filter((r): r is ManifestEventRoute => r !== undefined);\n if (handlers.length === 0) continue;\n\n const spent = new Set<string>();\n const listener = (...args: unknown[]) => {\n const live = handlers.filter((h) => !spent.has(h.id));\n for (const h of live) if (h.once) spent.add(h.id);\n if (spent.size === handlers.length) state.client.off(event.name, listener);\n return fanOut(state, event, live, args);\n };\n\n state.client.on(event.name, listener);\n bindings.push({ name: event.name, listener });\n }\n return bindings;\n}\n\nasync function fanOut(\n state: RuntimeState,\n event: ManifestEvent,\n handlers: ManifestEventRoute[],\n args: unknown[],\n): Promise<void> {\n if (event.mode === \"concurrent\") {\n await Promise.all(handlers.map((route) => invoke(state, event, route, args)));\n return;\n }\n for (const route of handlers) await invoke(state, event, route, args);\n}\n\nasync function invoke(\n state: RuntimeState,\n event: ManifestEvent,\n route: ManifestEventRoute,\n args: unknown[],\n) {\n const ctx: EventContext = {\n client: state.client,\n route: routeInfo(state, route),\n env: state.env,\n services: state.services,\n };\n try {\n const handler = await state.modules.loadDefault<EventHandler>(ctx.route.file, \"The handler\");\n await handler(...args, ctx);\n } catch (error) {\n const boundary = await handleError(\n error,\n ctx,\n chains(state, route).errors,\n state.modules,\n state.logger,\n );\n state.signals.emit({\n type: \"event:fail\",\n event: event.name,\n route: ctx.route,\n error,\n boundary,\n });\n }\n}\n","import type { Logger } from \"./types.js\";\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\n/** Structured metadata attached to a log line. `error` is the thrown value, when there is one. */\nexport type LogFields = Record<string, unknown>;\n\nexport interface LogRecord {\n level: LogLevel;\n message: string;\n /** Epoch milliseconds. */\n at: number;\n fields: LogFields;\n}\n\n/** Where log records go. The default prints to the console; an app can hand records to any library. */\nexport type LogSink = (record: LogRecord) => void;\n\nexport interface LoggerOptions {\n /** Lowest level that reaches the sink. Defaults to `info`. */\n level?: LogLevel;\n sink?: LogSink;\n}\n\nconst ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\n\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const threshold = ORDER[options.level ?? \"info\"];\n const sink = options.sink ?? consoleSink;\n const log = (level: LogLevel, message: string, fields: LogFields = {}) => {\n if (ORDER[level] < threshold) return;\n sink({ level, message, at: Date.now(), fields });\n };\n return {\n debug: (message, fields) => log(\"debug\", message, fields),\n info: (message, fields) => log(\"info\", message, fields),\n warn: (message, fields) => log(\"warn\", message, fields),\n error: (message, fields) => log(\"error\", message, fields),\n };\n}\n\n/** `[nectar] message key=value ...` on the matching console method, then the error if any. */\nexport const consoleSink: LogSink = ({ level, message, fields }) => {\n const { error, ...rest } = fields;\n const pairs = Object.entries(rest)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${key}=${typeof value === \"string\" ? value : JSON.stringify(value)}`);\n const line = [`[nectar] ${message}`, ...pairs].join(\" \");\n if (error === undefined) console[level](line);\n else console[level](line, error);\n};\n","import path from \"node:path\";\nimport { Client, DiscordjsError, DiscordjsErrorCodes, Events } from \"discord.js\";\nimport { registerComponentRoutes } from \"../components/registry.js\";\nimport type { Manifest, ManifestComponentRoute } from \"../manifest/schema.js\";\nimport { type NectarPlugin, type PluginApp, PluginError } from \"../plugins/index.js\";\nimport { createInteractionDispatcher } from \"./dispatch.js\";\nimport { bindEvents, type EventBinding } from \"./events.js\";\nimport { createLogger } from \"./logger.js\";\nimport { ModuleRegistry } from \"./modules.js\";\nimport { createSignals, type SignalEmitter } from \"./signals.js\";\nimport type { RuntimeState } from \"./state.js\";\nimport type { Env, Logger, RuntimeConfig } from \"./types.js\";\n\nexport interface RuntimeOptions {\n manifest: Manifest;\n /** Absolute path of the app directory the manifest was compiled from. */\n appDir: string;\n config: RuntimeConfig;\n /** Defaults from `NODE_ENV`. */\n env?: Env;\n /** Overrides `config.logger`. */\n logger?: Logger;\n /** Share an emitter created earlier, so signals from before the runtime existed line up. */\n signals?: SignalEmitter;\n /** Reuse an existing client instead of building one from `config`. Tests use this. */\n client?: Client;\n}\n\n/** `client.login` failed: Discord refused the token, or could not be reached. */\nexport class LoginError extends Error {\n readonly invalidToken: boolean;\n\n constructor(cause: unknown) {\n const invalidToken =\n cause instanceof DiscordjsError && cause.code === DiscordjsErrorCodes.TokenInvalid;\n super(\n invalidToken ? \"Discord rejected the bot token.\" : `Could not log in: ${describe(cause)}`,\n { cause },\n );\n this.name = \"LoginError\";\n this.invalidToken = invalidToken;\n }\n}\n\nexport interface StartOptions {\n token: string;\n /**\n * Stop on SIGINT and SIGTERM, and when the IPC channel to a parent process closes, so a shard\n * does not outlive the manager that spawned it. Defaults to `true`.\n */\n signals?: boolean;\n /** How long `stop()` waits for in-flight interactions, in milliseconds. Defaults to 10000. */\n drainTimeout?: number;\n}\n\nexport interface Runtime {\n readonly client: Client;\n readonly env: Env;\n readonly modules: ModuleRegistry;\n /** Framework signals. `config.observe` is subscribed already. */\n readonly signals: SignalEmitter;\n /** Loads handlers, attaches listeners, and logs in. A failed login stops it and throws `LoginError`. */\n start(options: StartOptions): Promise<void>;\n /**\n * Stops taking interactions, waits for the in-flight ones, detaches listeners, and\n * destroys the client. Safe to call twice.\n */\n stop(): Promise<void>;\n /**\n * Swaps in a recompiled manifest: dispatch tables, component routes, and event listeners\n * follow it. Handler modules are not touched; invalidate them through `modules`.\n */\n update(manifest: Manifest): void;\n}\n\nexport function createRuntime(options: RuntimeOptions): Runtime {\n const env = options.env ?? envFromProcess();\n const logger = options.logger ?? createLogger(options.config.logger);\n const signals = options.signals ?? createSignals(logger);\n if (options.config.observe !== undefined) signals.on(options.config.observe);\n const client =\n options.client ??\n new Client({\n ...options.config.client,\n intents: options.config.intents,\n ...(options.config.partials === undefined ? {} : { partials: options.config.partials }),\n });\n\n const state: RuntimeState = {\n manifest: options.manifest,\n appDir: path.resolve(options.appDir),\n client,\n modules: new ModuleRegistry(),\n env,\n logger,\n signals,\n services: {},\n };\n const plugins = options.config.plugins ?? [];\n const app: PluginApp = {\n client,\n env,\n logger,\n signals,\n get manifest() {\n return state.manifest;\n },\n };\n\n registerComponentRoutes(componentRoutes(state.manifest));\n let dispatch = createInteractionDispatcher(state);\n const inFlight = new Set<Promise<void>>();\n let bindings: EventBinding[] = [];\n let started = false;\n let globalStarted = false;\n let drainTimeout = 10_000;\n let stopping: Promise<void> | null = null;\n let onSignal: (() => void) | null = null;\n\n const onInteraction = (interaction: Parameters<typeof dispatch>[0]) => {\n const task = dispatch(interaction).finally(() => inFlight.delete(task));\n inFlight.add(task);\n };\n // The parent that forked this process closed the channel: a shard manager that exited.\n const onDisconnect = () => void runtime.stop();\n const gateway = {\n [Events.ShardReady]: (shard: number) =>\n signals.emit({ type: \"gateway:connect\", shard, resumed: false }),\n [Events.ShardResume]: (shard: number) =>\n signals.emit({ type: \"gateway:connect\", shard, resumed: true }),\n [Events.ShardDisconnect]: (event: { code: number }, shard: number) =>\n signals.emit({ type: \"gateway:disconnect\", shard, code: event.code }),\n };\n\n const runtime: Runtime = {\n client,\n env,\n modules: state.modules,\n signals,\n\n async start({ token, signals: osSignals = true, drainTimeout: timeout = 10_000 }) {\n drainTimeout = timeout;\n await startPlugins(plugins, app, state.services as Record<string, unknown>);\n if (runsShardZero(client.options.shards)) {\n await startGlobal(plugins, app);\n globalStarted = true;\n }\n if (options.config.eager ?? env === \"production\") {\n await state.modules.preload(manifestFiles(state.manifest, state.appDir));\n }\n\n bindings = bindEvents(state);\n started = true;\n client.on(Events.InteractionCreate, onInteraction);\n client.on(Events.ShardReady, gateway[Events.ShardReady]);\n client.on(Events.ShardResume, gateway[Events.ShardResume]);\n client.on(Events.ShardDisconnect, gateway[Events.ShardDisconnect]);\n\n if (osSignals) {\n onSignal = () => {\n if (stopping !== null) {\n logger.warn(\"Second signal received, exiting now.\");\n process.exit(1);\n }\n void this.stop();\n };\n process.once(\"SIGINT\", onSignal);\n process.once(\"SIGTERM\", onSignal);\n if (process.connected) process.once(\"disconnect\", onDisconnect);\n }\n\n try {\n await client.login(token);\n } catch (error) {\n await this.stop();\n throw new LoginError(error);\n }\n },\n\n stop() {\n if (stopping !== null) return stopping;\n stopping = (async () => {\n signals.emit({ type: \"shutdown\" });\n client.off(Events.InteractionCreate, onInteraction);\n client.off(Events.ShardReady, gateway[Events.ShardReady]);\n client.off(Events.ShardResume, gateway[Events.ShardResume]);\n client.off(Events.ShardDisconnect, gateway[Events.ShardDisconnect]);\n for (const { name, listener } of bindings) client.off(name, listener);\n bindings = [];\n if (onSignal !== null) {\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n onSignal = null;\n }\n process.off(\"disconnect\", onDisconnect);\n await drain(inFlight, drainTimeout, logger);\n if (globalStarted) await stopPlugins(plugins, app, logger, \"stopGlobal\");\n await stopPlugins(plugins, app, logger, \"stop\");\n if (process.send !== undefined) ignoreClosedChannel();\n await client.destroy();\n })();\n return stopping;\n },\n\n update(manifest) {\n state.manifest = manifest;\n registerComponentRoutes(componentRoutes(manifest));\n dispatch = createInteractionDispatcher(state);\n if (started && stopping === null) {\n for (const { name, listener } of bindings) client.off(name, listener);\n bindings = bindEvents(state);\n }\n },\n };\n return runtime;\n}\n\n/** Runs `start` hooks in config order. Two plugins offering the same service is a startup failure. */\nasync function startPlugins(\n plugins: readonly NectarPlugin[],\n app: PluginApp,\n services: Record<string, unknown>,\n): Promise<void> {\n const providers = new Map<string, string>();\n for (const plugin of plugins) {\n let provided: unknown;\n try {\n provided = await plugin.start?.(app);\n } catch (error) {\n throw new PluginError(plugin.name, `start failed: ${describe(error)}`);\n }\n if (provided === undefined || provided === null) continue;\n if (typeof provided !== \"object\") {\n throw new PluginError(plugin.name, `start must return an object of services or nothing.`);\n }\n for (const [name, service] of Object.entries(provided)) {\n const owner = providers.get(name);\n if (owner !== undefined) {\n throw new PluginError(\n plugin.name,\n `provides service \"${name}\", which plugin \"${owner}\" already provides.`,\n );\n }\n providers.set(name, plugin.name);\n services[name] = service;\n }\n }\n}\n\nlet ignoringClosedChannel = false;\n\n/**\n * discord.js's shard client reports gateway events to the manager with `process.send`, and\n * destroying the client produces some. If the manager has gone, each report fails with an\n * `error` event on `process` that would crash it. Only that failure is dropped; the listener\n * stays, since the failures arrive on a later tick.\n */\nfunction ignoreClosedChannel(): void {\n if (ignoringClosedChannel) return;\n ignoringClosedChannel = true;\n process.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code !== \"ERR_IPC_CHANNEL_CLOSED\") throw error;\n });\n}\n\n/** Runs `startGlobal` hooks in config order. */\nasync function startGlobal(plugins: readonly NectarPlugin[], app: PluginApp): Promise<void> {\n for (const plugin of plugins) {\n try {\n await plugin.startGlobal?.(app);\n } catch (error) {\n throw new PluginError(plugin.name, `startGlobal failed: ${describe(error)}`);\n }\n }\n}\n\n/** Runs `stopGlobal` or `stop` hooks in reverse order. A failing hook is logged; shutdown continues. */\nasync function stopPlugins(\n plugins: readonly NectarPlugin[],\n app: PluginApp,\n logger: Logger,\n hook: \"stop\" | \"stopGlobal\",\n): Promise<void> {\n for (const plugin of [...plugins].reverse()) {\n try {\n await plugin[hook]?.(app);\n } catch (error) {\n logger.error(`Plugin \"${plugin.name}\": ${hook} failed.`, { plugin: plugin.name, error });\n }\n }\n}\n\n/**\n * Whether this process runs shard 0. Application-global hooks run there, so they run once\n * however the shards are spread over processes. `auto` means this process runs them all.\n */\nfunction runsShardZero(shards: Client[\"options\"][\"shards\"]): boolean {\n if (shards === undefined || shards === \"auto\") return true;\n return typeof shards === \"number\" ? shards === 0 : shards.includes(0);\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function drain(inFlight: Set<Promise<void>>, timeout: number, logger: Logger) {\n if (inFlight.size === 0) return;\n let timer: NodeJS.Timeout | undefined;\n const expired = new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), timeout);\n });\n const result = await Promise.race([Promise.allSettled([...inFlight]), expired]);\n clearTimeout(timer);\n if (result === \"timeout\") {\n logger.warn(\n `${inFlight.size} interaction(s) still running after ${timeout}ms, shutting down anyway.`,\n );\n }\n}\n\nfunction componentRoutes(manifest: Manifest): ManifestComponentRoute[] {\n return manifest.routes.filter(\n (r): r is ManifestComponentRoute =>\n r.kind === \"button\" || r.kind === \"select\" || r.kind === \"modal\",\n );\n}\n\n/** Absolute paths of every handler, middleware, and error boundary file a manifest refers to. */\nexport function manifestFiles(manifest: Manifest, appDir: string): Set<string> {\n const files = new Set<string>();\n for (const route of manifest.routes) {\n files.add(route.file);\n for (const file of route.middleware) files.add(file);\n for (const file of route.errors) files.add(file);\n }\n return new Set([...files].map((file) => path.join(appDir, ...file.split(\"/\"))));\n}\n\nfunction envFromProcess(): Env {\n const value = process.env.NODE_ENV;\n return value === \"production\" || value === \"test\" ? value : \"development\";\n}\n"],"mappings":"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"}
package/dist/start.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as main } from "./cli-Ce-ZUj6M.js";
1
+ import { t as main } from "./cli-DJg1-t94.js";
2
2
  import { fileURLToPath } from "node:url";
3
3
  //#region src/start.ts
4
4
  /**
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as InteractionContext, T as ComponentPath, Y as Env, b as CommandRoutes, et as Logger, jt as OptionType, lt as Signal, n as NectarServices, t as NectarRoutes, v as CommandContext, w as ComponentParams, x as ComponentContext, y as CommandPath } from "./index-DGMxBsub.js";
1
+ import { $ as InteractionContext, T as ComponentPath, Y as Env, b as CommandRoutes, et as Logger, jt as OptionType, lt as Signal, n as NectarServices, t as NectarRoutes, v as CommandContext, w as ComponentParams, x as ComponentContext, y as CommandPath } from "./index-DNXcZ4Ff.js";
2
2
  import { AnySelectMenuInteraction, Attachment, AutocompleteInteraction, ButtonInteraction, Channel, Client, ClientEvents, GuildMember, Interaction, ModalSubmitInteraction, Role, User } from "discord.js";
3
3
  //#region src/testing.d.ts
4
4
  type Empty = Record<never, never>;
package/dist/testing.js CHANGED
@@ -1,5 +1,5 @@
1
- import { p as customIdFor, u as registerComponentRoutes } from "./plugins-CGvM19v9.js";
2
- import { a as bindEvents, c as ModuleRegistry, i as createLogger, l as createSignals, o as createInteractionDispatcher, u as loadManifest } from "./runtime-CZJeZvSL.js";
1
+ import { p as customIdFor, u as registerComponentRoutes } from "./plugins-BCYwtuo-.js";
2
+ import { a as bindEvents, c as ModuleRegistry, i as createLogger, l as createSignals, o as createInteractionDispatcher, u as loadManifest } from "./runtime-B_bjrg1W.js";
3
3
  import path from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectar-js/nectar",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A filesystem-based meta-framework for discord.js.",
5
5
  "license": "MIT",
6
6
  "type": "module",