@nodefony/devkit 10.0.0-beta.2 → 10.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -5
- package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
- package/dist/index.js +24 -11
- package/dist/nodefony/config/config.js +16 -137
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/controllers/DevkitController.js +7 -14
- package/dist/nodefony/controllers/McpController.js +27 -163
- package/dist/nodefony/controllers/ProfileSessionController.js +47 -0
- package/dist/nodefony/mcp/observe.js +235 -0
- package/dist/nodefony/mcp/profileReport.js +242 -0
- package/dist/nodefony/mcp/profileRoute.js +521 -0
- package/dist/nodefony/mcp/shotClient.js +84 -0
- package/dist/nodefony/mcp/shutdown.js +44 -0
- package/dist/nodefony/mcp/tools.js +696 -0
- package/dist/nodefony/service/DevkitService.js +93 -29
- package/dist/nodefony/src/card.js +1 -2
- package/dist/nodefony/src/errors/DevkitError.js +1 -2
- package/dist/nodefony/src/profileSession.js +14 -0
- package/dist/types/index.d.ts +15 -1
- package/dist/types/nodefony/config/config.d.ts +1 -1
- package/dist/types/nodefony/controllers/McpController.d.ts +13 -17
- package/dist/types/nodefony/controllers/ProfileSessionController.d.ts +31 -0
- package/dist/types/nodefony/interfaces/IDevkitService.d.ts +22 -1
- package/dist/types/nodefony/mcp/observe.d.ts +83 -0
- package/dist/types/nodefony/mcp/profileReport.d.ts +138 -0
- package/dist/types/nodefony/mcp/profileRoute.d.ts +87 -0
- package/dist/types/nodefony/mcp/shotClient.d.ts +53 -0
- package/dist/types/nodefony/mcp/shutdown.d.ts +25 -0
- package/dist/types/nodefony/mcp/tools.d.ts +109 -0
- package/dist/types/nodefony/service/DevkitService.d.ts +41 -4
- package/dist/types/nodefony/src/profileSession.d.ts +12 -0
- package/docs/index.md +217 -20
- package/package.json +17 -12
- package/skills/nodefony-add-crud/SKILL.md +5 -0
- package/skills/nodefony-add-realtime-channel/SKILL.md +66 -9
- package/skills/nodefony-add-service/SKILL.md +14 -3
- package/skills/nodefony-authentication/SKILL.md +234 -0
- package/skills/nodefony-browser/SKILL.md +65 -21
- package/skills/nodefony-browser/references/socket.md +13 -4
- package/skills/nodefony-browser/references/sondes.md +13 -0
- package/skills/nodefony-browser/scripts/audit.mjs +59 -10
- package/skills/nodefony-browser/scripts/inspect.mjs +123 -5
- package/skills/nodefony-browser/scripts/lib/browser.mjs +112 -6
- package/skills/nodefony-browser/scripts/lib/probes.mjs +219 -7
- package/skills/nodefony-browser/scripts/socket.mjs +70 -4
- package/skills/nodefony-browser/scripts/watch.mjs +101 -0
- package/skills/nodefony-dev/SKILL.md +78 -82
- package/skills/nodefony-dev/scripts/docs.mjs +24 -8
- package/skills/nodefony-devops/SKILL.md +98 -74
- package/skills/nodefony-devops/references/compose.md +8 -0
- package/skills/nodefony-devops/references/dimensionnement.md +122 -0
- package/skills/nodefony-devops/references/frontal.md +282 -48
- package/skills/nodefony-devops/references/kubernetes.md +41 -14
- package/skills/nodefony-devops/references/secrets.md +30 -5
- package/skills/nodefony-devops/references/variables.md +66 -31
- package/skills/nodefony-migrate-schema/SKILL.md +5 -2
- package/skills/nodefony-migrate-schema/references/verdicts.md +20 -19
- package/skills/nodefony-protect-route/SKILL.md +124 -21
- package/skills/nodefony-security-review/SKILL.md +127 -0
- package/skills/nodefony-security-review/scripts/review-diff.mjs +1090 -0
- package/skills/nodefony-troubleshoot/SKILL.md +225 -0
- package/skills/nodefony-troubleshoot/scripts/log-errors.mjs +465 -0
- package/skills/nodefony-upgrade/SKILL.md +130 -0
- package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
est-il fini", "je ne trouve rien sur ce sujet", "ce n'est pas documenté", "je touche au
|
|
15
15
|
frontend", "mon composant charge des données", "mon écran n'affiche rien".
|
|
16
16
|
metadata:
|
|
17
|
-
version: 1.
|
|
17
|
+
version: 1.3.0
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# nodefony-dev — développer dans cette application sans rien inventer
|
|
@@ -45,15 +45,19 @@ de son geste, et ceux-là ne se devinent pas — ils se paient.
|
|
|
45
45
|
| -------------------------------------------------------------------- | ------------------------------- |
|
|
46
46
|
| Une ressource : table, validation, service, REST et WebSocket | `nodefony-add-crud` |
|
|
47
47
|
| Une logique métier réutilisable, hors de tout controller | `nodefony-add-service` |
|
|
48
|
-
| Un flux temps réel, un canal, un abonnement
|
|
49
|
-
|
|
|
48
|
+
| Un flux temps réel (WebSocket ou SSE), un canal, un abonnement | `nodefony-add-realtime-channel` |
|
|
49
|
+
| Se connecter : /login, second facteur, retour après connexion, jeton | `nodefony-authentication` |
|
|
50
|
+
| Réserver une route ou une page à des rôles, ouvrir à un partenaire | `nodefony-protect-route` |
|
|
50
51
|
| Changer une entité déjà en base, ou déployer un schéma changé | `nodefony-migrate-schema` |
|
|
51
52
|
| Conclure quoi que ce soit d'un ÉCRAN — affichage, contraste, console | `nodefony-browser` |
|
|
53
|
+
| Quelque chose qui marchait ne marche plus : démarrage, 404, journal | `nodefony-troubleshoot` |
|
|
54
|
+
| Relire la sécurité de ton diff AVANT de commiter | `nodefony-security-review` |
|
|
55
|
+
| Monter Nodefony de version, ou une montée qui a laissé des rouges | `nodefony-upgrade` |
|
|
52
56
|
|
|
53
57
|
Aucune ligne ne correspond ? Alors seulement, continue ici. Et si ta tâche est EXACTEMENT celle
|
|
54
58
|
d'un spécialiste, va droit à lui : il n'y a pas à passer par cette page d'abord.
|
|
55
59
|
|
|
56
|
-
`ls .agents/skills/` liste ceux que TON projet a reçus ; `npx nodefony
|
|
60
|
+
`ls .agents/skills/` liste ceux que TON projet a reçus ; `npx nodefony upgrade` les remet à jour
|
|
57
61
|
après une montée de version. Ce sont des **pointeurs** : le contenu vit dans `node_modules` et
|
|
58
62
|
suit la version installée — les éditer ne servirait à rien.
|
|
59
63
|
|
|
@@ -124,6 +128,11 @@ Elle répond **toujours** — application pas encore construite, terminal sans a
|
|
|
124
128
|
d'environnement : elle ne lit que des fichiers. Elle le DIT alors (« modules installés », pas
|
|
125
129
|
« chargés ») et renvoie à `inspect modules` pour ce qui est vraiment monté.
|
|
126
130
|
|
|
131
|
+
**`rg` ou outil MCP ?** Question étroite, format connu (un motif, un fichier) → `rg`, bien moins
|
|
132
|
+
cher. Question ouverte sur l'application qui TOURNE (démarrage sain ? quelle route casse ?) →
|
|
133
|
+
`nodefony_check` (= `nodefony doctor`), `nodefony_observe` : un appel au lieu de plusieurs `rg`,
|
|
134
|
+
et des faits qu'aucun fichier ne porte.
|
|
135
|
+
|
|
127
136
|
```bash
|
|
128
137
|
npx nodefony inspect routes --json # routes réelles (chemin MONTÉ, méthodes, controller)
|
|
129
138
|
npx nodefony inspect services --json # services enregistrés, et le module qui les porte
|
|
@@ -152,7 +161,8 @@ npx nodefony symbols --module @nodefony/http # toute la surface exportée d'un
|
|
|
152
161
|
```
|
|
153
162
|
|
|
154
163
|
Le graphe symbolique de tout le framework est livré avec le paquet `nodefony` : la réponse ne
|
|
155
|
-
dépend ni d'un serveur, ni d'un build, ni de ta connexion.
|
|
164
|
+
dépend ni d'un serveur, ni d'un build, ni de ta connexion. Celui de TON code (`.ai/symbols.json`),
|
|
165
|
+
`npm run dev` le tient à jour — ou `npx nodefony symbols --generate` ; les deux sont lus ensemble. Va y chercher un symbole AVANT d'ouvrir
|
|
156
166
|
un `.d.ts` — et avant, surtout, d'inventer une signature.
|
|
157
167
|
|
|
158
168
|
**Si la commande te résiste, répare l'APPEL — ne te rabats pas sur les sources.** C'est le réflexe
|
|
@@ -165,31 +175,42 @@ tube, puis remets ton filtre.
|
|
|
165
175
|
(`npx nodefony ai:mcp` écrit `.mcp.json`) — mêmes réponses, et tes propres modules peuvent publier
|
|
166
176
|
les leurs. La porte est une ROUTE : elle n'existe que serveur démarré, et un client qui la trouve
|
|
167
177
|
éteinte la marque en échec pour toute sa session — démarre l'application D'ABORD, ta session
|
|
168
|
-
ENSUITE.
|
|
178
|
+
ENSUITE. Aucun serveur ne peut tourner (CI, agent dans un runner) ? Ton client lance lui-même
|
|
179
|
+
`npx nodefony ai:stdio` et lui parle sur l'entrée et la sortie standard : mêmes outils, mais le
|
|
180
|
+
code du démarrage, sans rechargement à chaud ni trafic réel à mesurer. Tout le reste (déclarer un outil métier, le réserver à des scopes, l'autorisation OAuth) :
|
|
169
181
|
`node_modules/@nodefony/devkit/docs/index.md`.
|
|
170
182
|
|
|
183
|
+
**Ta tâche est d'OFFRIR les outils de l'application à un agent** (« expose ceci par MCP ») ? Ce
|
|
184
|
+
n'est pas la porte ci-dessus, qui sert l'outillage en développement : c'est une porte de
|
|
185
|
+
l'APPLICATION, et elle se génère — n'écris pas le protocole à la main :
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npx nodefony create controller mcp --kind mcp # porte locale : la machine seulement
|
|
189
|
+
npx nodefony create controller mcp --kind mcp --expose oauth # production : jeton exigé (401 sans)
|
|
190
|
+
npx nodefony create controller mcp --kind mcp --expose mixte # outils publics sans jeton, les autres réservés à un scope
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Elle naît avec deux outils d'exemple (`nodefony/mcp/mcp-tools.ts`), sa définition
|
|
194
|
+
(`defineMcpGate`, `nodefony/mcp/mcp-gate.ts`) et son test. Ajoute tes outils à la LISTE de la
|
|
195
|
+
porte : elle publie ce qu'elle choisit, rien d'autre. Mode d'emploi :
|
|
196
|
+
`node_modules/@nodefony/ai/docs/mcp.md` § « Monter une porte ».
|
|
197
|
+
|
|
171
198
|
### L'environnement : ne devine JAMAIS, demande
|
|
172
199
|
|
|
173
200
|
```bash
|
|
174
|
-
npx nodefony env #
|
|
201
|
+
npx nodefony env # valeur EFFECTIVE de chaque variable, sa PROVENANCE
|
|
175
202
|
```
|
|
176
203
|
|
|
177
204
|
**Encadre toute modification de configuration par cette commande** : une fois AVANT, pour savoir ce
|
|
178
205
|
qui s'applique et d'où ça vient ; une fois APRÈS, pour prouver que ta valeur est celle qui gagne.
|
|
179
|
-
Lire
|
|
180
|
-
supposer, et une supposition fausse ne se voit qu'en production. La commande ne boote rien, donc
|
|
206
|
+
Lire `.env` toi-même donne un contenu ; ce que l'application voit dépend aussi du shell et de
|
|
207
|
+
`env.ts` — tu ne peux que le supposer, et une supposition fausse ne se voit qu'en production. La commande ne boote rien, donc
|
|
181
208
|
elle répond aussi quand l'application ne démarre plus.
|
|
182
209
|
|
|
183
|
-
**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
process.env > .env.<déploiement>.local > .env.<mode>.local > .env.local
|
|
188
|
-
> .env.<déploiement> > .env.<mode> > .env
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
`<mode>` = `NODE_ENV` · `<déploiement>` = `APP_ENV` (plus spécifique, donc plus fort). Les `*.local`
|
|
192
|
-
ne sont jamais committés : les secrets y vont, et nulle part ailleurs.
|
|
210
|
+
**Deux fichiers** : `.env` = valeurs du POSTE (secrets de dev compris), **jamais commité** ;
|
|
211
|
+
`.env.example` = notice commitée, **jamais chargée**. `process.env` > `.env`, rien d'autre. La
|
|
212
|
+
production n'a **aucun** fichier (orchestrateur ; réglages non secrets dans `nodefony.config.ts`).
|
|
213
|
+
Un module n'a pas de `.env`. N'écris JAMAIS `.env.local` ni `.env.<mode>` : refusés au démarrage (78).
|
|
193
214
|
|
|
194
215
|
| Forme | Ce que c'est | Où c'est déclaré |
|
|
195
216
|
| ------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------ |
|
|
@@ -261,6 +282,13 @@ une table à part — et ce nom est ce que `stop` accepte. Donc « aucune instan
|
|
|
261
282
|
REFUSÉE, avec un code de sortie non nul et rien d'arrêté — **lis ce code**, un refus ressemble
|
|
262
283
|
sinon à un succès.
|
|
263
284
|
|
|
285
|
+
**Le plein écran n'est pas pour toi — et il est le DÉFAUT sous macOS et Linux.** Il fige la barre
|
|
286
|
+
d'état en bas, capte le clavier et la souris (molette, PgUp/PgDn, Ctrl+C devient une touche) :
|
|
287
|
+
c'est un écran pour un humain. Il s'active dès que le terminal répond à une sonde, et un
|
|
288
|
+
pseudo-terminal (tmux, un outil qui t'en fournit un) y répond. Nodefony ne devine jamais qu'un
|
|
289
|
+
agent est aux commandes : si tu lances `development` toi-même, ajoute **toujours** `--no-ui`
|
|
290
|
+
(ou pose `NF_DEV_UI=0`), ou `--output plain` pour une sortie sans séquences d'écran.
|
|
291
|
+
|
|
264
292
|
**Pour faire tourner une suite contre un serveur, prends `--no-watch`.** Le mode développement
|
|
265
293
|
relance le serveur dès qu'un fichier bouge : pendant un run, le redémarrage coupe les connexions
|
|
266
294
|
sous les tests, et le rouge qui en sort accuse le code alors que le fautif est le décor.
|
|
@@ -283,8 +311,9 @@ routes auth` ne rend que les routes dont le chemin, le nom, le contrôleur, l'ac
|
|
|
283
311
|
4. **Édite le moins possible**, et regroupe : toutes les modifications serveur d'une même
|
|
284
312
|
fonctionnalité, PUIS un seul cycle de reconstruction. Le frontend passe en HMR, zéro
|
|
285
313
|
redémarrage.
|
|
286
|
-
5. **Prouve.** `npm run verify` — une seule commande : types, style,
|
|
287
|
-
ordre. Elle s'arrête au premier rouge, et
|
|
314
|
+
5. **Prouve.** `npm run verify` — une seule commande : types, style, mise en forme,
|
|
315
|
+
`.env.example`, tests, build, câblage, dans cet ordre. Elle s'arrête au premier rouge, et
|
|
316
|
+
**ce rouge est ta tâche suivante**.
|
|
288
317
|
|
|
289
318
|
**Le point d'arrêt qu'on rate** : `npm test` seul ne prouve pas que ça compile — vitest
|
|
290
319
|
n'inspecte aucun type. Une application peut être verte et ne pas compiler.
|
|
@@ -304,50 +333,19 @@ ici — ils changent tous les trimestres ; raisonne en poids.
|
|
|
304
333
|
|
|
305
334
|
## 7. Les pièges du serveur
|
|
306
335
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
- **Une
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
générateur, qui, lui, déclare. `npx nodefony doctor` la NOMME.
|
|
315
|
-
- **Une clé de configuration mal orthographiée est retirée en silence.** `satisfies` sur le
|
|
316
|
-
fragment n'est pas décoratif : sans lui, la faute compile, puis la validation écarte la clé et
|
|
317
|
-
le module démarre sur son défaut. Personne ne le voit.
|
|
336
|
+
Ceux qu'on pose en ÉCRIVANT. Ceux qu'on rencontre quand quelque chose qui marchait ne marche plus
|
|
337
|
+
— démarrage qui échoue, route en 404, refus inattendu, journal en erreur, tests rouges d'un coup —
|
|
338
|
+
ont leur skill, qui part du symptôme : **`nodefony-troubleshoot`**.
|
|
339
|
+
|
|
340
|
+
- **Une clé de configuration mal orthographiée ARRÊTE le démarrage** (code 78), en la nommant.
|
|
341
|
+
`satisfies` sur un fragment extrait n'est pas décoratif pour autant : il te la signale à la
|
|
342
|
+
compilation, au lieu du prochain démarrage.
|
|
318
343
|
- **La config d'un module est FIGÉE après le démarrage** : l'écrire lève `TypeError`. Une variante
|
|
319
344
|
par requête = `overlayConfig()` (clés de la liste blanche du module), lue par `useConfig()`.
|
|
320
345
|
- **Tu lis une liste sans la BORNER.** Un `find` sans limite matérialise la table entière —
|
|
321
346
|
indolore sur les quelques lignes du poste de développement, fatal sur les dizaines de milliers
|
|
322
347
|
de la production. Le service d'une entité hérite `findPage({ limit: 25 })`.
|
|
323
348
|
|
|
324
|
-
- **Des dizaines de tests d'intégration rouges d'un coup** (`ECONNREFUSED`) : ils FRAPPENT un
|
|
325
|
-
serveur, ils ne le lancent pas — il est éteint. `npx nodefony status` d'abord ; en e2e, laisse
|
|
326
|
-
la commande gérer le cycle.
|
|
327
|
-
- **Ta route NEUVE répond 404 et le `dist/` est à jour** : elle n'est pas montée où tu crois. Le
|
|
328
|
-
chemin réel est le PRÉFIXE de son controller suivi du `path` de la route — une action
|
|
329
|
-
`path: "/widget"` dans un controller `@controller("/api")` répond sur `/api/widget`.
|
|
330
|
-
`npx nodefony inspect routes --json` donne le chemin MONTÉ.
|
|
331
|
-
- **TOUT répond 404, même les routes du gabarit** : un AUTRE serveur tient les ports — ou LE TIEN
|
|
332
|
-
a glissé, le port voulu étant pris. `npx nodefony status` montre les ports RÉELS, pas ceux que
|
|
333
|
-
tu as configurés, et NOMME le projet voisin.
|
|
334
|
-
- 🔴 **`nodefony <commande>` échoue là où `npm run dev` réussit, sur la MÊME application** : DEUX
|
|
335
|
-
paquets `nodefony` tournent dans le processus — un binaire lié ailleurs (installation globale,
|
|
336
|
-
`npm link`) exécute un noyau pendant que l'application importe le sien. Chaque copie a ses
|
|
337
|
-
propres classes, son contexte de requête et ses registres d'injection : un module construit à la
|
|
338
|
-
frontière perd son container SANS la moindre erreur — il figure dans la liste des modules et ne
|
|
339
|
-
fait rien. ⚠️ Les deux copies peuvent être la MÊME version ; deux fichiers distincts suffisent,
|
|
340
|
-
il n'y a aucune incohérence à repérer. `npm ls nodefony` liste les copies (« deduped » partout =
|
|
341
|
-
une seule) ; `NF_CLI_DEBUG=1 nodefony --version` dit quel CLI s'exécute.
|
|
342
|
-
- **`localhost` et `127.0.0.1` te jouent des tours** : ce sont deux ORIGINES distinctes — cookies,
|
|
343
|
-
cache et passkeys ne les partagent pas. Une seule origine en développement, partout, URL ouverte
|
|
344
|
-
comme callbacks.
|
|
345
|
-
|
|
346
|
-
- **Les routes authentifiées plafonnent quand le reste tient la charge** : le stockage de session
|
|
347
|
-
par défaut est SYNCHRONE — chaque reprise bloque la boucle d'événements. Compare une route
|
|
348
|
-
anonyme et une route authentifiée AVANT d'accuser TLS ou le pare-feu ; passe le stockage sur
|
|
349
|
-
redis pour la charge.
|
|
350
|
-
|
|
351
349
|
### Ce qui tue le processus, pas la requête
|
|
352
350
|
|
|
353
351
|
- 🔴 **Une promesse lancée sans `await` porte TOUJOURS un `.catch()`.** Audit, courriel,
|
|
@@ -361,24 +359,6 @@ Chacun a déjà coûté au moins une heure à quelqu'un. Les quatre premiers son
|
|
|
361
359
|
- **Un `PATCH` au corps vide se refuse en `400`.** Le laisser passer produit un `updateOne({})`,
|
|
362
360
|
qui finit en `500 « No values to set »` — une erreur serveur pour une faute de client.
|
|
363
361
|
|
|
364
|
-
### Quand ça ne va pas, et que le message ne suffit pas
|
|
365
|
-
|
|
366
|
-
- **Un test vert seul et rouge en suite accuse une RESSOURCE PARTAGÉE**, pas ton code : base ou
|
|
367
|
-
index réutilisé, port, store jamais purgé, assertion `count === N` qui suppose une table
|
|
368
|
-
vierge. Lance deux fichiers ENSEMBLE pour isoler la paire, puis cloisonne. **Ne sérialise
|
|
369
|
-
jamais la suite** pour faire passer le rouge : tu masques la cause et tu paies la lenteur à
|
|
370
|
-
chaque exécution.
|
|
371
|
-
- **Une suite lancée contre un serveur en `production` reçoit `404` partout** : les modules
|
|
372
|
-
`policy:"dev"` n'y sont pas. `NF_WITH_DEV_MODULES=1` déroge pour 30 minutes
|
|
373
|
-
(`NF_WITH_DEV_MODULES_TTL_MIN`, 4 h au plus), et le `CRITIC « arrêt automatique … dérogation »`
|
|
374
|
-
du journal est cette garde qui se referme — pas une panne.
|
|
375
|
-
- **Ton application a démarré AMPUTÉE et tout a l'air sain** — base injoignable, module écarté
|
|
376
|
-
par sa politique. Seul `npx nodefony doctor` le dit, en relisant `var/last-boot.json`.
|
|
377
|
-
⚠️ Ce bilan a un ÂGE : il décrit le **dernier démarrage**, et une commande console (`inspect`,
|
|
378
|
-
une commande de module) ne l'écrase pas. C'est voulu — lis la date avant d'en conclure.
|
|
379
|
-
- **L'application ne démarre plus et le superviseur avale la sortie** :
|
|
380
|
-
`NF_DEV_CHILD=1 npx nodefony development` lance l'enfant seul et montre le crash brut.
|
|
381
|
-
|
|
382
362
|
## 8. Données et fichiers — les façades qu'on ne recompose pas
|
|
383
363
|
|
|
384
364
|
- **Un cache ne remplace pas une base : ils se COMPLÈTENT.** Chaque adaptateur déclare les
|
|
@@ -386,6 +366,14 @@ Chacun a déjà coûté au moins une heure à quelqu'un. Les quatre premiers son
|
|
|
386
366
|
huit, `redis` quatre (`session`, `tokens`, `passkeys`, `idempotency`) : un cache ne garde ni
|
|
387
367
|
comptes ni journal d'audit. Ne promets jamais une parité qui n'existe pas — lis ce champ, et
|
|
388
368
|
`npx nodefony inspect stores` dit où atterrit chaque donnée.
|
|
369
|
+
- **L'entrée d'une route se DÉCLARE — ne la valide pas à la main.** L'option `schema` d'un
|
|
370
|
+
décorateur de route (`@Post("/posts", { schema: { params, query, body, headers } })`, un schéma Zod
|
|
371
|
+
par partie) fait valider l'entrée AVANT l'action — **422** qui nomme chaque champ et sa partie, sur
|
|
372
|
+
HTTP comme par la socket (`api.request`) — et l'action la reçoit typée en UN argument,
|
|
373
|
+
`input: RouteInput<…>`. Une action à schéma ne prend **aucun** décorateur de paramètre (`@Body`,
|
|
374
|
+
`@Query`…) : le mélange est refusé au démarrage, qui nomme la route. L'identité se lit par
|
|
375
|
+
`this.user`. Un corps binaire arrive en octets (`Uint8Array`), un `text/*` en chaîne : un même
|
|
376
|
+
`z.instanceof(Uint8Array)` vaut pour les deux portes. Doc : `node_modules/@nodefony/framework/docs/decorateurs.md`.
|
|
389
377
|
- **Les violations de contrainte sont DÉJÀ traduites en HTTP — ne les rattrape pas.** Un doublon
|
|
390
378
|
sur une colonne unique ressort en **409**, une donnée qui viole le schéma Zod en **422**, chacun
|
|
391
379
|
avec son corps JSON : le rendu d'erreur lit le code du pilote (`23505`, `ER_DUP_ENTRY`,
|
|
@@ -454,11 +442,16 @@ Ne concerne qu'une application qui a un frontend. Sauf mention, **vaut pour les
|
|
|
454
442
|
**couleur seule**, `rel="noopener noreferrer"` sur les liens externes.
|
|
455
443
|
|
|
456
444
|
- **Une route d'API répond du HTML** : un repli SPA générique avale les routes voisines — le
|
|
457
|
-
premier motif qui correspond gagne.
|
|
458
|
-
|
|
445
|
+
premier motif qui correspond gagne. Déclare la route de la page monopage avec
|
|
446
|
+
`fallback: true` : elle ne sert alors que ce qu'aucune autre route ne sert, quel que soit
|
|
447
|
+
l'ordre. `npx nodefony inspect routes --json` montre l'ordre réel.
|
|
459
448
|
- **Des utilisateurs « déconnectés au hasard »** : le traitement global « 401 = session expirée »
|
|
460
|
-
frappe aussi
|
|
461
|
-
Exempte les
|
|
449
|
+
frappe aussi la connexion refusée (un mauvais mot de passe rend 401) — et détruit une session
|
|
450
|
+
valide. Exempte les routes d'authentification du traitement global. `me` ne renvoie jamais
|
|
451
|
+
401 : il répond toujours 200, `{ user: null }` sans session.
|
|
452
|
+
- **La connexion ne s'écrit pas en `fetch`** : `/login` est servie ; la liaison du front
|
|
453
|
+
(`useNodefonyLogin`…) porte `me()` et `logout()` — suis l'adresse que rend `logout()`, sinon
|
|
454
|
+
la session Keycloak reste ouverte. Détail : skill `nodefony-authentication`.
|
|
462
455
|
- **Ta page répond 200 et son script ne s'exécute pas** : la politique de contenu exige un `nonce`
|
|
463
456
|
sur les scripts, et le navigateur refuse un `<script>` en ligne qui n'en porte pas (« Refused to
|
|
464
457
|
execute inline script »). Un `curl` ne le voit JAMAIS, il ne lit que le corps. Signe le script
|
|
@@ -477,10 +470,13 @@ Ne concerne qu'une application qui a un frontend. Sauf mention, **vaut pour les
|
|
|
477
470
|
## 10. Avant de dire « fait »
|
|
478
471
|
|
|
479
472
|
```bash
|
|
480
|
-
npm run verify # types
|
|
473
|
+
npm run verify # types, style, mise en forme, .env.example, tests, build, câblage
|
|
481
474
|
npm run test:e2e # boot réel + HTTP/WS — le gate LENT, hors `verify`
|
|
482
475
|
```
|
|
483
476
|
|
|
477
|
+
Ton diff ajoute une route, une requête ou touche la sécurité ? Relis-le AVANT de commiter :
|
|
478
|
+
skill `nodefony-security-review` — un script y nomme, ligne par ligne, ce qu'un œil laisse passer.
|
|
479
|
+
|
|
484
480
|
Puis, en une phrase : **nomme ce que tu n'as PAS lancé.** Un vert ne couvre que le diff qui l'a
|
|
485
481
|
produit, et un banc sauté faute de son décor compte comme vert. Dire « e2e non lancé » coûte
|
|
486
482
|
cinq mots ; le taire coûte la confiance dans tout le reste.
|
|
@@ -21,10 +21,23 @@
|
|
|
21
21
|
* `@output` les pages classées, avec chemin, ligne et extrait — ou un refus qui DIT pourquoi
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
24
|
+
import { readFileSync, readdirSync, statSync, realpathSync } from "node:fs";
|
|
25
25
|
import path from "node:path";
|
|
26
26
|
import process from "node:process";
|
|
27
|
-
import {
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
import { findProjectRoot } from "nodefony/project-root";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Racine de l'APPLICATION vue depuis `cwd` — le dossier qui porte
|
|
32
|
+
* `nodefony.config.ts` —, ou `cwd` lui-même hors application. Un script lancé
|
|
33
|
+
* depuis `frontend/` ou `modules/blog/` travaille ainsi sur l'app entière.
|
|
34
|
+
* `nodefony/project-root` est le sous-chemin LÉGER du cœur (`node:` seul) :
|
|
35
|
+
* importer `nodefony` entier coûtait jusqu'à plusieurs secondes à froid.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} cwd - le dossier de lancement.
|
|
38
|
+
* @returns {string} la racine retenue.
|
|
39
|
+
*/
|
|
40
|
+
const appRoot = (cwd) => findProjectRoot(cwd) ?? cwd;
|
|
28
41
|
|
|
29
42
|
/** Nombre de pages rendues par défaut — au-delà, personne ne lit. */
|
|
30
43
|
const DEFAULT_LIMIT = 6;
|
|
@@ -55,14 +68,14 @@ const FLAGS = new Set([
|
|
|
55
68
|
* @returns les options lues.
|
|
56
69
|
* @throws Error quand un drapeau est inconnu ou qu'une valeur manque.
|
|
57
70
|
*/
|
|
58
|
-
export function parseArgs(argv) {
|
|
71
|
+
export function parseArgs(argv, cwd = process.cwd()) {
|
|
59
72
|
const opts = {
|
|
60
73
|
terms: [],
|
|
61
74
|
list: false,
|
|
62
75
|
open: /** @type {string | null} */ (null),
|
|
63
76
|
json: false,
|
|
64
77
|
limit: DEFAULT_LIMIT,
|
|
65
|
-
root:
|
|
78
|
+
root: appRoot(cwd),
|
|
66
79
|
help: false,
|
|
67
80
|
};
|
|
68
81
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -123,7 +136,7 @@ export function docDirectories(root) {
|
|
|
123
136
|
};
|
|
124
137
|
push(path.join(modules, "nodefony"));
|
|
125
138
|
const scope = path.join(modules, "@nodefony");
|
|
126
|
-
let entries
|
|
139
|
+
let entries;
|
|
127
140
|
try {
|
|
128
141
|
entries = readdirSync(scope);
|
|
129
142
|
} catch {
|
|
@@ -142,7 +155,7 @@ export function docDirectories(root) {
|
|
|
142
155
|
function markdownFiles(dir) {
|
|
143
156
|
const out = [];
|
|
144
157
|
const walk = (current) => {
|
|
145
|
-
let entries
|
|
158
|
+
let entries;
|
|
146
159
|
try {
|
|
147
160
|
entries = readdirSync(current, { withFileTypes: true });
|
|
148
161
|
} catch {
|
|
@@ -287,7 +300,7 @@ export function index(root) {
|
|
|
287
300
|
const pages = [];
|
|
288
301
|
for (const dir of docDirectories(root)) {
|
|
289
302
|
for (const file of markdownFiles(dir)) {
|
|
290
|
-
let source
|
|
303
|
+
let source;
|
|
291
304
|
try {
|
|
292
305
|
source = readFileSync(file, "utf8");
|
|
293
306
|
} catch {
|
|
@@ -538,7 +551,10 @@ export function main() {
|
|
|
538
551
|
// confondrait deux scripts homonymes de deux skills.
|
|
539
552
|
if (
|
|
540
553
|
process.argv[1] !== undefined &&
|
|
541
|
-
|
|
554
|
+
// Chemins RÉELS des deux côtés : Node résout `import.meta.url` en suivant
|
|
555
|
+
// les liens, pas `argv[1]` — lancé par `/var/…` (macOS) ou un dossier lié,
|
|
556
|
+
// la comparaison brute échouait et le script ne faisait RIEN.
|
|
557
|
+
realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
542
558
|
) {
|
|
543
559
|
process.exitCode = main();
|
|
544
560
|
}
|