Files
core/docs/PROJECT.md
T
hykocx 6b3bb6a4ee fix(storage): make next/headers import lazy in api.js to avoid module resolution failure
- replace top-level `import { cookies } from 'next/headers'` with lazy `await import('next/headers')` inside handler
- document the constraint in PROJECT.md: no top-level next/headers or next/navigation imports reachable from module register() chains
2026-04-25 13:08:14 -04:00

3.9 KiB

ZEN — Plan du projet

Une plateforme multi-usage construite sur l'essentiel, rien de plus, rien de moins.

ZEN est une plateforme admin Next.js qui sert de base à tout type d'application : site vitrine, système de facturation, espace cloud, boutique en ligne, ou n'importe quoi d'autre. L'admin est extensible, on y greffe ce qu'on veut, sans modifier le core.

Elle s'installe automatiquement dans n'importe quel projet Next.js via :

npx @zen/start

Le package principal est @zen/core. Il fournit toute l'infrastructure fondamentale : authentification, base de données, stockage, courriels, paiements, PDF, tâches planifiées, notifications. Chaque fonctionnalité est indépendante — les cores ne se connaissent pas entre eux. Le reste de la plateforme s'appuie sur chacun d'eux sans les coupler.


Principes directeurs

Core purity — Chaque core contient uniquement du code propre à son domaine. Aucune logique métier, aucune dépendance vers un autre core.

Minimal by default — Pas de fonctionnalité superflue. Si ce n'est pas nécessaire, ça n'existe pas.

Sécuritaire par défaut — Requêtes paramétrées, protection CSRF, limitation de débit, validation en entrée, erreurs opaques vers le client.

Performant — Connexions en pool, cache HTTP, génération différée des services.


Structure du projet

src/
  core/         # Infrastructure fondamentale — la base de tout
  features/     # Fonctionnalités de la palteforme utilisant les cores
  shared/       # Utilitaires, composants et styles partagés

Résumé des règles absolues

Domaine Règle
API Toutes les routes HTTP passent par core/api. Aucune autre route API.
Base de données Tout accès DB passe par core/database. Jamais de requêtes directes.
Courriels Tout envoi de courriel passe par core/email.
Cron Toutes les tâches planifiées s'enregistrent dans core/cron.
Stockage Tout accès fichier passe par core/storage.
Notifications Un seul système de toast dans toute l'app : core/toast.
Authentification Toute auth de site passe par features/auth.

Pas de next/headers ni next/navigation au top-level dans la chaîne register() des modules

Tout fichier qui peut être atteint depuis le register() d'un module externe (directement ou transitivement, via un barrel @zen/core/* qu'il importe) ne doit pas importer next/headers ou next/navigation au top-level. L'import doit être lazy à l'intérieur du handler :

export async function handleSomething(request) {
  const { cookies } = await import('next/headers');
  // ...
}

Pourquoi. discover.server.js charge chaque module via import(/* turbopackIgnore */ name) pour empêcher Turbopack/Webpack de bundler un nom dynamique. Conséquence : Node résout tout l'arbre d'imports transitifs en ESM natif, sans la condition react-server. Or next/headers (Next.js 15+) n'est exposé que via cette condition — un import top-level échoue alors avec Cannot find module 'next/headers'.

Conséquences pratiques.

  • Les fichiers qui ont besoin de Next.js au top-level (par ex. features/admin/protect.js, features/auth/actions.js) ne sont jamais réexportés par les barrels accessibles aux modules ; ils sont exposés via des sous-chemins dédiés dans package.json#exports (ex. @zen/core/features/admin/protect).
  • Pour les handlers qui restent dans un barrel exposé (ex. core/api/router.js, core/storage/api.js), cookies et compagnie sont importés en lazy à l'intérieur du handler.

Avant d'ajouter un import { cookies } from 'next/headers' à un fichier core, vérifier qu'aucun barrel exposé aux modules ne le tire transitivement. Sinon, garder l'import lazy.