Typing a plugin
Type the plugins you attached on this.core with seedcord codegen, and see when you need to run codegen again.
You attached the plugin, but this.core.db still fails to compile. The property is there when the bot runs, so only the types are missing. attach returns a bot typed with db, but a handler reads this.core. seedcord declares the type of this.core in its own package, which never imports your bot file.
A declare module block written by hand for every key works too, though you'd have to update it each time you rename one. seedcord codegen writes that block for you into src/seedcord-gen.ts.
import { SlashHandler, SlashRoute } from '@seedcord/gateway';
@SlashRoute('history')
export class History extends SlashHandler<'history'> {
public async execute(): Promise<void> {
await this.reply(String(this.core.db)); }
}TypeScript reports "Property 'db' does not exist on type 'Core'" on this.core.db. Nothing is wrong with the handler itself.
What codegen writes
Every key you attached becomes one row on Core, sorted by key. A group takes one row for all the plugins inside it. The same file also holds your command and emoji registries, which this sample leaves out.
// Generated by `seedcord codegen`. Do not edit by hand.
// Run `seedcord codegen` after changing your commands, plugins, or emoji config.
import type Bot from './bot';
// a row has to fit what its plugin's codegen() declares
type Fits<
Plugin extends { codegen(): Promise<unknown> },
Row extends Awaited<ReturnType<Plugin['codegen']>>
> = Row;
// prettier-ignore
declare module '@seedcord/gateway' {
interface Core {
db: (typeof Bot)['db'];
i18n: (typeof Bot)['i18n'];
}
// SlashRegistry, the context menu registries, and EmojiMap follow
interface PluginTypes {
'seedcord-locales': Fits<(typeof Bot)['i18n'], { messages: { bye: true; greet: true } }>;
}
}
export {};Bot is the default export of the file your instance config key points at. Each row reads its type straight from that bot, so db gets exactly the type attach produced, Mongoose here. That only works when the default export is the value attach returns. If you export the bot without it, this.core.db compiles as any. If you export the bot only as a named const, codegen stops with the code CliInstanceInvalid before it writes anything.
PluginTypes holds types that a plugin built from your project's files while codegen ran. The row above comes from a translations plugin attached as i18n, and it lists the message keys in your locales folder, so this.core.i18n.t() accepts 'bye' or 'greet' and rejects a typo. Fits checks each row against the plugin attached at that key. Writing types from codegen shows how a plugin adds a row.
The Core block only appears once you've attached something, and PluginTypes once a plugin has written a row.
Gateway and http differ
Each transport package declares its own Core, so the declare module line uses the package you import Seedcord from. A gateway bot's file uses declare module '@seedcord/gateway', and an http bot's uses declare module '@seedcord/http'.
When to run it
The generated file only changes when codegen runs, so a stale one either misses a new key or still types a key you removed.
- In
seedcord dev, pressgafter you add a service. Pressrafter you attach a plugin or rename its key, since dev rewrites the file when the bot starts. - Outside dev, run
seedcord codegen. - In CI,
seedcord codegen --checkthrowsCliCodegenOutOfDatewhen the file is stale.
Once codegen has run, your editor offers db on this.core.
this.core.- applicationId
- bot
- bus
- config
- db
- kv
- rateLimiter
- rest
- shutdown
- startup
Whether the plugin has started when your handler runs is a separate question, which the lifecycle answers.