@nodefony/http 10.0.0-beta.6 → 10.0.0-beta.7

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 (96) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.157.0 → _@oxc-project_runtime@0.158.0}/helpers/esm/decorate.js +1 -1
  2. package/dist/_virtual/{_@oxc-project_runtime@0.157.0 → _@oxc-project_runtime@0.158.0}/helpers/esm/decorateParam.js +1 -1
  3. package/dist/index.js +3 -2
  4. package/dist/nodefony/config/config.js +25 -2
  5. package/dist/nodefony/grpc/GrpcCall.js +298 -0
  6. package/dist/nodefony/grpc/GrpcEndpoint.js +528 -0
  7. package/dist/nodefony/grpc/index.js +4 -0
  8. package/dist/nodefony/interfaces/IRpcCall.js +1 -0
  9. package/dist/nodefony/service/audit-logger.js +52 -6
  10. package/dist/nodefony/service/error-renderer.js +41 -9
  11. package/dist/nodefony/service/http-kernel.js +198 -19
  12. package/dist/nodefony/service/pretty-request-logger.js +5 -3
  13. package/dist/nodefony/service/request-logger.js +21 -6
  14. package/dist/nodefony/service/reverse-proxy.js +1 -1
  15. package/dist/nodefony/service/servers/clientError.js +26 -1
  16. package/dist/nodefony/service/servers/server-h2c.js +3 -3
  17. package/dist/nodefony/service/servers/server-http.js +3 -3
  18. package/dist/nodefony/service/servers/server-https.js +4 -4
  19. package/dist/nodefony/service/servers/server-websocket-secure.js +3 -3
  20. package/dist/nodefony/service/servers/server-websocket.js +3 -3
  21. package/dist/nodefony/service/servers/serverShutdown.js +14 -36
  22. package/dist/nodefony/service/sessions/sessions-service.js +6 -6
  23. package/dist/nodefony/service/upload/upload-service.js +2 -2
  24. package/dist/nodefony/src/context/Context.js +12 -1
  25. package/dist/nodefony/src/context/http/ByteStream.js +9 -4
  26. package/dist/nodefony/src/context/http/HttpContext.js +86 -8
  27. package/dist/nodefony/src/context/http/Request.js +96 -16
  28. package/dist/nodefony/src/context/http/Response.js +120 -17
  29. package/dist/nodefony/src/context/http/SseStream.js +4 -4
  30. package/dist/nodefony/src/context/http/StreamLifecycle.js +328 -19
  31. package/dist/nodefony/src/context/http/parser.js +29 -11
  32. package/dist/nodefony/src/context/http/webResponse.js +56 -0
  33. package/dist/nodefony/src/context/http2/Response.js +47 -8
  34. package/dist/nodefony/src/context/requestTrace.js +6 -2
  35. package/dist/nodefony/src/context/rpcOutcome.js +20 -0
  36. package/dist/nodefony/src/context/websocket/Response.js +11 -1
  37. package/dist/nodefony/src/context/websocket/WebsocketContext.js +15 -4
  38. package/dist/nodefony/src/cookies/cookie.js +3 -1
  39. package/dist/nodefony/src/errors/httpError.js +58 -25
  40. package/dist/nodefony/src/grpc/cors.js +59 -0
  41. package/dist/nodefony/src/grpc/limits.js +16 -0
  42. package/dist/nodefony/src/grpc/refusal.js +271 -0
  43. package/dist/nodefony/src/profiler/aggregate.js +2 -2
  44. package/dist/nodefony/src/proxy/forward.js +1 -1
  45. package/dist/nodefony/src/proxy/generateProxyConfig.js +91 -15
  46. package/dist/nodefony/src/servers/http2Goaway.js +76 -0
  47. package/dist/nodefony/src/servers/http2Limits.js +41 -15
  48. package/dist/nodefony/src/servers/http2StreamGrace.js +100 -0
  49. package/dist/nodefony/src/servers/receiveDeadline.js +177 -0
  50. package/dist/types/index.d.ts +5 -2
  51. package/dist/types/nodefony/config/config.d.ts +10 -0
  52. package/dist/types/nodefony/grpc/GrpcCall.d.ts +142 -0
  53. package/dist/types/nodefony/grpc/GrpcEndpoint.d.ts +98 -0
  54. package/dist/types/nodefony/grpc/index.d.ts +15 -0
  55. package/dist/types/nodefony/interfaces/IContext.d.ts +9 -0
  56. package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +14 -0
  57. package/dist/types/nodefony/interfaces/IRequest.d.ts +2 -1
  58. package/dist/types/nodefony/interfaces/IRequestTrace.d.ts +10 -0
  59. package/dist/types/nodefony/interfaces/IRouting.d.ts +19 -0
  60. package/dist/types/nodefony/interfaces/IRpcCall.d.ts +83 -0
  61. package/dist/types/nodefony/interfaces/IUpload.d.ts +51 -4
  62. package/dist/types/nodefony/interfaces/index.d.ts +2 -1
  63. package/dist/types/nodefony/service/audit-logger.d.ts +33 -3
  64. package/dist/types/nodefony/service/error-renderer.d.ts +22 -1
  65. package/dist/types/nodefony/service/http-kernel.d.ts +21 -1
  66. package/dist/types/nodefony/service/request-logger.d.ts +10 -0
  67. package/dist/types/nodefony/service/servers/serverShutdown.d.ts +2 -2
  68. package/dist/types/nodefony/src/context/Context.d.ts +9 -1
  69. package/dist/types/nodefony/src/context/http/ByteStream.d.ts +1 -1
  70. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +56 -2
  71. package/dist/types/nodefony/src/context/http/Request.d.ts +38 -8
  72. package/dist/types/nodefony/src/context/http/Response.d.ts +61 -7
  73. package/dist/types/nodefony/src/context/http/SseStream.d.ts +1 -1
  74. package/dist/types/nodefony/src/context/http/StreamLifecycle.d.ts +53 -0
  75. package/dist/types/nodefony/src/context/http/parser.d.ts +17 -3
  76. package/dist/types/nodefony/src/context/http/webResponse.d.ts +31 -0
  77. package/dist/types/nodefony/src/context/http2/Response.d.ts +35 -2
  78. package/dist/types/nodefony/src/context/requestTrace.d.ts +3 -1
  79. package/dist/types/nodefony/src/context/rpcOutcome.d.ts +11 -0
  80. package/dist/types/nodefony/src/context/websocket/Response.d.ts +6 -1
  81. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +8 -0
  82. package/dist/types/nodefony/src/errors/httpError.d.ts +29 -4
  83. package/dist/types/nodefony/src/grpc/cors.d.ts +32 -0
  84. package/dist/types/nodefony/src/grpc/limits.d.ts +14 -0
  85. package/dist/types/nodefony/src/grpc/refusal.d.ts +136 -0
  86. package/dist/types/nodefony/src/proxy/forward.d.ts +7 -0
  87. package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +15 -2
  88. package/dist/types/nodefony/src/servers/http2Goaway.d.ts +41 -0
  89. package/dist/types/nodefony/src/servers/http2Limits.d.ts +27 -14
  90. package/dist/types/nodefony/src/servers/http2StreamGrace.d.ts +34 -0
  91. package/dist/types/nodefony/src/servers/receiveDeadline.d.ts +77 -0
  92. package/docs/byte-stream.md +52 -17
  93. package/docs/diagnostics-channel.md +45 -1
  94. package/docs/servers.md +57 -44
  95. package/docs/upload.md +43 -17
  96. package/package.json +26 -6
