docs(modules): update module discovery architecture to static manifest approach

- replace dynamic import strategy with static manifest generated by `zen-modules sync` cli
- add `zen-modules` binary entry point in `package.json`
- add `cli.js` implementing the `zen-modules sync` command
- update `discover.server.js` to consume static manifest instead of scanning at runtime
- update `index.js` to reflect new module registration flow
- update `init.js` to accept pre-resolved modules from manifest
- revise docs to document manifest format, sync triggers, and build requirements
This commit is contained in:
2026-04-25 14:24:56 -04:00
parent c9a3634fc9
commit 94ab6c36cb
7 changed files with 269 additions and 96 deletions
+17 -13
View File
@@ -21,13 +21,22 @@ import { routes as authRoutes } from '../../features/auth/api.js';
import { storageAccessPolicies } from '../../features/auth/storage-policies.js';
import { PERMISSION_DEFINITIONS } from '../../core/users/constants.js';
import { registerPermissions, clearRegisteredPermissions } from '../../core/users/permissions-registry.js';
import { discoverModules, validateModuleEnvVars } from '../../core/modules/discover.server.js';
import { registerModules, validateModuleEnvVars } from '../../core/modules/discover.server.js';
import { getRegisteredModules, clearRegisteredModules } from '../../core/modules/registry.js';
import { done, warn, fail } from './logger.js';
import { done, warn } from './logger.js';
const ZEN_INIT_KEY = Symbol.for('__ZEN_INITIALIZED__');
export async function initializeZen() {
/**
* @param {object} [options]
* @param {Array<{ name: string, exports: object }>} [options.modules]
* Manifeste de modules statique généré par `npx zen-modules sync` dans le
* projet consommateur. Le top-level await dans le manifeste a déjà déclenché
* `exports.register()` au moment de son import — initializeZen() se contente
* d'enregistrer chaque module dans le registre interne pour que `getRegisteredModules`
* retourne les bons objets côté serveur.
*/
export async function initializeZen({ modules = [] } = {}) {
if (typeof window !== 'undefined') {
return { skipped: true, reason: 'client-side' };
}
@@ -44,19 +53,14 @@ export async function initializeZen() {
registerStoragePolicies(storageAccessPolicies);
registerPermissions(PERMISSION_DEFINITIONS);
// Découverte et activation des modules @zen/module-*
await discoverModules();
const modules = getRegisteredModules();
validateModuleEnvVars(modules);
for (const mod of modules) {
// Activation des modules @zen/module-* via le manifeste statique fourni.
registerModules(modules);
const registered = getRegisteredModules();
validateModuleEnvVars(registered);
for (const mod of registered) {
if (Array.isArray(mod.manifest?.permissions)) {
registerPermissions(mod.manifest.permissions);
}
try {
await mod.register();
} catch (error) {
fail(`zen-modules: ${mod.manifest.name} register() threw — ${error.message}`);
}
}
done('ZEN: ready');