Plugin API
Exact plugin context, hooks, middleware, adapter registration, and typed client extension contracts.
EmailPlugin
type EmailPlugin<Extension extends object = object> = {
id: string;
adapters?: EmailAdapter[] | ((ctx: EmailPluginContext) => EmailAdapter[]);
hooks?: EmailHooks;
middleware?: EmailSendMiddleware[];
extendClient?: (ctx: EmailPluginContext) => Extension;
};Plugin ids are unique. Adapter registration and client extensions run synchronously during construction.
Plugin context
type EmailPluginContext = {
adapters: ReadonlyMap<string, EmailAdapter>;
defaultAdapter: string;
addAdapter(adapter: EmailAdapter): void;
};addAdapter rejects duplicate route names.
Hooks
type EmailHooks = {
beforeSend?(event: EmailHookEvent): MaybePromise<void>;
afterSend?(event: EmailAfterSendEvent): MaybePromise<void>;
onError?(event: EmailErrorEvent): MaybePromise<void>;
onRetry?(event: EmailHookEvent & {
error: EmailSdkError;
nextAttempt: number;
delayMs: number;
}): MaybePromise<void>;
};Hook exceptions are swallowed. Use them only for best-effort observation.
Middleware
type EmailSendMiddleware = {
beforeSend?(event: EmailBeforeSendEvent): MaybePromise<EmailBeforeSendResult | void>;
afterSend?(event: EmailAfterSendEvent): MaybePromise<void>;
onError?(event: EmailErrorEvent): MaybePromise<void>;
};beforeSend may replace the message and/or send options. Middleware exceptions become EmailMiddlewareError with a phase.
Multiple beforeSend middleware entries run in order. Replacement options shallowly merge into the current options.
Client extensions
extendClient returns an object merged into the client. The resulting extension type is the intersection of every plugin extension.
Reserved-key collisions throw EmailValidationError during construction.
Create a plugin
Build a typed middleware and extension example.
