Esta guía es para autores de plugins sandboxed escritos contra la forma anterior de definePlugin(). Recorre los cambios incompatibles en orden. Ninguno cambia cómo se comportan tus hooks o rutas en tiempo de ejecución; cambian cómo se declara, construye y publica el plugin.
Para la lista completa de cambios en cada paquete, consulta su entrada en la página de releases.
Cambios incompatibles
Renombrado: @emdash-cms/registry-cli ahora es @emdash-cms/plugin-cli
Las versiones anteriores distribuían la CLI como @emdash-cms/registry-cli, con un binario emdash-registry.
El paquete ahora es @emdash-cms/plugin-cli y el binario es emdash-plugin. El paquete antiguo ya no se publica.
¿Qué debo hacer?
Sustituye la dependencia:
pnpm remove @emdash-cms/registry-cli
pnpm add -D @emdash-cms/plugin-cli
Sustituye emdash-registry por emdash-plugin en todas partes donde lo llames. Cada subcomando conserva su nombre (bundle, publish, login, whoami, switch, validate), y se añaden init, build y dev. Consulta The plugin CLI.
Renombrado: los nombres de capabilities usan ortografía resource-first
Los manifiestos anteriores usaban nombres de capability como read:content y network:fetch. El manifiesto de authoring solo acepta los nombres actuales, aunque el runtime sigue normalizando nombres legacy en bundles ya publicados durante la ventana de compatibilidad.
¿Qué debo hacer?
Sustituye cada nombre legacy en el manifiesto:
| Nombre anterior | Nombre actual |
|---|---|
network:fetch | network:request |
network:fetch:any | network:request:unrestricted |
read:content | content:read |
write:content | content:write |
read:media | media:read |
write:media | media:write |
read:users | users:read |
email:provide | hooks.email-transport:register |
email:intercept | hooks.email-events:register |
page:inject | hooks.page-fragments:register |
Usa network:request con una lista allowedHosts no vacía. Usa network:request:unrestricted con una lista vacía solo cuando un operador elige el destino en tiempo de ejecución. Capabilities and security explica los permisos y reglas de red actuales.
Cambiado: los plugins sandboxed usan una anotación explícita SandboxedPlugin
Las versiones anteriores envolvían los hooks y rutas del plugin en definePlugin() importado de emdash, con los parámetros de cada handler anotados a mano.
Un plugin sandboxed asigna su definición a una constante tipada como SandboxedPlugin y exporta esa constante como default. Importa el tipo desde emdash/plugin con import type; el bundler borra esa importación. El mismo subpath también exporta los helpers de runtime ligeros pluginRoute() y pluginResponse(). TypeScript infiere el event y ctx de cada handler a partir del nombre del hook o ruta, así que los parámetros del handler no necesitan anotaciones. La anotación explícita también mantiene las declaraciones generadas portátiles bajo layouts aislados del gestor de paquetes.
¿Qué debo hacer?
Haz cuatro cambios en el archivo fuente del plugin. Sustituye la importación:
import { definePlugin, type ContentHookEvent, type PluginContext } from "emdash";
import type { SandboxedPlugin } from "emdash/plugin";
Sustituye el wrapper definePlugin() por una constante tipada explícitamente:
export default definePlugin({ /* hooks, routes */ });
const plugin: SandboxedPlugin = { /* hooks, routes */ };
export default plugin;
Elimina las anotaciones de parámetros de cada handler:
handler: async (event: ContentHookEvent, ctx: PluginContext) => {
handler: async (event, ctx) => {
El resultado es un objeto exportado por defecto:
import type { SandboxedPlugin } from "emdash/plugin";
const plugin: SandboxedPlugin = {
hooks: {
"content:beforeSave": {
handler: async (event, ctx) => {
return event.content;
},
},
},
};
export default plugin;
Para nombrar un tipo de evento en una función auxiliar, impórtalo desde emdash/plugin:
import type { ContentHookEvent, PluginContext } from "emdash/plugin";
El event de un handler es siempre el tipo canónico de ese hook. Anotar un handler con una interfaz más estrecha ya no pasa el type-check. Valida en tiempo de ejecución cualquier campo del que dependas con un chequeo typeof o un guard, que es el enfoque correcto para datos que vienen de fuera del sistema de tipos.
Cambiado: un plugin es un src/plugin.ts más emdash-plugin.jsonc
Las versiones anteriores dividían un plugin en dos archivos: src/index.ts devolvía un PluginDescriptor (id, version, capabilities, storage, entrypoint), y src/sandbox-entry.ts contenía los hooks y rutas.
Un plugin es ahora un archivo de runtime, src/plugin.ts (hooks y rutas), y un manifiesto editado a mano, emdash-plugin.jsonc (identidad y el contrato de confianza). Los campos entrypoint y format han desaparecido; el build los cablea.
¿Qué debo hacer?
Mueve los hooks y rutas a src/plugin.ts usando la forma de arriba. Mueve los metadatos del descriptor a emdash-plugin.jsonc junto a package.json. El id del descriptor se convierte en el slug del manifiesto; capabilities, allowedHosts y storage mantienen su forma; version se lee de package.json, así que omítelo.
El siguiente ejemplo muestra el equivalente en manifiesto de un descriptor que declaraba una colección de storage:
{
"$schema": "./node_modules/@emdash-cms/plugin-cli/schemas/emdash-plugin.schema.json",
"slug": "plugin-hello",
"publisher": "did:plc:abc123def456",
"license": "MIT",
"author": { "name": "Jane Doe", "url": "https://example.com" },
"security": { "email": "security@example.com" },
"capabilities": [],
"allowedHosts": [],
"storage": { "events": { "indexes": ["timestamp"] } }
}
Consulta The plugin manifest para cada campo, y Publisher pinning para el campo publisher.
En package.json, apunta la exportación "./sandbox" al archivo de runtime construido:
"./sandbox": "./dist/sandbox-entry.mjs"
"./sandbox": "./dist/plugin.mjs"
Añade el manifiesto a files para que se distribuya con el paquete:
"files": ["dist"]
"files": ["dist", "emdash-plugin.jsonc"]
Cambiado: construir con emdash-plugin build
Las versiones anteriores construían los dos archivos fuente con un script tsdown escrito a mano.
emdash-plugin build lee emdash-plugin.jsonc y src/plugin.ts y emite los artefactos de dist/. emdash-plugin dev observa y reconstruye.
¿Qué debo hacer?
Sustituye el script de build y añade un script de watch:
"scripts": {
"build": "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean"
"build": "emdash-plugin build",
"dev": "emdash-plugin dev"
}
Luego valida y construye:
emdash-plugin validate
emdash-plugin build
Eliminado: exportaciones de tipos y funciones de formato estándar desde emdash
Las versiones anteriores exportaban StandardPluginDefinition, StandardHookHandler, StandardHookEntry, StandardRouteHandler, StandardRouteEntry y la función isStandardPluginDefinition desde emdash.
Están eliminadas. Eran alias auxiliares para la forma anterior de definePlugin.
¿Qué debo hacer?
Usa SandboxedPlugin desde emdash/plugin para el mismo propósito. La definición exportada de un plugin sandboxed ya está tipada por su anotación SandboxedPlugin, así que no hay reemplazo para isStandardPluginDefinition; identifica un plugin por su estructura ({ hooks?, routes? }) si lo necesitas.
Renombrado: los handles del sandbox-runner usan SandboxedPluginInstance
Esto solo afecta a autores de un SandboxRunner personalizado, como @emdash-cms/cloudflare. La mayoría de autores de plugins puede omitirlo.
El tipo orientado al autor SandboxedPlugin está disponible desde el punto de entrada de authoring emdash/plugin. El handle de runtime que devuelve SandboxRunner.load se exporta desde emdash como SandboxedPluginInstance.
¿Qué debo hacer?
Si importas SandboxedPlugin desde emdash para tipar un sandbox runner o mantener handles de plugins en runtime, cambia la importación a SandboxedPluginInstance:
import type { SandboxedPlugin } from "emdash";
import type { SandboxedPluginInstance } from "emdash";
Informa a tus usuarios
Los sitios que instalen tu plugin también deben cambiar su importación. Señálales la nueva forma: quita las llaves y el ().
import { helloPlugin } from "@my-org/plugin-hello";
import hello from "@my-org/plugin-hello";
export default defineConfig({
integrations: [
emdash({
sandboxed: [helloPlugin()],
sandboxed: [hello],
}),
],
});
Si tu plugin aceptaba configuración a través de su factory, mueve esa configuración a una página de ajustes de administración y léela desde ctx.settings. Los descriptores de plugins sandboxed son objetos planos y no pueden recibir opciones de constructor. Consulta Settings.
Verificar el plugin migrado
Ejecuta las pruebas del plugin, valida el manifiesto de authoring y ejecuta las comprobaciones completas de build y bundle:
pnpm test
pnpm exec emdash-plugin validate
pnpm exec emdash-plugin build
pnpm exec emdash-plugin bundle --validate-only
Luego instala el paquete local en un sitio de desarrollo y ejercita cada hook y ruta migrados. El build puede confirmar sus nombres y formas, pero no puede confirmar que una ruta devuelva los datos previstos o que un hook preserve el contenido correctamente.