@nodefony/http 10.0.0-alpha.9 → 10.0.0-beta.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 (94) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +4 -2
  3. package/dist/nodefony/command/assetsPublishCommand.js +1 -1
  4. package/dist/nodefony/command/certificatesCommand.js +1 -1
  5. package/dist/nodefony/command/networkCommand.js +1 -1
  6. package/dist/nodefony/command/proxyGenerateCommand.js +1 -1
  7. package/dist/nodefony/config/config.js +22 -1
  8. package/dist/nodefony/interfaces/IRouting.js +1 -0
  9. package/dist/nodefony/interfaces/ISecurity.js +1 -0
  10. package/dist/nodefony/service/ProfilerAdminApi.js +2 -1
  11. package/dist/nodefony/service/audit-logger.js +16 -13
  12. package/dist/nodefony/service/certificates.js +25 -13
  13. package/dist/nodefony/service/error-renderer.js +11 -8
  14. package/dist/nodefony/service/http-kernel.js +340 -163
  15. package/dist/nodefony/service/pretty-request-logger.js +3 -10
  16. package/dist/nodefony/service/servers/clientError.js +2 -2
  17. package/dist/nodefony/service/servers/server-http.js +20 -20
  18. package/dist/nodefony/service/servers/server-https.js +38 -39
  19. package/dist/nodefony/service/servers/server-static.js +13 -17
  20. package/dist/nodefony/service/servers/server-websocket-secure.js +8 -7
  21. package/dist/nodefony/service/servers/server-websocket.js +8 -7
  22. package/dist/nodefony/service/servers/wsHeartbeat.js +2 -2
  23. package/dist/nodefony/service/sessions/sessions-service.js +19 -12
  24. package/dist/nodefony/service/trace.js +1 -1
  25. package/dist/nodefony/service/upload/upload-service.js +8 -7
  26. package/dist/nodefony/src/assets/prebuiltUi.js +1 -1
  27. package/dist/nodefony/src/context/Context.js +34 -19
  28. package/dist/nodefony/src/context/forwarded.js +4 -4
  29. package/dist/nodefony/src/context/http/HttpContext.js +133 -88
  30. package/dist/nodefony/src/context/http/Request.js +93 -44
  31. package/dist/nodefony/src/context/http/Response.js +107 -51
  32. package/dist/nodefony/src/context/http/parser.js +9 -10
  33. package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
  34. package/dist/nodefony/src/context/http2/Response.js +53 -25
  35. package/dist/nodefony/src/context/metaData.js +1 -1
  36. package/dist/nodefony/src/context/trustProxy.js +4 -3
  37. package/dist/nodefony/src/context/websocket/Response.js +13 -6
  38. package/dist/nodefony/src/context/websocket/WebsocketContext.js +59 -26
  39. package/dist/nodefony/src/context/websocket/wsCloseReason.js +41 -0
  40. package/dist/nodefony/src/cookies/cookie.js +3 -3
  41. package/dist/nodefony/src/errors/httpError.js +6 -6
  42. package/dist/nodefony/src/errors/thrownMessage.js +27 -0
  43. package/dist/nodefony/src/profiler/FrameProfile.js +4 -3
  44. package/dist/nodefony/src/profiler/Profiler.js +4 -4
  45. package/dist/nodefony/src/servers/kernelServers.js +29 -0
  46. package/dist/nodefony/src/servers/portBinder.js +8 -21
  47. package/dist/nodefony/src/session/session.js +18 -10
  48. package/dist/nodefony/src/session/sessionStoreFailure.js +2 -1
  49. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +24 -9
  50. package/dist/types/index.d.ts +16 -0
  51. package/dist/types/nodefony/config/config.d.ts +22 -0
  52. package/dist/types/nodefony/interfaces/IContext.d.ts +24 -7
  53. package/dist/types/nodefony/interfaces/ICookie.d.ts +18 -18
  54. package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -4
  55. package/dist/types/nodefony/interfaces/IResponse.d.ts +1 -1
  56. package/dist/types/nodefony/interfaces/IRouting.d.ts +87 -0
  57. package/dist/types/nodefony/interfaces/ISecurity.d.ts +45 -0
  58. package/dist/types/nodefony/interfaces/ISession.d.ts +8 -8
  59. package/dist/types/nodefony/interfaces/IUpload.d.ts +9 -9
  60. package/dist/types/nodefony/interfaces/index.d.ts +2 -0
  61. package/dist/types/nodefony/service/audit-logger.d.ts +14 -10
  62. package/dist/types/nodefony/service/certificates.d.ts +10 -10
  63. package/dist/types/nodefony/service/http-kernel.d.ts +192 -42
  64. package/dist/types/nodefony/service/servers/server-http.d.ts +4 -1
  65. package/dist/types/nodefony/service/servers/server-https.d.ts +4 -1
  66. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +4 -1
  67. package/dist/types/nodefony/service/servers/server-websocket.d.ts +4 -1
  68. package/dist/types/nodefony/service/sessions/sessions-service.d.ts +6 -2
  69. package/dist/types/nodefony/service/upload/upload-service.d.ts +1 -1
  70. package/dist/types/nodefony/src/context/Context.d.ts +21 -10
  71. package/dist/types/nodefony/src/context/forwarded.d.ts +3 -3
  72. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +75 -18
  73. package/dist/types/nodefony/src/context/http/Request.d.ts +39 -2
  74. package/dist/types/nodefony/src/context/http/Response.d.ts +47 -7
  75. package/dist/types/nodefony/src/context/http/parser.d.ts +1 -1
  76. package/dist/types/nodefony/src/context/http2/Response.d.ts +32 -2
  77. package/dist/types/nodefony/src/context/websocket/Response.d.ts +4 -4
  78. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +11 -2
  79. package/dist/types/nodefony/src/context/websocket/wsCloseReason.d.ts +21 -0
  80. package/dist/types/nodefony/src/cookies/cookie.d.ts +9 -9
  81. package/dist/types/nodefony/src/errors/httpError.d.ts +7 -7
  82. package/dist/types/nodefony/src/errors/thrownMessage.d.ts +12 -0
  83. package/dist/types/nodefony/src/profiler/Profiler.d.ts +28 -28
  84. package/dist/types/nodefony/src/servers/kernelServers.d.ts +20 -0
  85. package/dist/types/nodefony/src/servers/portBinder.d.ts +6 -19
  86. package/dist/types/nodefony/src/session/session.d.ts +12 -12
  87. package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +1 -1
  88. package/docs/cookies.md +20 -20
  89. package/docs/observabilite.md +25 -25
  90. package/docs/rate-limit.md +18 -18
  91. package/docs/servers.md +52 -52
  92. package/docs/session.md +65 -60
  93. package/docs/upload.md +36 -30
  94. package/package.json +7 -7
