@nectar-js/nectar 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{cli-Ce-ZUj6M.js → cli-Cv0mpiRy.js} +191 -131
- package/dist/cli-Cv0mpiRy.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{index-DGMxBsub.d.ts → index-IqoTGJFM.d.ts} +54 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +37 -3
- package/dist/index.js.map +1 -1
- package/dist/{plugins-CGvM19v9.js → plugins-C2uwN3-D.js} +106 -30
- package/dist/plugins-C2uwN3-D.js.map +1 -0
- package/dist/{registration-CaE0QBT6.js → registration-mUZJidnF.js} +66 -7
- package/dist/registration-mUZJidnF.js.map +1 -0
- package/dist/{runtime-CZJeZvSL.js → runtime-B7mCDO5Q.js} +97 -16
- package/dist/runtime-B7mCDO5Q.js.map +1 -0
- package/dist/start.js +1 -1
- package/dist/testing.d.ts +14 -6
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli-Ce-ZUj6M.js.map +0 -1
- package/dist/plugins-CGvM19v9.js.map +0 -1
- package/dist/registration-CaE0QBT6.js.map +0 -1
- package/dist/runtime-CZJeZvSL.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins-C2uwN3-D.js","names":["describe"],"sources":["../src/version.ts","../src/components/customId.ts","../src/compiler/diagnostics.ts","../src/compiler/load.ts","../src/compiler/segments.ts","../src/components/params.ts","../src/components/compile.ts","../src/components/registry.ts","../src/manifest/emit.ts","../src/plugins/transform.ts","../src/plugins/index.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\n/** From package.json, which sits one level up from both `src/` and `dist/`. */\nexport const { version } = createRequire(import.meta.url)(\"../package.json\") as {\n version: string;\n};\n","/** Discord rejects custom IDs longer than this. */\nexport const MAX_CUSTOM_ID_LENGTH = 100;\n\nconst PREFIX = \"n:\";\nconst SHORT_ID_LENGTH = 6;\n\n/** Characters a route with no parameters uses: the prefix and the short ID. */\nexport const BASE_OVERHEAD = PREFIX.length + SHORT_ID_LENGTH;\n\nexport class CustomIdTooLongError extends Error {\n constructor(\n readonly customId: string,\n readonly routeId: string,\n ) {\n super(\n `Custom ID for ${routeId} is ${customId.length} characters, Discord allows ${MAX_CUSTOM_ID_LENGTH}. Encode a shorter identifier instead of the full value.`,\n );\n this.name = \"CustomIdTooLongError\";\n }\n}\n\n/**\n * Builds `n:<shortId>:<v1>:<v2>...`. Values are escaped so they may contain `:` and `\\`.\n * Throws when the result is longer than Discord allows; it never truncates.\n */\nexport function encodeCustomId(shortId: string, values: readonly string[], routeId = shortId) {\n let out = PREFIX + shortId;\n for (const value of values) out += `:${escapeValue(value)}`;\n if (out.length > MAX_CUSTOM_ID_LENGTH) throw new CustomIdTooLongError(out, routeId);\n return out;\n}\n\nexport type DecodedCustomId =\n | { ok: true; shortId: string; values: string[] }\n | { ok: false; reason: \"not-nectar\" | \"malformed\" };\n\n/**\n * Splits a raw custom ID back into its short ID and positional values.\n * IDs without the Nectar prefix are reported as `not-nectar` so hand-built components pass through.\n */\nexport function decodeCustomId(raw: string): DecodedCustomId {\n if (!raw.startsWith(PREFIX)) return { ok: false, reason: \"not-nectar\" };\n\n const shortId = raw.slice(PREFIX.length, PREFIX.length + SHORT_ID_LENGTH);\n if (!/^[0-9a-z]{6}$/.test(shortId)) return { ok: false, reason: \"malformed\" };\n\n const values: string[] = [];\n let index = PREFIX.length + SHORT_ID_LENGTH;\n if (index === raw.length) return { ok: true, shortId, values };\n if (raw[index] !== \":\") return { ok: false, reason: \"malformed\" };\n index++;\n\n let current = \"\";\n while (index < raw.length) {\n const char = raw[index] as string;\n if (char === \"\\\\\") {\n const next = raw[index + 1];\n if (next !== \"\\\\\" && next !== \":\") return { ok: false, reason: \"malformed\" };\n current += next;\n index += 2;\n continue;\n }\n if (char === \":\") {\n values.push(current);\n current = \"\";\n index++;\n continue;\n }\n current += char;\n index++;\n }\n values.push(current);\n return { ok: true, shortId, values };\n}\n\nfunction escapeValue(value: string): string {\n return value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\":\", \"\\\\:\");\n}\n","export type Severity = \"error\" | \"warning\";\n\nexport interface Diagnostic {\n code: string;\n severity: Severity;\n message: string;\n /** Absolute path of the file or directory that caused the diagnostic. */\n file?: string;\n /** Canonical route identity, when the diagnostic is about a specific route. */\n route?: string;\n}\n\n/** Every code the compiler reports. Each has an entry in the diagnostics reference. */\nexport const DIAGNOSTIC_CODES = [\n \"file-outside-category\",\n \"unknown-category\",\n \"file-in-wrong-category\",\n \"invalid-segment\",\n \"route-without-path\",\n \"dynamic-segment-not-allowed\",\n \"duplicate-param\",\n \"catch-all-not-last\",\n \"duplicate-route\",\n \"module-load-failed\",\n \"missing-handler\",\n \"route-mismatch\",\n \"missing-meta\",\n \"invalid-meta\",\n \"invalid-name\",\n \"invalid-description\",\n \"invalid-option\",\n \"missing-route-meta\",\n \"route-meta-without-path\",\n \"unused-route-meta\",\n \"mixed-command-and-subcommands\",\n \"mixed-subcommand-and-group\",\n \"command-too-deep\",\n \"too-many-subcommands\",\n \"context-menu-nested\",\n \"top-level-field-on-group\",\n \"top-level-field-on-subcommand\",\n \"duplicate-command-name\",\n \"too-many-commands\",\n \"autocomplete-without-command\",\n \"autocomplete-export-not-function\",\n \"autocomplete-unknown-option\",\n \"autocomplete-missing-handler\",\n \"autocomplete-missing-file\",\n \"missing-select-kind\",\n \"invalid-select-kind\",\n \"invalid-param-validator\",\n \"catch-all-route\",\n \"short-id-collision\",\n \"duplicate-component-pattern\",\n \"unknown-event\",\n \"event-nested-path\",\n \"event-mode-conflict\",\n \"missing-intent\",\n \"plugin-failed\",\n \"plugin-invalid-change\",\n \"plugin-unknown-route\",\n \"plugin-missing-file\",\n] as const;\n\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[number];\n\nconst REFERENCE = \"https://nectar-js.github.io/nectar/reference/diagnostics\";\n\n/** The reference entry for a compiler code. Codes from plugins have none. */\nexport function docsUrl(code: string): string | undefined {\n return (DIAGNOSTIC_CODES as readonly string[]).includes(code)\n ? `${REFERENCE}#${code}`\n : undefined;\n}\n\n/** A value's type as a message puts it: `missing`, `a number`, `an array`. */\nexport function typeOf(value: unknown): string {\n if (value === undefined) return \"missing\";\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n return typeof value === \"object\" ? \"an object\" : `a ${typeof value}`;\n}\n\ninterface DiagnosticLocation {\n file?: string;\n route?: string;\n}\n\nexport class Diagnostics {\n readonly items: Diagnostic[] = [];\n\n error(code: DiagnosticCode, message: string, location: DiagnosticLocation = {}): void {\n this.push(\"error\", code, message, location);\n }\n\n warn(code: DiagnosticCode, message: string, location: DiagnosticLocation = {}): void {\n this.push(\"warning\", code, message, location);\n }\n\n get hasErrors(): boolean {\n return this.items.some((d) => d.severity === \"error\");\n }\n\n private push(\n severity: Severity,\n code: string,\n message: string,\n location: DiagnosticLocation,\n ): void {\n const item: Diagnostic = { code, severity, message };\n if (location.file !== undefined) item.file = location.file;\n if (location.route !== undefined) item.route = location.route;\n this.items.push(item);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport { registerHooks } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\n/**\n * Imports an application module by absolute path.\n *\n * Relies on Node's native TypeScript type stripping (unflagged since 22.18), so handler\n * files must use erasable syntax only: no enums, namespaces, or parameter properties.\n *\n * With reloading enabled (see `enableModuleReloading`) the URL carries a version query, so a\n * changed file evaluates again on the next import instead of coming back from the ESM cache.\n */\nexport async function loadModule(file: string): Promise<Record<string, unknown>> {\n const url = pathToFileURL(file).href;\n return (await import(reloading === null ? url : versioned(url))) as Record<string, unknown>;\n}\n\ninterface Reloading {\n /** Project root; only files under it (outside `node_modules`) are versioned. */\n root: string;\n /** Bumped by `invalidateModuleGraph` so every project module evaluates again. */\n generation: number;\n}\n\nlet reloading: Reloading | null = null;\n\n/**\n * Turns on cache busting for project files. Used by `nectar dev` only.\n *\n * Every import of a file under `root` gets `?nectar=<content hash>-<generation>` appended, the\n * direct ones here and the transitive ones through a resolve hook. A handler whose content\n * changed therefore gets a new URL and a fresh evaluation; its unchanged imports keep their\n * URL and are shared. Old instances stay in the ESM cache until the process exits.\n */\nexport function enableModuleReloading(root: string): void {\n if (reloading !== null) return;\n reloading = { root: path.resolve(root), generation: 0 };\n registerHooks({\n resolve(specifier, context, next) {\n const result = next(specifier, context);\n return { ...result, url: versioned(result.url) };\n },\n });\n}\n\n/**\n * Makes every project module evaluate again on its next import. For changes to files the\n * compiler does not track (helpers a handler imports), since nothing knows who imports them.\n */\nexport function invalidateModuleGraph(): void {\n if (reloading !== null) reloading.generation += 1;\n}\n\nfunction versioned(url: string): string {\n if (reloading === null || !url.startsWith(\"file:\") || url.includes(\"?\") || url.includes(\"#\")) {\n return url;\n }\n const file = fileURLToPath(url);\n const inside = !path.relative(reloading.root, file).startsWith(\"..\");\n if (!inside || file.split(path.sep).includes(\"node_modules\")) return url;\n let hash: string;\n try {\n hash = createHash(\"sha1\").update(readFileSync(file)).digest(\"base64url\").slice(0, 10);\n } catch {\n return url;\n }\n return `${url}?nectar=${hash}-${reloading.generation}`;\n}\n","export type Segment =\n | { type: \"static\"; name: string }\n | { type: \"dynamic\"; name: string }\n | { type: \"catchAll\"; name: string }\n | { type: \"group\"; name: string };\n\nexport type SegmentParseResult = { ok: true; segment: Segment } | { ok: false; reason: string };\n\nconst STATIC_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;\nconst PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Parses one directory name into a route segment.\n *\n * `name` static\n * `[name]` dynamic\n * `[...name]` catch-all\n * `(name)` group, organizational only\n */\nexport function parseSegment(dirName: string): SegmentParseResult {\n if (dirName.startsWith(\"[\") || dirName.endsWith(\"]\")) {\n if (!dirName.startsWith(\"[\") || !dirName.endsWith(\"]\")) {\n return fail(`\"${dirName}\" has an unmatched bracket. Parameters look like [name].`);\n }\n const inner = dirName.slice(1, -1);\n const isCatchAll = inner.startsWith(\"...\");\n const name = isCatchAll ? inner.slice(3) : inner;\n if (!PARAM_NAME.test(name)) {\n return fail(\n `\"${dirName}\" has an invalid parameter name. Parameters become keys of ctx.params, so use letters, digits, and underscores, and don't start with a digit.`,\n );\n }\n return ok({ type: isCatchAll ? \"catchAll\" : \"dynamic\", name });\n }\n\n if (dirName.startsWith(\"(\") || dirName.endsWith(\")\")) {\n if (!dirName.startsWith(\"(\") || !dirName.endsWith(\")\")) {\n return fail(`\"${dirName}\" has an unmatched parenthesis. Route groups look like (name).`);\n }\n const name = dirName.slice(1, -1);\n if (!STATIC_NAME.test(name)) {\n return fail(\n `\"${dirName}\" isn't a valid group name. Use letters, digits, hyphens, and underscores.`,\n );\n }\n return ok({ type: \"group\", name });\n }\n\n if (!STATIC_NAME.test(dirName)) {\n return fail(\n `\"${dirName}\" can't be part of a route path. Use letters, digits, hyphens, and underscores, and start with a letter or digit.`,\n );\n }\n return ok({ type: \"static\", name: dirName });\n}\n\n/** Renders a segment back into its directory form. Groups render as their directory name. */\nexport function formatSegment(segment: Segment): string {\n switch (segment.type) {\n case \"static\":\n return segment.name;\n case \"dynamic\":\n return `[${segment.name}]`;\n case \"catchAll\":\n return `[...${segment.name}]`;\n case \"group\":\n return `(${segment.name})`;\n }\n}\n\nfunction ok(segment: Segment): SegmentParseResult {\n return { ok: true, segment };\n}\n\nfunction fail(reason: string): SegmentParseResult {\n return { ok: false, reason };\n}\n","import { typeOf } from \"../compiler/diagnostics.js\";\n\n/**\n * A Standard Schema (https://standardschema.dev) validator, which zod, valibot, and arktype\n * all produce. Only the result's `issues` are looked at: a schema that transforms the value\n * does not change what the handler receives.\n */\nexport interface StandardSchemaLike<V = unknown> {\n \"~standard\": {\n validate(\n value: V,\n ):\n | { issues?: ReadonlyArray<unknown> | undefined }\n | Promise<{ issues?: ReadonlyArray<unknown> | undefined }>;\n };\n}\n\n/**\n * Checks one custom ID parameter. A function passes by returning anything but `false` and\n * fails by returning `false` or throwing. A schema fails by reporting issues.\n */\nexport type ParamValidator<V = string | string[]> = ((value: V) => unknown) | StandardSchemaLike<V>;\n\nexport type ParamValidators = Record<string, ParamValidator>;\n\n/**\n * Reads the validators `defineComponent` attached to a handler and checks them against the\n * route's parameters. Throws with a developer-facing message when the shape is wrong; the\n * compiler reports it as a diagnostic and the runtime as a load error.\n */\nexport function paramValidatorsOf(\n handler: unknown,\n route: { params: readonly string[] },\n): ParamValidators {\n const declared = (handler as { params?: unknown }).params;\n if (declared === undefined) return {};\n if (typeof declared !== \"object\" || declared === null || Array.isArray(declared)) {\n throw new Error(`params is ${typeOf(declared)}, not an object of validators.`);\n }\n const validators: ParamValidators = {};\n for (const [name, validator] of Object.entries(declared)) {\n if (!route.params.includes(name)) {\n throw new Error(\n `params validates \"${name}\", which is not a parameter of this route. ${\n route.params.length === 0 ? \"It has none.\" : `It has: ${route.params.join(\", \")}.`\n }`,\n );\n }\n if (!isValidator(validator)) {\n throw new Error(\n `params.${name} is ${typeOf(validator)}. A validator is a function or a Standard Schema.`,\n );\n }\n validators[name] = validator;\n }\n return validators;\n}\n\nfunction isValidator(value: unknown): value is ParamValidator {\n if (typeof value === \"function\") return true;\n if (typeof value !== \"object\" || value === null) return false;\n const standard = (value as Record<string, unknown>)[\"~standard\"];\n return (\n typeof standard === \"object\" &&\n standard !== null &&\n typeof (standard as Record<string, unknown>).validate === \"function\"\n );\n}\n\n/**\n * Runs every validator against the decoded parameters. Resolves to the first parameter that\n * failed, or `null` when all passed. A validator that throws counts as a failure; the\n * caller decides what to log, so the value never leaves this function.\n */\nexport async function findInvalidParam(\n validators: ParamValidators,\n params: Record<string, string | string[]>,\n): Promise<string | null> {\n for (const [name, validator] of Object.entries(validators)) {\n const value = params[name];\n if (value === undefined) return name;\n try {\n if (typeof validator === \"function\") {\n if ((await validator(value)) === false) return name;\n continue;\n }\n const result = await validator[\"~standard\"].validate(value);\n if (result.issues !== undefined && result.issues.length > 0) return name;\n } catch {\n return name;\n }\n }\n return null;\n}\n","import path from \"node:path\";\nimport { Diagnostics, typeOf } from \"../compiler/diagnostics.js\";\nimport { loadModule } from \"../compiler/load.js\";\nimport type { Route, RouteTable } from \"../compiler/routes.js\";\nimport { formatSegment } from \"../compiler/segments.js\";\nimport { BASE_OVERHEAD, encodeCustomId, MAX_CUSTOM_ID_LENGTH } from \"./customId.js\";\nimport { paramValidatorsOf } from \"./params.js\";\n\nexport type ComponentKind = \"button\" | \"select\" | \"modal\";\n\nexport type SelectKind = \"string\" | \"user\" | \"role\" | \"channel\" | \"mentionable\";\n\nconst SELECT_KINDS: ReadonlySet<string> = new Set([\n \"string\",\n \"user\",\n \"role\",\n \"channel\",\n \"mentionable\",\n]);\n\nexport interface ComponentRoute extends Route {\n category: \"component\";\n kind: ComponentKind;\n /** The `kind` export of a `select.ts`. `null` for buttons and modals. */\n selectKind: SelectKind | null;\n /** Name of the trailing catch-all parameter, if the route has one. */\n catchAll: string | null;\n /** Characters of the encoded custom ID taken by the prefix, short ID, and separators. */\n overhead: number;\n}\n\nexport interface CompiledComponents {\n routes: ComponentRoute[];\n diagnostics: Diagnostics;\n}\n\n/** Values for one route's parameters. A catch-all parameter takes an array. */\nexport type ComponentParams = Record<string, string | readonly string[]>;\n\n/** Validates the component routes of a route table and resolves their select kinds. */\nexport async function compileComponents(table: RouteTable): Promise<CompiledComponents> {\n const diagnostics = new Diagnostics();\n const candidates = table.routes.filter((r) => r.category === \"component\");\n\n // Each route reports into its own list, so diagnostics come out in route order rather than\n // in whatever order the imports finish.\n const results = await Promise.all(\n candidates.map(async (route) => {\n const own = new Diagnostics();\n return { route: await compileRoute(route, own), diagnostics: own };\n }),\n );\n const routes: ComponentRoute[] = [];\n for (const result of results) {\n diagnostics.items.push(...result.diagnostics.items);\n if (result.route !== null) routes.push(result.route);\n }\n\n detectShortIdCollisions(routes, diagnostics);\n detectDuplicatePatterns(routes, diagnostics);\n\n return { routes, diagnostics };\n}\n\n/** What the encoder needs from a route. Compiled, manifest, and registered routes all satisfy it. */\nexport interface EncodableRoute {\n id: string;\n shortId: string;\n params: string[];\n catchAll: string | null;\n}\n\n/**\n * Encodes a custom ID for a compiled route. Throws when a parameter is missing, a value is not\n * a string, or the result exceeds Discord's limit.\n */\nexport function customIdFor(route: EncodableRoute, params: ComponentParams = {}): string {\n const values: string[] = [];\n for (const name of route.params) {\n const value = params[name];\n if (name === route.catchAll) {\n if (value === undefined) continue;\n if (typeof value === \"string\") {\n values.push(value);\n continue;\n }\n values.push(...value);\n continue;\n }\n if (typeof value !== \"string\") {\n throw new TypeError(\n `Route ${route.id} needs a string for parameter \"${name}\", got ${describe(value)}.`,\n );\n }\n values.push(value);\n }\n for (const name of Object.keys(params)) {\n if (!route.params.includes(name)) {\n throw new TypeError(\n `Route ${route.id} has no parameter \"${name}\". ${route.params.length === 0 ? \"It takes none.\" : `It takes: ${route.params.join(\", \")}.`}`,\n );\n }\n }\n return encodeCustomId(route.shortId, values, route.id);\n}\n\nasync function compileRoute(\n route: Route,\n diagnostics: Diagnostics,\n): Promise<ComponentRoute | null> {\n const kind = route.kind as ComponentKind;\n const last = route.segments.at(-1);\n const catchAll = last?.type === \"catchAll\" ? last.name : null;\n const overhead = BASE_OVERHEAD + route.params.length;\n\n if (catchAll !== null) {\n diagnostics.warn(\n \"catch-all-route\",\n `${formatSegment(last as NonNullable<typeof last>)} takes any number of values. They all count toward Discord's ${MAX_CUSTOM_ID_LENGTH} character limit on custom IDs, and customId() throws when an ID goes over.`,\n { file: route.file, route: route.id },\n );\n }\n\n let module: Record<string, unknown>;\n try {\n module = await loadModule(route.file);\n } catch (error) {\n diagnostics.error(\n \"module-load-failed\",\n `The compiler imports every route file to read its exports, and this one threw: ${error instanceof Error ? error.message : String(error)}`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n\n if (!checkHandler(module, route, diagnostics)) return null;\n try {\n paramValidatorsOf(module.default, route);\n } catch (error) {\n diagnostics.error(\n \"invalid-param-validator\",\n `${error instanceof Error ? error.message : String(error)} Validators go in defineComponent's third argument, like { params: { id: (value) => ... } }.`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n\n let selectKind: SelectKind | null = null;\n if (kind === \"select\") {\n selectKind = validateSelectKind(module, route, diagnostics);\n if (selectKind === null) return null;\n }\n\n return { ...route, category: \"component\", kind, selectKind, catchAll, overhead };\n}\n\n/**\n * The default export is the handler Nectar calls, and one made with `defineComponent(path, ...)`\n * or the like must name the route its file sits in.\n */\nexport function checkHandler(\n module: Record<string, unknown>,\n route: Route,\n diagnostics: Diagnostics,\n expected = route.path,\n): boolean {\n const handler = module.default;\n if (typeof handler !== \"function\") {\n const define =\n route.kind === \"command\"\n ? \"defineCommand\"\n : route.kind === \"event\"\n ? \"defineEvent\"\n : \"defineComponent\";\n diagnostics.error(\n \"missing-handler\",\n `This ${path.basename(route.file)} ${handler === undefined ? \"has no default export\" : `exports ${typeOf(handler)} as its default`}. Nectar calls the default export when the route runs, so export the handler, like export default ${define}(\"${expected}\", handler).`,\n { file: route.file, route: route.id },\n );\n return false;\n }\n const declared = (handler as { route?: unknown }).route;\n if (declared === undefined || declared === expected) return true;\n diagnostics.error(\n \"route-mismatch\",\n `This file's route is \"${expected}\", but its handler says \"${String(declared)}\". The string types the handler, so it has to match where the file is. Change it to \"${expected}\", or move the file.`,\n { file: route.file, route: route.id },\n );\n return false;\n}\n\nfunction validateSelectKind(\n module: Record<string, unknown>,\n route: Route,\n diagnostics: Diagnostics,\n): SelectKind | null {\n const kind = module.kind;\n if (kind === undefined) {\n diagnostics.error(\n \"missing-select-kind\",\n 'This select.ts doesn\\'t export kind, which says what the select menu picks from: \"string\", \"user\", \"role\", \"channel\", or \"mentionable\". Add one, like export const kind = \"string\".',\n { file: route.file, route: route.id },\n );\n return null;\n }\n if (typeof kind !== \"string\" || !SELECT_KINDS.has(kind)) {\n diagnostics.error(\n \"invalid-select-kind\",\n `kind is ${describe(kind)}. Use \"string\", \"user\", \"role\", \"channel\", or \"mentionable\".`,\n { file: route.file, route: route.id },\n );\n return null;\n }\n return kind as SelectKind;\n}\n\nfunction detectShortIdCollisions(routes: ComponentRoute[], diagnostics: Diagnostics): void {\n const seen = new Map<string, ComponentRoute>();\n for (const route of routes) {\n const existing = seen.get(route.shortId);\n if (existing === undefined || existing.id === route.id) {\n seen.set(route.shortId, route);\n continue;\n }\n diagnostics.error(\n \"short-id-collision\",\n `${route.id} and ${existing.id} hash to the same short ID, \"${route.shortId}\", so Nectar can't tell their custom IDs apart. Rename a directory in one of them.`,\n { file: route.file, route: route.id },\n );\n }\n}\n\n/**\n * Two routes of the same kind whose paths differ only in parameter names, like\n * `tickets/[id]/close` and `tickets/[ticketId]/close`. Their hashes differ, but they take the\n * same values in the same places, so they are one route split in two.\n */\nfunction detectDuplicatePatterns(routes: ComponentRoute[], diagnostics: Diagnostics): void {\n const seen = new Map<string, ComponentRoute>();\n for (const route of routes) {\n const shape = route.segments\n .filter((s) => s.type !== \"group\")\n .map((s) => (s.type === \"static\" ? s.name : s.type === \"dynamic\" ? \"[]\" : \"[...]\"))\n .join(\"/\");\n const key = `${route.kind}#${shape}`;\n const existing = seen.get(key);\n if (existing === undefined) {\n seen.set(key, route);\n continue;\n }\n if (existing.id === route.id) continue;\n diagnostics.error(\n \"duplicate-component-pattern\",\n `${route.id} is the same path as ${existing.id} in ${relative(existing.file)}, with a different parameter name. Parameter names don't make routes distinct. Merge the two and keep one name.`,\n { file: route.file, route: route.id },\n );\n }\n}\n\nfunction describe(value: unknown): string {\n return typeof value === \"string\" ? JSON.stringify(value) : typeof value;\n}\n\nfunction relative(file: string): string {\n return path.relative(process.cwd(), file).split(path.sep).join(\"/\");\n}\n","import { type ComponentParams, customIdFor, type EncodableRoute } from \"./compile.js\";\n\nexport interface RegisteredComponentRoute extends EncodableRoute {\n path: string;\n}\n\n/**\n * Component routes the running app knows about, keyed by path. The runtime fills this from\n * the manifest before any handler runs, so `customId()` never needs the manifest itself.\n */\nconst routes = new Map<string, RegisteredComponentRoute>();\n\nexport function registerComponentRoutes(list: Iterable<RegisteredComponentRoute>): void {\n routes.clear();\n for (const route of list) routes.set(route.path, route);\n}\n\nexport function encodeComponentRoute(path: string, params: ComponentParams): string {\n const route = routes.get(path);\n if (route === undefined) {\n throw new Error(\n routes.size === 0\n ? `customId(\"${path}\") was called before the runtime registered any routes. Call it from a handler, or from code that runs after start().`\n : `No component route \"${path}\". Check the directory name under components/.`,\n );\n }\n return customIdFor(route, params);\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { RouteGraph } from \"../compiler/graph.js\";\nimport type { Route } from \"../compiler/routes.js\";\nimport { version } from \"../version.js\";\nimport { MANIFEST_VERSION, type Manifest, type ManifestRoute } from \"./schema.js\";\n\nexport const MANIFEST_FILE = \"manifest.json\";\n\n/** Serializes a route graph. `outDir` is where the manifest will live; paths are made relative to it. */\nexport function toManifest(graph: RouteGraph, outDir: string): Manifest {\n const rel = (file: string) => posix(path.relative(graph.appDir, file));\n const base = (route: Route) => {\n const chains = graph.chains.get(route.file) ?? { middleware: [], errors: [] };\n return {\n id: route.id,\n category: route.category,\n path: route.path,\n file: rel(route.file),\n middleware: chains.middleware.map(rel),\n errors: chains.errors.map(rel),\n plugins: graph.plugins.get(route.file) ?? [],\n };\n };\n\n const routes: ManifestRoute[] = [];\n for (const command of graph.commands) {\n for (const [key, route] of Object.entries(command.handlers)) {\n routes.push({ ...base(route), kind: \"command\", defer: command.defer[key] ?? null });\n }\n }\n for (const entry of graph.autocomplete) {\n routes.push({ ...base(entry.route), kind: \"autocomplete\", options: entry.options });\n }\n for (const route of graph.components) {\n routes.push({\n ...base(route),\n kind: route.kind,\n shortId: route.shortId,\n params: route.params,\n catchAll: route.catchAll,\n selectKind: route.selectKind,\n overhead: route.overhead,\n });\n }\n for (const event of graph.events) {\n for (const handler of event.handlers) {\n routes.push({\n ...base(handler.route),\n kind: \"event\",\n event: event.name,\n once: handler.once,\n order: handler.order,\n });\n }\n }\n routes.sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id));\n\n return {\n version: MANIFEST_VERSION,\n nectar: version,\n appDir: posix(path.relative(path.resolve(outDir), graph.appDir)),\n routes,\n commands: graph.commands.map((c) => ({\n name: c.name,\n type: c.type,\n payload: c.payload,\n handlers: Object.fromEntries(Object.entries(c.handlers).map(([k, r]) => [k, r.id])),\n })),\n events: graph.events.map((e) => ({\n name: e.name,\n mode: e.mode,\n handlers: e.handlers.map((h) => h.route.id),\n })),\n };\n}\n\n/** Writes `manifest.json` into `outDir` with sorted keys, so identical graphs give identical bytes. */\nexport function writeManifest(manifest: Manifest, outDir: string): string {\n mkdirSync(outDir, { recursive: true });\n const file = path.join(outDir, MANIFEST_FILE);\n writeFileSync(file, `${stableStringify(manifest)}\\n`);\n return file;\n}\n\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(value, (_key, v: unknown) => (isPlainObject(v) ? sortKeys(v) : v), 2);\n}\n\nfunction sortKeys(object: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(object).sort()) out[key] = object[key];\n return out;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction posix(file: string): string {\n return file.split(path.sep).join(\"/\");\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { RouteGraph } from \"../compiler/graph.js\";\nimport { toManifest } from \"../manifest/emit.js\";\nimport type { NectarPlugin, PluginChange, PluginGraph } from \"./index.js\";\n\n/** A frozen copy of the graph in manifest shape, with absolute file paths. */\nexport function pluginGraph(graph: RouteGraph): PluginGraph {\n const manifest = toManifest(graph, graph.appDir);\n const absolute = (file: string) => path.join(graph.appDir, ...file.split(\"/\"));\n // Cloned because the manifest shares arrays and payloads with the graph itself.\n return deepFreeze(\n structuredClone({\n appDir: graph.appDir,\n routes: manifest.routes.map((route) => ({\n ...route,\n file: absolute(route.file),\n middleware: route.middleware.map(absolute),\n errors: route.errors.map(absolute),\n })),\n commands: manifest.commands,\n events: manifest.events,\n }),\n );\n}\n\n/**\n * Runs every plugin's `transform` in config order and applies the returned changes to the\n * graph. Problems become diagnostics; a plugin never mutates the graph directly.\n */\nexport async function applyPlugins(\n graph: RouteGraph,\n plugins: readonly NectarPlugin[],\n): Promise<void> {\n for (const plugin of plugins) {\n if (plugin.transform === undefined) continue;\n let changes: PluginChange[];\n try {\n changes = (await plugin.transform(pluginGraph(graph))) ?? [];\n } catch (error) {\n graph.diagnostics.error(\n \"plugin-failed\",\n `Plugin \"${plugin.name}\" threw while transforming routes: ${describe(error)}`,\n );\n continue;\n }\n if (!Array.isArray(changes)) {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin.name}\" returned ${typeof changes} from transform. Return an array of changes, or nothing.`,\n );\n continue;\n }\n for (const change of changes) apply(graph, plugin.name, change);\n }\n}\n\nfunction apply(graph: RouteGraph, plugin: string, change: PluginChange): void {\n const type: unknown = isRecord(change) ? change.type : undefined;\n if (type !== \"middleware\" && type !== \"diagnostic\") {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin}\" returned a change with type ${JSON.stringify(type)}. A change's type is \"middleware\" or \"diagnostic\".`,\n );\n return;\n }\n if (change.type === \"diagnostic\") {\n const { severity, code, message, file, route } = change;\n graph.diagnostics.items.push({\n code,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n message,\n ...(file === undefined ? {} : { file }),\n ...(route === undefined ? {} : { route }),\n });\n return;\n }\n\n const routes = graph.routes.filter(\n (r) => r.id === change.route && (change.kind === undefined || r.kind === change.kind),\n );\n const target = change.kind === undefined ? change.route : `${change.route} (${change.kind})`;\n if (routes.length === 0) {\n graph.diagnostics.error(\n \"plugin-unknown-route\",\n `Plugin \"${plugin}\" adds middleware to route \"${target}\", which does not exist. Route IDs look like \"command:moderation/ban\".`,\n );\n return;\n }\n if (routes.some((r) => r.category === \"event\")) {\n graph.diagnostics.error(\n \"plugin-invalid-change\",\n `Plugin \"${plugin}\" adds middleware to route \"${target}\", but event handlers don't run middleware.`,\n { route: change.route },\n );\n return;\n }\n if (\n typeof change.file !== \"string\" ||\n !path.isAbsolute(change.file) ||\n !existsSync(change.file)\n ) {\n graph.diagnostics.error(\n \"plugin-missing-file\",\n `Plugin \"${plugin}\" adds middleware from ${JSON.stringify(change.file)}, which is not an absolute path to an existing file.`,\n { route: change.route },\n );\n return;\n }\n const file = path.normalize(change.file);\n for (const route of routes) {\n const chains = graph.chains.get(route.file);\n if (chains === undefined || chains.middleware.includes(file)) continue;\n if (change.position === \"inner\") chains.middleware.push(file);\n else chains.middleware.unshift(file);\n const touched = graph.plugins.get(route.file) ?? [];\n if (!touched.includes(plugin)) graph.plugins.set(route.file, [...touched, plugin]);\n }\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const inner of Object.values(value)) deepFreeze(inner);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import type { Client } from \"discord.js\";\nimport type { Project } from \"../cli/project.js\";\nimport type { Severity } from \"../compiler/diagnostics.js\";\nimport type { RouteKind } from \"../compiler/routes.js\";\nimport type { NectarServices } from \"../index.js\";\nimport type {\n Manifest,\n ManifestCommand,\n ManifestEvent,\n ManifestRoute,\n} from \"../manifest/schema.js\";\nimport type { SignalEmitter } from \"../runtime/signals.js\";\nimport type { Env, Logger } from \"../runtime/types.js\";\n\n/**\n * A plugin takes part in compilation and the runtime lifecycle. A library that only exports\n * functions for handlers to call does not need to be one.\n */\nexport interface NectarPlugin {\n /** Unique among the configured plugins. Named in diagnostics and in the manifest. */\n name: string;\n version?: string;\n /**\n * Runs after the route graph is validated and before the manifest is written. The graph is\n * frozen; return changes and the compiler applies and checks them. Plugins run in config\n * order, each seeing the changes of the ones before it.\n */\n transform?(graph: PluginGraph): Maybe<PluginChange[]> | Promise<Maybe<PluginChange[]>>;\n /** Declarations appended to `.nectar/types.d.ts`. */\n types?(graph: PluginGraph): Maybe<string>;\n /** Extra `nectar <name>` commands. */\n commands?: PluginCommand[];\n /**\n * Runs when the runtime starts, before any handler is imported and before login. A sharded\n * bot runs it in every process. Returned services land on `ctx.services` for every handler\n * and middleware.\n */\n start?(app: PluginApp): Maybe<Partial<NectarServices>> | Promise<Maybe<Partial<NectarServices>>>;\n /** Runs on shutdown, after in-flight interactions drain and before the client is destroyed. */\n stop?(app: PluginApp): void | Promise<void>;\n /**\n * Runs once per application, in the process that runs shard 0, after every `start`. For work\n * that must not repeat per shard process: a scheduled job, a web server, posting stats.\n */\n startGlobal?(app: PluginApp): void | Promise<void>;\n /** Runs on shutdown in the process that ran `startGlobal`, before any `stop`. */\n stopGlobal?(app: PluginApp): void | Promise<void>;\n}\n\n/** A hook may return nothing, so a body without `return` type-checks. */\n// biome-ignore lint/suspicious/noConfusingVoidType: that is the point\ntype Maybe<T> = T | undefined | void;\n\nexport type PluginChange =\n | {\n type: \"middleware\";\n /** Route ID, `<category>:<path>`. */\n route: string;\n /**\n * Only the route of this kind. A command and its autocomplete share an ID; without\n * `kind`, both get the middleware, as they would from a `middleware.ts`.\n */\n kind?: RouteKind;\n /** Absolute path of a module whose default export is a middleware. */\n file: string;\n /** `outer` (default) runs before the app's own middleware, `inner` right before the handler. */\n position?: \"outer\" | \"inner\";\n }\n | {\n type: \"diagnostic\";\n severity: Severity;\n code: string;\n message: string;\n file?: string;\n route?: string;\n };\n\ntype DeepReadonly<T> = T extends (infer U)[]\n ? readonly DeepReadonly<U>[]\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\n/** The compiled app as a plugin sees it: the manifest shape with absolute file paths, frozen. */\nexport interface PluginGraph {\n readonly appDir: string;\n readonly routes: DeepReadonly<ManifestRoute[]>;\n readonly commands: DeepReadonly<ManifestCommand[]>;\n readonly events: DeepReadonly<ManifestEvent[]>;\n}\n\nexport interface PluginApp {\n readonly client: Client;\n readonly env: Env;\n readonly logger: Logger;\n readonly signals: SignalEmitter;\n readonly manifest: Manifest;\n}\n\nexport interface PluginCommand {\n name: string;\n description: string;\n options?: Record<string, { type: \"boolean\" | \"string\"; description: string }>;\n /** Returns the exit code. */\n run(ctx: PluginCommandContext): number | Promise<number>;\n}\n\nexport interface PluginCommandContext {\n project: Project;\n flags: Record<string, string | boolean | undefined>;\n out(line: string): void;\n err(line: string): void;\n}\n\nexport function definePlugin(plugin: NectarPlugin): NectarPlugin {\n return plugin;\n}\n\n/** A plugin misbehaved: threw from a hook, or provided something that clashes. */\nexport class PluginError extends Error {\n constructor(\n readonly plugin: string,\n readonly detail: string,\n ) {\n super(`Plugin \"${plugin}\": ${detail}`);\n this.name = \"PluginError\";\n }\n}\n\nexport { applyPlugins, pluginGraph } from \"./transform.js\";\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as stableStringify } from "./plugins-
|
|
1
|
+
import { o as stableStringify } from "./plugins-C2uwN3-D.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -37,6 +37,30 @@ const LEVELS = /* @__PURE__ */ new Set([
|
|
|
37
37
|
"warn",
|
|
38
38
|
"error"
|
|
39
39
|
]);
|
|
40
|
+
/** Every option, so a misspelled or removed one fails instead of being ignored. */
|
|
41
|
+
const OPTIONS = {
|
|
42
|
+
token: true,
|
|
43
|
+
applicationId: true,
|
|
44
|
+
intents: true,
|
|
45
|
+
partials: true,
|
|
46
|
+
client: true,
|
|
47
|
+
eager: true,
|
|
48
|
+
env: true,
|
|
49
|
+
logger: true,
|
|
50
|
+
observe: true,
|
|
51
|
+
plugins: true,
|
|
52
|
+
appDir: true,
|
|
53
|
+
outDir: true,
|
|
54
|
+
dev: true,
|
|
55
|
+
commands: true,
|
|
56
|
+
environments: true
|
|
57
|
+
};
|
|
58
|
+
const DEV_OPTIONS = { guilds: true };
|
|
59
|
+
const COMMANDS_OPTIONS = { target: true };
|
|
60
|
+
const LOGGER_OPTIONS = {
|
|
61
|
+
level: true,
|
|
62
|
+
sink: true
|
|
63
|
+
};
|
|
40
64
|
/** Checks a loaded config's shape. Discord validates intent and partial values itself at login. */
|
|
41
65
|
function validateConfig(value, file) {
|
|
42
66
|
const fail = (detail) => {
|
|
@@ -44,6 +68,12 @@ function validateConfig(value, file) {
|
|
|
44
68
|
};
|
|
45
69
|
if (!isRecord(value)) fail("the default export must be an object. Use defineConfig({ ... }).");
|
|
46
70
|
const config = value;
|
|
71
|
+
checkKeys(config, OPTIONS, "", fail);
|
|
72
|
+
for (const [key, options] of [
|
|
73
|
+
["dev", DEV_OPTIONS],
|
|
74
|
+
["commands", COMMANDS_OPTIONS],
|
|
75
|
+
["logger", LOGGER_OPTIONS]
|
|
76
|
+
]) if (isRecord(config[key])) checkKeys(config[key], options, `${key}.`, fail);
|
|
47
77
|
for (const key of ["token", "applicationId"]) if (config[key] !== void 0 && typeof config[key] !== "string") fail(`\`${key}\` must be a string, usually read from process.env.`);
|
|
48
78
|
if (config.intents === void 0) fail("`intents` is required. Use [] for none.");
|
|
49
79
|
if (!isBitfield(config.intents)) fail("`intents` must be an array of intent names or bits, a single bit, or a bigint.");
|
|
@@ -134,6 +164,14 @@ function validatePlugins(value, fail) {
|
|
|
134
164
|
}
|
|
135
165
|
});
|
|
136
166
|
}
|
|
167
|
+
/** Fails on the first key `options` doesn't have, naming a likely intended one when there is one. */
|
|
168
|
+
function checkKeys(value, options, prefix, fail) {
|
|
169
|
+
for (const key of Object.keys(value)) {
|
|
170
|
+
if (Object.hasOwn(options, key)) continue;
|
|
171
|
+
const near = Object.keys(options).find((option) => option.toLowerCase() === key.toLowerCase() || option === `${key}s` || `${option}s` === key);
|
|
172
|
+
fail(`\`${prefix}${key}\` isn't a config option.${near === void 0 ? " The options are listed at https://nectar-js.github.io/nectar/reference/config." : ` Did you mean \`${prefix}${near}\`?`}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
137
175
|
function isGuildList(value) {
|
|
138
176
|
return Array.isArray(value) && value.every((g) => typeof g === "string" && /^\d+$/.test(g));
|
|
139
177
|
}
|
|
@@ -248,7 +286,7 @@ function checkIntents(events, intents, configFile) {
|
|
|
248
286
|
diagnostics.push({
|
|
249
287
|
code: "missing-intent",
|
|
250
288
|
severity: "warning",
|
|
251
|
-
message: `"${event.name}" never fires without the ${names} intent. Add it to
|
|
289
|
+
message: `"${event.name}" never fires without the ${names} intent. Add it to intents in ${configFile}.${privileged.length === 0 ? "" : ` ${privileged.map((i) => `"${i}"`).join(" and ")} is privileged, so also turn it on under Bot in the Discord Developer Portal.`}`,
|
|
252
290
|
file: first.route.file,
|
|
253
291
|
route: first.route.id
|
|
254
292
|
});
|
|
@@ -368,8 +406,9 @@ function scopeKey(scope) {
|
|
|
368
406
|
function scopeRoute(applicationId, scope) {
|
|
369
407
|
return scope === "global" ? Routes.applicationCommands(applicationId) : Routes.applicationGuildCommands(applicationId, scope.guild);
|
|
370
408
|
}
|
|
409
|
+
/** With full localization maps, which Discord leaves out unless asked. */
|
|
371
410
|
async function fetchCommands(rest, applicationId, scope) {
|
|
372
|
-
return await rest.get(scopeRoute(applicationId, scope));
|
|
411
|
+
return await rest.get(scopeRoute(applicationId, scope), { query: new URLSearchParams({ with_localizations: "true" }) });
|
|
373
412
|
}
|
|
374
413
|
/** Bulk overwrite: Discord replaces the scope's whole command set with `commands`. */
|
|
375
414
|
async function putCommands(rest, applicationId, scope, commands) {
|
|
@@ -455,6 +494,16 @@ function problem(path, commands, message) {
|
|
|
455
494
|
//#region src/registration/sync.ts
|
|
456
495
|
const REGISTRATION_CACHE_FILE = "registration.json";
|
|
457
496
|
const CACHE_VERSION = 1;
|
|
497
|
+
/**
|
|
498
|
+
* The command types an app declares. Other commands in a scope, like the Entry Point command
|
|
499
|
+
* Discord creates for Activities, go back unchanged in every overwrite: Discord rejects one
|
|
500
|
+
* that drops them, with error 50240.
|
|
501
|
+
*/
|
|
502
|
+
const DECLARED_TYPES = /* @__PURE__ */ new Set([
|
|
503
|
+
ApplicationCommandType.ChatInput,
|
|
504
|
+
ApplicationCommandType.User,
|
|
505
|
+
ApplicationCommandType.Message
|
|
506
|
+
]);
|
|
458
507
|
/** Thrown instead of applying when the guard trips and `force` is not set. */
|
|
459
508
|
var UnsafeSyncError = class extends Error {
|
|
460
509
|
reasons;
|
|
@@ -477,6 +526,8 @@ async function syncCommands(options) {
|
|
|
477
526
|
const targets = new Set(scopes.map(scopeKey));
|
|
478
527
|
for (const key of Object.keys(cache?.scopes ?? {})) if (!targets.has(key)) unsafe.push(`${key} received commands last time but is no longer a target. Its commands stay registered on Discord until removed.`);
|
|
479
528
|
const results = [];
|
|
529
|
+
/** Remote commands of other types, by scope key, to send back in the overwrite. */
|
|
530
|
+
const kept = /* @__PURE__ */ new Map();
|
|
480
531
|
for (const scope of scopes) {
|
|
481
532
|
const key = scopeKey(scope);
|
|
482
533
|
if (cache !== null && cache.applicationId === applicationId && cache.scopes[key] === hash) {
|
|
@@ -488,8 +539,10 @@ async function syncCommands(options) {
|
|
|
488
539
|
continue;
|
|
489
540
|
}
|
|
490
541
|
const remote = await fetchCommands(rest, applicationId, scope);
|
|
491
|
-
const
|
|
492
|
-
|
|
542
|
+
const declared = remote.filter((c) => DECLARED_TYPES.has(c.type));
|
|
543
|
+
kept.set(key, remote.filter((c) => !DECLARED_TYPES.has(c.type)).map(resubmit));
|
|
544
|
+
const diff = diffCommands(commands, declared);
|
|
545
|
+
if (commands.length === 0 && declared.length > 0) unsafe.push(`${key} has ${declared.length} command(s) registered and the app declares none.`);
|
|
493
546
|
results.push({
|
|
494
547
|
scope,
|
|
495
548
|
diff,
|
|
@@ -508,7 +561,8 @@ async function syncCommands(options) {
|
|
|
508
561
|
};
|
|
509
562
|
for (const result of results) {
|
|
510
563
|
if (result.diff?.hasChanges) {
|
|
511
|
-
|
|
564
|
+
const body = [...commands, ...kept.get(scopeKey(result.scope)) ?? []];
|
|
565
|
+
await putCommands(rest, applicationId, result.scope, body);
|
|
512
566
|
result.applied = true;
|
|
513
567
|
}
|
|
514
568
|
next.scopes[scopeKey(result.scope)] = hash;
|
|
@@ -519,6 +573,11 @@ async function syncCommands(options) {
|
|
|
519
573
|
unsafe
|
|
520
574
|
};
|
|
521
575
|
}
|
|
576
|
+
/** A fetched command as an overwrite takes it, without the fields Discord fills in itself. */
|
|
577
|
+
function resubmit(command) {
|
|
578
|
+
const { id: _id, application_id: _application, guild_id: _guild, version: _version, name_localized: _name, description_localized: _description, ...body } = command;
|
|
579
|
+
return body;
|
|
580
|
+
}
|
|
522
581
|
/** Order-insensitive, like the diff: reordering commands is not a change. */
|
|
523
582
|
function hashCommands(commands) {
|
|
524
583
|
const normalized = commands.map((c) => [commandKey(c), normalizeCommand(c)]).sort(([a], [b]) => a.localeCompare(b)).map(([, c]) => c);
|
|
@@ -542,4 +601,4 @@ function writeCache(dir, cache) {
|
|
|
542
601
|
//#endregion
|
|
543
602
|
export { checkIntents as a, configFor as c, scopeKey as i, defineConfig as l, syncCommands as n, requiredIntents as o, RegistrationError as r, ConfigError as s, UnsafeSyncError as t, validateConfig as u };
|
|
544
603
|
|
|
545
|
-
//# sourceMappingURL=registration-
|
|
604
|
+
//# sourceMappingURL=registration-mUZJidnF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration-mUZJidnF.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,8 +1,8 @@
|
|
|
1
|
-
import { b as loadModule, h as paramValidatorsOf, m as findInvalidParam, t as PluginError, u as registerComponentRoutes
|
|
1
|
+
import { E as decodeCustomId, b as loadModule, h as paramValidatorsOf, m as findInvalidParam, t as PluginError, u as registerComponentRoutes } from "./plugins-C2uwN3-D.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";
|
|
5
|
-
import { ApplicationCommandType, MessageFlags } from "discord-api-types/v10";
|
|
5
|
+
import { ApplicationCommandOptionType, ApplicationCommandType, MessageFlags } from "discord-api-types/v10";
|
|
6
6
|
//#region src/components/matcher.ts
|
|
7
7
|
/**
|
|
8
8
|
* Resolves incoming custom IDs to routes.
|
|
@@ -63,6 +63,57 @@ function loadManifest(file) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
//#endregion
|
|
66
|
+
//#region src/commands/options.ts
|
|
67
|
+
const OPTION_TYPE = {
|
|
68
|
+
[ApplicationCommandOptionType.String]: "string",
|
|
69
|
+
[ApplicationCommandOptionType.Integer]: "integer",
|
|
70
|
+
[ApplicationCommandOptionType.Number]: "number",
|
|
71
|
+
[ApplicationCommandOptionType.Boolean]: "boolean",
|
|
72
|
+
[ApplicationCommandOptionType.User]: "user",
|
|
73
|
+
[ApplicationCommandOptionType.Channel]: "channel",
|
|
74
|
+
[ApplicationCommandOptionType.Role]: "role",
|
|
75
|
+
[ApplicationCommandOptionType.Mentionable]: "mentionable",
|
|
76
|
+
[ApplicationCommandOptionType.Attachment]: "attachment"
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The options of one handler position (`""`, `"sub"`, or `"group/sub"`) of a registration
|
|
80
|
+
* payload, in declaration order. Subcommands and groups are not options of the handler.
|
|
81
|
+
*/
|
|
82
|
+
function optionsAt(payload, key) {
|
|
83
|
+
let options = payload.options;
|
|
84
|
+
for (const part of key === "" ? [] : key.split("/")) options = options?.find((o) => o.name === part)?.options;
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const option of options ?? []) {
|
|
87
|
+
const type = option.type === void 0 ? void 0 : OPTION_TYPE[option.type];
|
|
88
|
+
if (option.name !== void 0 && type !== void 0) out.push({
|
|
89
|
+
name: option.name,
|
|
90
|
+
type,
|
|
91
|
+
required: option.required === true
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
const READ = {
|
|
97
|
+
string: (o, n) => o.getString(n),
|
|
98
|
+
integer: (o, n) => o.getInteger(n),
|
|
99
|
+
number: (o, n) => o.getNumber(n),
|
|
100
|
+
boolean: (o, n) => o.getBoolean(n),
|
|
101
|
+
user: (o, n) => o.getUser(n),
|
|
102
|
+
channel: (o, n) => o.getChannel(n),
|
|
103
|
+
role: (o, n) => o.getRole(n),
|
|
104
|
+
mentionable: (o, n) => o.getMentionable(n),
|
|
105
|
+
attachment: (o, n) => o.getAttachment(n)
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Every option of the handler by name, read through discord.js's resolver. Options the user
|
|
109
|
+
* left out are `null`, so the handler's `ctx.options` always has every declared key.
|
|
110
|
+
*/
|
|
111
|
+
function resolveOptions(interaction, specs) {
|
|
112
|
+
const out = {};
|
|
113
|
+
for (const { name, type } of specs) out[name] = READ[type](interaction.options, name);
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
66
117
|
//#region src/runtime/signals.ts
|
|
67
118
|
/**
|
|
68
119
|
* Fan-out for framework signals. Listeners run synchronously in subscription order; one that
|
|
@@ -182,9 +233,10 @@ async function defaultBoundary(error, ctx, logger, middleware) {
|
|
|
182
233
|
if (!("interaction" in ctx)) return;
|
|
183
234
|
const interaction = ctx.interaction;
|
|
184
235
|
if (typeof interaction.isRepliable !== "function" || !interaction.isRepliable()) return;
|
|
185
|
-
if (interaction.replied
|
|
236
|
+
if (interaction.replied) return;
|
|
186
237
|
try {
|
|
187
|
-
await interaction.
|
|
238
|
+
if (interaction.deferred) await interaction.editReply({ content: GENERIC_ERROR_REPLY });
|
|
239
|
+
else await interaction.reply({
|
|
188
240
|
content: GENERIC_ERROR_REPLY,
|
|
189
241
|
flags: MessageFlags.Ephemeral
|
|
190
242
|
});
|
|
@@ -334,14 +386,26 @@ function routeInfo(state, route) {
|
|
|
334
386
|
id: route.id,
|
|
335
387
|
category: route.category,
|
|
336
388
|
path: route.path,
|
|
337
|
-
file:
|
|
389
|
+
file: paths(state, route).file
|
|
338
390
|
};
|
|
339
391
|
}
|
|
340
392
|
function chains(state, route) {
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
393
|
+
return paths(state, route);
|
|
394
|
+
}
|
|
395
|
+
/** A route's absolute paths, joined on its first interaction instead of every one. */
|
|
396
|
+
const resolved = /* @__PURE__ */ new WeakMap();
|
|
397
|
+
function paths(state, route) {
|
|
398
|
+
let found = resolved.get(route);
|
|
399
|
+
if (found === void 0 || found.appDir !== state.appDir) {
|
|
400
|
+
found = {
|
|
401
|
+
appDir: state.appDir,
|
|
402
|
+
file: absolute(state, route.file),
|
|
403
|
+
middleware: route.middleware.map((f) => absolute(state, f)),
|
|
404
|
+
errors: route.errors.map((f) => absolute(state, f))
|
|
405
|
+
};
|
|
406
|
+
resolved.set(route, found);
|
|
407
|
+
}
|
|
408
|
+
return found;
|
|
345
409
|
}
|
|
346
410
|
//#endregion
|
|
347
411
|
//#region src/runtime/dispatch.ts
|
|
@@ -359,7 +423,7 @@ function createInteractionDispatcher(state) {
|
|
|
359
423
|
const key = [interaction.options.getSubcommandGroup(false), interaction.options.getSubcommand(false)].filter((p) => p !== null).join("/");
|
|
360
424
|
const route = tables.commands.get(commandKey(ApplicationCommandType.ChatInput, interaction.commandName, key));
|
|
361
425
|
if (route === void 0) return unknown(state, interaction, meta, `chat input command /${meta.command}`);
|
|
362
|
-
return run(state, route, interaction, meta, {}, receivedAt);
|
|
426
|
+
return run(state, route, interaction, meta, {}, receivedAt, resolveOptions(interaction, tables.options.get(route.id) ?? []));
|
|
363
427
|
}
|
|
364
428
|
if (interaction.isContextMenuCommand()) {
|
|
365
429
|
const route = tables.commands.get(commandKey(interaction.commandType, interaction.commandName, ""));
|
|
@@ -372,7 +436,7 @@ function createInteractionDispatcher(state) {
|
|
|
372
436
|
const route = command === void 0 ? void 0 : tables.autocomplete.get(command.id);
|
|
373
437
|
const option = interaction.options.getFocused(true).name;
|
|
374
438
|
if (route === void 0 || !route.options.includes(option)) return unknown(state, interaction, meta, `autocomplete for /${meta.command} "${option}"`);
|
|
375
|
-
return run(state, route, interaction, meta, {}, receivedAt, option);
|
|
439
|
+
return run(state, route, interaction, meta, {}, receivedAt, {}, option);
|
|
376
440
|
}
|
|
377
441
|
const component = interaction.isButton() ? ["button", interaction.customId] : interaction.isAnySelectMenu() ? ["select", interaction.customId] : interaction.isModalSubmit() ? ["modal", interaction.customId] : null;
|
|
378
442
|
if (component === null) {
|
|
@@ -404,12 +468,13 @@ function createInteractionDispatcher(state) {
|
|
|
404
468
|
return run(state, match.route, interaction, meta, match.params, receivedAt);
|
|
405
469
|
};
|
|
406
470
|
}
|
|
407
|
-
async function run(state, route, interaction, meta, params, receivedAt, autocompleteOption) {
|
|
471
|
+
async function run(state, route, interaction, meta, params, receivedAt, options = {}, autocompleteOption) {
|
|
408
472
|
const ctx = {
|
|
409
473
|
interaction,
|
|
410
474
|
client: state.client,
|
|
411
475
|
route: routeInfo(state, route),
|
|
412
476
|
params,
|
|
477
|
+
options,
|
|
413
478
|
env: state.env,
|
|
414
479
|
trace: {
|
|
415
480
|
id: interaction.id,
|
|
@@ -450,7 +515,8 @@ async function run(state, route, interaction, meta, params, receivedAt, autocomp
|
|
|
450
515
|
return;
|
|
451
516
|
}
|
|
452
517
|
}
|
|
453
|
-
|
|
518
|
+
const deferred = route.kind === "command" && route.defer !== null ? route.defer : null;
|
|
519
|
+
await runChain(middleware, ctx, deferred === null ? handler : deferring(handler, deferred), {
|
|
454
520
|
middleware: (index) => state.signals.emit({
|
|
455
521
|
type: "middleware:enter",
|
|
456
522
|
...tag,
|
|
@@ -494,6 +560,17 @@ async function run(state, route, interaction, meta, params, receivedAt, autocomp
|
|
|
494
560
|
if (autocompleteOption !== void 0) await closeAutocomplete(interaction);
|
|
495
561
|
}
|
|
496
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* Defers the reply right before the handler, after the middleware chain, so a policy check can
|
|
565
|
+
* still answer with its own message. A middleware that already replied or deferred wins.
|
|
566
|
+
*/
|
|
567
|
+
function deferring(handler, mode) {
|
|
568
|
+
return async (ctx) => {
|
|
569
|
+
const interaction = ctx.interaction;
|
|
570
|
+
if (!interaction.replied && !interaction.deferred) await interaction.deferReply(mode === "ephemeral" ? { flags: MessageFlags.Ephemeral } : {});
|
|
571
|
+
return handler(ctx);
|
|
572
|
+
};
|
|
573
|
+
}
|
|
497
574
|
/** Discord shows a spinner until autocomplete answers, so a failed handler answers with nothing. */
|
|
498
575
|
async function closeAutocomplete(interaction) {
|
|
499
576
|
if (interaction.responded) return;
|
|
@@ -540,12 +617,16 @@ function buildTables(routes, commands) {
|
|
|
540
617
|
else if (route.kind === "autocomplete") autocomplete.set(route.id, route);
|
|
541
618
|
else if (route.kind !== "event") components.push(route);
|
|
542
619
|
const table = /* @__PURE__ */ new Map();
|
|
620
|
+
const options = /* @__PURE__ */ new Map();
|
|
543
621
|
for (const command of commands) for (const [key, id] of Object.entries(command.handlers)) {
|
|
544
622
|
const route = commandRoutes.get(id);
|
|
545
|
-
if (route
|
|
623
|
+
if (route === void 0) continue;
|
|
624
|
+
table.set(commandKey(command.type, command.name, key), route);
|
|
625
|
+
options.set(id, optionsAt(command.payload, key));
|
|
546
626
|
}
|
|
547
627
|
return {
|
|
548
628
|
commands: table,
|
|
629
|
+
options,
|
|
549
630
|
autocomplete,
|
|
550
631
|
matcher: createMatcher(components)
|
|
551
632
|
};
|
|
@@ -886,6 +967,6 @@ function envFromProcess() {
|
|
|
886
967
|
return value === "production" || value === "test" ? value : "development";
|
|
887
968
|
}
|
|
888
969
|
//#endregion
|
|
889
|
-
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,
|
|
970
|
+
export { bindEvents as a, ModuleRegistry as c, loadManifest as d, createLogger as i, createSignals as l, createRuntime as n, createInteractionDispatcher as o, manifestFiles as r, HandlerLoadError as s, LoginError as t, optionsAt as u };
|
|
890
971
|
|
|
891
|
-
//# sourceMappingURL=runtime-
|
|
972
|
+
//# sourceMappingURL=runtime-B7mCDO5Q.js.map
|