Publish a community plugin
Package an Email SDK plugin for npm and list it in the community registry.
This guide publishes a plugin — built in Create your first plugin — as an npm package and lists it in the community registry. The registry is a static JSON file in the docs repo; there is no hosted plugin service.
If your package adds a new email provider, follow Publish a community adapter instead — same process, plus field-support documentation.
Shape the package
Name it email-sdk-{plugin} and export the plugin factory (and its options type) from the package root:
email-sdk-audit-log/
src/
index.ts
audit-log.ts
audit-log.test.ts
package.json
README.mdexport { auditLogPlugin } from "./audit-log";
export type { AuditLog, AuditLogEntry } from "./audit-log";The bar: apps use it in one line inside plugins.
createEmailClient({
plugins: [auditLogPlugin()],
});Set the package.json requirements
Declare @opencoredev/email-sdk as a peer dependency so apps never end up with two copies of the core types:
{
"name": "email-sdk-audit-log",
"type": "module",
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"peerDependencies": {
"@opencoredev/email-sdk": "^0.6.0"
}
}Plugin packages should be boring libraries. To qualify for a verified listing later, the package must have:
- A public source repository (matching the registry entry).
- npm provenance or trusted publishing.
- No
preinstall,install, orpostinstallscripts. - No binaries.
- A small runtime dependency surface.
- No network calls during import or plugin construction.
These checks reduce supply-chain risk; they do not make third-party code risk-free.
Test registration and behavior
At minimum, prove the plugin registers with createEmailClient and does its job against the memory adapter:
import { expect, test } from "bun:test";
import { createEmailClient } from "@opencoredev/email-sdk";
import { memoryProvider } from "@opencoredev/email-sdk/testing";
import { auditLogPlugin } from "./index";
test("records sends on the extended client", async () => {
const email = createEmailClient({
adapters: [memoryProvider()],
plugins: [auditLogPlugin()],
});
await email.send({
from: "Acme <hello@acme.com>",
to: "user@example.com",
subject: "Hello",
text: "Hi",
});
expect(email.auditLog.entries).toHaveLength(1);
});Add a README with the install command, a minimal client example, and what the plugin extends the client with.
Publish and open the registry PR
Publish to npm from your own repository, then add an entry to apps/fumadocs/content/community/plugins.json in the Email SDK repo:
{
"name": "Audit log",
"package": "email-sdk-audit-log",
"kind": "plugin",
"status": "community",
"description": "Records every send outcome on a typed client extension.",
"href": "https://www.npmjs.com/package/email-sdk-audit-log",
"repo": "https://github.com/acme/email-sdk-audit-log",
"maintainer": "acme",
"pluginId": "audit-log"
}Every field except pluginId is required; href and repo must be https URLs, and name, package, and plugin id must be unique across the registry. Start with status: "community" — verified requires a per-version review with verifiedVersion and verification metadata in the entry.
Verify the entry
bun run community:checkCI runs the same validation on the PR. For verified and official entries it also downloads the published tarball and audits it: install scripts, binaries, the peer dependency, repository match, and runtime dependency count.
Next
Create an adapter
Implement the EmailProvider contract for a new provider — payload mapping, fail-fast field validation, retryable error mapping, and tests with injected fetch.
Publish a community adapter
Ship a third-party provider adapter as its own npm package and list it in the Email SDK community registry.
