3.3 KiB
define
(no submodules)
Purpose
The define module exports a single function — definePlugin<TConfig>(descriptor)
— that is the canonical entry point for authoring a plugin. The function
returns its input unchanged at runtime; the value is entirely in its
type signature, which threads the TConfig generic through every place
the descriptor references it: configSchema?: ZodType<TConfig>,
defaults?: Partial<TConfig>, and the four lifecycle hooks
(onRegister/onInit/onReady/onClose). Inside each hook,
ctx.getOwnConfig() returns TConfig with no cast required.
Using definePlugin is preferred over building a raw PluginDescriptor
literal because it pins the generic at the call site. TypeScript will
then flag mismatches at the source of the error (the hook body), not at
the registration site one or two files away.
Features
- Zero runtime overhead — the function body is
return descriptor. - Full
TConfiginference flows into hooks viaPluginContext<TConfig>. - Compatible with descriptor field defaults (
description,dependencies,optionalDependencies,priority,enabledByDefault,tags). - Plays nicely with
as constliterals — readers preservereadonlyarrays fordependenciesetc.
Usage: Basic
import { definePlugin } from "@suaveplan/api-plugin/define";
import z from "zod";
export const auth = definePlugin({
name: "auth",
version: "0.1.0",
configSchema: z.object({ sessionTtlSeconds: z.number().int().positive() }),
defaults: { sessionTtlSeconds: 3600 },
onInit(ctx) {
const { sessionTtlSeconds } = ctx.getOwnConfig();
ctx.logger.info("auth ready", { sessionTtlSeconds });
},
});
Usage: Advanced
Multiple hooks with await and the abort signal:
export const queue = definePlugin<{ concurrency: number }>({
name: "queue",
version: "0.1.0",
dependencies: ["db"],
defaults: { concurrency: 4 },
async onInit(ctx) {
const { concurrency } = ctx.getOwnConfig();
ctx.logger.info("starting queue", { concurrency });
},
async onReady(ctx) {
if (ctx.signal.aborted) return;
ctx.logger.info("queue ready");
},
async onClose(ctx) {
ctx.logger.info("queue draining");
},
});
API Reference
| Export | Signature |
|---|---|
definePlugin<TConfig>(descriptor) |
(PluginDescriptor<TConfig>) => PluginDescriptor<TConfig> |
Implementation Notes
The function is intentionally an identity at runtime so it can be
constant-folded by bundlers — the dependency graph rewards leaf
helpers that the optimiser can drop. Because the descriptor parameter
is typed as PluginDescriptor<TConfig>, TypeScript will not narrow
TConfig away if the call site supplies an explicit generic
(definePlugin<MyConfig>({...})). Implicit inference also works
when configSchema is provided as a Zod schema, because the
descriptor field is typed ZodType<TConfig> and Zod's schema generic
is contravariant in this position.
Calling definePlugin is preferred over a bare object literal even
though both are runtime-equivalent: the call site is the right place
for an editor to surface "Quick Info" on the generic, and it keeps the
public author surface consistent across the 40+ in-flight
@suaveplan/api-plugin-* packages.