Saltar a contenido

El manifest

El manifest es el contrato declarativo del módulo: identidad, versión, tablas, permisos, dependencias y eventos. Se define en src/manifest.ts y se valida con el schema Zod de @didacta/core-kernel en tiempo de import — un manifest inválido impide arrancar la API, así que los errores se ven al instante.

Ejemplo mínimo (hello-world)

import { parseModuleManifest, type ModuleManifest } from '@didacta/core-kernel';

export const manifest: ModuleManifest = parseModuleManifest({
  name: 'mod.hello-world',
  displayName: 'Hello World',
  description: 'Módulo de ejemplo. Plantilla de referencia para nuevos módulos.',
  version: '1.0.0',
  author: 'VA360 LABS',
  license: 'Proprietary',
  category: 'example',
  coreVersionRequired: '^1.0.0',
  tablePrefix: 'mod_helloworld_',
  permissions: ['hello-world.greeting.read'],
  eventsEmitted: ['hello-world.greeting.requested'],
  eventsConsumed: [],
  apiNamespace: '/modules/hello-world',
});

Campos

Campo Obligatorio Formato Qué es
name mod.<slug> (^mod\.[a-z0-9-]+$) Identificador único del módulo.
displayName texto Nombre visible en el panel.
description texto Qué hace el módulo, en una frase.
version SemVer X.Y.Z Versión del módulo.
coreVersionRequired rango SemVer (^1.0.0) Versión del core que necesita; se valida al registrar.
tablePrefix mod_<slug>_ (^mod_[a-z0-9_]+_$) Prefijo de todas sus tablas.
apiNamespace empieza por / (convención: /modules/<slug>) Prefijo de sus endpoints; lo usa el gating por tenant.
category texto 'core' marca el módulo como no desactivable. Otras usadas: ai, engagement, compliance, live, integration, migration.
dependencies.modules { name, version }[] Dependencias duras: sin ellas el módulo no arranca (error de registro).
dependencies.optionalModules { name, version }[] Dependencias blandas: documentan integraciones opcionales.
permissions string[] Permisos que define el módulo (<slug>.<recurso>.<acción>).
eventsEmitted string[] Eventos de dominio que publica. Todo evento publicado debe estar aquí.
eventsConsumed string[] Eventos de otros módulos a los que se suscribe.
hooksExposed { name, description?, async }[] Puntos de extensión que ofrece a otros módulos.
author, license texto Metadatos.

Campos declarativos sin efecto en runtime (hoy)

El schema admite también roles, defaultConfig, uiExtensions, pages y hooksConsumed, pero actualmente ningún componente los consume: la UI in-tree se declara con el registro web y los roles del sistema son fijos. Puedes declararlos como documentación, sabiendo que aún no tienen efecto.

Ejemplo completo con dependencias y eventos (access-groups)

export const manifest: ModuleManifest = parseModuleManifest({
  name: 'mod.access-groups',
  displayName: 'Grupos de acceso',
  description: 'Grupos configurables que otorgan acceso a un set de cursos…',
  version: '1.0.0',
  author: 'VA360 LABS',
  license: 'Proprietary',
  category: 'core',
  coreVersionRequired: '^1.0.0',
  tablePrefix: 'mod_access_groups_',
  permissions: [
    'access_groups.group.read',
    'access_groups.group.manage',
    'access_groups.member.manage',
  ],
  dependencies: {
    modules: [                                    // duras
      { name: 'mod.courses',  version: '^1.0.0' },
      { name: 'mod.learning', version: '^1.0.0' },
    ],
    optionalModules: [                            // blandas
      { name: 'mod.payment-connections', version: '^1.0.0' },
      { name: 'mod.subscriptions',       version: '^1.0.0' },
    ],
  },
  eventsEmitted: [],
  eventsConsumed: [
    'courses.course.published',
    'payment_connections.user_tier.changed',
    'subscriptions.membership.activated',
    'subscriptions.subscription.activated',
    'subscriptions.subscription.canceled',
    'subscriptions.subscription.unpaid',
  ],
  apiNamespace: '/modules/access-groups',
});

Declarar un hook

El único módulo que expone un hook hoy es mod.courses — sirve de patrón:

hooksExposed: [
  {
    name: 'courses.publish.validate',
    description: 'Permite que otros módulos añadan validaciones antes de publicar un curso (ej. mod.fundae verifica objetivos y duración).',
    async: true,
  },
],

Cómo se consume y se dispara, en Eventos y hooks.

El segundo manifest: module.json

Cada módulo lleva además un module.json en su raíz, usado por el linter scripts/module-doctor.ts (no por el runtime). Campos que exige: name, version, edition (siempre community), coreVersionRequired, tablePrefix, apiNamespace. Los module.json marketplace-style (con vendor/isolation/http/didacta, el shape expandido que valida el schema strict del host) son la excepción: no llevan edition — el schema del host rechaza esa key al instalar. Mantenlo coherente con src/manifest.ts — el doctor detecta divergencias. Ver Validación.