@coffer-org/web-sdk
v1.3.0
Published
The UI contract for Coffer plugin **web bundles**. A plugin's `dist/web.js` is loaded at runtime by the host's federation loader; this package defines what that bundle declares (`ModuleUIBundle`) and the field-renderer registry it plugs into.
Readme
@coffer-org/web-sdk
The UI contract for Coffer plugin web bundles. A plugin's dist/web.js is
loaded at runtime by the host's federation loader; this package defines what that
bundle declares (ModuleUIBundle) and the field-renderer registry it plugs into.
What a web bundle exports
web.js default-exports a ModuleUIBundle — or an array of them:
export default ModuleUIBundle | ModuleUIBundle[];Build one with defineModuleUI(vault, module, bundle):
import { defineModuleUI } from '@coffer-org/web-sdk';
export default defineModuleUI('myplugin', 'note', {
// RecordView?: custom record renderer (replaces the generic RecordContent)
// renderers?: FieldRenderer[] — own field-type renderers (each carries kinds[])
// translations?: Partial<Record<'uk' | 'ru' | 'en', Record<string, unknown>>>
translations: { en: { 'myplugin.hello': 'Hello from myplugin' } },
});The host's federation loader applies each bundle once: it registers any
RecordView for vault/module, calls registerRenderer for every entry in
renderers, and merges translations into the global i18next instance. See
packages/web/federation.ts for the loader.
Vendor singleton model (importmap)
Plugin web bundles must not ship their own copy of React, i18next, or
@coffer-org/web-sdk — they share the host's. The host publishes one vendor bundle
under /vendor/* and wires an import map
so every bare specifier resolves to that single instance:
<script type="importmap">
{
"imports": {
"react": "/vendor/react.js",
"react-dom": "/vendor/react-dom.js",
"i18next": "/vendor/i18next.js",
"@coffer-org/web-sdk": "/vendor/web-sdk.js"
}
}
</script>This keeps a single React (so hooks/context work across bundles) and a single
i18next (so addResourceBundle merges into the one live instance). In dev, Vite
resolves these specifiers via its own aliases, so the import map is a prod-only
concern.
Supported vendor majors
| Vendor | Range |
| --------------------- | ---------------------------- |
| react / react-dom | 19 (>=19) |
| i18next | 26 (23+ host-compatible) |
A scaffolded plugin declares these as peer dependencies (see
create-coffer-plugin's package.json template), so it builds against the
host's instance instead of bundling its own.
Mismatched vendor versions warn, don't block
The import map always resolves a plugin's react / react-dom / i18next /
@coffer-org/web-sdk imports to the host's single instance — there is no runtime
version gate that refuses to load a bundle. A vendor-major mismatch surfaces as a
peer-dependency warning at install/build time, not a hard runtime failure: a
plugin built against an older major still loads against the host's instance and
runs as far as the shared API allows. Keep peers in range to avoid surprises; a
mismatch is a warning, not a block.