package/docs/upload.md CHANGED
@@ -297,6 +297,30 @@ du calque de configuration — une application peut les changer pour une requêt
297
297
  sont lus au moment de lire le corps (`Request.readBodyLimits()`), donc un calque posé après ce moment
298
298
  (au pare-feu, dans un contrôleur) n'agit pas. Détail : [configuration](../../../../../docs/guides/configuration.md).
299
299
 
300
+ ### Par route — l'option `upload`
301
+
302
+ Une route règle ses propres bornes d'envoi, sans toucher à celles des autres : un dépôt de vidéos
303
+ relève `maxFileSize`, un avatar l'abaisse. Chaque clé déclarée remplace celle de la configuration,
304
+ **dans les deux sens** ; une clé absente garde la valeur de la configuration
305
+ (`framework/nodefony/src/Route.ts#RouteOptions.upload`).
306
+
307
+ ```ts ignore
308
+ // un avatar : 256 Kio au plus, un seul fichier, son empreinte calculée pendant le flux
309
+ @Post("/avatar", { upload: { maxFileSize: 262_144, maxFiles: 1, hashAlgorithm: "sha256" } })
310
+ async avatar(@UploadedFile() file: IUploadedFile | undefined) {
311
+ return { size: file?.size ?? 0, sha256: file?.hash ?? null };
312
+ }
313
+ ```
314
+
315
+ - **Le fichier trop gros est coupé PENDANT son flux** (413) : la route est résolue avant la lecture
316
+ du corps, ses bornes s'appliquent donc à busboy comme celles de la configuration — jamais un
317
+ fichier écrit en entier puis refusé (`context/http/Request.ts#HttpRequest.readBodyLimits`).
318
+ - **Une route sans l'option ne paie rien** : aucune fusion, aucune allocation.
319
+ - **Validée à la déclaration** (`framework/nodefony/decorators/routerDecorators.ts#checkedUpload`) :
320
+ entiers ≥ 1, empreinte connue, aucune clé inconnue — une faute de frappe (`maxFilesize`) laisserait
321
+ sinon la route sur la borne de l'application. `uploadDir` et `encoding` restent ceux de la
322
+ configuration. Exclue avec `bodyStream: true`, dont le corps n'est jamais lu par le pipeline.
323
+
300
324
  ### Fichiers multipart — `upload.*`
