6b3bb6a4ee
- 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
71 lines
3.9 KiB
Markdown
71 lines
3.9 KiB
Markdown
# 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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```js
|
|
export async function handleSomething(request) {
|
|
const { cookies } = await import('next/headers');
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**Pourquoi.** [`discover.server.js`](../src/core/modules/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`](../src/features/admin/protect.js), [`features/auth/actions.js`](../src/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`](../src/core/api/router.js), [`core/storage/api.js`](../src/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. |