@@ -1,6 +1,6 @@
1
1
  import type { Severity } from "nodefony";
2
2
  import type { IRequestLogger, IRequestLogEntry } from "../interfaces/IRequestLogger.js";
3
- import type { IHttpContext, IWebsocketContext } from "../interfaces/IContext.js";
3
+ import type { IHttpContext, IWebsocketContext, PhaseTiming } from "../interfaces/IContext.js";
4
4
  /**
5
5
  * Canonical audit log entry — 1 JSON PDU per request (P3.1).
6
6
  *
@@ -32,9 +32,9 @@ export interface AuditLogEntry {
32
32
  phases?: {
33
33
  name: string;
34
34
  durationMs: number | null;
35
- }[];
36
- error?: AuditErrorEntry;
37
- protocol?: string | null;
35
+ }[] | undefined;
36
+ error?: AuditErrorEntry | undefined;
37
+ protocol?: string | null | undefined;
38
38
  }
39
39
  /**
40
40
  * Enriched error description for audit logs (P3.5).
@@ -51,6 +51,15 @@ export interface AuditErrorEntry {
51
51
  /** Recursive — capped to depth 5 to avoid pathological cycles. */
52
52
  cause?: AuditErrorEntry;
53
53
  }
54
+ /**
55
+ * Durée totale de la requête, de la première phase (`startMs`) à maintenant.
56
+ * `null` si le chronométrage est coupé (aucune phase enregistrée). Partagée
57
+ * par le journal d'audit et le journal lisible — une seule définition.
58
+ *
59
+ * @param phases - phases chronométrées du contexte
60
+ * @returns durée en millisecondes, ou `null`
61
+ */
62
+ declare function computeDurationMs(phases: PhaseTiming[]): number | null;
54
63
  /**
55
64
  * Severity derived from HTTP status code — RFC 9110 categories.
56
65
  * 1xx/2xx/3xx → INFO ; 4xx → WARNING ; 5xx → ERROR.
@@ -127,11 +136,6 @@ declare class JsonAuditLogger implements IRequestLogger {
127
136
  shouldSample(context: IHttpContext, error?: Error | null): boolean;
128
137
  renderHttp(context: IHttpContext, error?: Error | null): IRequestLogEntry;
129
138
  renderWebsocket(context: IWebsocketContext, error?: Error | null, acceptedProtocol?: string | null): IRequestLogEntry;
130
- /**
131
- * Total request duration computed from the first phase startMs to now.
132
- * Returns null if timing is disabled (no phases recorded).
133
- */
134
- private computeDurationMs;
135
139
  /**
136
140
  * Serialise an Error (recursively for `cause` chain) into an AuditErrorEntry.
137
141
  * Stack is included only when `includeStack === true` (dev default).
@@ -140,4 +144,4 @@ declare class JsonAuditLogger implements IRequestLogger {
140
144
  private serializeError;
141
145
  }
142
146
  export default JsonAuditLogger;
143
- export { severityFromStatus };
147
+ export { computeDurationMs, severityFromStatus };
@@ -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;
@@ -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
  /**
@@ -1,6 +1,7 @@
1
1
  import { Container, Service, Severity, Msgid, Message, Pci, Pdu, KernelEventsType, nodefonyError, Scope } from "nodefony";
2
2
  import type { IProfilerQuery } from "nodefony";
3
- import type { Resolver, Router } from "@nodefony/framework";
3
+ import type { IRouteResolver, IRequestRouter } from "../../interfaces/IRouting.js";
4
+ import type { ISecurityZone } from "../../interfaces/ISecurity.js";
4
5
  import { WebSocketServer } from "ws";
5
6
  import http2 from "node:http2";
6
7
  import http from "node:http";
@@ -16,7 +17,6 @@ import { URL } from "node:url";
16
17
  import Session from "../session/session.js";
17
18
  import Cookie from "../cookies/cookie.js";
18
19
  import HttpError from "../errors/httpError.js";
19
- import { SecuredArea } from "@nodefony/security";
20
20
  /**
21
21
  * Bascule runtime du gate des logs d'events lifecycle. Réservé HORS hot path :
22
22
  * tests et futur « Audit à chaud » (fenêtre bornée prod→verbeux, auto-revert
@@ -41,7 +41,7 @@ export declare function nextCspNonce(): string;
41
41
  declare class Context extends Service implements IContextInterface {
42
42
  #private;
43
43
  secure: boolean;
44
- security?: SecuredArea | null;
44
+ security?: ISecurityZone | null | undefined;
45
45
  cleaned: boolean;
46
46
  isControlledAccess: boolean;
47
47
  validDomain: boolean;
@@ -52,7 +52,7 @@ declare class Context extends Service implements IContextInterface {
52
52
  sessionStarting: boolean;
53
53
  domain: string;
54
54
  type: ServerType;
55
- server?: WebSocketServer | http.Server | https.Server | http2.Http2SecureServer | null;
55
+ server?: WebSocketServer | http.Server | https.Server | http2.Http2SecureServer | null | undefined;
56
56
  httpKernel: HttpKernel | null;
57
57
  request: contextRequest | null;
58
58
  response: contextResponse | null;
@@ -64,7 +64,7 @@ declare class Context extends Service implements IContextInterface {
64
64
  set originUrl(value: URL | undefined | null);
65
65
  cookies: Cookies;
66
66
  error: Error | HttpError | nodefonyError | null | undefined;
67
- sessionService?: SessionsService | null;
67
+ sessionService?: SessionsService | null | undefined;
68
68
  session: Session | null | undefined;
69
69
  cookieSession: Cookie | null | undefined;
70
70
  user: unknown;
@@ -79,8 +79,8 @@ declare class Context extends Service implements IContextInterface {
79
79
  */