301
325
 
302
326
  Table dérivée de `uploadSchema` (`config/config.ts#uploadSchema`). Toutes les bornes sont `runtimeMutable`.
@@ -342,15 +366,15 @@ Les décorateurs `@UploadedFile` / `@UploadedFiles` sont des fabriques de param
342
366
  Chaque entrée de `files` est un `UploadedFile` (`service/upload/upload-service.ts#UploadedFile`), déjà écrit
343
367
  sur disque. Ses membres utiles :
344
368
 
345
- | Membre | Ce qu'il donne | Ancrage |
346
- | ------------------------ | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
347
- | `filename` | Nom **d'origine** déclaré par le client (métadonnée, jamais le chemin). | `realName()` (`service/upload/upload-service.ts#UploadedFile.realName`) |
348
- | `size` | Taille réellement écrite (octets). | `getSize()` (`service/upload/upload-service.ts#UploadedFile.getSize`) |
349
- | `prettySize` | Taille lisible (`« 1.2 MB »`). | `getPrettySize()` (`service/upload/upload-service.ts#UploadedFile.getPrettySize`) |
350
- | `mimeType` | Type MIME déclaré (`image/png`…), sinon deviné de l'extension. | `getMimeType()` (`service/upload/upload-service.ts#UploadedFile.getMimeType`) |
351
- | `hash` / `hashAlgorithm` | Empreinte d'intégrité si `upload.hashAlgorithm` est réglé. | `interfaces/IUpload.ts#IUploadedFile.hashAlgorithm` |
352
- | `moveAsync(target)` | Déplace le temp — **non bloquant, recommandé** dans le pipeline. | `moveAsync()` (`service/upload/upload-service.ts#UploadedFile.moveAsync`) |
353
- | `move(target)` | Variante **synchrone** (compat) — bloque l'event-loop. | `move()` (`service/upload/upload-service.ts#UploadedFile.move`) |
369
+ | Membre | Ce qu'il donne | Ancrage |
370
+ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
371
+ | `filename` | Nom **d'origine** déclaré par le client (métadonnée, jamais le chemin). | `realName()` (`service/upload/upload-service.ts#UploadedFile.realName`) |
372
+ | `size` | Taille réellement écrite (octets). | `getSize()` (`service/upload/upload-service.ts#UploadedFile.getSize`) |
373
+ | `prettySize` | Taille lisible (`« 1.2 MB »`). | `getPrettySize()` (`service/upload/upload-service.ts#UploadedFile.getPrettySize`) |
374
+ | `mimeType` | Type MIME déclaré (`image/png`…), sinon deviné de l'extension ; `false` si ni l'un ni l'autre. Déclaratif : jamais une preuve du contenu. | `getMimeType()` (`service/upload/upload-service.ts#UploadedFile.getMimeType`) |
375
+ | `hash` / `hashAlgorithm` | Empreinte d'intégrité si `upload.hashAlgorithm` est réglé. | `interfaces/IUpload.ts#IUploadedFile.hashAlgorithm` |
376
+ | `moveAsync(target)` | Déplace le temp — **non bloquant, recommandé** dans le pipeline. Rend le `FileClass` de la destination (sans nom d'origine ni empreinte : ils restent sur le fichier reçu). | `moveAsync()` (`service/upload/upload-service.ts#UploadedFile.moveAsync`) |
377
+ | `move(target)` | Variante **synchrone** (compat) — bloque l'event-loop. | `move()` (`service/upload/upload-service.ts#UploadedFile.move`) |
354
378
 
355
379
  `move`/`moveAsync` acceptent un **fichier cible** ou un **dossier existant** : sur un dossier, la
356
380
  destination est bâtie avec `filename` — d'où l'avertissement de sécurité ci-dessous.
@@ -429,6 +453,7 @@ pipeline : `npm run test:memory` (skill `nodefony-check-memory-health`).
429
453
  | `@Body()` est vide sur un upload de fichier | Les fichiers vont dans `files`, **pas** dans `body` | Lire les fichiers avec `@UploadedFiles()` / `this.files` ; `@Body()` = champs texte |
430
454
  | Un `POST` JSON de 1,5 Mo est refusé en `413` | `maxBodySize` vaut **1 MiB** par défaut | Augmenter `maxBodySize` (pas `upload.maxFileSize`) |
431
455
  | Un gros fichier passe alors que `maxBodySize` est petit | Le multipart **n'écoute pas** `maxBodySize` | Régler `upload.maxFileSize` / `maxTotalFileSize` |
456
+ | Un avatar de 2 Go remplit le disque avant d'être refusé | La borne de l'application (500 Mo) vaut pour toutes les routes | Option de route `upload: { maxFileSize }` — coupé pendant le flux |
432
457
  | `413` sur upload sans message clair | Une borne busboy atteinte en streaming (fichier, cumul, nombre) | Vérifier les bornes `upload.*` — 413 émis sur `stream.on("limit")` (`context/http/Request.ts#HttpRequest.enforceBodyLimit`) |
433
458
  | Un fichier écrit `../../etc/…` après un `move` | `move(dossier)` utilise le nom **client** (`upload-service.ts#UploadedFile.move`) | Passer une cible complète, ou `path.basename(file.filename)` |
434
459
  | Des fichiers temporaires s'accumulent dans `uploadDir` | Le contrôleur ne déplace jamais le temp | Appeler `moveAsync()` (ou purger l'ancien temp par TTL) |
