@seedcord/core 0.11.0 → 0.12.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +3 -3
  2. package/dist/Bus-VJw-QpoY.mjs +986 -0
  3. package/dist/Bus-VJw-QpoY.mjs.map +1 -0
  4. package/dist/{CoordinatedShutdown-H5YjZn8A.d.mts → CoordinatedShutdown-fPKLWjc5.d.mts} +13 -4
  5. package/dist/{CoordinatedStartup-CGnJrqcl.mjs → CoordinatedStartup-Zw_Lk7Zk.mjs} +22 -20
  6. package/dist/CoordinatedStartup-Zw_Lk7Zk.mjs.map +1 -0
  7. package/dist/Generated-ByC53_A2.d.mts +26 -0
  8. package/dist/HmrModuleHandler-Btgu8r96.mjs +165 -0
  9. package/dist/HmrModuleHandler-Btgu8r96.mjs.map +1 -0
  10. package/dist/{PluginHost-DB4j8unu.d.mts → PluginHost-DFnkIldj.d.mts} +43 -15
  11. package/dist/PluginHost-DTtxWRiL.mjs +552 -0
  12. package/dist/PluginHost-DTtxWRiL.mjs.map +1 -0
  13. package/dist/{PluginLifecycle-AhQ46NIr.mjs → PluginLifecycle-hx5zUc-P.mjs} +4 -4
  14. package/dist/PluginLifecycle-hx5zUc-P.mjs.map +1 -0
  15. package/dist/{ServerHost-DYeh0KTk.d.mts → ServerHost-B5o3ScCF.d.mts} +7 -6
  16. package/dist/{ServerHost-CtmAhEXn.mjs → ServerHost-CDaEDR4C.mjs} +17 -6
  17. package/dist/{ServerHost-CtmAhEXn.mjs.map → ServerHost-CDaEDR4C.mjs.map} +1 -1
  18. package/dist/bindViteEnv-NFzvdqlT.mjs +32 -0
  19. package/dist/{bindViteEnv-DWq5rftJ.mjs.map → bindViteEnv-NFzvdqlT.mjs.map} +1 -1
  20. package/dist/env-internal.index.mjs +1 -1
  21. package/dist/hmr.index.d.mts +20 -20
  22. package/dist/hmr.index.mjs +1 -1
  23. package/dist/{index-Du71Xx69.d.mts → index-DGHng1IN.d.mts} +115 -201
  24. package/dist/index.d.mts +321 -74
  25. package/dist/index.mjs +323 -102
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/internal.index.d.mts +223 -83
  28. package/dist/internal.index.mjs +573 -74
  29. package/dist/internal.index.mjs.map +1 -1
  30. package/dist/node-internal.index.d.mts +8 -4
  31. package/dist/node-internal.index.mjs +19 -6
  32. package/dist/node-internal.index.mjs.map +1 -1
  33. package/dist/node.index.d.mts +3 -3
  34. package/dist/node.index.mjs +22 -28
  35. package/dist/node.index.mjs.map +1 -1
  36. package/dist/{phases-FTM69J3a.d.mts → phases-C2QCSqRu.d.mts} +164 -55
  37. package/dist/plugin.index.d.mts +3 -2
  38. package/dist/plugin.index.mjs +1 -1
  39. package/dist/subscribers-CGuVjbrg.mjs +982 -0
  40. package/dist/subscribers-CGuVjbrg.mjs.map +1 -0
  41. package/dist/{bindViteEnv-DWq5rftJ.mjs → usingCtx-Cf2cZi96.mjs} +1 -31
  42. package/package.json +14 -11
  43. package/dist/CoordinatedStartup-CGnJrqcl.mjs.map +0 -1
  44. package/dist/HmrModuleHandler-8ZC-UuZh.mjs +0 -162
  45. package/dist/HmrModuleHandler-8ZC-UuZh.mjs.map +0 -1
  46. package/dist/PluginHost-B8kxLOeb.mjs +0 -245
  47. package/dist/PluginHost-B8kxLOeb.mjs.map +0 -1
  48. package/dist/PluginLifecycle-AhQ46NIr.mjs.map +0 -1
  49. package/dist/phases-BauKJOTT.mjs +0 -31
  50. package/dist/phases-BauKJOTT.mjs.map +0 -1
  51. package/dist/publishDefault-D3dAyzZ9.mjs +0 -7
  52. package/dist/publishDefault-D3dAyzZ9.mjs.map +0 -1
  53. package/dist/subscribers-Ce3SWy23.mjs +0 -1913
  54. package/dist/subscribers-Ce3SWy23.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribers-CGuVjbrg.mjs","names":["allOf"],"sources":["../src/stops/NoticeCard.ts","../src/stops/Notice.ts","../src/commands/contextMenuLeaves.ts","../src/commands/scopes.ts","../src/miscellaneous/guarded.ts","../src/commands/linkCommands.ts","../src/commands/CommandInjector.ts","../src/decorators/Command.ts","../src/customId/routing.ts","../src/decorators/interactionRoutes.ts","../src/decorators/middleware.ts","../src/reply/ackLegality.ts","../src/reply/AckTrace.ts","../src/miscellaneous/lazyLogger.ts","../src/reply/responseReport.ts","../src/reply/translateSerialization.ts","../src/reply/serializeReply.ts","../src/notices/index.ts","../src/stops/Fault.ts","../src/gates/effects.ts","../src/stops/Silence.ts","../src/miscellaneous/ResolvedEmoji.ts","../src/pagination/cursor.ts"],"sourcesContent":["import { TextDisplayBuilder } from '@discordjs/builders';\n\nimport { BuilderComponent } from '#components/Component';\n\n// built fresh inside a Notice's render to back its ComponentsV2 reply\n// discord renders the ### line as an h3 with the description below\nexport class NoticeCard extends BuilderComponent<'container'> {\n public constructor(description: string, title = 'Cannot Proceed') {\n super('container');\n this.instance.addTextDisplayComponents(new TextDisplayBuilder().setContent(`### ${title}\\n${description}`));\n }\n}\n","import { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\nimport type { Constructor } from 'type-fest';\n\n/**\n * Base class for a user-facing refusal or a reported fault.\n *\n * Throw a `Notice` to stop a handler and reply to the user. The framework catches it at the controller\n * boundary and renders {@link Notice.render}, which always decides what the user sees. With `report`\n * false that render is all that happens. With `report` true the framework also logs the fault and\n * publishes it to the `handledException` bus. A raw, non-Notice throw shows the generic message.\n *\n * @example\n * ```ts\n * import { Notice, BuilderComponent, type RenderContext, type ReplyResponse } from '@seedcord/gateway';\n * import { TextDisplayBuilder } from '@discordjs/builders';\n *\n * // reading `.component` applies the configured bot color to the container accent\n * class TooPoorCard extends BuilderComponent<'container'> {\n * constructor(balance: number) {\n * super('container');\n * this.instance.addTextDisplayComponents(\n * new TextDisplayBuilder().setContent(`### Insufficient balance\\nYou need more than ${balance} coins.`)\n * );\n * }\n * }\n *\n * class TooPoor extends Notice {\n * constructor(private readonly balance: number) {\n * super(`balance ${balance} is below the cost`);\n * }\n *\n * override render(_ctx: RenderContext): ReplyResponse {\n * return { components: [new TooPoorCard(this.balance).component] };\n * }\n * }\n *\n * // in a handler, throwing stops the handler and replies with render(ctx)\n * if (wallet.balance < cost) throw new TooPoor(wallet.balance);\n * ```\n */\nexport abstract class Notice extends Error {\n /**\n * Whether this denial is a reported fault. True also logs it and publishes it to the `handledException`\n * bus. The user always sees {@link Notice.render} either way.\n *\n * @defaultValue `false`\n */\n public report = false;\n\n /**\n * Whether the reply is ephemeral, seen only by the invoking user. Set it false for a refusal the\n * whole channel should see.\n *\n * @defaultValue `true`\n */\n public ephemeral = true;\n\n /**\n * A short one-line reason. When every gate in an {@link or} refuses and each one sets this, `or`\n * throws a refusal listing them under `You need to meet at least one of these:`. Write a verb phrase that\n * finishes that line, for example, \"be the bot owner\".\n */\n public summary?: string;\n\n protected constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n\n // Error sets name to 'Error', so stamp the concrete subclass name for logs and the fault report\n this.name = new.target.name;\n Error.captureStackTrace(this, this.constructor);\n }\n\n /**\n * Builds what the user sees. Called fresh each time the denial is shown, so the builders are new\n * and the bot color resolves at render time. By default it returns seedcord's refusal card with the message as\n * its text. Override it to show your own components.\n */\n public render(_ctx: RenderContext): ReplyResponse {\n return { components: [new NoticeCard(this.message).component] };\n }\n}\n\n/**\n * A Notice class that seedcord constructs and throws in place of a default refusal. It passes the data that\n * refusal carries, like the retry time for `Cooldown` or the subject and permission names for a permission check.\n *\n * @example\n * ```ts\n * class OwnersOnly extends Notice {\n * public constructor() {\n * super('Owners only.');\n * }\n * }\n *\n * OwnerOnly({ notice: OwnersOnly });\n * ```\n */\nexport type NoticeConstructor<Args extends unknown[] = []> = Constructor<Notice, Args>;\n","import { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport type { RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';\n\nexport interface ContextMenuLeaves {\n readonly user: Set<string>;\n readonly message: Set<string>;\n}\n\n// a user and a message command can share a name\nexport function contextMenuLeaves(commands: Iterable<RESTPostAPIApplicationCommandsJSONBody>): ContextMenuLeaves {\n const user = new Set<string>();\n const message = new Set<string>();\n for (const command of commands) {\n if (command.type === ApplicationCommandType.User) user.add(command.name);\n else if (command.type === ApplicationCommandType.Message) message.add(command.name);\n }\n return { user, message };\n}\n","import { Routes } from 'discord-api-types/v10';\n\nimport type { DeployTarget } from '#src/commands/deployTarget';\nimport type { Snowflake } from 'discord-api-types/v10';\nimport type { LiteralUnion } from 'type-fest';\n\nexport const GLOBAL_ID = 'global';\n\nconst allOf = new Intl.ListFormat('en', { type: 'conjunction' });\n\n// discord's guild ids are all digits\nexport type ScopeId = LiteralUnion<typeof GLOBAL_ID, Snowflake>;\n\nexport function commandsRoute(applicationId: Snowflake, scope: ScopeId): `/${string}` {\n return scope === GLOBAL_ID\n ? Routes.applicationCommands(applicationId)\n : Routes.applicationGuildCommands(applicationId, scope);\n}\n\nexport function globalFirst<ValueT>(scopes: Iterable<readonly [ScopeId, ValueT]>): (readonly [ScopeId, ValueT])[] {\n return [...scopes].toSorted(([a], [b]) => Number(b === GLOBAL_ID) - Number(a === GLOBAL_ID));\n}\n\nexport function scopeIdsOf(target: DeployTarget): readonly ScopeId[] {\n return target.scope === 'global' ? [GLOBAL_ID] : target.guilds;\n}\n\nexport function skippedNote(skipped: readonly ScopeId[], guildLabel: (guildId: string) => string): string {\n const guilds = skipped.filter((scope) => scope !== GLOBAL_ID).map(guildLabel);\n const parts = [\n ...(skipped.includes(GLOBAL_ID) ? ['the global commands'] : []),\n ...(guilds.length > 0 ? [`the commands in ${allOf.format(guilds)}`] : [])\n ];\n return `Skipped ${allOf.format(parts)} because they match the last deploy.`;\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\n// null-prototype, on a plain object a key named __proto__ sets the store's prototype and adds no entry\nexport function accessorStore<Value>(): Record<string, Value> {\n return Object.create(null) as Record<string, Value>;\n}\n\n// empties a store in place, keeping the reference the accessor's Proxy wraps\nexport function clearStore(store: Record<string, unknown>): void {\n for (const key of Object.keys(store)) Reflect.deleteProperty(store, key);\n}\n\n// handler modules import before startup fills the record, and an unguarded read there returns undefined and renders as an empty string\nexport function guardedAccessor<Value>(accessor: string, storage: Record<string, Value>): Record<string, Value> {\n return new Proxy(storage, {\n get(target, key, receiver): unknown {\n // hasOwn, since `in` walks the prototype and would read a key like `constructor` as resolved\n if (typeof key !== 'string' || Object.hasOwn(target, key)) return Reflect.get(target, key, receiver);\n throw new SeedcordError(SeedcordErrorCode.CoreAccessorUnresolved, [accessor, key]);\n }\n });\n}\n","import { routeLeavesOf } from '@seedcord/utils/internal';\nimport { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport { contextMenuLeaves } from '#src/commands/contextMenuLeaves';\nimport { GLOBAL_ID } from '#src/commands/scopes';\nimport { accessorStore } from '#src/miscellaneous/guarded';\n\nimport type { ContextMenuKind } from '#registries/ContextMenuRegistry';\nimport type { CommandInfo, ContextMenuInfo } from '#src/commands/CommandInjector';\nimport type { DeployResult } from '#src/commands/types';\nimport type {\n APIApplicationCommand,\n RESTPostAPIApplicationCommandsJSONBody as CommandJson,\n RESTPostAPIChatInputApplicationCommandsJSONBody,\n Snowflake\n} from 'discord-api-types/v10';\n\nexport interface CommandRef {\n readonly name: string;\n readonly type: ApplicationCommandType;\n}\n\ninterface LinkedContextMenus {\n readonly user: Record<string, ContextMenuInfo>;\n readonly message: Record<string, ContextMenuInfo>;\n}\n\nexport interface LinkedCommands {\n readonly commands: Record<string, CommandInfo>;\n readonly contextMenus: LinkedContextMenus;\n readonly multiGuild: CommandRef[];\n readonly notInCode: CommandRef[];\n}\n\nconst MULTI_GUILD = Symbol('seedcord:commands:multi-guild');\ntype DeployedId = Snowflake | typeof MULTI_GUILD;\n\ninterface CommandLike {\n name: string;\n type?: ApplicationCommandType | undefined;\n}\n\nexport function commandRefOf({ name, type }: CommandLike): CommandRef {\n // discord reads a missing type as a slash command\n return { name, type: type ?? ApplicationCommandType.ChatInput };\n}\n\n// Discord keeps command names unique per type\nexport function commandKey(command: CommandLike): string {\n const { type, name } = commandRefOf(command);\n return `${type}:${name}`;\n}\n\nexport function indexById(commands: readonly APIApplicationCommand[]): Map<string, APIApplicationCommand> {\n return new Map(commands.map((command) => [command.id, command]));\n}\n\nfunction isSlash(json: CommandJson): json is RESTPostAPIChatInputApplicationCommandsJSONBody {\n return commandRefOf(json).type === ApplicationCommandType.ChatInput;\n}\n\n// a guild command in N guilds appears once per guild\nfunction uniqueRefs(refs: readonly CommandRef[]): CommandRef[] {\n return [...new Map(refs.map((ref) => [commandKey(ref), ref])).values()];\n}\n\nfunction deployedIds(deploy: DeployResult): Map<string, DeployedId> {\n const ids = new Map<string, DeployedId>();\n for (const [scope, collection] of deploy) {\n if (scope === GLOBAL_ID) continue;\n for (const command of collection.values()) {\n const key = commandKey(command);\n const existing = ids.get(key);\n // Discord gives a guild command its own id in each guild\n if (existing === undefined) ids.set(key, command.id);\n else if (existing !== command.id) ids.set(key, MULTI_GUILD);\n }\n }\n // a global command's one id wins over any guild copy\n for (const command of deploy.get(GLOBAL_ID)?.values() ?? []) ids.set(commandKey(command), command.id);\n return ids;\n}\n\nfunction allDeployed(deploy: DeployResult): APIApplicationCommand[] {\n return [...deploy.values()].flatMap((collection) => [...collection.values()]);\n}\n\nfunction mentionOf(route: string, id: Snowflake | undefined): string {\n const spaced = route.replaceAll('/', ' ');\n return id ? `</${spaced}:${id}>` : `/${spaced}`;\n}\n\nclass CommandLinker {\n private readonly ids: Map<string, DeployedId>;\n\n constructor(private readonly deploy: DeployResult) {\n this.ids = deployedIds(deploy);\n }\n\n public link(all: readonly CommandJson[]): LinkedCommands {\n const slash = all.filter(isSlash);\n const leaves = contextMenuLeaves(all);\n const inCode = uniqueRefs([\n ...slash.map(commandRefOf),\n ...[...leaves.user].map((name) => ({ name, type: ApplicationCommandType.User })),\n ...[...leaves.message].map((name) => ({ name, type: ApplicationCommandType.Message }))\n ]);\n\n return {\n commands: this.slashRoutes(slash),\n contextMenus: {\n user: this.menus(leaves.user, ApplicationCommandType.User),\n message: this.menus(leaves.message, ApplicationCommandType.Message)\n },\n multiGuild: inCode.filter((ref) => this.ids.get(commandKey(ref)) === MULTI_GUILD),\n notInCode: this.notInCode(inCode)\n };\n }\n\n private slashRoutes(\n slash: readonly RESTPostAPIChatInputApplicationCommandsJSONBody[]\n ): Record<string, CommandInfo> {\n const commands = accessorStore<CommandInfo>();\n for (const command of slash) {\n const id = this.idOf(commandRefOf(command));\n for (const { route, description } of routeLeavesOf(command)) {\n commands[route] = { mention: mentionOf(route, id), ...(id && { id }), route, description };\n }\n }\n return commands;\n }\n\n private menus(names: ReadonlySet<string>, kind: ContextMenuKind): Record<string, ContextMenuInfo> {\n const menus = accessorStore<ContextMenuInfo>();\n for (const name of names) {\n const id = this.idOf({ name, type: kind });\n menus[name] = { ...(id && { id }), name, kind };\n }\n return menus;\n }\n\n private idOf(ref: CommandRef): Snowflake | undefined {\n const id = this.ids.get(commandKey(ref));\n return id === MULTI_GUILD ? undefined : id;\n }\n\n private notInCode(inCode: readonly CommandRef[]): CommandRef[] {\n const inCodeKeys = new Set(inCode.map(commandKey));\n const extra = new Map<string, CommandRef>();\n for (const command of allDeployed(this.deploy)) {\n const key = commandKey(command);\n if (!inCodeKeys.has(key)) extra.set(key, commandRefOf(command));\n }\n return [...extra.values()];\n }\n}\n\nexport function linkCommands(deploy: DeployResult, all: readonly CommandJson[]): LinkedCommands {\n return new CommandLinker(deploy).link(all);\n}\n","import { paint } from '@seedcord/errors';\nimport { Logger } from '@seedcord/logger';\nimport { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport { linkCommands } from '#src/commands/linkCommands';\nimport { accessorStore, clearStore, guardedAccessor } from '#src/miscellaneous/guarded';\n\nimport type {\n ContextMenuKind,\n MessageContextMenuRegistry,\n UserContextMenuRegistry\n} from '#registries/ContextMenuRegistry';\nimport type { SlashRegistry } from '#registries/SlashRegistry';\nimport type { CommandRef, LinkedCommands } from '#src/commands/linkCommands';\nimport type { CommandBuilder, DeployResult } from '#src/commands/types';\nimport type { ILogger } from '@seedcord/types';\n\nconst commandStorage = accessorStore<CommandInfo>();\nconst userMenuStorage = accessorStore<ContextMenuInfo>();\nconst messageMenuStorage = accessorStore<ContextMenuInfo>();\n\n/** What {@link Commands} resolves to for one slash route. */\nexport interface CommandInfo {\n /** The clickable `</config set:id>` mention. A route whose id did not resolve falls back to `/config set`. */\n readonly mention: string;\n /** The deployed command id. Absent when the command deployed to two or more guilds. */\n readonly id?: string;\n /** The full route, e.g. `config/set`. */\n readonly route: string;\n /** The route's own description, taken from the subcommand for a nested route. */\n readonly description: string;\n}\n\n/** Each registered slash route mapped to its deployed command. */\nexport type InjectedCommandMap = {\n [K in keyof SlashRegistry]: CommandInfo;\n};\n\n/**\n * The bot's deployed slash commands, keyed by {@link SlashRegistry}. A server bot fills these after\n * each deploy. An edge bot fills them on its first request, from the ids `seedcord build` wrote into\n * the worker.\n *\n * @throws A **SeedcordError** when you read a command before the bot fills it.\n *\n * @example\n * ```ts\n * import { Commands } from '@seedcord/gateway';\n *\n * await this.reply(`Try ${Commands['config/set'].mention}`);\n *\n * // a help listing\n * const lines = Object.values(Commands).map((c) => `${c.mention} ${c.description}`);\n * ```\n */\nexport const Commands = guardedAccessor('Commands', commandStorage) as InjectedCommandMap;\n\n/** What {@link ContextMenus} resolves to for one context-menu command. */\nexport interface ContextMenuInfo {\n /** The deployed command id. Absent when the command deployed to two or more guilds. */\n readonly id?: string;\n /** The name shown in Discord's right-click menu. */\n readonly name: string;\n readonly kind: ContextMenuKind;\n}\n\n/** Deployed context-menu commands, split by kind because Discord scopes name uniqueness per command type. */\nexport interface InjectedContextMenuMap {\n readonly user: { [K in keyof UserContextMenuRegistry]: ContextMenuInfo };\n readonly message: { [K in keyof MessageContextMenuRegistry]: ContextMenuInfo };\n}\n\n/**\n * The bot's deployed context-menu commands. A server bot fills these after each deploy. An edge bot\n * fills them on its first request, from the ids `seedcord build` wrote into the worker.\n *\n * @throws A **SeedcordError** when you read a command before the bot fills it.\n *\n * @example\n * ```ts\n * import { ContextMenus } from '@seedcord/gateway';\n *\n * const id = ContextMenus.user['View Profile'].id;\n * ```\n */\nexport const ContextMenus = {\n user: guardedAccessor('ContextMenus.user', userMenuStorage),\n message: guardedAccessor('ContextMenus.message', messageMenuStorage)\n} as InjectedContextMenuMap;\n\nexport function installLinks({ commands, contextMenus }: Pick<LinkedCommands, 'commands' | 'contextMenus'>): void {\n const pairs = [\n [commandStorage, commands],\n [userMenuStorage, contextMenus.user],\n [messageMenuStorage, contextMenus.message]\n ] as const;\n for (const [store, values] of pairs) {\n clearStore(store);\n Object.assign(store, values);\n }\n}\n\nconst KIND_LABELS: Record<ApplicationCommandType, string> = {\n [ApplicationCommandType.ChatInput]: 'slash command',\n [ApplicationCommandType.User]: 'user menu',\n [ApplicationCommandType.Message]: 'message menu',\n [ApplicationCommandType.PrimaryEntryPoint]: 'entry point command'\n};\n\nfunction multiGuildWarning({ name, type }: CommandRef): string {\n const consequence =\n type === ApplicationCommandType.ChatInput\n ? 'so its mention stays plain text (not clickable)'\n : 'so its id stays unset';\n return `${paint.sky.bold(name)} is deployed to multiple guilds, ${consequence}.`;\n}\n\nexport function warnLinks(\n logger: ILogger,\n { multiGuild, notInCode }: Pick<LinkedCommands, 'multiGuild' | 'notInCode'>\n): void {\n for (const ref of multiGuild) logger.warn(multiGuildWarning(ref));\n for (const { name, type } of notInCode) {\n logger.warn(\n `The ${KIND_LABELS[type]} ${paint.sky.bold(name)} is deployed on Discord with no command for it in your code.`\n );\n }\n}\n\n/** @internal */\nexport class CommandInjector {\n private readonly logger = new Logger('Commands', { channel: 'bot' });\n\n public inject(deploy: DeployResult, all: readonly CommandBuilder[]): void {\n const linked = linkCommands(\n deploy,\n all.map((command) => command.toJSON())\n );\n installLinks(linked);\n\n warnLinks(this.logger, linked);\n\n const routes = Object.values(linked.commands);\n const clickable = routes.filter((command) => command.id !== undefined).length;\n this.logger.utils.summary('Linked commands', {\n clickable,\n plain: routes.length - clickable,\n 'context menus':\n Object.keys(linked.contextMenus.user).length + Object.keys(linked.contextMenus.message).length\n });\n }\n}\n","import 'reflect-metadata';\n\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport { CommandMetadataKey } from '#src/metadataKeys';\n\nimport type { BuilderComponent } from '#components/Component';\nimport type { Constructor, LiteralUnion } from 'type-fest';\n\ntype CommandCtor = Constructor<BuilderComponent<'command' | 'context_menu'>>;\n\nexport const ConfigGuilds = 'config';\n\n/** A guild id, or `'config'` for every id in `commands.guilds`. */\nexport type GuildTarget = LiteralUnion<typeof ConfigGuilds, string>;\n\ninterface GlobalMeta {\n scope: 'global';\n}\n\ninterface GuildMeta {\n scope: 'guild';\n guilds: GuildTarget[];\n}\n\ninterface ConfigMeta {\n scope: 'config';\n}\n\nexport type CommandMeta = GlobalMeta | GuildMeta | ConfigMeta;\n\ntype CommandScope = CommandMeta['scope'];\n\n/**\n * Registers a command in the guilds listed under `commands.guilds`. With no ids listed there, the\n * command deploys globally.\n *\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand()\n * class PingCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\nexport function RegisterCommand(): (ctor: CommandCtor) => void;\n\n/**\n * Registers a command globally, ignoring `commands.guilds`.\n *\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand('global')\n * class PingCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/unified-signatures -- merging these puts two different deploy targets under one doc block\nexport function RegisterCommand(scope: 'global'): (ctor: CommandCtor) => void;\n\n/**\n * Registers a command in the guilds you pass. These ids replace `commands.guilds`. Include\n * `'config'` among them to keep that list too.\n *\n * @decorator\n * @example\n * ```typescript\n * \\@RegisterCommand('guild', ['config', '123456789'])\n * class AdminCommand extends BuilderComponent<'command'> {} // or BuilderComponent<'context_menu'>\n * ```\n */\nexport function RegisterCommand(scope: 'guild', guilds: GuildTarget[]): (ctor: CommandCtor) => void;\n\nexport function RegisterCommand(scope: CommandScope = 'config', guilds: GuildTarget[] = []) {\n return (ctor: CommandCtor): void => {\n const existingMeta = Reflect.getOwnMetadata(CommandMetadataKey, ctor) as CommandMeta | undefined;\n if (existingMeta) {\n throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandAlreadyRegistered, [\n ctor.name,\n decoratorCall(existingMeta.scope),\n decoratorCall(scope)\n ]);\n }\n\n if (scope === 'global' && guilds.length > 0) {\n throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGlobalWithGuilds, [ctor.name]);\n }\n\n if (scope === 'guild' && (!Array.isArray(guilds) || guilds.length === 0)) {\n throw new SeedcordTypeError(SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds, [ctor.name]);\n }\n\n const meta: CommandMeta = scope === 'guild' ? { scope, guilds } : { scope };\n Reflect.defineMetadata(CommandMetadataKey, meta, ctor);\n };\n}\n\nfunction decoratorCall(scope: CommandScope): string {\n if (scope === 'global') return \"@RegisterCommand('global')\";\n if (scope === 'guild') return \"@RegisterCommand('guild', [...])\";\n\n return '@RegisterCommand()';\n}\n","import 'reflect-metadata';\n\nimport { decodeFor } from '@seedcord/custom-id';\nimport { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport type { ComponentDefsBrand } from '#src/decorators/brands';\nimport type { CaseReturn } from '#src/handlers/runCase';\nimport type { AnyCustomId, DecodedParams } from '@seedcord/custom-id';\n\n/**\n * route decorators store a handler's customId defs here, so the component base can decode\n * against them at runtime.\n *\n * @internal\n */\n// storeComponentRoute writes this beside the route keys in metadataKeys.ts\nexport const ComponentDefsKey = Symbol.for('seedcord:custom-id:component-defs');\n\n/**\n * the phantom a component handler base carries. a route decorator constrains its argument to this, so\n * passing different defs to the decorator and the handler's generic is a compile error. never set at runtime.\n *\n * @internal\n */\nexport interface HasComponentDefs<Defs extends readonly AnyCustomId[]> {\n /** @internal */\n readonly [ComponentDefsBrand]?: Defs;\n}\n\n/** @internal */\nexport interface DecodedComponentRoute {\n prefix: string;\n params: Record<string, unknown>;\n}\n\n/**\n * throws `CustomIdHandlerRouteMissing` when the class has no stored defs. a drifted or corrupt wire\n * throws `StaleCustomId` or `InvalidCustomId`.\n *\n * @internal\n */\nexport function decodeComponentRoute(handlerClass: { name: string }, wire: string): DecodedComponentRoute {\n const defs = Reflect.getMetadata(ComponentDefsKey, handlerClass) as readonly AnyCustomId[] | undefined;\n if (!defs) throw new SeedcordTypeError(SeedcordErrorCode.CustomIdHandlerRouteMissing, [handlerClass.name]);\n return decodeFor(defs, wire);\n}\n\n/** @internal */\nexport type SingleParams<Defs extends readonly AnyCustomId[]> = Defs extends readonly [infer One extends AnyCustomId]\n ? DecodedParams<One['shape']>\n : never;\n\n/** @internal */\nexport type MatchCases<Defs extends readonly AnyCustomId[], Ret> = {\n [Def in Defs[number] as Def['prefix']]: (params: DecodedParams<Def['shape']>) => CaseReturn<Ret>;\n};\n","import 'reflect-metadata';\n\nimport { isStringArray } from '@seedcord/utils/internal';\nimport { ApplicationCommandType } from 'discord-api-types/v10';\n\nimport { ComponentDefsKey } from '#customId/routing';\nimport { assertUniqueKeys } from '#src/decorators/assertUniqueKeys';\nimport { InteractionMetadataKey, InteractionRouteKeys, InteractionKind } from '#src/metadataKeys';\n\nimport type { ContextMenuKind } from '#registries/ContextMenuRegistry';\nimport type { AnyCustomId } from '@seedcord/custom-id';\n\n// loose on purpose, each transport's typed decorators pass their asserted ctors straight in\nexport type RoutableConstructor = new (...args: never[]) => unknown;\n\n// the build's manifest emitter reads the same pairs\nexport function interactionRoutesOf(constructor: RoutableConstructor): [InteractionKind, string[]][] {\n const pairs: [InteractionKind, string[]][] = [];\n for (const route of Object.values(InteractionKind)) {\n const meta: unknown = Reflect.getMetadata(InteractionRouteKeys[route], constructor);\n if (isStringArray(meta)) pairs.push([route, meta]);\n }\n return pairs;\n}\n\nexport function routeDecoratorOf(kind: InteractionKind): string {\n return `@${kind.charAt(0).toUpperCase()}${kind.slice(1)}Route`;\n}\n\nexport function storeInteractionRoute(\n route: InteractionKind,\n routes: string | readonly string[],\n constructor: RoutableConstructor,\n fix = 'Remove the repeat.'\n): void {\n const key = InteractionRouteKeys[route];\n const saved: unknown = Reflect.getMetadata(key, constructor);\n const existing = isStringArray(saved) ? saved : [];\n const toStore = typeof routes === 'string' ? [routes] : routes;\n // own metadata, since a subclass can list a route that its parent lists\n const own: unknown = Reflect.getOwnMetadata(key, constructor);\n assertUniqueKeys(routeDecoratorOf(route), constructor, [...(isStringArray(own) ? own : []), ...toStore], fix);\n Reflect.defineMetadata(key, [...existing, ...toStore], constructor);\n Reflect.defineMetadata(InteractionMetadataKey, true, constructor);\n}\n\nexport function storeComponentRoute(\n route: InteractionKind,\n defs: readonly AnyCustomId[],\n constructor: RoutableConstructor\n): void {\n storeInteractionRoute(\n route,\n defs.map((def) => def.prefix),\n constructor,\n 'Remove the repeat, or give each customId its own prefix.'\n );\n Reflect.defineMetadata(ComponentDefsKey, defs, constructor);\n}\n\nexport function contextMenuRouteOf(kind: ContextMenuKind): InteractionKind {\n return kind === ApplicationCommandType.User ? InteractionKind.UserContextMenu : InteractionKind.MessageContextMenu;\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordTypeError } from '@seedcord/errors/internal';\n\nimport { InteractionMiddlewareMetadataKey } from '#src/metadataKeys';\n\nimport type { MiddlewareKind } from '#src/metadataKeys';\nimport type { MiddlewareKindsBrand } from './brands';\nimport type { Constructor, NonEmptyTuple } from 'type-fest';\n\n/** @internal */\nexport type AnyMiddlewareCtor = Constructor<unknown, never[]>;\n\n// core cannot import the transport's middleware base. the kinds arrive on a phantom brand.\ntype KindsOf<TCtor extends AnyMiddlewareCtor> =\n InstanceType<TCtor> extends { [MiddlewareKindsBrand]?: infer Kind extends MiddlewareKind } ? Kind : never;\n\ntype AssertMiddlewareKinds<Kinds extends MiddlewareKind, TCtor extends AnyMiddlewareCtor> = [Kinds] extends [\n KindsOf<TCtor>\n]\n ? [KindsOf<TCtor>] extends [Kinds]\n ? TCtor\n : Constructor<\n ['the middleware generic declares a kind that RegisterInteractionMiddleware omits', KindsOf<TCtor>]\n >\n : Constructor<['RegisterInteractionMiddleware lists a kind that the middleware generic omits', Kinds]>;\n\n/** Registration options for an interaction middleware. */\nexport interface InteractionMiddlewareOptions<Kinds extends NonEmptyTuple<MiddlewareKind>> {\n /**\n * Restrict this middleware to certain interaction kinds. The middleware's generic must list the same\n * kinds, or applying the decorator is a compile error. Omit it to run on every repliable kind.\n */\n readonly kinds?: Kinds;\n /**\n * Lower runs earlier. Two middleware sharing a priority run in registration order.\n *\n * @defaultValue 0\n */\n readonly priority?: number;\n}\n\n/** @internal */\nexport interface InteractionMiddlewareMetadata {\n readonly priority: number;\n readonly kinds?: readonly MiddlewareKind[];\n}\n\n/**\n * Registers an interaction middleware. It runs after the dispatcher builds the handler and before its\n * gates, over that handler's reply surface.\n *\n * @param options - The kinds this middleware runs on and its ordering.\n * @decorator\n *\n * @example\n * ```ts\n * \\@RegisterInteractionMiddleware({ kinds: [InteractionKind.Button], priority: 10 })\n * class Audit extends InteractionMiddleware<InteractionKind.Button> {\n * async execute() {\n * this.logger.info(this.dispatch.routeId);\n * }\n * }\n * ```\n *\n * @throws A **SeedcordTypeError** when `priority` is not a finite number.\n */\nexport function RegisterInteractionMiddleware<\n const Kinds extends NonEmptyTuple<MiddlewareKind> = NonEmptyTuple<MiddlewareKind>\n>(options: InteractionMiddlewareOptions<Kinds> = {}) {\n return function <TCtor extends AnyMiddlewareCtor>(ctor: AssertMiddlewareKinds<Kinds[number], TCtor>): void {\n const priority = Number(options.priority ?? 0);\n if (!Number.isFinite(priority))\n throw new SeedcordTypeError(SeedcordErrorCode.DecoratorInvalidMiddlewarePriority, [\n ctor.name,\n String(options.priority)\n ]);\n if (options.kinds?.length === 0)\n throw new SeedcordTypeError(SeedcordErrorCode.DecoratorEmptyMiddlewareFilter, [ctor.name, 'kinds']);\n\n const metadata: InteractionMiddlewareMetadata = {\n priority,\n ...(options.kinds && { kinds: options.kinds })\n };\n\n Reflect.defineMetadata(InteractionMiddlewareMetadataKey, metadata, ctor);\n };\n}\n\n/** @internal */\nexport function interactionMiddlewareMetaOf(constructor: AnyMiddlewareCtor): InteractionMiddlewareMetadata | undefined {\n const saved: unknown = Reflect.getMetadata(InteractionMiddlewareMetadataKey, constructor);\n return saved as InteractionMiddlewareMetadata | undefined;\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport type { AckTrace } from './AckTrace';\n\nexport type AckState = 'unacked' | 'deferred-reply' | 'deferred-update' | 'replied';\n\nexport type ReplyMethod =\n 'reply' | 'defer' | 'deferUpdate' | 'update' | 'followUp' | 'edit' | 'delete' | 'send' | 'showModal';\n\ninterface Illegal {\n // spliced into the message after \"was called when\"\n readonly reason: string;\n readonly alternative: string;\n}\n\nconst showModalReason: Illegal = {\n reason: 'this interaction was already acknowledged',\n alternative: 'showModal() must be the initial response.'\n};\n\nfunction deferReason(what: string): Illegal {\n return {\n reason: `this interaction was already acknowledged with ${what}`,\n alternative: 'A deferral can only be the initial response.'\n };\n}\n\n// a missing entry means legal in that state\nconst ILLEGAL: Record<Exclude<ReplyMethod, 'send'>, Partial<Record<AckState, Illegal>>> = {\n reply: {\n 'deferred-reply': {\n reason: 'this interaction was already deferred',\n alternative: 'Use edit() to fill the deferred reply or followUp() for a new message.'\n },\n 'deferred-update': {\n reason: 'this interaction was already acknowledged with a deferred update',\n alternative: 'Use followUp() for a new message.'\n },\n replied: {\n reason: 'this interaction was already replied to',\n alternative: 'Use followUp() for a new message or edit() to rewrite the reply.'\n }\n },\n defer: {\n 'deferred-reply': deferReason('a deferred reply'),\n 'deferred-update': deferReason('a deferred update'),\n replied: deferReason('a reply')\n },\n deferUpdate: {\n 'deferred-reply': deferReason('a deferred reply'),\n 'deferred-update': deferReason('a deferred update'),\n replied: deferReason('a reply')\n },\n update: {\n 'deferred-reply': {\n reason: 'this interaction was deferred as a reply',\n alternative: 'update() only rewrites a source message. Use edit() to fill the placeholder.'\n },\n replied: {\n reason: 'this interaction was already replied to',\n alternative: 'update() rewrites a source message. Use edit() or followUp() instead.'\n }\n },\n followUp: {\n unacked: {\n reason: 'this interaction has not been acknowledged yet',\n alternative: 'Ack first with reply() or defer().'\n }\n },\n edit: {\n unacked: {\n reason: 'nothing has been sent yet',\n alternative: 'Send with reply() or defer() first. After that, edit() rewrites it.'\n }\n },\n delete: {\n unacked: {\n reason: 'nothing has been sent yet',\n alternative: 'Acknowledge first with reply() or defer(). After that, delete() removes the reply.'\n }\n },\n showModal: {\n 'deferred-reply': showModalReason,\n 'deferred-update': showModalReason,\n replied: showModalReason\n }\n};\n\nconst SEND_TARGET: Record<AckState, 'reply' | 'edit' | 'followUp'> = {\n unacked: 'reply',\n 'deferred-reply': 'edit',\n 'deferred-update': 'followUp',\n replied: 'followUp'\n};\n\n// throws before any API call, and ackedBy becomes the throw cause with its stack pointing at the first ack\nexport function checkAckLegality(method: ReplyMethod, state: AckState, routeId: string, ackedBy?: AckTrace): void {\n if (method === 'send') return;\n const illegal = ILLEGAL[method][state];\n if (!illegal) return;\n throw new SeedcordError(\n SeedcordErrorCode.ReplyIllegalAckState,\n [method, illegal.reason, illegal.alternative, routeId],\n { cause: ackedBy }\n );\n}\n\nexport function sendTarget(state: AckState): 'reply' | 'edit' | 'followUp' {\n return SEND_TARGET[state];\n}\n","import type { ReplyMethod } from './ackLegality';\n\n// the first ack's stack, passed as the throw cause so a double-ack points to both lines\nexport class AckTrace extends Error {\n public constructor(method: ReplyMethod) {\n super(`${method}() acknowledged this interaction`);\n this.name = 'AckTrace';\n }\n}\n","import { Logger } from '@seedcord/logger';\n\nimport type { LoggerOptions } from '@seedcord/logger';\n\n// lazy because env binds after the calling module loads\nexport function lazyLogger(label: string, options: LoggerOptions): () => Logger {\n let logger: Logger | undefined;\n return () => (logger ??= new Logger(label, options));\n}\n","import { paint } from '@seedcord/errors';\nimport { asError } from '@seedcord/utils/internal';\n\nimport { lazyLogger } from '#src/miscellaneous/lazyLogger';\nimport { PublishDefault } from '#subscribers/publishDefault';\n\nimport type { DispatchContext } from '#src/dispatch/DispatchContext';\nimport type { Bus } from '#subscribers/Bus';\nimport type { ReplyMethod } from './ackLegality';\n\nconst logger = lazyLogger('Reply', { channel: 'interactions' });\n\nexport interface ReplyTelemetry {\n readonly bus: Bus;\n readonly dispatch: DispatchContext;\n readonly interactionId: string;\n}\n\n// the reply verbs plus the autocomplete choices callback, which runs outside the ack state machine\nexport type WriteMethod = ReplyMethod | 'respond';\n\ninterface WriteStart {\n readonly method: WriteMethod;\n // performance.now() captured before the write began\n readonly startedAt: number;\n}\n\ninterface SentReport extends WriteStart {\n readonly outcome: 'sent';\n readonly messageId: string | null;\n}\n\ninterface FailedReport extends WriteStart {\n readonly outcome: 'failed';\n readonly error: Error;\n}\n\nexport type ResponseReport = SentReport | FailedReport;\n\n/** @internal */\nexport function publishResponse(telemetry: ReplyTelemetry, report: ResponseReport): void {\n const durationMs = performance.now() - report.startedAt;\n const { routeId } = telemetry.dispatch;\n const write = {\n dispatchId: telemetry.dispatch.id,\n routeId,\n interactionId: telemetry.interactionId,\n method: report.method,\n durationMs\n };\n telemetry.bus[PublishDefault](\n 'responseAttempted',\n report.outcome === 'failed'\n ? { ...write, outcome: 'failed', messageId: null, error: report.error }\n : { ...write, outcome: 'sent', messageId: report.messageId }\n );\n\n // after publish so a throwing sink doesn't reach the publish above\n logger().trace(\n `${paint.sky.bold(routeId)} ${report.method} ${report.outcome} ${paint.mute('in')} ${Math.round(durationMs)}ms`\n );\n}\n\nexport async function reportedWrite<Result>(\n telemetry: ReplyTelemetry,\n method: WriteMethod,\n write: () => Promise<Result>\n): Promise<Result> {\n const startedAt = performance.now();\n const result = await attemptWrite(telemetry, method, startedAt, write);\n publishResponse(telemetry, { method, startedAt, outcome: 'sent', messageId: null });\n return result;\n}\n\n// publishes the failed outcome when write() throws, since the caller's success report sits after the write and a throw skips it\nexport async function attemptWrite<Result>(\n telemetry: ReplyTelemetry,\n method: WriteMethod,\n startedAt: number,\n write: () => Promise<Result>\n): Promise<Result> {\n try {\n return await write();\n } catch (caught) {\n const error = asError(caught);\n publishResponse(telemetry, { method, startedAt, outcome: 'failed', error });\n // rethrown raw\n throw caught;\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\nimport { pluralize } from '@seedcord/utils/internal';\n\nconst allOf = new Intl.ListFormat('en', { type: 'conjunction' });\n\n// shapeshift aggregate errors nest, so the cap bounds the recursion\nconst MAX_DEPTH = 8;\n\ninterface ShapeshiftLike {\n readonly message?: unknown;\n readonly errors?: unknown;\n readonly constraint?: unknown;\n readonly validator?: unknown;\n readonly expected?: unknown;\n readonly given?: unknown;\n}\n\nconst OPERATOR_WORD: Record<string, string> = {\n '<=': 'at most',\n '>=': 'at least',\n '<': 'fewer than',\n '>': 'more than'\n};\n\nconst KIND_WORD: Record<string, string> = {\n string: 'a string',\n number: 'a number',\n boolean: 'a boolean',\n bigint: 'a bigint',\n array: 'a list',\n tuple: 'a list',\n date: 'a date',\n object: 'an object'\n};\n\nfunction asRecord(value: unknown): ShapeshiftLike | null {\n return typeof value === 'object' && value !== null ? value : null;\n}\n\nfunction firstLine(message: unknown): string {\n if (typeof message !== 'string') return 'unknown validation failure';\n return message.split('\\n')[0] ?? message;\n}\n\n// strings over this limit render as a character count\nconst GIVEN_PREVIEW_LIMIT = 40;\n\nfunction gotPhrase(given: unknown): string {\n if (given === undefined) return 'nothing';\n if (given === null) return 'null';\n if (typeof given === 'string') {\n if (given.length === 0) return 'an empty string';\n if (given.length > GIVEN_PREVIEW_LIMIT) return `${given.length} characters`;\n return JSON.stringify(given);\n }\n if (Array.isArray(given)) return given.length === 0 ? 'an empty list' : `${given.length} items`;\n if (typeof given === 'number' || typeof given === 'bigint' || typeof given === 'boolean') return String(given);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- a null-prototype value has no constructor at runtime\n if (typeof given === 'object') return given.constructor?.name ?? 'an object';\n return typeof given;\n}\n\n// shapeshift constraint strings take the form 'expected.length >= 1' or 'expected <= 16777215'\nfunction needPhrase(expected: string, given: unknown): string | null {\n const bounds = [...expected.matchAll(/expected(\\.length)?\\s*(<=|>=|<|>)\\s*(\\d+)/g)];\n if (bounds.length === 0) return null;\n const phrases = bounds.map(([, lengthPart, operator, limit]) => {\n const word = OPERATOR_WORD[operator ?? ''] ?? operator ?? '';\n if (!lengthPart) return `${word} ${limit}`;\n const unit = typeof given === 'string' ? 'character' : 'item';\n return `${word} ${limit} ${pluralize(Number(limit), unit)}`;\n });\n return allOf.format(phrases);\n}\n\nfunction instanceName(expected: unknown): string {\n if (typeof expected === 'function' && expected.name) return expected.name;\n return 'specific class instance';\n}\n\nfunction describeLeaf(record: ShapeshiftLike): string {\n if (typeof record.constraint === 'string') {\n const need = typeof record.expected === 'string' ? needPhrase(record.expected, record.given) : null;\n if (need) return `need ${need}, got ${gotPhrase(record.given)}`;\n return `${firstLine(record.message)}${'given' in record ? ` (got ${gotPhrase(record.given)})` : ''}`;\n }\n if (typeof record.expected === 'function') {\n return `need a ${instanceName(record.expected)}, got ${gotPhrase(record.given)}`;\n }\n if (typeof record.validator === 'string') {\n const kind = KIND_WORD[record.validator.replace(/^s\\./, '').replace(/\\(.*$/, '')];\n if (kind) return `need ${kind}, got ${gotPhrase(record.given)}`;\n return `${firstLine(record.message)}, got ${gotPhrase(record.given)}`;\n }\n return firstLine(record.message);\n}\n\nfunction describeUnion(branches: unknown[], depth: number): string {\n const records: ShapeshiftLike[] = [];\n for (const branch of branches) {\n const record = asRecord(branch);\n if (record) records.push(record);\n }\n const bounded = records.find((record) => typeof record.constraint === 'string');\n if (bounded) return describeLeaf(bounded);\n\n const names: string[] = [];\n let allInstances = records.length > 0;\n for (const record of records) {\n if (typeof record.expected === 'function') names.push(instanceName(record.expected));\n else allInstances = false;\n }\n if (allInstances) {\n return `need a ${[...new Set(names)].join(' or ')}, got ${gotPhrase(records[0]?.given)}`;\n }\n return detailOf(branches[0], depth + 1);\n}\n\n// shapeshift nests errors as [key, inner] tuples\nfunction detailOf(value: unknown, depth: number): string {\n const record = asRecord(value);\n if (!record || depth >= MAX_DEPTH) return firstLine(record?.message);\n\n if (Array.isArray(record.errors) && record.errors.length > 0) {\n const errors = record.errors as unknown[];\n const [first] = errors;\n if (Array.isArray(first)) {\n const [key, inner] = first as [unknown, unknown];\n return `${String(key)}: ${detailOf(inner, depth + 1)}`;\n }\n return describeUnion(errors, depth);\n }\n\n return describeLeaf(record);\n}\n\n// a shapeshift throw becomes an actionable message with the cause kept, while anything else contributes its own message as the detail\nexport function translateSerializationError(\n error: unknown,\n componentClass: string,\n index: number,\n routeId: string\n): SeedcordError<SeedcordErrorCode.ReplyComponentSerialization> {\n const detail = detailOf(error, 0);\n return new SeedcordError(SeedcordErrorCode.ReplyComponentSerialization, [componentClass, index, detail, routeId], {\n cause: error\n });\n}\n","import { TextDisplayBuilder } from '@discordjs/builders';\n\nimport { translateSerializationError } from './translateSerialization';\n\nimport type { ReplyResponse } from '@seedcord/types';\nimport type { APIMessageTopLevelComponent } from 'discord-api-types/v10';\n\nexport interface SerializedReply<TNative = never> {\n readonly components: APIMessageTopLevelComponent[];\n readonly allowedMentions?: ReplyResponse['allowedMentions'];\n readonly files?: ReplyResponse<TNative>['files'];\n}\n\nexport function serializeReply<TNative>(\n response: ReplyResponse<TNative> | string,\n routeId: string\n): SerializedReply<TNative> {\n if (typeof response === 'string') {\n try {\n return { components: [new TextDisplayBuilder().setContent(response).toJSON()] };\n } catch (error) {\n throw translateSerializationError(error, 'TextDisplayBuilder', 0, routeId);\n }\n }\n\n const components = response.components.map((component, index) => {\n try {\n return 'toJSON' in component ? component.toJSON() : component;\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- untyped code can pass null, and a null-prototype value has no constructor\n const name = component?.constructor?.name;\n throw translateSerializationError(error, !name || name === 'Object' ? 'component' : name, index, routeId);\n }\n });\n\n return {\n components,\n ...(response.allowedMentions && { allowedMentions: response.allowedMentions }),\n ...(response.files && { files: response.files })\n };\n}\n","import { toEpochSeconds } from '@seedcord/utils';\n\nimport { Notice } from '#stops/Notice';\nimport { NoticeCard } from '#stops/NoticeCard';\n\nimport type { EpochMs, ReplyResponse } from '@seedcord/types';\n\nexport class NotOwner extends Notice {\n public constructor() {\n super('Only the bot owner can use this.');\n this.summary = 'be the bot owner';\n }\n}\n\nexport class NotInGuild extends Notice {\n public constructor() {\n super('This can only be used in a server.');\n this.summary = 'be in a server';\n }\n}\n\nexport class NotInDm extends Notice {\n public constructor() {\n super('This can only be used in a direct message.');\n this.summary = 'be in a direct message';\n }\n}\n\nfunction retryAt(resetAt: EpochMs): string {\n return `<t:${toEpochSeconds(resetAt)}:R>`;\n}\n\nexport class OnCooldown extends Notice {\n public constructor(public readonly resetAt: EpochMs) {\n super(`You are doing that too fast. Try again ${retryAt(resetAt)}.`);\n this.summary = `try again ${retryAt(resetAt)}`;\n }\n}\n\nexport class NotAllowed extends Notice {\n public constructor() {\n super('not allowed');\n }\n\n public override render(): ReplyResponse {\n return { components: [new NoticeCard('You are not allowed to use this command.').component] };\n }\n}\n\nexport class NeedsAny extends Notice {\n private readonly summaries: readonly string[];\n\n public constructor(summaries: readonly string[]) {\n super('not allowed');\n // two gates in an or can refuse with the same summary\n this.summaries = [...new Set(summaries)];\n }\n\n public override render(): ReplyResponse {\n const bullets = this.summaries.map((summary) => `• ${summary}`).join('\\n');\n return { components: [new NoticeCard(`You need to meet at least one of these:\\n${bullets}`).component] };\n }\n}\n\nexport class MissingRole extends Notice {\n public constructor(roleId: string) {\n super(`You need the <@&${roleId}> role to use this.`);\n this.summary = `hold the <@&${roleId}> role`;\n }\n}\n\nexport class PermissionsUnresolved extends Notice {\n public constructor() {\n super('Your server member data could not be resolved. Try again.');\n }\n}\n\ninterface Wording {\n lead: string;\n summary: string;\n}\n\ninterface ListWording {\n caller: Wording;\n subject: Wording;\n}\n\nabstract class PermissionListNotice extends Notice {\n private readonly lead: string;\n\n protected constructor(\n message: string,\n subject: string | null,\n private readonly permissions: readonly string[],\n wording: ListWording\n ) {\n super(message);\n const [prefix, words] = subject === null ? ['', wording.caller] : [`${subject} `, wording.subject];\n this.lead = `${prefix}${words.lead}`;\n this.summary = `${prefix}${words.summary} ${permissions.join(', ')}`;\n }\n\n public override render(): ReplyResponse {\n const bullets = this.permissions.map((perm) => `• ${perm}`).join('\\n');\n return { components: [new NoticeCard(`${this.lead}\\n${bullets}`).component] };\n }\n}\n\nexport class MissingPermissions extends PermissionListNotice {\n public constructor(subject: string | null, permissions: readonly string[]) {\n super('A required permission is missing.', subject, permissions, {\n caller: { lead: 'You are missing the following permission entries:', summary: 'hold' },\n subject: { lead: 'is missing the following permission entries:', summary: 'needs' }\n });\n }\n}\n\nexport class HasPresentPermissions extends PermissionListNotice {\n public constructor(subject: string | null, permissions: readonly string[]) {\n super('A refused permission is held.', subject, permissions, {\n caller: { lead: \"You hold permissions that aren't allowed here:\", summary: 'not hold' },\n subject: { lead: \"holds permissions that aren't allowed here:\", summary: 'must not hold' }\n });\n }\n}\n","import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. When the user should see a real message for a fault,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/gateway';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, and the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public override render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","import type { EffectGate, Gate, GateContextBase } from './Gate';\n\ntype CommitFn = () => Promise<void>;\n\n// commits wait here until the whole gate set passes, one queue per request. the key is the context\n// itself and the map is weak, so an entry goes when its request does\nconst commitQueues = new WeakMap<GateContextBase, CommitFn[]>();\n\nfunction isEffectGate(gate: Gate<GateContextBase>): gate is EffectGate<GateContextBase> {\n return typeof (gate as Partial<EffectGate<GateContextBase>>).commit === 'function';\n}\n\n// a refused effect gate never commits. combinators call this too\nexport async function runCheck(gate: Gate<GateContextBase>, ctx: GateContextBase): Promise<void> {\n await gate.check(ctx);\n if (!isEffectGate(gate)) return;\n\n let queue = commitQueues.get(ctx);\n if (!queue) {\n queue = [];\n commitQueues.set(ctx, queue);\n }\n queue.push(() => gate.commit(ctx));\n}\n\nexport async function runCommits(ctx: GateContextBase): Promise<void> {\n const queue = commitQueues.get(ctx);\n if (!queue) return;\n for (const commit of queue) await commit();\n}\n\n// the runner calls this from a finally, refused run or not\nexport function discardCommits(ctx: GateContextBase): void {\n commitQueues.delete(ctx);\n}\n\n// each gate in an `or` takes this mark before it runs\nexport function markCommits(ctx: GateContextBase): number {\n return commitQueues.get(ctx)?.length ?? 0;\n}\n\n// and rolls back to it when the gate refuses, leaving no commit behind\nexport function rollbackCommits(ctx: GateContextBase, mark: number): void {\n const queue = commitQueues.get(ctx);\n if (queue && queue.length > mark) queue.length = mark;\n}\n","/**\n * Throw to stop a handler with no reply and no report.\n *\n * The framework boundary catches `Silence` before {@link Notice}, makes zero Discord calls, and stops.\n * Ideally you'd only throw this in `EventHandlers` (or `Gates` for those), because it doesn't make sense to\n * leave the user with no reply for an interaction.\n *\n * @example\n * ```ts\n * import { Silence } from '@seedcord/gateway';\n *\n * // a helper under a guildMemberAdd handler, stopping when this guild configured nothing to send\n * const template = await settings.welcome(member.guild.id);\n * if (!template) throw new Silence('no welcome message set');\n * ```\n */\nexport class Silence extends Error {\n /**\n * @param reason - Optional note written only to a debug log, never shown to the user or reported.\n */\n public constructor(public readonly reason?: string) {\n super(reason ?? 'Silence');\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import type { APIMessageComponentEmoji } from 'discord-api-types/v10';\n\n/**\n * A resolved custom emoji. Renders as `<:name:id>` in message content, or `<a:name:id>` when animated,\n * and passes straight to a builder's `setEmoji`.\n */\n// setEmoji in @discordjs/builders rejects any own key past these three, scanning with Object.entries\nexport class ResolvedEmoji implements APIMessageComponentEmoji {\n constructor(\n public readonly name: string,\n public readonly id: string,\n public readonly animated = false\n ) {}\n\n public toString(): string {\n return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;\n }\n}\n","import { CustomId } from '@seedcord/custom-id';\n\nimport type { CustomIdField } from '@seedcord/custom-id';\n\n// Included in the layout hash so it is fixed per prefix. Reducing it marks every active button as StaleCustomId.\n// @internal\nexport const PAGE_MAX = 1_000_000;\n\n// Discord rejects a message with duplicate custom_ids, and two controls can target the same page (first and\n// prev both hit 0).\n// @internal\nconst SLOT_MAX = 4;\n\n/** Page cursor for a paginator. */\nexport type PageCursor<Prefix extends string> = CustomId<\n Prefix,\n { page: CustomIdField<number>; slot: CustomIdField<number> }\n>;\n\n/** Build the page cursor for a paginator. */\nexport function pageCursor<Prefix extends string>(prefix: Prefix): PageCursor<Prefix> {\n return new CustomId(prefix).int('page', 0, PAGE_MAX).int('slot', 0, SLOT_MAX);\n}\n"],"mappings":"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"}
