@nodefony/http 10.0.0-alpha.9 → 10.0.0-beta.2

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 (100) hide show
  1. package/README.md +12 -3
  2. package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateMetadata.js +1 -1
  4. package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateParam.js +1 -1
  5. package/dist/index.js +6 -4
  6. package/dist/nodefony/command/assetsPublishCommand.js +1 -1
  7. package/dist/nodefony/command/certificatesCommand.js +1 -2
  8. package/dist/nodefony/command/networkCommand.js +1 -1
  9. package/dist/nodefony/command/proxyGenerateCommand.js +1 -1
  10. package/dist/nodefony/config/config.js +26 -4
  11. package/dist/nodefony/interfaces/IRouting.js +1 -0
  12. package/dist/nodefony/interfaces/ISecurity.js +1 -0
  13. package/dist/nodefony/service/ProfilerAdminApi.js +2 -1
  14. package/dist/nodefony/service/audit-logger.js +16 -13
  15. package/dist/nodefony/service/certificates.js +87 -144
  16. package/dist/nodefony/service/error-renderer.js +11 -8
  17. package/dist/nodefony/service/http-kernel.js +342 -165
  18. package/dist/nodefony/service/pretty-request-logger.js +3 -10
  19. package/dist/nodefony/service/servers/clientError.js +2 -2
  20. package/dist/nodefony/service/servers/server-http.js +23 -23
  21. package/dist/nodefony/service/servers/server-https.js +41 -42
  22. package/dist/nodefony/service/servers/server-static.js +13 -17
  23. package/dist/nodefony/service/servers/server-websocket-secure.js +11 -10
  24. package/dist/nodefony/service/servers/server-websocket.js +11 -10
  25. package/dist/nodefony/service/servers/wsHeartbeat.js +2 -2
  26. package/dist/nodefony/service/sessions/sessions-service.js +22 -15
  27. package/dist/nodefony/service/trace.js +3 -2
  28. package/dist/nodefony/service/upload/upload-service.js +11 -10
  29. package/dist/nodefony/service/x509.js +324 -0
  30. package/dist/nodefony/src/assets/prebuiltUi.js +1 -1
  31. package/dist/nodefony/src/context/Context.js +34 -19
  32. package/dist/nodefony/src/context/forwarded.js +4 -4
  33. package/dist/nodefony/src/context/http/HttpContext.js +133 -88
  34. package/dist/nodefony/src/context/http/Request.js +108 -46
  35. package/dist/nodefony/src/context/http/Response.js +134 -54
  36. package/dist/nodefony/src/context/http/parser.js +9 -10
  37. package/dist/nodefony/src/context/http/urlFastPath.js +35 -11
  38. package/dist/nodefony/src/context/http2/Response.js +53 -25
  39. package/dist/nodefony/src/context/metaData.js +1 -1
  40. package/dist/nodefony/src/context/trustProxy.js +4 -3
  41. package/dist/nodefony/src/context/websocket/Response.js +13 -6
  42. package/dist/nodefony/src/context/websocket/WebsocketContext.js +59 -26
  43. package/dist/nodefony/src/context/websocket/wsCloseReason.js +41 -0
  44. package/dist/nodefony/src/cookies/cookie.js +3 -3
  45. package/dist/nodefony/src/errors/httpError.js +6 -6
  46. package/dist/nodefony/src/errors/thrownMessage.js +27 -0
  47. package/dist/nodefony/src/profiler/FrameProfile.js +4 -3
  48. package/dist/nodefony/src/profiler/Profiler.js +4 -4
  49. package/dist/nodefony/src/servers/kernelServers.js +29 -0
  50. package/dist/nodefony/src/servers/portBinder.js +65 -23
  51. package/dist/nodefony/src/session/session.js +18 -10
  52. package/dist/nodefony/src/session/sessionStoreFailure.js +2 -1
  53. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +24 -9
  54. package/dist/types/index.d.ts +16 -0
  55. package/dist/types/nodefony/config/config.d.ts +23 -0
  56. package/dist/types/nodefony/interfaces/IContext.d.ts +24 -7
  57. package/dist/types/nodefony/interfaces/ICookie.d.ts +18 -18
  58. package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -4
  59. package/dist/types/nodefony/interfaces/IResponse.d.ts +1 -1
  60. package/dist/types/nodefony/interfaces/IRouting.d.ts +87 -0
  61. package/dist/types/nodefony/interfaces/ISecurity.d.ts +45 -0
  62. package/dist/types/nodefony/interfaces/ISession.d.ts +8 -8
  63. package/dist/types/nodefony/interfaces/IUpload.d.ts +9 -9
  64. package/dist/types/nodefony/interfaces/index.d.ts +2 -0
  65. package/dist/types/nodefony/service/audit-logger.d.ts +14 -10
  66. package/dist/types/nodefony/service/certificates.d.ts +42 -57
  67. package/dist/types/nodefony/service/http-kernel.d.ts +192 -42
  68. package/dist/types/nodefony/service/servers/server-http.d.ts +4 -1
  69. package/dist/types/nodefony/service/servers/server-https.d.ts +4 -1
  70. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +4 -1
  71. package/dist/types/nodefony/service/servers/server-websocket.d.ts +4 -1
  72. package/dist/types/nodefony/service/sessions/sessions-service.d.ts +6 -2
  73. package/dist/types/nodefony/service/upload/upload-service.d.ts +1 -1
  74. package/dist/types/nodefony/service/x509.d.ts +86 -0
  75. package/dist/types/nodefony/src/context/Context.d.ts +21 -10
  76. package/dist/types/nodefony/src/context/forwarded.d.ts +3 -3
  77. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +75 -18
  78. package/dist/types/nodefony/src/context/http/Request.d.ts +39 -2
  79. package/dist/types/nodefony/src/context/http/Response.d.ts +47 -7
  80. package/dist/types/nodefony/src/context/http/parser.d.ts +1 -1
  81. package/dist/types/nodefony/src/context/http2/Response.d.ts +32 -2
  82. package/dist/types/nodefony/src/context/websocket/Response.d.ts +4 -4
  83. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +11 -2
  84. package/dist/types/nodefony/src/context/websocket/wsCloseReason.d.ts +21 -0
  85. package/dist/types/nodefony/src/cookies/cookie.d.ts +9 -9
  86. package/dist/types/nodefony/src/errors/httpError.d.ts +7 -7
  87. package/dist/types/nodefony/src/errors/thrownMessage.d.ts +12 -0
  88. package/dist/types/nodefony/src/profiler/Profiler.d.ts +28 -28
  89. package/dist/types/nodefony/src/servers/kernelServers.d.ts +20 -0
  90. package/dist/types/nodefony/src/servers/portBinder.d.ts +16 -65
  91. package/dist/types/nodefony/src/session/session.d.ts +12 -12
  92. package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +1 -1
  93. package/docs/cookies.md +25 -25
  94. package/docs/index.md +4 -2
  95. package/docs/observabilite.md +27 -27
  96. package/docs/rate-limit.md +20 -20
  97. package/docs/servers.md +82 -79
  98. package/docs/session.md +74 -69
  99. package/docs/upload.md +49 -43
  100. package/package.json +9 -12
