@nodefony/devkit 10.0.0-alpha.1

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.
Files changed (67) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +318 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.142.0/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.142.0/helpers/esm/decorateMetadata.js +6 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.142.0/helpers/esm/decorateParam.js +8 -0
  6. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
  7. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js +6 -0
  8. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.js +8 -0
  9. package/dist/_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js +9 -0
  10. package/dist/_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js +6 -0
  11. package/dist/_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateParam.js +8 -0
  12. package/dist/_virtual/_@oxc-project_runtime@0.147.0/helpers/esm/decorate.js +9 -0
  13. package/dist/_virtual/_@oxc-project_runtime@0.147.0/helpers/esm/decorateMetadata.js +6 -0
  14. package/dist/_virtual/_@oxc-project_runtime@0.147.0/helpers/esm/decorateParam.js +8 -0
  15. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
  16. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
  17. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js +8 -0
  18. package/dist/index.js +47 -0
  19. package/dist/nodefony/command/CardCommand.js +70 -0
  20. package/dist/nodefony/config/config.js +200 -0
  21. package/dist/nodefony/config/defineModuleConfig.js +36 -0
  22. package/dist/nodefony/controllers/DevkitController.js +60 -0
  23. package/dist/nodefony/controllers/McpController.js +223 -0
  24. package/dist/nodefony/controllers/OAuthMetadataController.js +89 -0
  25. package/dist/nodefony/interfaces/IDevkitService.js +1 -0
  26. package/dist/nodefony/interfaces/index.js +1 -0
  27. package/dist/nodefony/service/DevkitService.js +198 -0
  28. package/dist/nodefony/src/card.js +2 -0
  29. package/dist/nodefony/src/errors/DevkitError.js +21 -0
  30. package/dist/nodefony/src/mcp/guard.js +51 -0
  31. package/dist/nodefony/src/mcp/protocol.js +127 -0
  32. package/dist/nodefony/src/mcp/server.js +133 -0
  33. package/dist/nodefony/src/mcp/tools.js +163 -0
  34. package/dist/types/index.d.ts +52 -0
  35. package/dist/types/nodefony/command/CardCommand.d.ts +33 -0
  36. package/dist/types/nodefony/config/config.d.ts +25 -0
  37. package/dist/types/nodefony/config/defineModuleConfig.d.ts +27 -0
  38. package/dist/types/nodefony/controllers/DevkitController.d.ts +37 -0
  39. package/dist/types/nodefony/controllers/McpController.d.ts +70 -0
  40. package/dist/types/nodefony/controllers/OAuthMetadataController.d.ts +39 -0
  41. package/dist/types/nodefony/interfaces/IDevkitService.d.ts +69 -0
  42. package/dist/types/nodefony/interfaces/index.d.ts +1 -0
  43. package/dist/types/nodefony/service/DevkitService.d.ts +143 -0
  44. package/dist/types/nodefony/src/card.d.ts +18 -0
  45. package/dist/types/nodefony/src/errors/DevkitError.d.ts +14 -0
  46. package/dist/types/nodefony/src/mcp/guard.d.ts +66 -0
  47. package/dist/types/nodefony/src/mcp/protocol.d.ts +139 -0
  48. package/dist/types/nodefony/src/mcp/server.d.ts +48 -0
  49. package/dist/types/nodefony/src/mcp/tools.d.ts +81 -0
  50. package/docs/index.md +358 -0
  51. package/package.json +77 -0
  52. package/skills/nodefony-add-crud/SKILL.md +199 -0
  53. package/skills/nodefony-add-realtime-channel/SKILL.md +95 -0
  54. package/skills/nodefony-add-service/SKILL.md +90 -0
  55. package/skills/nodefony-browser/SKILL.md +416 -0
  56. package/skills/nodefony-browser/references/socket.md +115 -0
  57. package/skills/nodefony-browser/references/sondes.md +175 -0
  58. package/skills/nodefony-browser/scripts/audit.mjs +169 -0
  59. package/skills/nodefony-browser/scripts/inspect.mjs +903 -0
  60. package/skills/nodefony-browser/scripts/lib/browser.mjs +357 -0
  61. package/skills/nodefony-browser/scripts/lib/probes.mjs +501 -0
  62. package/skills/nodefony-browser/scripts/lib/wcag.mjs +153 -0
  63. package/skills/nodefony-browser/scripts/socket.mjs +354 -0
  64. package/skills/nodefony-browser/scripts/watch.mjs +125 -0
  65. package/skills/nodefony-migrate-schema/SKILL.md +359 -0
  66. package/skills/nodefony-migrate-schema/references/verdicts.md +139 -0
  67. package/skills/nodefony-protect-route/SKILL.md +195 -0