@@ -441,14 +466,15 @@ pipeline : `npm run test:memory` (skill `nodefony-check-memory-health`).
441
466
  Les chiffres exacts vivent dans la carte de tests de cette page (régénérée depuis vitest, jamais figés
442
467
  dans le Markdown).
443
468
 
444
- | Type | Où |
445
- | --------------------------- | ------------------------------------------------------------------------------------------------------------------- |
446
- | Unitaires — `UploadedFile` | `unit/UploadedFile.test.ts` — `create()` async, `move`/`moveAsync`, `mimeType`, `realName` |
447
- | Unitaires — `@Body` stream | `framework` `unit/BodyStream.test.ts` — `@Body({ stream })`, `resolveParamArg`, `routeExpectsBodyStream` |
448
- | Intégration — multipart | `http/upload.test.ts` — upload simple, multi-fichiers, mime non-octet, champ + fichier, `413` par fichier et cumulé |
449
- | Intégration — content-types | `http/body-content-types.test.ts` — JSON, urlencoded, `@Body("champ")`, XML → `body` |
450
- | Intégration — bornes | `http/body-limit.test.ts` — sous la limite `200`, `Content-Length` > 1 MiB → `413`, chunked > 1 MiB refusé |
451
- | Intégration — flux brut | `integration/bodyStream.test.ts` — `@Body({ stream })` bout-en-bout (TLS + clair), non-régression `@Body()` |
469
+ | Type | Où |
470
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
471
+ | Unitaires — `UploadedFile` | `unit/UploadedFile.test.ts` — `create()` async, `move`/`moveAsync`, `mimeType`, `realName` |
472
+ | Unitaires — `@Body` stream | `framework` `unit/BodyStream.test.ts` — `@Body({ stream })`, `resolveParamArg`, `routeExpectsBodyStream` |
473
+ | Intégration — multipart | `http/upload.test.ts` — upload simple, multi-fichiers, mime non-octet, champ + fichier, `413` par fichier et cumulé ; bornes et empreinte PAR ROUTE (abaissée, relevée), aucun temporaire laissé par un refus |
474
+ | Unitaires — option `upload` | `framework` `unit/httpMethodDecorators.test.ts` — portée jusqu'à la route, figée ; refus à la déclaration |
475
+ | Intégration — content-types | `http/body-content-types.test.ts` — JSON, urlencoded, `@Body("champ")`, XML → `body` |
476
+ | Intégration — bornes | `http/body-limit.test.ts` — sous la limite `200`, `Content-Length` > 1 MiB → `413`, chunked > 1 MiB refusé |
477
+ | Intégration — flux brut | `integration/bodyStream.test.ts` — `@Body({ stream })` bout-en-bout (TLS + clair), non-régression `@Body()` |
452
478
 