@@ -1,18 +1,18 @@
1
- import type pkg from "node-forge";
2
1
  import { Service, Module } from "nodefony";
3
- /**
4
- * Type-valeur du backend node-forge runtime (objet `.pki`/`.md`). L'import type
5
- * `pkg` sert pour les namespaces de types (`pkg.pki.Certificate`) ; `ForgeModule`
6
- * pour la valeur chargée dynamiquement (node-forge est un module `export =`).
7
- */
8
- type ForgeModule = typeof import("node-forge");
2
+ import { type KeyObject } from "node:crypto";
3
+ import { type ICertificateAttribute } from "./x509.js";
4
+ /** Paire de clés RSA du certificat auto-signé. */
5
+ export interface IRsaKeyPair {
6
+ privateKey: KeyObject;
7
+ publicKey: KeyObject;
8
+ }
9
9
  /** Algorithmes de hachage de signature autorisés (SHA-1 banni). */
10
10
  export type CertHash = "sha256" | "sha384" | "sha512";
11
11
  /** Stratégie de fourniture du certificat exposée en configuration. */
12
12
  export type CertStrategyConfig = "auto" | "mkcert" | "selfsigned" | "explicit";
13
13
  /**
14
- * Options de génération du certificat AUTO-SIGNÉ (node-forge, JavaScript pur —
15
- * aucun binaire externe n'est invoqué).
14
+ * Options de génération du certificat AUTO-SIGNÉ (`node:crypto` — aucun
15
+ * binaire externe ni aucune dépendance tierce).
16
16
  *
17
17
  * ⚠️ Portée : ces réglages ne valent QUE pour la stratégie `selfsigned`. Sous
18
18
  * `mkcert` (le défaut en développement) ils sont ignorés — mkcert ne reçoit que
@@ -29,7 +29,7 @@ export interface SelfSignedOptions {
29
29
  /** Recul de `notBefore` (minutes) — tolérance au décalage d'horloge client. */
30
30
  backdateMinutes: number;
31
31
  /** Attributs du sujet/issuer (commonName, organizationName…). */
32
- attrs: pkg.pki.CertificateField[];
32
+ attrs: ICertificateAttribute[];
33
33
  }
34
34
  /** Options de génération du certificat TLS en mode développement. */