package/README.md ADDED
@@ -0,0 +1,318 @@
1
+ # @nodefony/devkit
2
+
3
+ L'outillage de **développement** d'une application Nodefony : sa carte de visite,
4
+ les **skills d'agent** qui disent comment faire les tâches courantes, et les
5
+ portes qui mènent au reste.
6
+
7
+ Il répond à la question que tout le monde se pose en arrivant sur une application
8
+ — humain qui reprend un projet, agent qui code : **qui répond ici, et où faut-il
9
+ aller ensuite ?**
10
+
11
+ ```bash
12
+ npx nodefony card # -j pour du JSON (| jq)
13
+ ```
14
+
15
+ > La commande est servie par le **cœur**, pas par ce module : elle doit répondre
16
+ > sur une application pas encore construite et dans un terminal sans `NODE_ENV`,
17
+ > deux cas où aucun module n'est chargé. Ce paquet, lui, sert la même carte en
18
+ > **HTTP** — et c'est la seule porte qui connaisse les modules réellement
19
+ > CHARGÉS.
20
+
21
+ ```
22
+ ma-boutique 1.4.0 — development (nodefony 10.0.0)
23
+
24
+ Modules chargés (7) : drizzle, framework, frontend, http, security, studio, user
25
+
26
+ Où aller :
27
+ AGENTS.md
28
+ Les instructions de cette application — générateurs disponibles, table
29
+ tâche → fichier, gates à passer. À lire AVANT d'écrire du code.
30
+ node_modules/nodefony/docs/catalogue.md
31
+ Le catalogue des briques — quel module prendre pour quel besoin.
32
+
33
+
34
+ Quoi lancer :
35
+ npx nodefony doctor
36
+ diagnostic STATIQUE : il répond même quand l'application ne démarre plus.
37
+
38
+ ```
39
+
40
+ ## Installation
41
+
42
+ `nodefony create app` l'ajoute déjà — en **`devDependencies`**, et déclaré
43
+ `policy: "dev"` :
44
+
45
+ ```ts
46
+ // nodefony.config.ts
47
+ use("@nodefony/devkit", {}, { policy: "dev" }),
48
+ ```
49
+
50
+ Les deux moitiés comptent : la `devDependency` fait qu'un `npm ci --omit=dev` ne
51
+ l'installe pas ; la `policy` fait qu'un déploiement qui installerait tout ne le
52
+ charge pas quand même. **En production, le module n'est même pas importé** — le
53
+ coût y est nul, pas « faible ».
54
+
55
+ Corollaire : **la route** n'existe que hors production. La **commande**, elle,
56
+ répond toujours — elle ne dépend pas de ce module.
57
+
58
+ ## Ce qu'il expose
59
+
60
+ | Porte | Pour qui |
61
+ | ------------------------------- | ------------------------------------------------------------ |
62
+ | `GET /nodefony/devkit/api/card` | Studio, un script authentifié — modules réellement CHARGÉS |
63
+ | **`POST /nodefony/mcp`** | **un agent qui appelle des outils** (Model Context Protocol) |
64
+ | `buildCard()` (export du cœur) | une porte de plus, à écrire — rien à réimplémenter |
65
+ | `npx nodefony card` | un agent, un humain au terminal — **servie par le cœur** |
66
+ | `skills/` (dossier du paquet) | l'agent de codage que vous utilisez déjà — voir ci-dessous |
67
+
68
+ La route de la carte vit sous `/nodefony/<module>/api`, que le pare-feu d'une
69
+ application réelle couvre : un agent qui code ne s'authentifie pas et n'a pas de
70
+ navigateur — d'où la commande, qui reste la porte utile.
71
+
72
+ ## Le serveur MCP — les mêmes réponses, en outils
73
+
74
+ ```bash
75
+ npx nodefony ai:mcp # écrit .mcp.json ; --dry-run pour voir sans écrire
76
+ ```
77
+
78
+ Quatre outils : **`nodefony_inspect`** (ce qui est monté), **`nodefony_check`**
79
+ (ce qui manque), **`nodefony_symbols`** (ce qu'une API du framework signifie),
80
+ **`nodefony_card`** (par où commencer). Ce sont les mêmes briques que les
81
+ commandes du même nom — une source, plusieurs portes.
82
+
83
+ **Ce n'est pas un process de plus.** Depuis la révision `2026-07-28` du transport
84
+ « Streamable HTTP », un serveur MCP est un simple endpoint `POST` sans session :
85
+ c'est donc une **route de votre application**. Elle n'existe que pendant qu'elle
86
+ tourne, suit chaque rechargement du serveur de développement, et n'a aucun cache
87
+ à invalider.
88
+
89
+ **Ce qui la protège**, et il faut le savoir avant de s'étonner d'un `403` :
90
+
91
+ - toute **adresse non locale** est refusée (`mcp.allowRemote`) ;
92
+ - toute **origine de navigateur** non déclarée est refusée (`mcp.allowedOrigins`).
93
+ Un client MCP natif n'envoie pas d'en-tête `Origin` ; une page web en envoie
94
+ toujours un. C'est ce qui ferme le détournement DNS, seul vecteur réel contre
95
+ un serveur local ;
96
+ - le module étant `policy: "dev"`, **la route n'existe pas en production** ;
97
+ - les outils sont en **lecture seule**, et la liste est une allowlist
98
+ (`mcp.tools`).
99
+
100
+ ### Protéger la porte par OAuth 2.1
101
+
102
+ Par défaut la porte est anonyme, et ce qui la borne est le périmètre ci-dessus.
103
+ Déclarez un serveur d'autorisation, et elle prend son rôle de **resource
104
+ server** : elle publie ses métadonnées, valide le porteur présenté, et refuse en
105
+ disant où obtenir un jeton.
106
+
107
+ ```ts
108
+ use("@nodefony/devkit", {
109
+ mcp: {
110
+ authorization: {
111
+ // Un seul réglage commande : vide = porte anonyme, comme avant.
112
+ authorizationServers: ["https://auth.example"],
113
+ // L'URI PUBLIQUE de la porte — l'audience que les jetons doivent porter.
114
+ // Elle s'écrit : la déduire de l'en-tête `Host` permettrait à un Host
115
+ // forgé d'obtenir un jeton d'audience arbitraire ET de passer la
116
+ // vérification.
117
+ resource: "https://mon-app.example/nodefony/mcp",
118
+ },
119
+ },
120
+ });
121
+ ```
122
+
123
+ > **Les scopes ne s'écrivent pas ici.** Ce que la porte publie
124
+ > (`scopes_supported`) et nomme dans son défi est **dérivé** des outils qu'elle
125
+ > sert : l'union de leurs `IMcpTool.scopes`. Pour qu'un scope soit publié, le
126
+ > poser sur l'outil qu'il ouvre — le seul endroit où il a un effet. Une liste
127
+ > écrite à côté du code annonçait au client autre chose que ce qu'on exige de
128
+ > lui, dans les deux sens et sans qu'aucun contrôle ne s'en aperçoive.
129
+
130
+ Le document est alors servi sur
131
+ `GET /.well-known/oauth-protected-resource/nodefony/mcp` (RFC 9728 — le suffixe
132
+ s'**insère** entre l'hôte et le chemin, ce qui permet plusieurs ressources par
133
+ hôte), et une requête sans jeton reçoit :
134
+
135
+ ```http
136
+ HTTP/1.1 401 Unauthorized
137
+ WWW-Authenticate: Bearer resource_metadata="https://mon-app.example/.well-known/oauth-protected-resource/nodefony/mcp", scope="admin:read"
138
+ ```
139
+
140
+ C'est cet en-tête qui rend l'autorisation **apprenable** : sans lui, un refus
141
+ est un mur — le client ignore qu'un jeton existe et où le demander.
142
+
143
+ > 🔴 **La vérification du jeton est fournie par l'application.** Ce module est
144
+ > `policy: "dev"` et ne porte aucune cryptographie : il cherche un service
145
+ > `accessTokenVerifier` dans le conteneur (contrat `IAccessTokenVerifier` du cœur).
146
+ > Déclarer un serveur d'autorisation **sans** ce service fait répondre `503` à
147
+ > la porte, avec un journal `CRITIC` — accepter des porteurs sans les lire
148
+ > serait pire que rester anonyme.
149
+ >
150
+ > `anonymous: true` laisse la porte ouverte aux outils publics tout en gardant
151
+ > les outils réservés retenus. C'est un choix qui s'écrit, jamais un défaut.
152
+
153
+ Le serveur est **dual-ère** : il répond à `server/discover` et aux métadonnées
154
+ par requête des clients modernes, **et** au handshake `initialize` des clients
155
+ déployés aujourd'hui — ce que la spec autorise explicitement. Il sert cinq
156
+ révisions (`2026-07-28`, `2025-11-25`, `2025-06-18`, `2025-03-26`,
157
+ `2024-11-05`) et **répond celle que le client demande** : annoncer la plus
158
+ récente à tous rendrait la porte injoignable par les clients dont le SDK ne la
159
+ connaît pas encore.
160
+
161
+ Éprouvé avec deux clients indépendants : Claude Code et Mistral Vibe.
162
+
163
+ ### Vos propres outils
164
+
165
+ Ces quatre-là décrivent le framework ; ils ne savent rien de votre métier. Tout
166
+ module de votre application peut publier les siens en implémentant
167
+ `getMcpTools(): IMcpTool[]` :
168
+
169
+ ```ts
170
+ import { Module, mcpText, type IMcpTool } from "nodefony";
171
+
172
+ class Shop extends Module {
173
+ getMcpTools(): IMcpTool[] {
174
+ return [
175
+ {
176
+ name: "shop_stock",
177
+ description:
178
+ "Stock réel d'une référence produit. À utiliser avant de proposer " +
179
+ "une commande — la réponse vient de la base, pas d'un cache.",
180
+ inputSchema: {
181
+ type: "object",
182
+ properties: { sku: { type: "string" } },
183
+ required: ["sku"],
184
+ },
185
+ handler: async (args) => mcpText(await this.stock(String(args.sku))),
186
+ },
187
+ ];
188
+ }
189
+ }
190
+ ```
191
+
192
+ Rien ne s'enregistre au démarrage : la liste est relue à chaque requête, et
193
+ `mcp.tools` ne filtre que les outils **intégrés** — le vôtre est publié dès qu'il
194
+ est déclaré. Un outil écarté (nom hors forme, nom déjà pris, handler absent) le
195
+ dit en `WARNING`, jamais en silence.
196
+
197
+ Un outil peut aussi se **réserver** — `scopes: ["shop:read"]` (tous exigés) ou
198
+ `requiresAuth: true` — et son handler reçoit alors l'appelant en second
199
+ paramètre. Il est alors absent de `tools/list` **et** inappelable en le nommant.
200
+ 🔴 Tant que la porte n'authentifie personne, un tel outil ne sortira jamais :
201
+ c'est fermé par défaut, et c'est voulu. Détail et pièges :
202
+ [la documentation du module](./docs/index.md).
203
+
204
+ ## Les skills d'agent
205
+
206
+ Le paquet livre six **skills** au format [Agent Skills](https://agentskills.io)
207
+ — la marche à suivre complète pour les tâches où un agent, sans eux, inventerait
208
+ du code : créer une ressource REST, ajouter un service injectable, réserver une
209
+ route à qui est habilité, ouvrir un canal temps réel, faire évoluer le schéma
210
+ d'une base sans la détruire, et voir puis MESURER un écran dans un navigateur
211
+ piloté. Ils sont lus par tout client conforme (Claude Code, Cursor, Copilot,
212
+ VS Code, Codex, Goose…).
213
+
214
+ `nodefony create app` les met à disposition à la création. Après un
215
+ `npm update`, une commande les remet à jour :
216
+
217
+ ```bash
218
+ npx nodefony ai:sync # --dry-run pour voir sans écrire, --json pour un script
219
+ ```
220
+
221
+ ```
222
+ Skills d'agent — .agents/skills
223
+
224
+ = nodefony-add-crud @nodefony/devkit
225
+ = nodefony-add-realtime-channel @nodefony/devkit
226
+ = nodefony-add-service @nodefony/devkit
227
+ = nodefony-browser @nodefony/devkit
228
+ = nodefony-migrate-schema @nodefony/devkit
229
+ = nodefony-protect-route @nodefony/devkit
230
+
231
+ 0 posé(s) · 0 mis à jour · 6 inchangé(s)
232
+ ```
233
+
234
+ **Le préfixe `nodefony-` vous laisse la place.** Ces pointeurs arrivent dans
235
+ `.agents/skills/`, le dossier où vous écrivez aussi les vôtres : sans namespace,
236
+ un `add-crud` propre à votre métier et le nôtre se disputeraient un nom — et
237
+ c'est `ai:sync` qui écraserait le vôtre à la synchronisation suivante. Vos skills
238
+ n'ont donc aucune contrainte de nommage, sauf à commencer par `nodefony-`.
239
+
240
+ **Ce qui est écrit chez vous est un POINTEUR, pas une copie.** Le contenu reste
241
+ dans le paquet et suit vos montées de version ; un skill recopié dans le projet
242
+ décrirait, six mois plus tard, un framework qui a changé — sans casser le build,
243
+ donc sans que personne le voie. Les pointeurs sont faits pour être **commités** :
244
+ votre équipe et votre intégration continue disposent alors des mêmes skills.
245
+
246
+ Le dossier visé, `.agents/skills/`, est celui que **tous** les clients conformes
247
+ lisent — pas le dossier propriétaire d'un seul. Si le vôtre ne scanne que le
248
+ sien, ajoutez-y ce chemin plutôt que de dupliquer un contenu qui divergerait.
249
+
250
+ Deux garanties de la commande : un pointeur déjà à jour n'est **pas réécrit**
251
+ (votre arbre git reste propre, l'horodatage ne bouge pas), et un pointeur que
252
+ plus aucun paquet ne livre est **signalé, jamais supprimé** — vous avez pu en
253
+ écrire un à la main sous le même nom.
254
+
255
+ > **Aucun `postinstall`** ne fait ce geste, volontairement : `--ignore-scripts`
256
+ > est courant, les scripts d'installation sont un vecteur d'attaque connu de
257
+ > l'écosystème npm, et écrire dans un dossier versionné à chaque installation
258
+ > produirait des différences surprises. La commande, elle, se lance quand vous
259
+ > le décidez. Un module tiers qui livre ses propres skills est servi par la même
260
+ > commande : elle scanne tout paquet `@nodefony/*` **et** les modules locaux de
261
+ > l'application, sans que le cœur ait à les connaître.
262
+
263
+ ## Configuration
264
+
265
+ | Clé | Type | Défaut | Rôle |
266
+ | --------- | --------- | ------ | ---------------------- |
267
+ | `enabled` | `boolean` | `true` | Interrupteur du module |
268
+
269
+ La source unique est le schéma Zod de `nodefony/config/config.ts` : c'est lui qui
270
+ porte les défauts, les descriptions et la validation. Une clé inconnue ou mal
271
+ typée fait échouer le **boot**, en nommant le champ fautif.
272
+
273
+ Surcharge par l'application : `use("@nodefony/devkit", { enabled: false })`.
274
+ Par l'environnement : `NF__DEVKIT__ENABLED=false`.
275
+
276
+ ## Ce qu'il ne fait pas
277
+
278
+ - **Il n'invente rien.** Tout ce qu'il rend est DÉRIVÉ de l'état du Kernel,
279
+ recalculé à chaque lecture, jamais mis en cache — une carte en cache mentirait
280
+ au premier module ajouté.
281
+ - **Il ne crée rien.** Le scaffold (`nodefony create …`), le diagnostic
282
+ (`nodefony doctor`) et l'introspection (`nodefony inspect`) vivent dans le
283
+ cœur : ils doivent répondre sans qu'aucun module soit installé, et quand
284
+ l'application est cassée.
285
+ - **Il ne dépend d'aucun fournisseur de modèle.** Son intérêt est de servir
286
+ l'agent que vous avez déjà.
287
+ - **Il ne s'installe pas tout seul dans votre projet.** Aucun `postinstall` :
288
+ les pointeurs de skills sont posés par `create app` à la création, et remis à
289
+ jour par `ai:sync` quand vous le demandez.
290
+
291
+ ## Développer
292
+
293
+ ```bash
294
+ npm run build # rolldown → dist/ + déclarations .d.ts
295
+ npm run typecheck # tsgo --noEmit (sources + tests)
296
+ npm test # vitest
297
+ ```
298
+
299
+ ## Structure
300
+
301
+ ```
302
+ @nodefony/devkit/
303
+ ├── index.ts ← la classe Module + les exports publics
304
+ ├── nodefony/
305
+ │ ├── config/config.ts ← schéma Zod = source unique des défauts
306
+ │ ├── config/defineModuleConfig.ts ← builder pur (valide, gèle)
307
+ │ ├── src/card.ts ← ré-export du cœur (la composition y vit)
308
+ │ ├── service/DevkitService.ts ← dérive la carte du Kernel (`container.get("devkit")`)
309
+ │ ├── controllers/DevkitController.ts ← la porte HTTP
310
+ │ └── interfaces/ ← l'API publique du service
311
+ ├── skills/<nom>/SKILL.md ← les skills d'agent livrés par npm
312
+ ├── docs/ ← documentation, surfacée dans Studio
313
+ └── tests/
314
+ ```
315
+
316
+ `dist/`, `docs/` et `skills/` sont les trois dossiers publiés (`files`) : un
317
+ skill se corrige ici, et la correction arrive chez l'utilisateur par
318
+ `npm update`, sans qu'il ait un fichier à réécrire.
@@ -0,0 +1,9 @@
1
+ //#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+ //#endregion
9
+ export { __decorate as default };
@@ -0,0 +1,6 @@
1
+ //#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorateMetadata.js
2
+ function __decorateMetadata(k, v) {
3
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
+ }
5
+ //#endregion
6
+ export { __decorateMetadata as default };
@@ -0,0 +1,8 @@
1
+ //#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorateParam.js
2
+ function __decorateParam(paramIndex, decorator) {
3
+ return function(target, key) {
4
+ decorator(target, key, paramIndex);
5
+ };
6
+ }
7
+ //#endregion
8
+ export { __decorateParam as default };
@@ -0,0 +1,9 @@
1
+ //#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+ //#endregion
9
+ export { __decorate as default };
@@ -0,0 +1,6 @@
1
+ //#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorateMetadata.js
2
+ function __decorateMetadata(k, v) {
3
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
+ }
5
+ //#endregion
6
+ export { __decorateMetadata as default };
@@ -0,0 +1,8 @@
1
+ //#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorateParam.js
2
+ function __decorateParam(paramIndex, decorator) {
3
+ return function(target, key) {
4
+ decorator(target, key, paramIndex);
5
+ };
6
+ }
7
+ //#endregion
8
+ export { __decorateParam as default };
@@ -0,0 +1,9 @@
1
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+ //#endregion
9
+ export { __decorate as default };
@@ -0,0 +1,6 @@
1
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/decorateMetadata.js
2
+ function __decorateMetadata(k, v) {
3
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
+ }
5
+ //#endregion
6
+ export { __decorateMetadata as default };
@@ -0,0 +1,8 @@
1
+ //#region \0@oxc-project+runtime@0.146.0/helpers/esm/decorateParam.js
2
+ function __decorateParam(paramIndex, decorator) {
3
+ return function(target, key) {
4
+ decorator(target, key, paramIndex);
5
+ };
6
+ }
7
+ //#endregion
8
+ export { __decorateParam as default };
@@ -0,0 +1,9 @@
1
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+ //#endregion
9
+ export { __decorate as default };
@@ -0,0 +1,6 @@
1
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/decorateMetadata.js
2
+ function __decorateMetadata(k, v) {
3
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
+ }
5
+ //#endregion
6
+ export { __decorateMetadata as default };
@@ -0,0 +1,8 @@
1
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/decorateParam.js
2
+ function __decorateParam(paramIndex, decorator) {
3
+ return function(target, key) {
4
+ decorator(target, key, paramIndex);
5
+ };
6
+ }
7
+ //#endregion
8
+ export { __decorateParam as default };
@@ -0,0 +1,9 @@
1
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+ //#endregion
9
+ export { __decorate as default };
@@ -0,0 +1,6 @@
1
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorateMetadata.js
2
+ function __decorateMetadata(k, v) {
3
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
+ }
5
+ //#endregion
6
+ export { __decorateMetadata as default };
@@ -0,0 +1,8 @@
1
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorateParam.js
2
+ function __decorateParam(paramIndex, decorator) {
3
+ return function(target, key) {
4
+ decorator(target, key, paramIndex);
5
+ };
6
+ }
7
+ //#endregion
8
+ export { __decorateParam as default };
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ import defaults, { devkitConfigSchema } from "./nodefony/config/config.js";
2
+ import { defineDevkitConfig } from "./nodefony/config/defineModuleConfig.js";
3
+ import { buildCard } from "./nodefony/src/card.js";
4
+ import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
5
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
6
+ import DevkitService_default from "./nodefony/service/DevkitService.js";
7
+ import DevkitController_default from "./nodefony/controllers/DevkitController.js";
8
+ import McpController_default from "./nodefony/controllers/McpController.js";
9
+ import { Kernel, Module, services } from "nodefony";
10
+ import { controllers } from "@nodefony/framework";
11
+ //#region index.ts
12
+ let DevkitModule = class DevkitModule extends Module {
13
+ /**
14
+ * ⚠️ Ce module ne pose PLUS de commande CLI.
15
+ *
16
+ * `devkit:card` vivait ici, et n'existait donc que lorsque le module était
17
+ * chargé : hors développement (`policy: "dev"`) le CLI répondait
18
+ * `unknown command`, et sur une application non encore construite le Kernel
19
+ * refusait de démarrer avant elle. Une carte de visite qui disparaît selon
20
+ * l'environnement n'accueille personne. Elle est désormais servie par le cœur
21
+ * (`nodefony card`, alias `devkit:card`, standalone 0-boot — fast-path de
22
+ * `CliKernel.start`), qui ne lit que des fichiers.
23
+ *
24
+ * Ce module garde la porte HTTP : elle, et elle seule, connaît les modules
25
+ * réellement CHARGÉS.
26
+ */
27
+ constructor(kernel) {
28
+ super("devkit", kernel, import.meta.url, defaults);
29
+ }
30
+ /**
31
+ * Valide la config au boot — défauts du schéma fusionnés avec ce que l'app
32
+ * passe dans `use()`. Une clé inconnue ou mal typée plante ICI, avec le champ
33
+ * fautif nommé, plutôt qu'en `undefined.x` au premier appel en production.
34
+ */
35
+ async onKernelRegister() {
36
+ this.options = defineDevkitConfig(this.options ?? {});
37
+ return this;
38
+ }
39
+ };
40
+ DevkitModule = __decorate([
41
+ controllers([DevkitController_default, McpController_default]),
42
+ services([DevkitService_default]),
43
+ __decorateMetadata("design:paramtypes", [typeof Kernel === "undefined" ? Object : Kernel])
44
+ ], DevkitModule);
45
+ var devkit_default = DevkitModule;
46
+ //#endregion
47
+ export { DevkitController_default as DevkitController, DevkitService_default as DevkitService, McpController_default as McpController, buildCard, devkit_default as default, defineDevkitConfig, devkitConfigSchema };
@@ -0,0 +1,70 @@
1
+ import { Command } from "nodefony";
2
+ //#region nodefony/command/CardCommand.ts
3
+ /**
4
+ * `onReady` : les services sont construits, AUCUN serveur n'écoute. La carte se
5
+ * lit dans l'état du kernel — ouvrir un port pour la rendre serait payer un
6
+ * démarrage complet pour une réponse qui n'en dépend pas.
7
+ */
8
+ const options = {
9
+ showBanner: false,
10
+ kernelEvent: "onReady"
11
+ };
12
+ /**
13
+ * `nodefony devkit:card [-j]` — qui répond, et où aller ensuite.
14
+ *
15
+ * ## Pourquoi une commande, alors que la route existe
16
+ *
17
+ * La route HTTP vit sous `/nodefony`, que le pare-feu d'une application réelle
18
+ * couvre : un agent qui code ne s'authentifie pas, et n'a pas de navigateur. La
19
+ * porte qu'il a déjà, c'est le terminal. Même source (le service), deux rendus —
20
+ * ajouter une porte n'ajoute jamais une vérité.
21
+ *
22
+ * ⚠️ Le module est `policy: "dev"` : hors développement il n'est pas chargé, donc
23
+ * cette commande **n'existe pas**. C'est voulu — et c'est pour ça qu'elle
24
+ * s'invoque `NODE_ENV=development npx nodefony devkit:card` depuis un terminal
25
+ * qui n'aurait pas posé la variable.
26
+ */
27
+ var CardCommand = class CardCommand extends Command {
28
+ constructor(cli) {
29
+ super("devkit:card", "Imprime la carte de visite de l application", cli, options);
30
+ this.addOption("-j, --json", "sortie JSON brute (scriptable, `| jq`)");
31
+ }
32
+ async generate(opts) {
33
+ const svc = this.kernel?.container?.get("devkit");
34
+ if (!svc) {
35
+ this.log("service « devkit » non enregistré — module non chargé (policy dev) ?", "ERROR");
36
+ return this;
37
+ }
38
+ const card = svc.getCard();
39
+ if (opts.json) {
40
+ process.stdout.write(`${JSON.stringify(card, null, 2)}\n`);
41
+ return this;
42
+ }
43
+ process.stdout.write(CardCommand.format(card));
44
+ return this;
45
+ }
46
+ /**
47
+ * Rend la carte pour un HUMAIN (ou un agent qui lit un terminal).
48
+ *
49
+ * Statique et PURE : elle ne touche ni au kernel ni au service, donc elle
50
+ * s'éprouve seule. Sortie sur `stdout` plutôt que par le journal — une carte
51
+ * de visite n'est pas un événement de log, et le préfixe horodaté rendrait le
52
+ * copier-coller inutilisable.
53
+ */
54
+ static format(card) {
55
+ return [
56
+ `${card.app.name} ${card.app.version} — ${card.app.environment} (nodefony ${card.nodefony.version})`,
57
+ "",
58
+ `Modules chargés (${card.modules.length}) : ${card.modules.join(", ")}`,
59
+ "",
60
+ "Où aller :",
61
+ ...card.portes.map((p) => ` ${p.ou}\n ${p.titre} — ${p.pourquoi}`),
62
+ "",
63
+ "Quoi lancer :",
64
+ ...card.verbes.map((v) => ` ${v.commande}\n ${v.pourquoi}`),
65
+ ""
66
+ ].join("\n");
67
+ }
68
+ };
69
+ //#endregion
70
+ export { CardCommand as default };