@@ -1,5 +1,3 @@
1
- import { PortableSource, merge } from "envapt";
2
-
3
1
  //#region \0@oxc-project+runtime@0.152.0/helpers/esm/usingCtx.js
4
2
  function _usingCtx() {
5
3
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
@@ -57,32 +55,4 @@ function _usingCtx() {
57
55
  }
58
56
 
59
57
  //#endregion
60
- //#region src/miscellaneous/bindViteEnv.ts
61
- function bindEnv(envapter, source, directory) {
62
- try {
63
- var _usingCtx$1 = _usingCtx();
64
- const previousSource = envapter.source;
65
- const previousBaseDir = previousSource.supportsFiles ? envapter.baseDir : void 0;
66
- const cleanup = _usingCtx$1.u(new DisposableStack());
67
- cleanup.defer(() => {
68
- envapter.useSource(previousSource);
69
- if (previousSource.supportsFiles) envapter.baseDir = previousBaseDir;
70
- });
71
- if (previousSource.supportsFiles && directory !== void 0) envapter.baseDir = directory;
72
- envapter.useSource(source);
73
- return cleanup.move();
74
- } catch (_) {
75
- _usingCtx$1.e = _;
76
- } finally {
77
- _usingCtx$1.d();
78
- }
79
- }
80
- function bindViteEnv(envapter, vite, { bindings, directory } = {}) {
81
- const { MODE: mode, BASE_URL: _baseUrl, DEV: _dev, PROD: _prod, SSR: _ssr, ...values } = vite;
82
- const source = bindings === void 0 ? envapter.source : merge((key) => key === "MODE" && typeof mode === "string" ? mode : void 0, new PortableSource(bindings));
83
- return bindEnv(envapter, merge(source, new PortableSource(values)), directory);
84
- }
85
-
86
- //#endregion
87
- export { bindViteEnv as n, bindEnv as t };
88
- //# sourceMappingURL=bindViteEnv-DWq5rftJ.mjs.map
58
+ export { _usingCtx as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@seedcord/core",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.12.0-next.2",
5
5
  "engines": {
6
6
  "node": "^24.22.1 || >=26.12.1",
7
7
  "bun": ">=1.4.2"
@@ -76,30 +76,33 @@
76
76
  "LICENSE"
77
77
  ],
78
78
  "peerDependencies": {
79
+ "@discordjs/builders": "^1.14.1",
79
80
  "envapt": "^9.2.0",
80
81
  "typescript": "^5.9.3 || ^6.0.0 || ^7.0.0"
81
82
  },
82
83
  "dependencies": {
83
- "@discordjs/builders": "^1.14.1",
84
84
  "@discordjs/rest": "^2.6.3",
85
- "@seedcord/custom-id": "^0.3.0",
86
- "@seedcord/errors": "^0.14.0",
85
+ "@seedcord/custom-id": "^0.3.1-next.1",
86
+ "@seedcord/errors": "^0.15.0-next.2",
87
87
  "@seedcord/event-emitter": "^0.2.0",
88
- "@seedcord/logger": "^0.5.0",
89
- "@seedcord/types": "^0.15.0",
90
- "@seedcord/utils": "^0.9.0",
88
+ "@seedcord/logger": "^0.6.0-next.1",
89
+ "@seedcord/types": "^0.16.0-next.1",
90
+ "@seedcord/utils": "^0.9.1-next.1",
91
91
  "discord-api-types": "^0.38.52",
92
92
  "reflect-metadata": "^0.2.2",
93
+ "type-fest": "^5.9.0",
93
94
  "verkit": "^0.5.0"
94
95
  },
95
96
  "devDependencies": {
96
- "@seedcord/eslint-config": "^3.0.0",
97
- "@seedcord/rate-limiter": "^0.2.0",
97
+ "@discordjs/builders": "^1.14.1",
98
+ "@seedcord/eslint-config": "^3.0.1-next.2",
98
99
  "@seedcord/tsconfig": "^3.0.0",
99
100
  "@seedcord/tsdown-config": "^2.0.1",
100
101
  "@seedcord/vitest-config": "^0.0.1",
101
102
  "envapt": "^9.2.0",
102
- "type-fest": "^5.9.0",
103
+ "keyv": "6.1.0",
104
+ "unstorage": "2.0.0-beta.0",
105
+ "unstorage-v1": "npm:unstorage@1.17.6",
103
106
  "vite": "^8.3.4"
104
107
  },
105
108
  "publishConfig": {
@@ -118,5 +121,5 @@
118
121
  "test:watch": "vitest dev",
119
122
  "coverage": "vitest run --coverage"
120
123
  },
121
- "readme": "<div align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.seedcord.org/assets/wordmark-dark.webp\" />\n <img src=\"https://cdn.seedcord.org/assets/wordmark-light.webp\" alt=\"seedcord\" width=\"440\" />\n </picture>\n</div>\n\n<div align=\"center\">\n <h3>The whole Discord bot, typed end to end</h3>\n <a href=\"https://seedcord.org\">Website</a> ·\n <a href=\"https://seedcord.org/guide\">Guide</a> ·\n <a href=\"https://seedcord.org/docs\">Reference</a> ·\n <a href=\"https://seedcord.org/chat/\">Discord</a>\n</div>\n\n<br />\n\n<div align=\"center\">\n\n[![npm](https://img.shields.io/npm/v/@seedcord/core?style=flat-square&label=npm&labelColor=1f1f1f&color=c8341f)](https://www.npmjs.com/package/@seedcord/core) [![node](https://img.shields.io/node/v/@seedcord/core?style=flat-square&label=node&labelColor=1f1f1f&color=4d7d33)](https://nodejs.org) [![bun](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fseedcord%2Fseedcord%2Fnext%2Fpackages%2Fcore%2Fpackage.json&query=%24.engines.bun&style=flat-square&label=bun&labelColor=1f1f1f&color=fbf0df)](https://bun.com) [![license](https://img.shields.io/npm/l/@seedcord/core?style=flat-square&label=license&labelColor=1f1f1f&color=f8f6e8)](LICENSE)\n\n</div>\n\n## About\n\n`@seedcord/core` is the code both seedcord transports share. It has the route decorators, the handler bases, checks, pagination, the startup and shutdown phases, and the plugin base. [`@seedcord/gateway`](https://www.npmjs.com/package/@seedcord/gateway) and [`@seedcord/http`](https://www.npmjs.com/package/@seedcord/http) connect it to Discord.\n\nBoth transports depend on core and re-export all of it, so a bot imports everything from its transport. Install core directly when you write a plugin that works on both transports.\n\nseedcord is pre-1.0, so a minor version can break your plugin. Each release's changelog says what changed and how to update.\n\n## Installation\n\n```sh\npnpm add -D @seedcord/core envapt typescript\n```\n\nAlso list `@seedcord/core` in your plugin's `peerDependencies`, with the versions you tested against. A plugin that extends a different copy of core than the bot's throws `CorePluginFromOtherCore` when the bot attaches it.\n\n## Usage\n\nA plugin extends `Plugin` from `@seedcord/core/plugin` and implements `init()`:\n\n```ts\nimport { Plugin } from '@seedcord/core/plugin';\n\nexport class Uptime extends Plugin {\n private startedAt = 0;\n\n public async init(): Promise<void> {\n this.startedAt = Date.now();\n }\n\n public elapsed(): number {\n return Date.now() - this.startedAt;\n }\n}\n```\n\nA gateway or http bot attaches it with `.attach('uptime', Uptime)`, and handlers call `this.core.uptime.elapsed()`. The [Writing plugins guide](https://seedcord.org/guide/writing-plugins) covers options, lifecycle phases, testing a plugin locally, and publishing.\n"
124
+ "readme": "<div align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.seedcord.org/assets/wordmark-dark.webp\" />\n <img src=\"https://cdn.seedcord.org/assets/wordmark-light.webp\" alt=\"seedcord\" width=\"440\" />\n </picture>\n</div>\n\n<div align=\"center\">\n <h3>The whole Discord bot, typed end to end</h3>\n <a href=\"https://seedcord.org\">Website</a> ·\n <a href=\"https://seedcord.org/guide\">Guide</a> ·\n <a href=\"https://seedcord.org/docs\">Reference</a> ·\n <a href=\"https://seedcord.org/chat/\">Discord</a>\n</div>\n\n<br />\n\n<div align=\"center\">\n\n[![npm](https://img.shields.io/npm/v/@seedcord/core?style=flat-square&label=npm&labelColor=1f1f1f&color=c8341f)](https://www.npmjs.com/package/@seedcord/core) [![node](https://img.shields.io/node/v/@seedcord/core?style=flat-square&label=node&labelColor=1f1f1f&color=4d7d33)](https://nodejs.org) [![bun](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fseedcord%2Fseedcord%2Fnext%2Fpackages%2Fcore%2Fpackage.json&query=%24.engines.bun&style=flat-square&label=bun&labelColor=1f1f1f&color=fbf0df)](https://bun.com) [![license](https://img.shields.io/npm/l/@seedcord/core?style=flat-square&label=license&labelColor=1f1f1f&color=f8f6e8)](LICENSE)\n\n</div>\n\n## About\n\n`@seedcord/core` is the code both seedcord transports share. It has the route decorators, the handler bases, checks, pagination, the startup and shutdown phases, and the plugin base. [`@seedcord/gateway`](https://www.npmjs.com/package/@seedcord/gateway) and [`@seedcord/http`](https://www.npmjs.com/package/@seedcord/http) connect it to Discord.\n\nBoth transports depend on core and re-export all of it, so a bot imports everything from its transport. Install core directly when you write a plugin that works on both transports.\n\nseedcord is pre-1.0, so a minor version can break your plugin. Each release's changelog says what changed and how to update.\n\n## Installation\n\n```sh\npnpm add -D @seedcord/core @discordjs/builders envapt typescript\n```\n\nAlso list `@seedcord/core` in your plugin's `peerDependencies`, with the versions you tested against. A plugin that extends a different copy of core than the bot's throws `CorePluginFromOtherCore` when the bot attaches it.\n\n## Usage\n\nTo write a plugin, extend `Plugin` from `@seedcord/core/plugin` and implement `init()`:\n\n```ts\nimport { Plugin } from '@seedcord/core/plugin';\n\nexport class Uptime extends Plugin {\n private startedAt = 0;\n\n public async init(): Promise<void> {\n this.startedAt = Date.now();\n }\n\n public elapsed(): number {\n return Date.now() - this.startedAt;\n }\n}\n```\n\nAttach it to a gateway or http bot with `.attach('uptime', Uptime)`, then call `this.core.uptime.elapsed()` from a handler. The [Writing plugins guide](https://seedcord.org/guide/writing-plugins) covers options, lifecycle phases, testing a plugin locally, and publishing.\n"
122
125
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"CoordinatedStartup-CGnJrqcl.mjs","names":[],"sources":["../src/lifecycle/CoordinatedLifecycle.ts","../src/lifecycle/CoordinatedStartup.ts"],"sourcesContent":["/*\n * Inspired by Akka Coordinated Shutdown: https://doc.akka.io/libraries/akka-core/current/coordinated-shutdown.html\n * and Lewis's implementation in a private repo elsewhere (https://github.com/Yomanz)\n */\n\nimport { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordAggregateError, SeedcordTypeError } from '@seedcord/errors/internal';\nimport { Logger } from '@seedcord/logger';\n\nimport { withTimeout } from './withTimeout';\n\nimport type { LifecycleTask } from './LifecycleTypes';\n\n// base for the startup and shutdown coordinators, runs phase-ordered tasks off a per-phase map\nexport abstract class CoordinatedLifecycle<TPhase extends number> {\n protected readonly logger: Logger;\n protected readonly tasksMap = new Map<TPhase, LifecycleTask[]>();\n #run = new AbortController();\n\n protected constructor(\n loggerName: string,\n protected readonly phaseOrder: TPhase[],\n protected readonly phaseEnum: Record<number, string>\n ) {\n this.logger = new Logger(loggerName, { channel: 'lifecycle' });\n this.phaseOrder.forEach((phase) => this.tasksMap.set(phase, []));\n }\n\n public addTask(phase: TPhase, taskName: string, task: LifecycleTask['task'], timeoutMs: number): void {\n if (!this.canAddTask()) return;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) {\n throw new SeedcordTypeError(SeedcordErrorCode.LifecycleUnknownPhase, [phase]);\n }\n\n tasks.push({ name: taskName, task, timeout: timeoutMs });\n }\n\n public removeTask(phase: TPhase, taskName: string): boolean {\n if (!this.canRemoveTask()) return false;\n\n const tasks = this.tasksMap.get(phase);\n if (!tasks) return false;\n\n const initialLength = tasks.length;\n const filteredTasks = tasks.filter((task) => task.name !== taskName);\n this.tasksMap.set(phase, filteredTasks);\n\n return initialLength !== filteredTasks.length;\n }\n\n protected async runPhase(phase: TPhase): Promise<void> {\n const tasks = this.tasksMap.get(phase) ?? [];\n if (tasks.length === 0) {\n this.logger.trace(`No tasks to run in phase ${paint.iris.bold(this.phaseEnum[phase])}`);\n return;\n }\n\n this.logger.debug(\n `${paint.amber.bold('Running')} ${this.getTaskType()} phase ${paint.iris.bold(this.phaseEnum[phase])} with ${paint.sky.bold(String(tasks.length))} tasks`\n );\n\n const results: PromiseSettledResult<void>[] = await this.executeTasksInPhase(phase, tasks);\n\n const reasons = results.reduce<unknown[]>((rejected, result) => {\n if (result.status === 'rejected') rejected.push(result.reason as unknown);\n return rejected;\n }, []);\n if (reasons.length > 0) {\n // paint here would leak ANSI codes into the serialized error message (the unknown-exception webhook)\n throw new SeedcordAggregateError(SeedcordErrorCode.LifecyclePhaseFailures, reasons, [\n this.phaseEnum[phase],\n reasons.length\n ]);\n }\n\n this.logger.debug(\n `Phase ${paint.iris.bold(this.phaseEnum[phase])} ${paint.mint.bold('completed successfully')}`\n );\n }\n\n protected async runTaskWithTimeout(phase: TPhase, task: LifecycleTask): Promise<void> {\n this.logger.trace(\n `${paint.italic('Starting')} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}`\n );\n\n const stopTask = new AbortController();\n const signal = AbortSignal.any([stopTask.signal, this.#run.signal]);\n try {\n await withTimeout(task.name, () => task.task(signal), task.timeout);\n\n this.logger.trace(\n `${paint.italic('Completed')} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}`\n );\n } catch (error) {\n stopTask.abort(error);\n if (this.isAborted()) return;\n this.logger.error(\n `${paint.italic('Failed')} task ${paint.sky.bold(task.name)} in phase ${paint.iris.bold(this.phaseEnum[phase])}:`,\n error\n );\n throw error;\n }\n }\n\n protected isAborted(): boolean {\n return false;\n }\n\n protected beginRun(): void {\n this.#run = new AbortController();\n }\n\n protected abortRun(): void {\n this.#run.abort();\n }\n\n protected abstract canAddTask(): boolean;\n protected abstract canRemoveTask(): boolean;\n protected abstract getTaskType(): string;\n protected abstract executeTasksInPhase(\n phase: TPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]>;\n}\n","import { SeedcordErrorCode, paint } from '@seedcord/errors';\nimport { SeedcordError } from '@seedcord/errors/internal';\n\nimport { StartupPhase } from '#src/lifecycle/phases';\n\nimport { CoordinatedLifecycle } from './CoordinatedLifecycle';\n\nimport type { LifecycleTask } from './LifecycleTypes';\nimport type { Promisable } from 'type-fest';\n\nconst PHASE_ORDER: StartupPhase[] = [StartupPhase.Configuration, StartupPhase.Login, StartupPhase.Ready];\n\nexport class CoordinatedStartup extends CoordinatedLifecycle<StartupPhase> {\n #isStartingUp = false;\n #hasStarted = false;\n\n public constructor() {\n super('Startup', PHASE_ORDER, StartupPhase);\n }\n\n /**\n * Adds a task to a startup phase. The task gets a signal that aborts when it runs past `timeoutMs`\n * or when startup is aborted. Pass it to any request the task makes.\n */\n public override addTask(\n phase: StartupPhase,\n taskName: string,\n task: LifecycleTask['task'],\n timeoutMs = 10_000\n ): void {\n super.addTask(phase, taskName, task, timeoutMs);\n }\n\n protected canAddTask(): boolean {\n if (this.#hasStarted) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleTaskChangeRejected, ['add', 'finished']);\n }\n\n if (this.#isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleTaskChangeRejected, ['add', 'running']);\n }\n\n return true;\n }\n\n protected canRemoveTask(): boolean {\n if (this.#isStartingUp) {\n throw new SeedcordError(SeedcordErrorCode.LifecycleTaskChangeRejected, ['remove', 'running']);\n }\n\n return true;\n }\n\n protected getTaskType(): string {\n return 'startup';\n }\n\n protected async executeTasksInPhase(\n phase: StartupPhase,\n tasks: LifecycleTask[]\n ): Promise<PromiseSettledResult<void>[]> {\n const promises = tasks.map((task) => this.runTaskWithTimeout(phase, task));\n return Promise.allSettled(promises);\n }\n\n // each phase completes fully before the next begins\n /** @internal */\n public async run(afterReady?: () => Promisable<void>): Promise<void> {\n if (this.#hasStarted) {\n this.logger.warn('Startup sequence has already completed');\n return;\n }\n\n if (this.#isStartingUp) {\n this.logger.warn('Startup sequence already in progress');\n return;\n }\n\n this.#isStartingUp = true;\n this.beginRun();\n this.logger.info(`${paint.mint.bold('Starting')} coordinated startup sequence`);\n\n const steps: (() => Promisable<void>)[] = PHASE_ORDER.map((phase) => () => this.runPhase(phase));\n if (afterReady) steps.push(afterReady);\n\n try {\n for (const step of steps) {\n await step();\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false mid-loop from the cli\n if (!this.#isStartingUp) {\n this.logger.warn('Startup sequence aborted');\n return;\n }\n }\n\n this.#hasStarted = true;\n this.logger.info(`${paint.mint.bold('Coordinated startup completed')} successfully`);\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- abort() can flip isStartingUp to false before this catch runs\n if (!this.#isStartingUp) {\n this.logger.warn('Startup sequence aborted during error handling');\n return;\n }\n this.logger.error(`${paint.coral.bold('Coordinated startup failed')}`);\n throw error;\n } finally {\n this.#isStartingUp = false;\n }\n }\n\n protected override isAborted(): boolean {\n return !this.#isStartingUp;\n }\n\n /** @internal */\n public abort(): void {\n if (!this.#isStartingUp) return;\n\n this.#isStartingUp = false;\n this.abortRun();\n this.logger.warn('Aborting coordinated startup sequence');\n }\n\n /** @internal */\n public get isReady(): boolean {\n return this.#hasStarted;\n }\n}\n"],"mappings":"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"}
@@ -1,162 +0,0 @@
1
- import { paint } from "@seedcord/errors";
2
- import { botFolder, vitePluginThrew } from "@seedcord/utils/internal";
3
- import { formatFilePath } from "@seedcord/utils";
4
- import { pathToFileURL } from "node:url";
5
-
6
- //#region src/hmr/HmrModuleHandler.ts
7
- const displayName = (unit, fallback) => unit.name ?? fallback;
8
- /**
9
- * Reloads THandler and optional TMiddleware modules on HMR updates.
10
- *
11
- * Call `handle()` from your `HmrAware.onHmr()` for standard reload behavior, or write a fully custom `onHmr()` and skip this class.
12
- *
13
- * This is only useful during development.
14
- */
15
- var HmrModuleHandler = class {
16
- store;
17
- options;
18
- constructor(options) {
19
- this.options = {
20
- ...options,
21
- logger: options.logger.inChannel("hmr")
22
- };
23
- this.store = {
24
- fileToHandlers: /* @__PURE__ */ new Map(),
25
- fileToMiddlewares: /* @__PURE__ */ new Map(),
26
- handlerArtifacts: /* @__PURE__ */ new Map()
27
- };
28
- }
29
- /** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */
30
- async handle(event) {
31
- const { file, affectedModules, type } = event;
32
- if (type === "delete" || type === "deleteDir") {
33
- this.report("Unloaded", this.unload(file), [file]);
34
- return;
35
- }
36
- if (type === "createDir") return;
37
- const rollbackEnabled = event.rollback ?? true;
38
- const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];
39
- const startedAt = Date.now();
40
- const names = [];
41
- const touched = [];
42
- for (const fileToReload of filesToReload) {
43
- if (!this.inScope(fileToReload)) continue;
44
- const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);
45
- if (reloaded.length === 0) continue;
46
- names.push(...reloaded);
47
- touched.push(fileToReload);
48
- }
49
- this.report(type === "create" ? "Registered" : "Reloaded", names, touched, Date.now() - startedAt);
50
- }
51
- inScope(file) {
52
- const { handlersDir, middlewaresDir } = this.options;
53
- if (botFolder(handlersDir).holdsModule(file)) return true;
54
- return middlewaresDir ? botFolder(middlewaresDir).holdsModule(file) : false;
55
- }
56
- report(verb, names, files, elapsedMs) {
57
- if (names.length === 0) return;
58
- const from = files.length === 1 ? formatFilePath(files[0] ?? "") : `${String(files.length)} files`;
59
- const timing = elapsedMs === void 0 ? "" : ` ${paint.mute("in")} ${String(elapsedMs)}ms`;
60
- this.options.logger.debug(`${verb} ${paint.sky.bold(names.join(", "))} from ${paint.mute(from)}${timing}`);
61
- }
62
- async reloadWithRollback(file, rollbackEnabled) {
63
- const { logger } = this.options;
64
- const snapshot = this.snapshotUnits(file);
65
- this.unload(file);
66
- const reloaded = await this.reloadFile(file);
67
- if (reloaded !== null) return reloaded;
68
- this.unload(file);
69
- if (!rollbackEnabled) return [];
70
- if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return [];
71
- this.restoreUnits(file, snapshot);
72
- logger.warn(`${paint.amber.bold("Rolled back")} ${paint.mute(formatFilePath(file))} to the last-good version`);
73
- return [];
74
- }
75
- /** Links the handler to its source file so a later update can unload it. */
76
- trackHandler(file, handler) {
77
- let set = this.store.fileToHandlers.get(file);
78
- if (!set) {
79
- set = /* @__PURE__ */ new Set();
80
- this.store.fileToHandlers.set(file, set);
81
- }
82
- set.add(handler);
83
- if (!this.options.getArtifacts) return;
84
- this.store.handlerArtifacts.set(handler, this.options.getArtifacts(handler));
85
- }
86
- /** Links the middleware to its source file so a later update can unload it. */
87
- trackMiddleware(file, middleware) {
88
- let set = this.store.fileToMiddlewares.get(file);
89
- if (!set) {
90
- set = /* @__PURE__ */ new Set();
91
- this.store.fileToMiddlewares.set(file, set);
92
- }
93
- set.add(middleware);
94
- }
95
- unload(file) {
96
- const names = [];
97
- const handlers = this.store.fileToHandlers.get(file);
98
- if (handlers) {
99
- for (const handler of handlers) {
100
- const artifacts = this.store.handlerArtifacts.get(handler);
101
- this.options.unregisterHandler(handler, artifacts);
102
- this.store.handlerArtifacts.delete(handler);
103
- names.push(displayName(handler, "Handler"));
104
- }
105
- this.store.fileToHandlers.delete(file);
106
- }
107
- const middlewares = this.store.fileToMiddlewares.get(file);
108
- if (middlewares) {
109
- for (const middleware of middlewares) {
110
- this.options.unregisterMiddleware?.(middleware);
111
- names.push(displayName(middleware, "Middleware"));
112
- }
113
- this.store.fileToMiddlewares.delete(file);
114
- }
115
- return names;
116
- }
117
- async reloadFile(file) {
118
- const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;
119
- try {
120
- let fileUrl = pathToFileURL(file).href;
121
- if (process.env.VITEST === "true") fileUrl += `?update=${Date.now()}`;
122
- const imported = await import(
123
- /* @vite-ignore */
124
- fileUrl
125
- );
126
- const names = [];
127
- for (const val of Object.values(imported)) if (isHandler(val)) {
128
- registerHandler(val, file);
129
- this.trackHandler(file, val);
130
- names.push(displayName(val, "Handler"));
131
- } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {
132
- registerMiddleware(val, file);
133
- this.trackMiddleware(file, val);
134
- names.push(displayName(val, "Middleware"));
135
- }
136
- return names;
137
- } catch (error) {
138
- logger.error(`Failed to reload file: ${formatFilePath(file)}`, vitePluginThrew(error) ?? error);
139
- return null;
140
- }
141
- }
142
- snapshotUnits(file) {
143
- return {
144
- handlers: [...this.store.fileToHandlers.get(file) ?? []],
145
- middlewares: [...this.store.fileToMiddlewares.get(file) ?? []]
146
- };
147
- }
148
- restoreUnits(file, snapshot) {
149
- for (const handler of snapshot.handlers) {
150
- this.options.registerHandler(handler, file);
151
- this.trackHandler(file, handler);
152
- }
153
- if (this.options.registerMiddleware) for (const middleware of snapshot.middlewares) {
154
- this.options.registerMiddleware(middleware, file);
155
- this.trackMiddleware(file, middleware);
156
- }
157
- }
158
- };
159
-
160
- //#endregion
161
- export { HmrModuleHandler as t };
162
- //# sourceMappingURL=HmrModuleHandler-8ZC-UuZh.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HmrModuleHandler-8ZC-UuZh.mjs","names":[],"sources":["../src/hmr/HmrModuleHandler.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url';\n\nimport { paint } from '@seedcord/errors';\nimport { formatFilePath } from '@seedcord/utils';\nimport { botFolder, vitePluginThrew } from '@seedcord/utils/internal';\n\nimport type { Logger } from '@seedcord/logger';\nimport type { HmrUpdateEvent } from '@seedcord/types';\n\ninterface HmrStore<THandler, TMiddleware, TArtifacts> {\n fileToHandlers: Map<string, Set<THandler>>;\n fileToMiddlewares: Map<string, Set<TMiddleware>>;\n handlerArtifacts: Map<THandler, TArtifacts>;\n}\n\n// justified: units are caller-supplied classes, name is read for log labels only\nconst displayName = (unit: unknown, fallback: string): string => (unit as { name?: string }).name ?? fallback;\n\ninterface HmrModuleHandlerOptions<THandler, TMiddleware = void, TArtifacts = unknown> {\n handlersDir: string;\n middlewaresDir?: string;\n isHandler: (val: unknown) => val is THandler;\n isMiddleware?: (val: unknown) => val is TMiddleware;\n registerHandler: (handler: THandler, file: string) => void;\n registerMiddleware?: (middleware: TMiddleware, file: string) => void;\n unregisterHandler: (handler: THandler, artifacts?: TArtifacts) => void;\n unregisterMiddleware?: (middleware: TMiddleware) => void;\n /** Snapshotted per handler so a rollback re-registers with the same artifacts after the module re-imports. */\n getArtifacts?: (handler: THandler) => TArtifacts;\n logger: Logger;\n}\n\n/**\n * Reloads THandler and optional TMiddleware modules on HMR updates.\n *\n * Call `handle()` from your `HmrAware.onHmr()` for standard reload behavior, or write a fully custom `onHmr()` and skip this class.\n *\n * This is only useful during development.\n */\nexport class HmrModuleHandler<THandler, TMiddleware = void, TArtifacts = unknown> {\n private readonly store: HmrStore<THandler, TMiddleware, TArtifacts>;\n private readonly options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>;\n\n constructor(options: HmrModuleHandlerOptions<THandler, TMiddleware, TArtifacts>) {\n this.options = { ...options, logger: options.logger.inChannel('hmr') };\n\n // no import.meta.hot.data stash needed because a leaf reload never reconstructs the singleton\n // holding these Maps\n this.store = {\n fileToHandlers: new Map(),\n fileToMiddlewares: new Map(),\n handlerArtifacts: new Map()\n };\n }\n\n /** Unloads or reloads the modules affected by an {@link HmrUpdateEvent}, scoped to the handler and middleware directories. */\n public async handle(event: HmrUpdateEvent): Promise<void> {\n const { file, affectedModules, type } = event;\n\n if (type === 'delete' || type === 'deleteDir') {\n this.report('Unloaded', this.unload(file), [file]);\n return;\n }\n\n // a directory is never importable. its files arrive as their own create events\n if (type === 'createDir') return;\n\n const rollbackEnabled = event.rollback ?? true;\n const filesToReload = affectedModules && affectedModules.length > 0 ? affectedModules : [file];\n\n const startedAt = Date.now();\n const names: string[] = [];\n const touched: string[] = [];\n\n for (const fileToReload of filesToReload) {\n if (!this.inScope(fileToReload)) continue;\n\n const reloaded = await this.reloadWithRollback(fileToReload, rollbackEnabled);\n if (reloaded.length === 0) continue;\n\n names.push(...reloaded);\n touched.push(fileToReload);\n }\n\n this.report(type === 'create' ? 'Registered' : 'Reloaded', names, touched, Date.now() - startedAt);\n }\n\n private inScope(file: string): boolean {\n const { handlersDir, middlewaresDir } = this.options;\n\n if (botFolder(handlersDir).holdsModule(file)) return true;\n return middlewaresDir ? botFolder(middlewaresDir).holdsModule(file) : false;\n }\n\n // one line per save, since one edit can reload every module that imports the file\n private report(verb: string, names: string[], files: string[], elapsedMs?: number): void {\n if (names.length === 0) return;\n\n const from = files.length === 1 ? formatFilePath(files[0] ?? '') : `${String(files.length)} files`;\n const timing = elapsedMs === undefined ? '' : ` ${paint.mute('in')} ${String(elapsedMs)}ms`;\n this.options.logger.debug(`${verb} ${paint.sky.bold(names.join(', '))} from ${paint.mute(from)}${timing}`);\n }\n\n private async reloadWithRollback(file: string, rollbackEnabled: boolean): Promise<string[]> {\n const { logger } = this.options;\n const snapshot = this.snapshotUnits(file);\n this.unload(file);\n\n const reloaded = await this.reloadFile(file);\n if (reloaded !== null) return reloaded;\n\n // a failed reload can leave a partial registration\n this.unload(file);\n if (!rollbackEnabled) return [];\n if (snapshot.handlers.length === 0 && snapshot.middlewares.length === 0) return [];\n\n // a typo keeps the last-good units live until the next good save\n this.restoreUnits(file, snapshot);\n logger.warn(`${paint.amber.bold('Rolled back')} ${paint.mute(formatFilePath(file))} to the last-good version`);\n return [];\n }\n\n /** Links the handler to its source file so a later update can unload it. */\n public trackHandler(file: string, handler: THandler): void {\n let set = this.store.fileToHandlers.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToHandlers.set(file, set);\n }\n set.add(handler);\n\n if (!this.options.getArtifacts) return;\n this.store.handlerArtifacts.set(handler, this.options.getArtifacts(handler));\n }\n\n /** Links the middleware to its source file so a later update can unload it. */\n public trackMiddleware(file: string, middleware: TMiddleware): void {\n let set = this.store.fileToMiddlewares.get(file);\n if (!set) {\n set = new Set();\n this.store.fileToMiddlewares.set(file, set);\n }\n set.add(middleware);\n }\n\n private unload(file: string): string[] {\n const names: string[] = [];\n const handlers = this.store.fileToHandlers.get(file);\n if (handlers) {\n for (const handler of handlers) {\n const artifacts = this.store.handlerArtifacts.get(handler);\n this.options.unregisterHandler(handler, artifacts);\n this.store.handlerArtifacts.delete(handler);\n names.push(displayName(handler, 'Handler'));\n }\n this.store.fileToHandlers.delete(file);\n }\n\n const middlewares = this.store.fileToMiddlewares.get(file);\n if (middlewares) {\n for (const middleware of middlewares) {\n this.options.unregisterMiddleware?.(middleware);\n names.push(displayName(middleware, 'Middleware'));\n }\n this.store.fileToMiddlewares.delete(file);\n }\n\n return names;\n }\n\n private async reloadFile(file: string): Promise<string[] | null> {\n const { logger, isHandler, isMiddleware, registerHandler, registerMiddleware } = this.options;\n\n try {\n let fileUrl = pathToFileURL(file).href;\n // vitest has no real vite server managing the module graph\n if (process.env.VITEST === 'true') fileUrl += `?update=${Date.now()}`;\n\n // justified: a dynamic import resolves to an untyped export map\n const imported = (await import(/* @vite-ignore */ fileUrl)) as Record<string, unknown>;\n\n const names: string[] = [];\n for (const val of Object.values(imported)) {\n if (isHandler(val)) {\n registerHandler(val, file);\n this.trackHandler(file, val);\n names.push(displayName(val, 'Handler'));\n } else if (isMiddleware && registerMiddleware && isMiddleware(val)) {\n registerMiddleware(val, file);\n this.trackMiddleware(file, val);\n names.push(displayName(val, 'Middleware'));\n }\n }\n\n return names;\n } catch (error) {\n logger.error(`Failed to reload file: ${formatFilePath(file)}`, vitePluginThrew(error) ?? error);\n return null;\n }\n }\n\n // these are the same class objects, which is how a live db connection survives a rollback\n private snapshotUnits(file: string): { handlers: THandler[]; middlewares: TMiddleware[] } {\n return {\n handlers: [...(this.store.fileToHandlers.get(file) ?? [])],\n middlewares: [...(this.store.fileToMiddlewares.get(file) ?? [])]\n };\n }\n\n private restoreUnits(file: string, snapshot: { handlers: THandler[]; middlewares: TMiddleware[] }): void {\n for (const handler of snapshot.handlers) {\n this.options.registerHandler(handler, file);\n this.trackHandler(file, handler);\n }\n\n if (this.options.registerMiddleware) {\n for (const middleware of snapshot.middlewares) {\n this.options.registerMiddleware(middleware, file);\n this.trackMiddleware(file, middleware);\n }\n }\n }\n}\n"],"mappings":"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"}