@nodefony/devkit 10.0.0-beta.5 → 10.0.0-beta.6
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/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +3 -3
- package/dist/nodefony/controllers/DevkitController.js +1 -1
- package/dist/nodefony/controllers/McpController.js +2 -2
- package/dist/nodefony/controllers/ProfileSessionController.js +1 -1
- package/dist/nodefony/mcp/profileRoute.js +28 -9
- package/dist/nodefony/service/DevkitService.js +4 -4
- package/docs/index.md +1 -1
- package/package.json +11 -11
- package/skills/nodefony-add-crud/SKILL.md +38 -23
- package/skills/nodefony-add-realtime-channel/SKILL.md +376 -111
- package/skills/nodefony-add-realtime-channel/references/binaire.md +191 -0
- package/skills/nodefony-add-realtime-channel/references/production.md +121 -0
- package/skills/nodefony-browser/SKILL.md +27 -2
- package/skills/nodefony-browser/scripts/audit.mjs +12 -0
- package/skills/nodefony-browser/scripts/inspect.mjs +40 -81
- package/skills/nodefony-browser/scripts/lib/actions.mjs +113 -0
- package/skills/nodefony-browser/scripts/lib/browser.mjs +50 -11
- package/skills/nodefony-browser/scripts/lib/probes.mjs +56 -0
- package/skills/nodefony-browser/scripts/watch.mjs +27 -11
- package/skills/nodefony-dev/SKILL.md +11 -0
- package/skills/nodefony-dev/references/rapport-de-build.md +19 -0
- package/skills/nodefony-devops/SKILL.md +28 -21
- package/skills/nodefony-migrate-schema/SKILL.md +110 -20
- package/skills/nodefony-migrate-schema/references/verdicts.md +22 -20
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// \0@oxc-project+runtime@0.
|
|
1
|
+
// \0@oxc-project+runtime@0.157.0/helpers/esm/decorate.js
|
|
2
2
|
function __decorate(decorators, target, key, desc) {
|
|
3
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { BUILTIN_MCP_TOOL_KEYS, builtinMcpTools, selectBuiltinMcpTools } from "./nodefony/mcp/tools.js";
|
|
2
1
|
import defaults, { devkitConfigSchema } from "./nodefony/config/config.js";
|
|
3
2
|
import { defineDevkitConfig } from "./nodefony/config/defineModuleConfig.js";
|
|
4
|
-
import { buildCard } from "./nodefony/src/card.js";
|
|
5
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
6
3
|
import DevkitService_default from "./nodefony/service/DevkitService.js";
|
|
7
4
|
import DevkitController_default from "./nodefony/controllers/DevkitController.js";
|
|
8
5
|
import McpController_default from "./nodefony/controllers/McpController.js";
|
|
9
6
|
import ProfileSessionController_default from "./nodefony/controllers/ProfileSessionController.js";
|
|
7
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
8
|
+
import { BUILTIN_MCP_TOOL_KEYS, builtinMcpTools, selectBuiltinMcpTools } from "./nodefony/mcp/tools.js";
|
|
9
|
+
import { buildCard } from "./nodefony/src/card.js";
|
|
10
10
|
import { Module, services } from "nodefony";
|
|
11
11
|
import { controllers } from "@nodefony/framework";
|
|
12
12
|
// index.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
2
2
|
import { Controller, controller, route } from "@nodefony/framework";
|
|
3
3
|
// nodefony/controllers/DevkitController.ts
|
|
4
4
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import __decorateParam from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js";
|
|
2
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
3
3
|
import { Body, Controller, controller, route } from "@nodefony/framework";
|
|
4
4
|
// nodefony/controllers/McpController.ts
|
|
5
5
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
2
2
|
import { Controller, controller, route } from "@nodefony/framework";
|
|
3
3
|
// nodefony/controllers/ProfileSessionController.ts
|
|
4
4
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { renderProfileReport } from "./profileReport.js";
|
|
2
|
-
import { watchToolStop } from "./shutdown.js";
|
|
3
1
|
import { NOT_SERVING_MESSAGE } from "./observe.js";
|
|
2
|
+
import { renderProfileReport } from "./profileReport.js";
|
|
4
3
|
import { SHOT_REQUEST_TIMEOUT_MS, createShotClient } from "./shotClient.js";
|
|
4
|
+
import { watchToolStop } from "./shutdown.js";
|
|
5
5
|
import { PROFILE_SESSION_PATH } from "../src/profileSession.js";
|
|
6
6
|
import { ADMIN_SCOPE_WRITE, EVENT_LOOP_RESOLUTION_MS, ProcessProbe } from "nodefony";
|
|
7
7
|
import { MCP_RESERVED_PREFIX, SESSION_TICKET_HEADER, mcpText, readTicketSessionVerdict } from "@nodefony/ai";
|
|
@@ -171,26 +171,42 @@ async function openShotSession(client, issueTicket, account, signal) {
|
|
|
171
171
|
} catch (error) {
|
|
172
172
|
return {
|
|
173
173
|
ok: false,
|
|
174
|
+
aborted: false,
|
|
174
175
|
message: error instanceof Error ? error.message : String(error)
|
|
175
176
|
};
|
|
176
177
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
let res;
|
|
179
|
+
try {
|
|
180
|
+
res = await client.send({
|
|
181
|
+
method: "POST",
|
|
182
|
+
path: PROFILE_SESSION_PATH,
|
|
183
|
+
headers: { [SESSION_TICKET_HEADER]: ticket },
|
|
184
|
+
readBody: true
|
|
185
|
+
}, signal);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
188
|
+
ok: false,
|
|
189
|
+
aborted: true
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
aborted: false,
|
|
194
|
+
message: `session de profilage non ouverte pour \`${account}\` (${error instanceof Error ? error.message : String(error)}). L'application écoute-t-elle sur son adresse ?`
|
|
195
|
+
};
|
|
196
|
+
}
|
|
183
197
|
const read = readTicketSessionVerdict(res.body);
|
|
184
198
|
if (res.status !== 200 || "message" in read) {
|
|
185
199
|
const why = "message" in read ? read.message : `statut ${res.status}`;
|
|
186
200
|
return {
|
|
187
201
|
ok: false,
|
|
202
|
+
aborted: false,
|
|
188
203
|
message: `session de profilage non ouverte pour \`${account}\` (${res.status} — ${why}). ` + (res.status === 403 ? "Compte inconnu, inactif ou verrouillé ?" : res.status === 503 ? "" : "La route de connexion du profilage répond-elle ?")
|
|
189
204
|
};
|
|
190
205
|
}
|
|
191
206
|
const cookie = res.setCookie.map((c) => c.split(";", 1)[0] ?? "").filter((c) => c.length > 0).join("; ");
|
|
192
207
|
if (cookie.length === 0) return {
|
|
193
208
|
ok: false,
|
|
209
|
+
aborted: false,
|
|
194
210
|
message: `session ouverte pour \`${account}\`, mais aucun cookie reçu`
|
|
195
211
|
};
|
|
196
212
|
return {
|
|
@@ -417,7 +433,10 @@ async function profileRoute(profiler, target, deps, opts) {
|
|
|
417
433
|
if (opts.account !== null) {
|
|
418
434
|
if (deps.issueTicket === void 0) return mcpText("Profilage authentifié indisponible : aucun émetteur de ticket.", true);
|
|
419
435
|
const opened = await openShotSession(client, deps.issueTicket, opts.account, stop.signal);
|
|
420
|
-
if (!opened.ok)
|
|
436
|
+
if (!opened.ok) {
|
|
437
|
+
if (!opened.aborted) return mcpText(opened.message, true);
|
|
438
|
+
return mcpText(stop.interrupted ? "tir interrompu avant le premier envoi : le serveur s'arrête pendant l'ouverture de la session de profilage." : "tir abandonné avant le premier envoi : la session de profilage n'était pas encore ouverte.");
|
|
439
|
+
}
|
|
421
440
|
session = opened.session;
|
|
422
441
|
headers.cookie = session.cookie;
|
|
423
442
|
identity = {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { selectBuiltinMcpTools } from "../mcp/tools.js";
|
|
1
2
|
import { asProfilerReader } from "../mcp/observe.js";
|
|
2
|
-
import { readShotTarget } from "../mcp/shotClient.js";
|
|
3
3
|
import { PROFILE_SESSION_FACTOR } from "../src/profileSession.js";
|
|
4
|
-
import {
|
|
5
|
-
import defaults from "../config/config.js";
|
|
4
|
+
import { readShotTarget } from "../mcp/shotClient.js";
|
|
6
5
|
import { buildCard } from "../src/card.js";
|
|
7
|
-
import
|
|
6
|
+
import defaults from "../config/config.js";
|
|
7
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
8
8
|
import { ADMIN_DEFAULT_ROLE, Nodefony, ROLES_WITHIN, Service, extend, injectable } from "nodefony";
|
|
9
9
|
import { defineMcpGate, defineTicketSessionGate, mcpDeclaredScopes } from "@nodefony/ai";
|
|
10
10
|
// nodefony/service/DevkitService.ts
|
package/docs/index.md
CHANGED
|
@@ -477,7 +477,7 @@ agent invente : faute d'une marche à suivre, il écrit un CRUD à la main, un
|
|
|
477
477
|
service à méthodes `static` que le conteneur ne voit pas, un contrôle de droits
|
|
478
478
|
dans le corps de l'action.
|
|
479
479
|
|
|
480
|
-
Le paquet livre donc
|
|
480
|
+
Le paquet livre donc douze **skills** au format [Agent Skills](https://agentskills.io) :
|
|
481
481
|
un qui porte la conduite commune de toute tâche, un qui part du symptôme d'une panne,
|
|
482
482
|
et un par geste où l'invention coûte cher :
|
|
483
483
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodefony/devkit",
|
|
3
|
-
"version": "10.0.0-beta.
|
|
3
|
+
"version": "10.0.0-beta.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Outillage de développement d'une application Nodefony : carte de visite du projet et portes de découverte pour un agent de développement",
|
|
6
6
|
"author": "Christophe CAMENSULI <ccamensuli@gmail.com>",
|
|
@@ -27,24 +27,24 @@
|
|
|
27
27
|
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@nodefony/ai": "^10.0.0-beta.
|
|
31
|
-
"@nodefony/framework": "^10.0.0-beta.
|
|
32
|
-
"@nodefony/http": "^10.0.0-beta.
|
|
33
|
-
"lighthouse": "^13.
|
|
34
|
-
"nodefony": "^10.0.0-beta.
|
|
30
|
+
"@nodefony/ai": "^10.0.0-beta.6",
|
|
31
|
+
"@nodefony/framework": "^10.0.0-beta.6",
|
|
32
|
+
"@nodefony/http": "^10.0.0-beta.6",
|
|
33
|
+
"lighthouse": "^13.6.0",
|
|
34
|
+
"nodefony": "^10.0.0-beta.6",
|
|
35
35
|
"playwright": "^1.50.0",
|
|
36
36
|
"zod": "^4.6.7"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "1.32.2",
|
|
40
|
-
"@nodefony/ai": "^10.0.0-beta.
|
|
41
|
-
"@nodefony/framework": "^10.0.0-beta.
|
|
42
|
-
"@nodefony/http": "^10.0.0-beta.
|
|
43
|
-
"nodefony": "^10.0.0-beta.
|
|
40
|
+
"@nodefony/ai": "^10.0.0-beta.6",
|
|
41
|
+
"@nodefony/framework": "^10.0.0-beta.6",
|
|
42
|
+
"@nodefony/http": "^10.0.0-beta.6",
|
|
43
|
+
"nodefony": "^10.0.0-beta.6"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"axe-core": "4.14.0",
|
|
47
|
-
"tslib": "2.
|
|
47
|
+
"tslib": "2.9.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
50
|
"playwright": {
|
|
@@ -32,7 +32,7 @@ description: >
|
|
|
32
32
|
## Le geste
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npx nodefony create entity Article title:string body:text? published:bool
|
|
35
|
+
npx nodefony create entity Article title:string "body:text?" published:bool
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Une seule commande produit la chaîne entière : la table, son interface de ligne, les schémas de
|
|
@@ -57,24 +57,25 @@ fixe un défaut LITTÉRAL, `:unique` pose une contrainte d'unicité, `:index` un
|
|
|
57
57
|
camelCase (`publishedAt`). Une faute de casse ou un type d'un autre outil (`boolean`, `integer`)
|
|
58
58
|
est refusé avec la forme juste proposée.
|
|
59
59
|
|
|
60
|
-
| Type | Ce que ça produit | SQLite
|
|
61
|
-
| -------------- | ----------------------------- |
|
|
62
|
-
| `string(n)` | texte court, borné (255) | `text` ¹
|
|
63
|
-
| `text` | texte long | `text`
|
|
64
|
-
| `int` | entier | `integer`
|
|
65
|
-
| `float` | nombre à virgule | `real`
|
|
66
|
-
| `decimal(p,s)` | décimal EXACT (voyage chaîne) | `
|
|
67
|
-
| `bool` | booléen | `integer` (0/1)
|
|
68
|
-
| `json` | document libre | `text` (JSON)
|
|
69
|
-
| `date` | horodatage (ms) | `integer` (ms)
|
|
70
|
-
| `uuid` | identifiant | `text`
|
|
71
|
-
| `char(n)` | longueur EXACTE | `text` ¹
|
|
72
|
-
| `enum(a,b)` | valeurs admises ² | `text`
|
|
73
|
-
| `ref:<Entité>` | relation ³ | clé étrangère
|
|
74
|
-
|
|
75
|
-
¹ SQLite
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
| Type | Ce que ça produit | SQLite | PostgreSQL | MySQL / MariaDB | MongoDB |
|
|
61
|
+
| -------------- | ----------------------------- | ------------------- | ------------------ | --------------------- | ---------------------- |
|
|
62
|
+
| `string(n)` | texte court, borné (255) | `text(n)` + CHECK ¹ | `varchar(n)` | `varchar(n)` | `String` + `maxlength` |
|
|
63
|
+
| `text` | texte long | `text` | `text` | `text` | `String` |
|
|
64
|
+
| `int` | entier | `integer` | `integer` | `int` | `Number` |
|
|
65
|
+
| `float` | nombre à virgule | `real` | `double precision` | `double` | `Number` |
|
|
66
|
+
| `decimal(p,s)` | décimal EXACT (voyage chaîne) | `text` | `numeric(p,s)` | `decimal(p,s)` | `String` |
|
|
67
|
+
| `bool` | booléen | `integer` (0/1) | `boolean` | `boolean` | `Boolean` |
|
|
68
|
+
| `json` | document libre | `text` (JSON) | `jsonb` | `json` | `Mixed` |
|
|
69
|
+
| `date` | horodatage (ms) | `integer` (ms) | `timestamptz(3)` | `datetime(3)` | `Date` |
|
|
70
|
+
| `uuid` | identifiant | `text` | `uuid` | `varchar(36)` | `String` |
|
|
71
|
+
| `char(n)` | longueur EXACTE | `text` + CHECK ¹ | `char(n)` + CHECK | `char(n)` + CHECK | `String`, `n` exact |
|
|
72
|
+
| `enum(a,b)` | valeurs admises ² | `text` + CHECK | `text` + CHECK | `varchar(64)` + CHECK | `String` + `enum` |
|
|
73
|
+
| `ref:<Entité>` | relation ³ | clé étrangère | clé étrangère | clé étrangère | `ObjectId` + `ref` |
|
|
74
|
+
|
|
75
|
+
¹ SQLite ne borne pas un `text` : une contrainte `CHECK` sur la longueur le fait en base, et le
|
|
76
|
+
schéma d'entrée (Zod) avant elle, sur TOUS les transports. ² Aucun type `ENUM` SQL : une colonne
|
|
77
|
+
texte et une contrainte `CHECK` qui liste les valeurs, chacune de 64 caractères au plus ; le type
|
|
78
|
+
TypeScript et le schéma Zod bornent avant la base. ³ En SQL, la colonne prend le type de la clé
|
|
78
79
|
visée (`uuid`, entier pour `--id serial`, texte pour `User`) ; en MongoDB, aucune clé étrangère.
|
|
79
80
|
|
|
80
81
|
La table exacte, par moteur, telle que CETTE version l'écrit :
|
|
@@ -108,7 +109,7 @@ tests — sans table ni migration (la collection naît à la première écriture
|
|
|
108
109
|
les options qui ont un sens ici :
|
|
109
110
|
|
|
110
111
|
```bash
|
|
111
|
-
npx nodefony create entity Post title:string(120) body:text? views:int=0 status:enum(draft,published)=draft slug:string:unique tags:json? author:ref:User --soft-delete --route /api/posts --dry-run
|
|
112
|
+
npx nodefony create entity Post "title:string(120)" "body:text?" views:int=0 "status:enum(draft,published)=draft" slug:string:unique "tags:json?" author:ref:User --soft-delete --route /api/posts --dry-run
|
|
112
113
|
```
|
|
113
114
|
|
|
114
115
|
- La clé est l'`_id` natif, servie en `id` ; `ref:<Entité>` est un `ObjectId` indexé, chargé
|
|
@@ -121,6 +122,17 @@ npx nodefony create entity Post title:string(120) body:text? views:int=0 status:
|
|
|
121
122
|
`--id`, `--index`, `--unique`. Un index composite s'écrit à la main dans le schéma.
|
|
122
123
|
- `User` ne se régénère pas : il s'étend dans `nodefony/entity/User.ts` (son TSDoc donne le geste).
|
|
123
124
|
|
|
125
|
+
## Le graphe d'entités de la console
|
|
126
|
+
|
|
127
|
+
Les liens du graphe viennent des `ref:` eux-mêmes : la clé étrangère que la base porte devient
|
|
128
|
+
une arête, sans rien répéter ailleurs. Le regroupement et la couleur suivent le **domaine** de
|
|
129
|
+
l'entité, sinon son module — et une application de centaines de tables tient souvent dans UN
|
|
130
|
+
module :
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npx nodefony create entity Invoice "number:string(30):unique" "total:decimal(24,8)" customer:ref:Customer --domain facturation --dry-run
|
|
134
|
+
```
|
|
135
|
+
|
|
124
136
|
## Épouser une table qui existe déjà
|
|
125
137
|
|
|
126
138
|
Trois réglages, et ils ne touchent **que** le SQL — la propriété TypeScript reste `id`, `siteId` :
|
|
@@ -200,8 +212,11 @@ refus tombe tout seul.
|
|
|
200
212
|
`--no-migrate` s'en dispense ; si des entités modifiées attendaient, la migration est écrite mais
|
|
201
213
|
PAS appliquée, et la sortie nomme les tables en trop. Sinon, la table naît au prochain démarrage
|
|
202
214
|
en développement (`CREATE TABLE IF NOT EXISTS`).
|
|
203
|
-
2. **La modifier
|
|
204
|
-
|
|
215
|
+
2. **La modifier passe par une migration** dès que l'application versionne ses migrations :
|
|
216
|
+
`npx nodefony orm:generate --name <nom> --apply` (skill **`nodefony-migrate-schema`**). Sans
|
|
217
|
+
migrations versionnées, le démarrage en développement rattrape seul une colonne ajoutée qui
|
|
218
|
+
**accepte le vide** (`ADD COLUMN`, annoncé) ; une colonne **obligatoire** ne l'est jamais — le
|
|
219
|
+
démarrage nomme l'écart au lieu d'inventer la valeur des lignes existantes.
|
|
205
220
|
3. **La production ne fabrique JAMAIS le schéma.** Elle l'applique par des migrations, écrites par
|
|
206
221
|
`npx nodefony orm:generate` et posées par `npx nodefony orm:migrate` — c'est un geste à part,
|
|
207
222
|
avec ses refus et ses interdits : skill **`nodefony-migrate-schema`**.
|
|
@@ -218,7 +233,7 @@ geste :
|
|
|
218
233
|
- **nom réservé par un module du framework** (`session`, `access_token`, `audit_event`…) — un
|
|
219
234
|
homonyme dépossède le module, et l'application ne démarre plus sur un message parlant d'une
|
|
220
235
|
colonne inconnue. **`User` fait exception** : l'identité appartient à l'application, et
|
|
221
|
-
`create entity User firstName:string(100)
|
|
236
|
+
`create entity User "firstName:string(100)?"` écrit l'entité avec les colonnes du contrat plus
|
|
222
237
|
les tiennes. Trois refus s'y appliquent alors — renommer la table ou changer la casse des
|
|
223
238
|
colonnes (des requêtes les écrivent en dur), la poser ailleurs que dans l'application racine
|
|
224
239
|
(l'ordre de chargement n'y est pas garanti), et déclarer un champ obligatoire **sans valeur par
|