refactor(api): update README and refactor api route registration

Restructure the core API to separate infrastructure routes from feature
routes. Key changes:

- Add `runtime.js` for global state: session resolver and feature route
  registry
- Add `file-response.js` for streaming file responses (storage endpoint)
- Remove feature routes (auth/users) from `core-routes.js`, keeping only
  true infrastructure routes (health, storage)
- Introduce `registerFeatureRoutes()` so features self-register during
  `initializeZen()` instead of being hardcoded in `core-routes.js`
- Add `UserFacingError` class to safely surface client-facing errors
  without leaking internal details
- Fix import path for `rateLimit.js` to use shared lib location
- Update README to reflect new two-step registration flow and clarify
  the role of `core-routes.js`
This commit is contained in:
2026-04-13 17:20:14 -04:00
parent a3921a0b98
commit 59fce3cd91
14 changed files with 515 additions and 380 deletions
+16 -3
View File
@@ -4,6 +4,9 @@
*/
import { discoverModules, registerExternalModules, startModuleCronJobs, stopModuleCronJobs } from '../../core/modules/index.js';
import { configureRouter, registerFeatureRoutes, clearFeatureRoutes, clearRouterConfig } from '../../core/api/index.js';
import { validateSession } from '../../features/auth/lib/session.js';
import { routes as authRoutes } from '../../features/auth/api.js';
import { step, done, warn, fail } from './logger.js';
// Use globalThis to persist initialization flag across module reloads
@@ -64,7 +67,13 @@ export async function initializeZen(config = {}) {
};
try {
// Step 1: Discover and register internal modules (from modules.registry.js)
// Step 1: Wire core feature dependencies into the API router.
// init.js is the composition root — the only place that wires features
// into core, keeping core/api/ free of static feature-level imports.
configureRouter({ resolveSession: validateSession });
registerFeatureRoutes(authRoutes);
// Step 2: Discover and register internal modules (from modules.registry.js)
result.discovery = await discoverModules();
const enabledCount = result.discovery.enabled?.length || 0;
@@ -77,7 +86,7 @@ export async function initializeZen(config = {}) {
warn(`ZEN: skipped ${skippedCount} module(s): ${result.discovery.skipped.join(', ')}`);
}
// Step 2: Register external modules from zen.config.js (if any)
// Step 3: Register external modules from zen.config.js (if any)
if (externalModules.length > 0) {
result.external = await registerExternalModules(externalModules);
@@ -86,7 +95,7 @@ export async function initializeZen(config = {}) {
}
}
// Step 3: Start cron jobs for all enabled modules (internal + external)
// Step 4: Start cron jobs for all enabled modules (internal + external)
if (!skipCron) {
result.cron = await startModuleCronJobs();
@@ -119,5 +128,9 @@ export function resetZenInitialization() {
// Cron system not available
}
// Clear router config and feature routes so they are re-registered on next initializeZen()
clearRouterConfig();
clearFeatureRoutes();
warn('ZEN: initialization reset');
}