Contrato de módulo
Um módulo do painel é um arquivo que exporta um manifesto criado por defineModule. Esse manifesto descreve o módulo: o identificador, as coleções, as telas, as rotas públicas, as tarefas, os ganchos e o que o módulo publica no site. Tudo o que o módulo pode importar do núcleo sai de um só lugar, panel/contract/index.ts. Um módulo não importa nada de panel/core/ nem dos adaptadores.
Exemplo
Seção intitulada “Exemplo”O exemplo abaixo é um módulo mínimo que usa os tipos principais: o manifesto (ModuleDef), o que o módulo publica no site (ModuleSite), o tipo dos serviços do painel (PanelServices), um gancho (HookHandler) e o tipo do conteúdo publicado (SiteSnapshot). A primeira linha diz em que lugar do projeto o arquivo vai. O restante é o código.
import { collection, defineModule, field, type HookHandler, type ModuleDef, type ModuleSite, type PanelServices, type SiteSnapshot,} from "../../panel/contract/index";
/** Publica no site a contagem de recados; o site só lê o que o módulo entrega em `content`. */const site: ModuleSite = { content: async (services: PanelServices): Promise<Partial<SiteSnapshot>> => { const { items } = await services.store.list("recados", { perPage: 100 }); return { modules: { recados: { total: items.length } } }; },};
/** Gancho depois de criar um recado: aqui ele só confirma que a escrita passou. */const depoisDeCriar: HookHandler = async () => undefined;
/** Módulo mínimo com os tipos principais do contrato: manifesto, coleção, site e gancho. */export const recados: ModuleDef = defineModule({ id: "recados", version: "1.0.0", core: "^1.0.0", title: "Recados", description: "Recados curtos do site.", permissions: { read: ["admin", "editor"], write: ["admin", "editor"], }, permissionLabels: { read: "Ver recados", write: "Criar e editar recados", }, menu: [{ group: "organizacao", label: "Recados", collection: "recados", order: 40 }], collections: { recados: collection({ label: "Recados", labelSingular: "Recado", fields: { titulo: field.text({ label: "Título", required: true }), }, list: { columns: ["titulo"], search: ["titulo"], sort: { field: "titulo", direction: "asc" } }, permissions: { read: "read", create: "write", update: "write", delete: "write" }, }), }, hooks: { "recados.afterCreate": depoisDeCriar }, site,});
export { recados as module };Para o painel carregar o módulo, ele precisa estar no índice src/modules/index.ts. O slash module add faz isso para os módulos oficiais. Para um módulo seu, acrescente o módulo ao índice e reinicie o servidor.
O que o contrato exporta
Seção intitulada “O que o contrato exporta”O arquivo panel/contract/index.ts reúne as exportações por assunto.
- Manifesto:
defineModule, com os tiposModuleDefeModuleInput. A função confere o manifesto inteiro e junta todos os erros em uma mensagem: identidade, dependências, permissões, rótulos de atividade, grupos de menu, coleções, telas, menu, rotas, tarefas, configurações, extensões, ganchos, site e migrações. - Coleções:
collectionefield, com os tiposCollectionDef,CollectionInput,Fields,FieldDefeFieldKind. TambémuniqueConstraints,checkCollection,SYSTEM_FIELDS,COLLECTION_ACTIONSeGENERATED_SCREENS. - Telas e rotas:
screeneroute, comScreenDef,ScreenResult,ScreenNotice,RouteDef,RouteAccess,RouteContexteHttpMethod. - Rotas públicas:
publicGuard,readPublicBody,publicReply,PUBLIC_BODY_MAX_BYTESeHONEYPOT_FIELD, comPublicBody,PublicOutcomeePublicRefusal. - Tarefas:
jobeJOB_INTERVALS, comJobDef,JobContexteJobInterval. - Ganchos: os tipos
HookKey,HookHandler,HookEvent,HookOutcomeeDeleteReason. A chave tem a formacolecao.beforeAcaooucolecao.afterAcao, comoposts.afterCreate. - Site:
ModuleSite, comSiteBlockDef,SiteFormDefeSiteRedirect, e os tipos publicados (SiteSnapshot,PublishedPost,PublishedPage,PublishedCategory,PublishedTag,PublishedAuthore outros). - Banco:
MigrationDefeMigrationDb. Cada passo é ordenado e idempotente, e é desfeito por inteiro se falhar. - Serviços e contexto:
PanelServices,RequestContext,ScreenContext,SessionUser,RoleeROLES. Os papéis sãoadmin,editoreautor. Os serviços são as portas do núcleo: quem escreve o módulo recebeservices.store,services.mailere as outras (ver Portas). - Menu e permissões:
MenuItem,MenuGroup,CORE_MENU_GROUPS,permissionKey,RESERVED_MODULE_IDSeRESERVED_COLLECTIONS. - Mídia, SEO e autores:
libraryImage,ImageLibrary,publishedSeo,SEO_FIELDS,publishedAuthoreauthorSlug. - Extensões de módulos existentes:
extensionControls,extensionValuesemediaChoices. - Versões:
satisfies,isRangeeisVersion, usados para checar o campocore. - Utilidades:
listAll,LIST_PAGE_SIZE,slugify,canonicalPath,isLocalPatheisInvited. - Erros:
ManifestErroreMESSAGES.
Regras de identificação
Seção intitulada “Regras de identificação”- O
iddo módulo segue o padrão de minúsculas com hífen ([a-z][a-z0-9]*(-[a-z0-9]+)*). Ele também é o prefixo das permissões, das telas e da API do módulo. - Alguns ids e nomes de coleção são reservados ao núcleo, porque o núcleo usa as rotas
/painel/<id>,/api/<id>e/painel/api/<id>.RESERVED_MODULE_IDSeRESERVED_COLLECTIONSlistam quais. - O campo
coreé um intervalo de versões do núcleo, como^1.0.0. Oslash module addrecusa o módulo quando a versão do núcleo do projeto não está no intervalo.
Testar um módulo
Seção intitulada “Testar um módulo”O kit de conformidade confere a estrutura do módulo, as coleções, as permissões e as telas. O tutorial de conformidade mostra como rodar o kit. Os limites de instalação e de atualização dos módulos estão em Limites da 1.1.0.