Skip to content

Where it runs

Limit a plugin to one transport or runtime so a wrong attach fails to compile, and keep Workers plugins free of server-only imports.

A plugin that reads this.core.bot needs a gateway bot. One that talks to a database through a Node driver can't run on Workers. If you attach either to the wrong bot, you find out when the plugin first reads something that isn't there.

You can catch the wrong bot at compile time. If you declare the transport or the runtime your plugin needs, an attach call on a bot that doesn't match fails to compile. A transport is how the bot talks to Discord, 'gateway' or 'http'. A runtime is where the bot's code runs, 'server' for Node or Bun and 'edge' for Cloudflare Workers. A type cast or plain JavaScript gets past the compiler, and then attach throws when it runs.

Limiting where a plugin runs

The package you import Plugin from sets the transport. A plugin for one transport extends that transport's base, which also types this.core with that transport's members. The runtime stays open on the http and @seedcord/core/plugin bases, and you limit it with the PluginOptions generic. The compile errors below call this narrowing.

your plugin attaches toimport Plugin fromdeclare
gateway bots@seedcord/gatewaynothing, the base sets 'server'
http bots on any runtime@seedcord/httpnothing
http bots on Node or Bun@seedcord/httpruntime: 'server'
http bots on Workers@seedcord/httpruntime: 'edge'
bots on either transport, any runtime@seedcord/core/pluginnothing
bots on either transport, on Node or Bun@seedcord/core/pluginruntime: 'server', like Mongoose

A plugin limited to 'server' runs on Node and on Bun. Before your README says the plugin runs on Bun, check that its dependencies do. A package that ships a native addon, for example bcrypt, is the most likely to fail.

When you limit the runtime, pass the same value to super().

import { Plugin } from '@seedcord/http';

import type { CoreBase } from '@seedcord/http';

export class DriverCache extends Plugin<{ runtime: 'server' }> {
    public constructor(host: CoreBase) {
        super(host, { runtime: 'server' });
    }

    public async init(): Promise<void> {}
}

DriverCache repeats server in super() because TypeScript erases the type argument when it compiles. At run time, attach can only read what the constructor passes. The second argument of super() is typed from the type argument, so the two can't disagree. It also takes the PluginLifecycleSpec fields, like an init timeout in milliseconds.

super(host, { runtime: 'server', init: { timeout: 30_000 } });

If you limit a plugin and don't write a constructor, the attach call fails to compile with this message.

this plugin narrows its transport or runtime. Give it a constructor that passes them to super(), like super(host, { runtime: 'server' })

If your constructor calls super(host) alone, TypeScript reports "Expected 2 arguments, but got 1".

A bot on the wrong runtime fails to compile when it attaches DriverCache. The sample below types the bot as PluginHost<'http', 'edge'>, the base class of the Workers Seedcord, because the guide's samples compile against the Node build.

import { Plugin } from '@seedcord/http';
import { PluginHost } from '@seedcord/core/plugin';

import type { CoreBase } from '@seedcord/http';

class DriverCache extends Plugin<{ runtime: 'server' }> {
    public constructor(host: CoreBase) {
        super(host, { runtime: 'server' });
    }

    public async init(): Promise<void> {}
}

declare const workersBot: PluginHost<'http', 'edge'>;

workersBot.attach('cache', DriverCache);
Argument of type 'typeof DriverCache' is not assignable to parameter of type 'RuntimeMismatch<"server", "edge">'. Property '"this plugin declares runtime 'server' but this bot runs 'edge'"' is missing in type 'typeof DriverCache' but required in type 'RuntimeMismatch<"server", "edge">'.

The error prints the runtime DriverCache declares beside the one the bot runs. A cast or plain JavaScript skips that check, and then attach throws CorePluginScopeMismatch with the plugin, the runtime it declares, and the runtime the bot uses.

DriverCache only runs on the server runtime, but this bot uses the edge runtime. Remove the attach call, or move the bot to the server runtime.

Cloudflare Workers

A Workers bot starts its plugins on the first POST that each isolate receives. An isolate is one running copy of the worker, and Cloudflare can run several at once. The lifecycle page lists what that changes for init(), dispose() and shutdown tasks. The constructor still runs when the worker loads bot.ts, so keep timers and connections in init().

A plugin that targets Workers imports from @seedcord/http or @seedcord/core/plugin. Under the workerd condition, @seedcord/http resolves to the edge Seedcord, while @seedcord/gateway has no workerd build. Everything your plugin imports is bundled into the worker. Keep node:http and other server-only code out of it. The Workers build also needs Cloudflare's Node compatibility on, which Workers shows how to set.

If a dependency can't run on Workers, declare runtime: 'server'. Attaching the plugin to a Workers bot then fails to compile.