80
80
  protected resolveIsHtml(): boolean;
81
81
  crossDomain: boolean;
82
- router: Router | null;
83
- resolver: Resolver | null;
82
+ router: IRequestRouter | null;
83
+ resolver: IRouteResolver | null;
84
84
  /**
85
85
  * Intent de session de la route courante (posé par le Resolver depuis
86
86
  * `@UseSession` / paramètre `@Session`). Pilote le point d'activation unique
@@ -110,8 +110,8 @@ declare class Context extends Service implements IContextInterface {
110
110
  profilerQueries: IProfilerQuery[] | null;
111
111
  profiling: boolean;
112
112
  securityTrace: ISecurityTrace | null;
113
- private _timingEnabled;
114
- private _timingVerbose;
113
+ private readonly _timingEnabled;
114
+ private readonly _timingVerbose;
115
115
  readonly phases: PhaseTiming[];
116
116
  private _phaseIndex;
117
117
  private _afterResponseFns;
@@ -145,7 +145,18 @@ declare class Context extends Service implements IContextInterface {
145
145
  phaseStart(name: PhaseName): void;
146
146
  phaseEnd(name: PhaseName): void;
147
147
  onAfterResponse(fn: AfterResponseHandler): void;
148
- _runAfterResponse(): Promise<void>;
148
+ /**
149
+ * Exécute les hooks `onAfterResponse`, une seule fois, dans l'ordre
150
+ * d'enregistrement ; un hook qui lève est journalisé sans arrêter les suivants.
151
+ *
152
+ * @returns `undefined` quand aucun hook n'est enregistré (le cas nominal) ou
153
+ * qu'ils ont déjà tourné : rien à attendre, le démontage continue sans
154
+ * suspension. Une `Promise` sinon, réglée quand le dernier hook a fini — elle
155
+ * ne rejette jamais.
156
+ */
157
+ _runAfterResponse(): Promise<void> | undefined;
158
+ /** Boucle des hooks `onAfterResponse` — séquentielle, erreurs journalisées. */
159
+ private runAfterResponseHooks;
149
160
  get signal(): AbortSignal;
150
161
  _abortIfPending(reason?: string): void;
151
162
  log(pci: Pci, severity?: Severity, msgid?: Msgid, msg?: Message): Pdu;
@@ -32,11 +32,11 @@ export interface ForwardedElement {
32
32
  /** Résolution canonique unifiée des en-têtes forwarded. */
33
33
  export interface ResolvedProxy {
34
34
  /** Scheme effectif côté client (`http` / `https`), ou `undefined` si indéterminé. */
35
- proto?: string;
35
+ proto?: string | undefined;
36
36
  /** Host effectif côté client (Host d'origine), ou `undefined` si non fourni. */
37
- host?: string;
37
+ host?: string | undefined;
38
38
  /** Chaîne `for` brute (node identifiers, gauche→droite) — pour log/metadata interne. */
39
- forwardedFor?: string;
39
+ forwardedFor?: string | undefined;
40
40
  /** IP cliente réelle, résolue from-right (toujours fiable, jamais l'IP forgée). */
41
41
  clientIp: string | null;
42
42
  /** Le header standard `Forwarded` a-t-il été la source (sinon `X-Forwarded-*`) ? */