453
479
  Ce qui **manque** aujourd'hui : pas de banc de **charge** dédié à l'upload (le streaming multipart n'est
454
480
  exercé que fonctionnellement), pas de test **d'attaque** (`*.attack.test.ts`) sur le path traversal de
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodefony/http",
3
- "version": "10.0.0-beta.6",
3
+ "version": "10.0.0-beta.7",
4
4
  "description": "Serveurs HTTP, HTTPS, HTTP/2 et WebSocket natifs pour Nodefony : sessions, contextes de requête, certificats TLS",
5
5
  "author": "Christophe CAMENSULI <ccamensuli@gmail.com>",
6
6
  "main": "./dist/index.js",
@@ -12,6 +12,12 @@
12
12
  "types": "./dist/types/index.d.ts",
13
13
  "import": "./dist/index.js",
14
14
  "default": "./dist/index.js"
15
+ },
16
+ "./grpc": {
17
+ "nodefony-source": "./nodefony/grpc/index.ts",
18
+ "types": "./dist/types/nodefony/grpc/index.d.ts",
19
+ "import": "./dist/nodefony/grpc/index.js",
20
+ "default": "./dist/nodefony/grpc/index.js"
15
21
  }
16
22
  },
17
23
  "scripts": {
@@ -27,7 +33,7 @@
27
33
  },
28
34
  "private": false,
29
35
  "engines": {
30
- "node": ">=24.14.0"
36
+ "node": "^24.16.0 || >=26.0.0"
31
37
  },
32
38
  "keywords": [
33
39
  "nodefony",
@@ -63,7 +69,7 @@
63
69
  "qs": "^6.17.1",
64
70
  "serve-static": "2.2.1",
65
71
  "tslib": "2.9.1",
66
- "ws": "8.22.1",
72
+ "ws": "8.23.0",
67
73
  "xml2js": "0.6.2"
68
74
  },
69
75
  "devDependencies": {
@@ -72,7 +78,7 @@
72
78
  "@types/node": "26.6.6",
73
79
  "@types/websocket": "1.0.10",
74
80
  "@vitest/coverage-v8": "5.1.0",
75
- "nodefony": "^10.0.0-beta.6",
81
+ "nodefony": "^10.0.0-beta.7",
76
82
  "tsx": "4.23.15",
77
83
  "vitest": "5.1.0"
78
84
  },
@@ -80,9 +86,23 @@
80
86
  "readmeFilename": "README.md",
81
87
  "contributors": [],
82
88
  "peerDependencies": {
83
- "nodefony": "^10.0.0-beta.6",
89
+ "@bufbuild/protobuf": "^2.7.0",
90
+ "@connectrpc/connect": "^2.3.0",
91
+ "@connectrpc/connect-node": "^2.3.0",
92
+ "nodefony": "^10.0.0-beta.7",
84
93
  "zod": "^4.6.7"
85
94
  },
95
+ "peerDependenciesMeta": {
96
+ "@bufbuild/protobuf": {
97
+ "optional": true
98
+ },
99
+ "@connectrpc/connect": {
100
+ "optional": true
101
+ },
102
+ "@connectrpc/connect-node": {
103
+ "optional": true
104
+ }
105
+ },
86
106
  "files": [
87
107
  "dist",
88
108
  "docs"
@@ -90,7 +110,7 @@
90
110
  "publishConfig": {
91
111
  "access": "public"
92
112
  },
93
- "homepage": "https://nodefony.github.io/nodefony-core/",
113
+ "homepage": "https://nodefony.net/",
94
114
  "bugs": {
95
115
  "url": "https://github.com/nodefony/nodefony-core/issues"
96
116
  }