35
35
  export interface CertificateDevOptions {
@@ -37,7 +37,7 @@ export interface CertificateDevOptions {
37
37
  * Préférer `mkcert` (CA locale ajoutée au trust store système) pour générer
38
38
  * le certificat de dev. Donne un HTTPS sans erreur navigateur — indispensable
39
39
  * pour les sous-ressources cross-origin (Vite) et le WSS du HMR.
40
- * Si `mkcert` est absent, on retombe sur un auto-signé node-forge (non trusté).
40
+ * Si `mkcert` est absent, on retombe sur un auto-signé (non trusté).
41
41
  * Ignoré hors `development`.
42
42
  */
43
43
  useMkcert: boolean;
@@ -56,19 +56,19 @@ export interface CertificateOptions {
56
56
  * est un confort de DÉVELOPPEMENT — en production, fournir un vrai certificat
57
57
  * (Let's Encrypt, ingress, reverse-proxy) : Nodefony n'est pas une CA.
58
58
  */
59
- strategy?: CertStrategyConfig;
59
+ strategy?: CertStrategyConfig | undefined;
60
60
  selfSigned: SelfSignedOptions;
61
61
  dev: CertificateDevOptions;
62
- san?: CertificateSanOptions;
62
+ san?: CertificateSanOptions | undefined;
63
63
  /** Permissions POSIX de la clé privée écrite (0600 = owner-only). */
64
- privateKeyMode?: number;
65
- path?: string;
66
- keyPath?: string;
67
- certPath?: string;
68
- caPath?: string;
69
- key?: string | Buffer;
70
- cert?: string | Buffer;
71
- ca?: string | Buffer;
64
+ privateKeyMode?: number | undefined;
65
+ path?: string | undefined;
66
+ keyPath?: string | undefined;
67
+ certPath?: string | undefined;
68
+ caPath?: string | undefined;
69
+ key?: string | Buffer | undefined;
70
+ cert?: string | Buffer | undefined;
71
+ ca?: string | Buffer | undefined;
72
72
  }
73
73
  interface filesCertType {
74
74
  path: string;
@@ -96,7 +96,7 @@ export interface CertificateInfo {
96
96
  *
97
97
  * Trois stratégies : `explicit` (certificat fourni en config — le cas de
98
98
  * PRODUCTION), `mkcert` (CA locale trustée, confort de dev) et `selfsigned`
99
- * (auto-signé node-forge, secours). La génération est réservée au
99
+ * (auto-signé, secours). La génération est réservée au
100
100
  * DÉVELOPPEMENT : en production sans certificat fourni, le service crie un
101
101
  * avertissement (Nodefony n'est pas une autorité de certification).
102
102
  *
@@ -108,8 +108,7 @@ export interface CertificateInfo {
108
108
  declare class Certificate extends Service {
109
109
  module: Module;
110
110
  files: filesCertType[];
111
- keysPair: pkg.pki.rsa.KeyPair | null;
112
- certForge: pkg.pki.Certificate | null;
111
+ keysPair: IRsaKeyPair | null;
113
112
  ca: Buffer | string | null;
114
113
  key: Buffer | string | null;
115
114
  cert: Buffer | string | null;
@@ -119,13 +118,6 @@ declare class Certificate extends Service {
119
118
  intermediateCertPem: Buffer | string | null;
120
119
  /** CAROOT résolu de mkcert (null tant que non détecté / indisponible). */
121
120
  private mkcertCaRoot;
122
- /**
123
- * Backend crypto node-forge, chargé PARESSEUSEMENT. La génération de
124
- * certificat est un confort de DÉVELOPPEMENT : en production avec un
125
- * certificat fourni (`strategy: "explicit"`), cette grosse dépendance n'est
126
- * jamais importée (règle perf-mémoire — pas de dep chargée « au cas où »).
127
- */
128
- private forge;
129
121
  path: string;
130
122
  serverPath: string;
131
123
  caPath: string;
@@ -138,14 +130,6 @@ declare class Certificate extends Service {
138
130
  private get certOptions();
139
131
  /** Vrai en environnement de développement (mkcert réservé à ce mode). */
140
132
  private isDev;
141
- /**
142
- * Charge node-forge à la demande (idempotent) — uniquement sur le chemin de
143
- * GÉNÉRATION (selfsigned / mkcert / inspection). Le chemin `explicit` (prod)
144
- * ne l'appelle jamais.
145
- */
146
- loadForge(): Promise<ForgeModule>;
147
- /** Accès au backend forge déjà chargé (lève si `loadForge` n'a pas été appelé). */
148
- private get forgeLib();
149
133
  /**
150
134
  * Fabrique-t-on un certificat au démarrage ?
151
135
  *
@@ -182,7 +166,7 @@ declare class Certificate extends Service {
182
166
  /**
183
167
  * Génère (ou recharge) le certificat serveur selon la stratégie résolue :
184
168
  * `explicit` (fourni en config / prod), `mkcert` (dev, CA trustée) ou
185
- * `selfsigned` (auto-signé node-forge, secours). Régénère automatiquement si
169
+ * `selfsigned` (auto-signé, secours). Régénère automatiquement si
186
170
  * le certificat présent sur disque n'est pas adéquat pour la stratégie active
187
171
  * (expiré, SHA-1, SAN incomplet).
188
172
  *
@@ -195,7 +179,7 @@ declare class Certificate extends Service {
195
179
  * erreur si absents.
196
180
  * - `mkcert` : dev + binaire mkcert + CA locale présents. Forcé hors dev →
197
181
  * retombe sur `selfsigned` avec avertissement.
198
- * - `selfsigned` : auto-signé node-forge (fallback). En PRODUCTION, crie un
182
+ * - `selfsigned` : auto-signé (fallback). En PRODUCTION, crie un
199
183
  * avertissement : la génération n'est pas le rôle d'un serveur de prod.
200
184
  */
201
185
  private resolveStrategy;
@@ -248,30 +232,31 @@ declare class Certificate extends Service {
248
232
  * par mkcert passe aussi par ici). Échec silencieux hors POSIX.
249
233
  */
250
234
  private restrictPrivateKey;
251
- generateKeys(): pkg.pki.rsa.KeyPair;
235
+ /** Génère la paire RSA (taille configurée) hors de la boucle d'événements. */
236
+ generateKeys(): Promise<IRsaKeyPair>;
237
+ /** Clé privée en PEM PKCS#1 (`BEGIN RSA PRIVATE KEY`). */
252
238
  generatePrivateKeyPem(): Buffer;
239
+ /** Clé publique en PEM SPKI (`BEGIN PUBLIC KEY`). */
253
240
  generatePublickeyPem(): Buffer;
254
- generateCertPem(): Buffer;
255
- createCertificate(): pkg.pki.Certificate;
256
- /** subjectAltName de l'auto-signé : DNS + IP (config explicite sinon dérivé). */
257
- private altNames;
258
- setExtension(): void;
259
241
  /**
260
- * Signe le certificat avec le hachage configuré (SHA-256 par défaut).
261
- * SHA-1 est INTERDIT (collision SHAttered 2017, CA/Browser Forum depuis 2016) :
262
- * `node-forge` signe en SHA-1 par défaut si on ne passe pas de digest → on en
263
- * passe toujours un.
242
+ * Fabrique et signe le certificat auto-signé avec la paire courante.
243
+ *
244
+ * Série aléatoire unique (RFC 5280 §4.1.2.2), `notBefore` reculé (décalage
245
+ * d'horloge du client), SAN DNS + IP (config explicite sinon dérivé), signé
246
+ * avec le hachage configuré — jamais SHA-1 (collision SHAttered 2017,
247
+ * CA/Browser Forum depuis 2016).
248
+ *
249
+ * @returns le certificat en PEM
250
+ * @throws Si la paire de clés n'a pas été générée
264
251
  */
265
- sign(): void;
252
+ createCertificate(): Promise<Buffer>;
266
253
  /**
267
254
  * Résumé introspectable du certificat serveur courant — réutilisé par la
268
255
  * commande CLI `certificates` et un futur endpoint d'admin Studio (parité
269
- * CLI ↔ Web). Parse le certificat chargé (charge node-forge à la demande).
256
+ * CLI ↔ Web).
270
257
  */
271
258
  describe(): Promise<CertificateInfo>;
272
- /** Nom lisible de l'OID d'algorithme de signature. */
273
- private oidName;
274
- /** Digest node-forge correspondant au hachage configuré (jamais SHA-1). */
275
- private digestFor;
259
+ /** Hachage configuré, ramené à SHA-256 s'il sort du contrat (jamais SHA-1). */
260
+ private allowedHash;
276
261
  }
277
262
  export default Certificate;
@@ -1,6 +1,6 @@
1
1
  import { Service, Module, Scope, EnvironmentType, DebugType, nodefonyError } from "nodefony";
2
- import type { Resolver, Router } from "@nodefony/framework";
3
- import type { Controller } from "@nodefony/framework";
2
+ import type { MaybePromise } from "nodefony";
3
+ import type { IRouteResolver, IRequestRouter } from "../interfaces/IRouting.js";
4
4
  import HttpError from "../src/errors/httpError.js";
5
5
  import { type TrustProxyChecker } from "../src/context/trustProxy.js";
6
6
  import type { IRateLimitStore } from "../src/rateLimit/IRateLimitStore.js";
@@ -20,9 +20,11 @@ import Context, { HTTPMethod, WebSocketState } from "../src/context/Context.js";
20
20
  import Certicates from "./certificates.js";
21
21
  import SessionsService from "./sessions/sessions-service.js";
22
22
  import Session from "../src/session/session.js";
23
- import { Firewall } from "@nodefony/security";
23
+ import type { IFirewallGate } from "../interfaces/ISecurity.js";
24
24
  import type { IErrorRenderer } from "../interfaces/IErrorRenderer.js";
25
25
  import type { IRequestLogger } from "../interfaces/IRequestLogger.js";
26
+ import type { DefaultOptionsService } from "nodefony";
27
+ import type { IHttpConfig } from "../config/config.js";
26
28
  export type ProtocolType = "1.1" | "2.0" | "3.0";
27
29
  export type httpRequest = http.IncomingMessage | http2.Http2ServerRequest;
28
30
  export type httpResponse = http.ServerResponse | http2.Http2ServerResponse;
@@ -31,11 +33,11 @@ export type ServerType = "http" | "https" | "http2" | "http3" | "websocket" | "w
31
33
  export type responseTimeoutType = "http" | "https" | "http2" | "http3";
32
34
  export type SchemeType = "http" | "https" | "ws" | "wss";
33
35
  export interface WsMetaData {
34
- type?: "message" | "handshake";
35
- state?: WebSocketState;
36
- messageType?: "utf8" | "binary";
37
- protocol?: string | null;
38
- id?: string;
36
+ type?: "message" | "handshake" | undefined;
37
+ state?: WebSocketState | undefined;
38
+ messageType?: "utf8" | "binary" | undefined;
39
+ protocol?: string | null | undefined;
40
+ id?: string | undefined;
39
41
  }
40
42
  /**
41
43
  * Vue minimale d'une route exposée dans l'enveloppe `nodefony.route` (frame WS
@@ -46,32 +48,33 @@ export interface WsMetaData {
46
48
  */
47
49
  export interface RouteMetaData {
48
50
  name: string;
49
- path?: string;
51
+ path?: string | undefined;
50
52
  variablesMap: Record<string, unknown>;
51
53
  }
52
54
  export interface MetaData {
53
- name?: string;
54
- version?: string;
55
- url?: URL | string;
56
- environment?: EnvironmentType;
57
- debug?: DebugType;
58
- token?: string;
59
- method?: HTTPMethod;
60
- scheme?: SchemeType;
61
- requestId?: string;
62
- websocket?: WsMetaData;
63
- route?: RouteMetaData;
55
+ name?: string | undefined;
56
+ version?: string | undefined;
57
+ url?: URL | string | undefined;
58
+ environment?: EnvironmentType | undefined;
59
+ debug?: DebugType | undefined;
60
+ token?: string | undefined;
61
+ method?: HTTPMethod | undefined;
62
+ scheme?: SchemeType | undefined;
63
+ requestId?: string | undefined;
64
+ websocket?: WsMetaData | undefined;
65
+ route?: RouteMetaData | undefined;
64
66
  }
65
67
  export interface Data {
66
- error?: Error;
68
+ error?: Error | undefined;
67
69
  nodefony: MetaData;
68
70
  message?: unknown;
69
- code?: number;
71
+ code?: number | undefined;
70
72
  result: unknown;
71
73
  }
72
74
  import type { IHttpKernel as IHttpKernelInterface } from "../interfaces/IHttpKernel.js";
73
75
  declare class HttpKernel extends Service implements IHttpKernelInterface {
74
76
  #private;
77
+ options: IHttpConfig & DefaultOptionsService;
75
78
  certificates: unknown;
76
79
  serviceCerticats: Certicates | null;
77
80
  key: string;
@@ -79,12 +82,12 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
79
82
  ca: string;
80
83
  serverStatic: Statics | null;
81
84
  domain: string;
82
- trustedHosts?: ITrustedHostsConfig;
85
+ trustedHosts?: ITrustedHostsConfig | undefined;
83
86
  domainCheck: boolean;
84
87
  regAlias: RegExp[];
85
88
  module: Module;
86
- httpsPort?: number;
87
- httpPort?: number;
89
+ httpsPort?: number | undefined;
90
+ httpPort?: number | undefined;
88
91
  responseTimeout: {
89
92
  http: number;
90
93
  https: number;
@@ -98,9 +101,9 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
98
101
  private secContentTypeOptions;
99
102
  private secFrameOptions;
100
103
  private secHsts;
101
- sessionService?: SessionsService | null;
102
- router?: Router | null;
103
- firewall?: Firewall | null;
104
+ sessionService?: SessionsService | null | undefined;
105
+ router?: IRequestRouter | null | undefined;
106
+ firewall?: IFirewallGate | null | undefined;
104
107
  private errorRenderer;
105
108
  private requestLogger;
106
109
  private profiler;
@@ -225,7 +228,17 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
225
228
  * gagne toujours sur la config (idempotent — last setter wins).
226
229
  */
227
230
  private applyRequestLoggerFromConfig;
228
- handle(request: httpRequest, response: httpResponse | null, type: ServerType): Promise<HttpContext>;
231
+ /**
232
+ * Ouvre le scope `request` de la requête et la confie au pipeline HTTP.
233
+ *
234
+ * Pas `async` : rien n'y est attendu — la fonction rend directement ce que
235
+ * rend le pipeline, sans l'envelopper dans une promesse (#505).
236
+ *
237
+ * @returns le contexte servi, ou sa promesse (celle de {@link handleHttp}).
238
+ * @throws de façon synchrone si le scope `request` n'est pas déclaré — faute
239
+ * de configuration ; l'appelant ({@link onHttpRequest}) l'absorbe.
240
+ */
241
+ handle(request: httpRequest, response: httpResponse | null, type: ServerType): MaybePromise<HttpContext>;
229
242
  /**
230
243
  * Arme le contexte pour l'exécution : zone firewall (`context.secure`) et
231
244
  * résolveur de route — **sans instancier le controller**.
@@ -237,13 +250,16 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
237
250
  * elle, exécute du code utilisateur (`initialize()`) et résout des dépendances
238
251
  * DI : elle n'a rien à faire avant que la requête soit autorisée.
239
252
  *
253
+ * Synchrone : le match est un calcul pur, rien n'y est attendu (#505).
254
+ *
240
255
  * @param checkFirewall - calcule `context.secure` (zone protégée) ; laissé à
241
256
  * `false` par un appelant qui l'a déjà tranché.
257
+ * @returns le résolveur de la route, posé sur `context.resolver`.
242
258
  * @throws HttpError 404 quand aucune route ne matche, ou l'exception portée
243
- * par le résolveur (405…) — inchangé, et toujours avant le firewall : une
244
- * route inexistante ne devient pas un 401.
259
+ * par le résolveur (405…) — de façon SYNCHRONE, et toujours avant le
260
+ * firewall : une route inexistante ne devient pas un 401.
245
261
  */
246
- prepareFrontController(context: ContextType, checkFirewall?: boolean): Promise<Resolver>;
262
+ prepareFrontController(context: ContextType, checkFirewall?: boolean): IRouteResolver;
247
263
  /**
248
264
  * {@link prepareFrontController} + instanciation immédiate du controller
249
265
  * (DI + hook `initialize()`).
@@ -254,7 +270,7 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
254
270
  * est trop tard. Le HTTP, lui, arme la route ici et laisse
255
271
  * `Resolver.executeAction` créer l'instance une fois la requête autorisée.
256
272
  */
257
- handleFrontController(context: ContextType, checkFirewall?: boolean): Promise<Controller>;
273
+ handleFrontController(context: ContextType, checkFirewall?: boolean): Promise<object>;
258
274
  /**
259
275
  * Handles errors by setting appropriate response codes and messages.
260
276
  *
@@ -300,7 +316,29 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
300
316
  getRequestLogger(): IRequestLogger;
301
317
  onError(error: Error | HttpError | nodefonyError, context?: ContextType, _extraHeaders?: Record<string, unknown> | object): Promise<HttpContext | WebsocketContext>;
302
318
  compileAlias(): RegExp[];
303
- onHttpRequest(request: httpRequest, response: httpResponse, type: ServerType): Promise<unknown>;
319
+ /**
320
+ * Point d'entrée d'une requête HTTP, appelé par les serveurs depuis leur
321
+ * écouteur `request`.
322
+ *
323
+ * Ne lève et ne rejette JAMAIS : dans un écouteur d'événement, une exception
324
+ * deviendrait une erreur non capturée du processus. Ce qui échappe au
325
+ * pipeline a déjà été traité — rendu au client par `onError`, qui journalise
326
+ * en CRITIC l'échec de son propre rendu avant de le relever. C'était le
327
+ * `.catch` vide des serveurs ; il vit désormais ici, une seule fois.
328
+ *
329
+ * @returns `undefined` quand la requête est servie sans pipeline (sonde de
330
+ * santé, 429) ; sinon la promesse de la fin du pipeline — elle ne rejette
331
+ * pas, et personne n'est tenu de l'attendre.
332
+ */
333
+ onHttpRequest(request: httpRequest, response: httpResponse, type: ServerType): Promise<void> | undefined;
334
+ /**
335
+ * Le travail d'{@link onHttpRequest} : en-têtes de transport, sondes de
336
+ * santé, rate-limit, hook `onServerRequest`, puis le pipeline.
337
+ *
338
+ * @returns `undefined` quand la requête est servie ici ; sinon la promesse du
339
+ * pipeline.
340
+ */
341
+ private dispatchHttpRequest;
304
342
  initServers(): Promise<(httpServer | httpsServer | websocketServer | websocketSecureServer)[]>;
305
343
  /**
306
344
  * Publie les ports EFFECTIFS du runtime (state file), une fois l'écoute établie.
@@ -342,24 +380,136 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
342
380
  * session (lazy). Remplace l'ancien `sessionAutoStart` global « démarre sur
343
381
  * toutes les routes » (le moteur du ×23).
344
382
  *
383
+ * Le contrat de retour suit ce qui est réellement attendu : `null` tout de
384
+ * suite quand aucune session n'est à ouvrir (le cas nominal — ni intent, ni
385
+ * cookie), la promesse de la session seulement quand le store est interrogé
386
+ * (#505).
387
+ *
345
388
  * @param context - contexte HTTP/HTTP2/WS courant.
346
- * @returns la session active, ou `null` si la requête n'en requiert aucune.
389
+ * @returns `null` si la requête n'en requiert aucune ; sinon la promesse de
390
+ * la session active.
347
391
  */
348
- startSession(context: WebsocketContext | HttpContext): Promise<Session | null>;
392
+ startSession(context: WebsocketContext | HttpContext): MaybePromise<Session | null>;
349
393
  /**
350
- * Teardown post-réponse — fire-and-forget depuis le `once("close")` posé par
351
- * `createHttpContext` (un seul fire possible : once auto-détaché). Loggue la
352
- * requête, draine les hooks afterResponse/onFinish, libère le scope DI.
394
+ * Teardown post-réponse — fire-and-forget depuis l'écouteur `close` posé par
395
+ * `createHttpContext`. Loggue la requête, draine les hooks
396
+ * afterResponse/onFinish, libère le scope DI.
397
+ *
398
+ * Synchrone dans le cas nominal — aucun hook `onAfterResponse`, aucun écouteur
399
+ * `onFinish` : rien n'y est attendu (#505). Ne lève et ne rejette jamais —
400
+ * l'appelant est un écouteur d'événement : une faute est journalisée, et le
401
+ * scope TOUJOURS libéré.
402
+ *
403
+ * @returns `undefined` quand le démontage est terminé ; sinon la promesse de
404
+ * sa fin, qui ne rejette pas.
353
405
  */
354
406
  private teardownHttp;
407
+ /**
408
+ * Suite ASYNCHRONE du démontage, quand il y a des hooks à attendre :
409
+ * `onAfterResponse` puis `onFinish`, avant de libérer le contexte. Ne rejette
410
+ * jamais (cf {@link teardownHttp}).
411
+ */
412
+ private finishTeardownHttp;
413
+ /** Fin d'un contexte HTTP : marqué fini, scope DI libéré, contexte nettoyé. */
414
+ private releaseHttpContext;
415
+ /**
416
+ * R2 — le démontage est fire-and-forget : un throw (hook onFinish /
417
+ * afterResponse) serait une erreur non capturée du processus. On journalise,
418
+ * et on GARANTIT la libération du scope DI (sinon le scope `request` fuit à
419
+ * chaque hook qui lève).
420
+ */
421
+ private failTeardownHttp;
355
422
  createHttpContext(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): HttpContext;
356
- handleHttp(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): Promise<HttpContext>;
357
- onRequestEnd(context: HttpContext, error?: Error | null | undefined): Promise<HttpContext | number>;
423
+ /**
424
+ * Crée le contexte HTTP de la requête et la sert dans sa bulle ALS ; toute
425
+ * erreur — synchrone ou rejet — passe par {@link onError}.
426
+ *
427
+ * Pas `async` : un GET servi par une action synchrone traverse le pipeline
428
+ * sans une seule promesse (#505). Une promesse seulement quand une étape
429
+ * attend, ou quand l'erreur est rendue (`onError`).
430
+ *
431
+ * @returns le contexte servi, ou sa promesse.
432
+ */
433
+ handleHttp(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): MaybePromise<HttpContext>;
434
+ /**
435
+ * Traceparent, tampon du profileur, puis le pipeline dans la bulle ALS de la
436
+ * requête (`RequestContext.run`).
437
+ */
438
+ private serveHttpContext;
439
+ /**
440
+ * Chemin d'erreur de {@link handleHttp} : libère le scope d'un contexte qui
441
+ * n'a pas pu se construire, puis rend l'erreur au client (`onError`).
442
+ */
443
+ private failHttp;
444
+ /**
445
+ * Le trajet d'une requête HTTP DANS la bulle ALS : CORS, calque de
446
+ * configuration, route, en-têtes de sécurité, repli statique, corps, gardes,
447
+ * action.
448
+ *
449
+ * Chaque étape rend sa valeur, ou une promesse quand elle attend réellement
450
+ * (préflight à terminer, hook asynchrone, fichier statique, corps à lire,
451
+ * session, pare-feu, action asynchrone) — et la suite ne s'enchaîne par
452
+ * `then` que dans ce cas. Un `await` sur une valeur déjà connue suspendait le
453
+ * pipeline pour rien : ~50 évènements asynchrones par GET (#505).
454
+ *
455
+ * @returns le contexte servi, ou la promesse de ce contexte.
456
+ * @throws l'erreur d'une étape synchrone — {@link handleHttp} la traite comme
457
+ * un rejet (même `catch`, même `onError`).
458
+ */
459
+ private runHttpPipeline;
460
+ /**
461
+ * Route, en-têtes de sécurité applicatifs, repli statique — puis le corps.
462
+ * N'attend que le repli statique, et seulement quand aucune route ne matche.
463
+ */
464
+ private routeHttpRequest;
465
+ /**
466
+ * Le corps — lu seulement s'il y en a un à lire —, puis les gardes
467
+ * ({@link onRequestEnd}) et l'action. Chacun n'est attendu que s'il attend.
468
+ */
469
+ private serveHttpRequest;
470
+ /**
471
+ * Les gardes d'une requête HTTP, corps lu : en-têtes de scheme, domaine, hook
472
+ * `beforeResolve`, armement de la route, CSRF, session, pare-feu — dans cet
473
+ * ordre.
474
+ *
475
+ * Synchrone tant qu'aucune garde n'attend (#505) : le hook `beforeResolve`
476
+ * n'est attendu que s'il a un écouteur, la session que si la requête en
477
+ * ouvre une, le pare-feu que sur une zone protégée ou à accès contrôlé.
478
+ *
479
+ * @param context - le contexte de la requête.
480
+ * @param error - une erreur survenue en amont : relevée telle quelle.
481
+ * @returns le contexte une fois les gardes passées, ou la promesse de ce
482
+ * contexte quand une garde attend.
483
+ * @throws de façon SYNCHRONE l'erreur reçue, l'exception du résolveur (404,
484
+ * 405…) et le refus CSRF ; un refus du pare-feu ou de session rompt la
485
+ * promesse rendue.
486
+ */
487
+ onRequestEnd(context: HttpContext, error?: Error | null): MaybePromise<HttpContext>;
488
+ /**
489
+ * Suite d'{@link onRequestEnd} : armement de la route, CSRF, session, puis
490
+ * pare-feu. Synchrone tant que ni la session ni le pare-feu n'attendent.
491
+ */
492
+ private armAndGuard;
493
+ /**
494
+ * Le pare-feu d'une requête en zone protégée : `handleSecurity`, puis les
495
+ * hooks `afterAuth` (succès) ou `onAuthFailure` (refus, relevé ensuite).
496
+ *
497
+ * @returns la promesse du contexte autorisé.
498
+ * @throws (rejet) le refus du pare-feu, après `onAuthFailure`.
499
+ */
500
+ private runFirewall;
358
501
  createWebsocketContext(scope: Scope, req: IncomingMessage, ws: Ws, type: ServerType): WebsocketContext;
502
+ /**
503
+ * Teardown d'une connexion WS à sa fermeture : signal d'abandon, hooks
504
+ * post-réponse, persistance de la session, puis libération du scope.
505
+ * Ne rejette jamais : toute erreur est journalisée (l'appelant est un
506
+ * écouteur d'événement synchrone, qui n'attend rien).
507
+ */
508
+ private finishWebsocket;
359
509
  onWebsocketRequest(ws: Ws, req: IncomingMessage, type: ServerType): Promise<unknown>;
360
510
  handleWebsocket(scope: Scope, ws: Ws, req: IncomingMessage, type: ServerType): Promise<unknown>;
361
511
  private releaseOrphanWsScope;
362
- onConnect(context: WebsocketContext, error?: null | undefined | unknown): Promise<Ws | number>;
512
+ onConnect(context: WebsocketContext | null, error?: unknown): Promise<Ws | number>;
363
513
  checkValidDomain(context: ContextType): number;
364
514
  isValidDomain(context: ContextType): boolean;
365
515
  /**
@@ -4,8 +4,11 @@ import http from "node:http";
4
4
  import http2 from "node:http2";
5
5
  import { AddressInfo } from "node:net";
6
6
  import { HttpTerminator } from "./serverShutdown.js";
7
+ import type { IHttpConfig } from "../../config/config.js";
8
+ import type { DefaultOptionsService } from "nodefony";
7
9
  declare class ServerHttp extends Service {
8
- private httpKernel;
10
+ private readonly httpKernel;
11
+ options: IHttpConfig["http"] & DefaultOptionsService;
9
12
  module: Module;
10
13
  server: http.Server | http2.Http2Server | null;
11
14
  httpTerminator: HttpTerminator | null;
@@ -4,8 +4,11 @@ import https from "node:https";
4
4
  import http2 from "node:http2";
5
5
  import { HttpTerminator } from "./serverShutdown.js";
6
6
  import { AddressInfo } from "node:net";
7
+ import type { IHttpConfig } from "../../config/config.js";
8
+ import type { DefaultOptionsService } from "nodefony";
7
9
  declare class ServerHttps extends Service {
8
- private httpKernel;
10
+ private readonly httpKernel;
11
+ options: IHttpConfig["https"] & DefaultOptionsService;
9
12
  httpTerminator: HttpTerminator | null;
10
13
  module: Module;
11
14
  server: https.Server | http2.Http2SecureServer | null;
@@ -1,11 +1,14 @@
1
1
  import Ws, { WebSocketServer } from "ws";
2
2
  import { Service, Module, FamilyType } from "nodefony";
3
+ import type { DefaultOptionsService } from "nodefony";
4
+ import type { IHttpConfig } from "../../config/config.js";
3
5
  import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
4
6
  import { AddressInfo } from "node:net";
5
7
  import type { IncomingMessage } from "node:http";
6
8
  import httpsServers from "./server-https.js";
7
9
  declare class WebsocketSecure extends Service {
8
- private httpKernel;
10
+ private readonly httpKernel;
11
+ options: IHttpConfig["websocketSecure"] & DefaultOptionsService;
9
12
  module: Module;
10
13
  ready: boolean;
11
14
  server: WebSocketServer | null;
@@ -1,11 +1,14 @@
1
1
  import Ws, { WebSocketServer } from "ws";
2
2
  import { Service, Module, FamilyType } from "nodefony";
3
+ import type { DefaultOptionsService } from "nodefony";
4
+ import type { IHttpConfig } from "../../config/config.js";
3
5
  import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
4
6
  import { AddressInfo } from "node:net";
5
7
  import type { IncomingMessage } from "node:http";
6
8
  import httpServer from "./server-http.js";
7
9
  declare class Websocket extends Service {
8
- private httpKernel;
10
+ private readonly httpKernel;
11
+ options: IHttpConfig["websocket"] & DefaultOptionsService;
9
12
  module: Module;
10
13
  ready: boolean;
11
14
  server: WebSocketServer | null;
@@ -3,6 +3,8 @@ import type { IPage } from "nodefony";
3
3
  import type { ISessionStorage, ISessionSummary, ISessionRecord, ISessionListQuery } from "../../interfaces/ISession.js";
4
4
  import HttpKernel, { ContextType } from "../http-kernel.js";
5
5
  import Session, { OptionsSessionType } from "../../src/session/session.js";
6
+ import type { IHttpConfig } from "../../config/config.js";
7
+ import type { DefaultOptionsService } from "nodefony";
6
8
  import Certificate from "../../service/certificates.js";
7
9
  import type { ISessionCounts } from "../../src/session/storage/sessionFilters.js";
8
10
  export type sessionStrategyType = "none" | "migrate" | "invalidate";
@@ -25,6 +27,7 @@ export declare function computeSessionRef(secret: Buffer, id: string): string;
25
27
  * testée isolément).
26
28
  */
27
29
  export declare function toSessionSummary(rec: ISessionRecord, ref: string, currentRef?: string | null): ISessionSummary;
30
+ export type ISessionServiceOptions = IHttpConfig["session"] & DefaultOptionsService;
28
31
  declare class SessionsService extends Service {
29
32
  httpKernel: HttpKernel;
30
33
  /**
@@ -48,12 +51,13 @@ declare class SessionsService extends Service {
48
51
  static getStorage(name: string): SessionStorageCtor | undefined;
49
52
  /** Noms des handlers de session enregistrés. */
50
53
  static storageHandlers(): string[];
54
+ options: ISessionServiceOptions;
51
55
  sessionStrategy: sessionStrategyType;
52
56
  storage: ISessionStorage | null;
53
57
  module: Module;
54
58
  defaultSessionName: string;
55
- secret?: Buffer;
56
- iv?: Buffer;
59
+ secret?: Buffer | undefined;
60
+ iv?: Buffer | undefined;
57
61
  certificates: Certificate | null;
58
62
  private gcScheduler;
59
63
  constructor(module: Module, httpKernel: HttpKernel);
@@ -4,7 +4,7 @@ import fs from "node:fs";
4
4
  import type { IParsedUploadFile } from "../../interfaces/IUpload.js";
5
5
  export declare class upload extends Service {
6
6
  httpKernel: HttpKernel;
7
- path?: string | fs.PathLike;
7
+ path?: string | fs.PathLike | undefined;
8
8
  module: Module;
9
9
  constructor(module: Module, httpKernel: HttpKernel);
10
10
  /**