@nodefony/http 10.0.0-alpha.8 → 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 (98) hide show
  1. package/README.md +7 -0
  2. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
  4. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.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 -1
  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 +22 -1
  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 +25 -13
  16. package/dist/nodefony/service/error-renderer.js +118 -5
  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 +25 -17
  27. package/dist/nodefony/service/trace.js +1 -1
  28. package/dist/nodefony/service/upload/upload-service.js +11 -10
  29. package/dist/nodefony/src/assets/prebuiltUi.js +1 -1
  30. package/dist/nodefony/src/context/Context.js +34 -19
  31. package/dist/nodefony/src/context/forwarded.js +4 -4
  32. package/dist/nodefony/src/context/http/HttpContext.js +133 -88
  33. package/dist/nodefony/src/context/http/Request.js +93 -44
  34. package/dist/nodefony/src/context/http/Response.js +107 -51
  35. package/dist/nodefony/src/context/http/parser.js +9 -10
  36. package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
  37. package/dist/nodefony/src/context/http2/Response.js +53 -25
  38. package/dist/nodefony/src/context/metaData.js +1 -1
  39. package/dist/nodefony/src/context/trustProxy.js +4 -3
  40. package/dist/nodefony/src/context/websocket/Response.js +13 -6
  41. package/dist/nodefony/src/context/websocket/WebsocketContext.js +59 -26
  42. package/dist/nodefony/src/context/websocket/wsCloseReason.js +41 -0
  43. package/dist/nodefony/src/cookies/cookie.js +3 -3
  44. package/dist/nodefony/src/errors/httpError.js +6 -6
  45. package/dist/nodefony/src/errors/thrownMessage.js +27 -0
  46. package/dist/nodefony/src/profiler/FrameProfile.js +4 -3
  47. package/dist/nodefony/src/profiler/Profiler.js +4 -4
  48. package/dist/nodefony/src/servers/kernelServers.js +29 -0
  49. package/dist/nodefony/src/servers/portBinder.js +8 -21
  50. package/dist/nodefony/src/session/session.js +18 -10
  51. package/dist/nodefony/src/session/sessionStoreFailure.js +2 -1
  52. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +24 -9
  53. package/dist/types/index.d.ts +16 -0
  54. package/dist/types/nodefony/config/config.d.ts +22 -0
  55. package/dist/types/nodefony/interfaces/IContext.d.ts +24 -7
  56. package/dist/types/nodefony/interfaces/ICookie.d.ts +18 -18
  57. package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -4
  58. package/dist/types/nodefony/interfaces/IResponse.d.ts +1 -1
  59. package/dist/types/nodefony/interfaces/IRouting.d.ts +87 -0
  60. package/dist/types/nodefony/interfaces/ISecurity.d.ts +45 -0
  61. package/dist/types/nodefony/interfaces/ISession.d.ts +8 -8
  62. package/dist/types/nodefony/interfaces/IUpload.d.ts +9 -9
  63. package/dist/types/nodefony/interfaces/index.d.ts +2 -0
  64. package/dist/types/nodefony/service/audit-logger.d.ts +14 -10
  65. package/dist/types/nodefony/service/certificates.d.ts +10 -10
  66. package/dist/types/nodefony/service/error-renderer.d.ts +14 -0
  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/src/context/Context.d.ts +21 -10
  75. package/dist/types/nodefony/src/context/forwarded.d.ts +3 -3
  76. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +75 -18
  77. package/dist/types/nodefony/src/context/http/Request.d.ts +39 -2
  78. package/dist/types/nodefony/src/context/http/Response.d.ts +47 -7
  79. package/dist/types/nodefony/src/context/http/parser.d.ts +1 -1
  80. package/dist/types/nodefony/src/context/http2/Response.d.ts +32 -2
  81. package/dist/types/nodefony/src/context/websocket/Response.d.ts +4 -4
  82. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +11 -2
  83. package/dist/types/nodefony/src/context/websocket/wsCloseReason.d.ts +21 -0
  84. package/dist/types/nodefony/src/cookies/cookie.d.ts +9 -9
  85. package/dist/types/nodefony/src/errors/httpError.d.ts +7 -7
  86. package/dist/types/nodefony/src/errors/thrownMessage.d.ts +12 -0
  87. package/dist/types/nodefony/src/profiler/Profiler.d.ts +28 -28
  88. package/dist/types/nodefony/src/servers/kernelServers.d.ts +20 -0
  89. package/dist/types/nodefony/src/servers/portBinder.d.ts +6 -19
  90. package/dist/types/nodefony/src/session/session.d.ts +12 -12
  91. package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +1 -1
  92. package/docs/cookies.md +25 -25
  93. package/docs/observabilite.md +30 -30
  94. package/docs/rate-limit.md +22 -22
  95. package/docs/servers.md +61 -61
  96. package/docs/session.md +82 -77
  97. package/docs/upload.md +43 -37
  98. package/package.json +11 -11
@@ -1,7 +1,7 @@
1
1
  import { ServerType, SchemeType } from "../../../service/http-kernel.js";
2
2
  import Context, { HTTPMethod } from "../Context.js";
3
3
  import { Container, Scope } from "nodefony";
4
- import { Csrf } from "@nodefony/security";
4
+ import type { MaybePromise } from "nodefony";
5
5
  import HttpRequest from "./Request.js";
6
6
  import HttpResponse from "./Response.js";
7
7
  import Http2Request from "../http2/Request.js";
@@ -20,28 +20,26 @@ import uploadService from "../../../service/upload/upload-service.js";
20
20
  * (logs, prédicat « derrière un proxy »).
21
21
  */
22
22
  export interface ProxyType {
23
- proxyServer?: string;
24
- proxyProto?: string;
25
- proxyScheme?: SchemeType;
26
- proxyPort?: string;
27
- proxyFor?: string;
28
- proxyHost?: string;
29
- proxyUri?: string;
30
- proxyRealIp?: string;
31
- proxyVia?: string;
23
+ proxyServer?: string | undefined;
24
+ proxyProto?: string | undefined;
25
+ proxyScheme?: SchemeType | undefined;
26
+ proxyPort?: string | undefined;
27
+ proxyFor?: string | undefined;
28
+ proxyHost?: string | undefined;
29
+ proxyUri?: string | undefined;
30
+ proxyRealIp?: string | undefined;
31
+ proxyVia?: string | undefined;
32
32
  }
33
33
  export type HttpRequestType = Http2Request | HttpRequest;
34
34
  export type HttpRsponseType = Http2Response | HttpResponse;
35
35
  import type { IHttpContext as IHttpContextInterface } from "../../../interfaces/IContext.js";
36
36
  declare class HttpContext extends Context implements IHttpContextInterface {
37
- #private;
38
37
  proxy: ProxyType | null;
39
38
  isRedirect: boolean;
40
39
  sended: boolean;
41
40
  request: HttpRequestType;
42
41
  response: HttpRsponseType;
43
42
  uploadService: uploadService | null;
44
- csrf?: Csrf;
45
43
  constructor(container: Container | Scope, request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse, type: ServerType);
46
44
  /**
47
45
  * URL d'origine effective, construite À LA DEMANDE (1ʳᵉ lecture, puis
@@ -59,15 +57,74 @@ declare class HttpContext extends Context implements IHttpContextInterface {
59
57
  */
60
58
  _onTimeout(): void;
61
59
  setScheme(): SchemeType;
62
- handle(): Promise<this>;
60
+ /**
61
+ * Sert la requête : délai de réponse, hooks `onRequest`, puis l'action
62
+ * résolue et son rendu (`resolver.callController()`).
63
+ *
64
+ * Pas `async` : synchrone quand l'action et son rendu le sont, rien n'y est
65
+ * alors attendu (#505).
66
+ *
67
+ * @returns ce que rend `callController()` — le résultat du rendu, pas le
68
+ * contexte (comportement historique, conservé) —, ou sa promesse. Sur une
69
+ * redirection déjà posée : le contexte, une fois l'envoi fait.
70
+ * @throws HttpError 404, de façon SYNCHRONE, quand aucune route ne résout.
71
+ */
72
+ handle(): MaybePromise<this>;
63
73
  setTimeout(): void;
64
- render(chunk: unknown, encoding?: BufferEncoding, status?: string | number, headers?: Record<string, string | number>): Promise<Http2Response | HttpResponse>;
74
+ /**
75
+ * Sérialise la donnée selon son type (JSON, HTML, texte), pose statut et
76
+ * en-têtes, puis l'envoie ({@link send}). Synchrone quand l'envoi l'est (#505).
77
+ *
78
+ * @returns la réponse, ou sa promesse.
79
+ * @throws de façon SYNCHRONE ce que lèvent la sérialisation ou l'envoi.
80
+ */
81
+ render(chunk: unknown, encoding?: BufferEncoding, status?: string | number, headers?: Record<string, string | number>): MaybePromise<Http2Response | HttpResponse>;
65
82
  end(): Promise<Http2Response | HttpResponse>;
66
- send(chunk?: unknown, encoding?: BufferEncoding): Promise<Http2Response | HttpResponse>;
83
+ /**
84
+ * Envoie la réponse : session sauvée, hook `onSend`, en-têtes, corps, puis
85
+ * fin de la réponse.
86
+ *
87
+ * Synchrone dans le cas nominal — aucune session ouverte, aucun écouteur
88
+ * `onSend`, réponse unique : rien n'y est à attendre (#505). Une promesse
89
+ * seulement quand une étape attend réellement (écriture du store de session,
90
+ * hook asynchrone, streaming chunké, flux HTTP/2).
91
+ *
92
+ * @param chunk - le corps ; absent, le corps déjà posé sur la réponse.
93
+ * @param encoding - l'encodage d'un corps texte.
94
+ * @returns la réponse, ou sa promesse.
95
+ * @throws de façon SYNCHRONE `Response Already sended` quand la réponse est
96
+ * déjà partie, et ce que lève une étape synchrone de l'envoi.
97
+ */
98
+ send(chunk?: unknown, encoding?: BufferEncoding): MaybePromise<Http2Response | HttpResponse>;
99
+ /**
100
+ * Le travail de {@link send}, journalisé en ERROR quand il échoue — de façon
101
+ * synchrone ou par rejet, selon l'étape qui échoue.
102
+ */
103
+ private doSend;
104
+ /** Session, puis hook `onSend`, puis en-têtes et corps — chacun attendu seulement s'il attend. */
105
+ private sendSteps;
106
+ /** Hook `onSend` (attendu seulement s'il a un écouteur), puis en-têtes et corps. */
107
+ private emitAndWrite;
108
+ /** En-têtes, puis corps — ou la seule fin de réponse pour une redirection. */
109
+ private writeHeadAndBody;
67
110
  writeHead(statusCode?: number, headers?: http.OutgoingHttpHeaders | http.OutgoingHttpHeader[]): void;
68
- write(chunk: unknown, encoding?: BufferEncoding, _flush?: boolean): Promise<Http2Response | HttpResponse>;
69
- flush(chunk: unknown, encoding: BufferEncoding): Promise<HttpResponse>;
70
- close(): Promise<Http2Response | HttpResponse>;
111
+ /**
112
+ * Écrit le corps, puis termine la requête ({@link close}). Synchrone pour une
113
+ * réponse unique (#505).
114
+ *
115
+ * @returns la réponse, ou sa promesse quand l'écriture attend.
116
+ */
117
+ write(chunk: unknown, encoding?: BufferEncoding, _flush?: boolean): MaybePromise<Http2Response | HttpResponse>;
118
+ flush(chunk: unknown, encoding: BufferEncoding): MaybePromise<HttpResponse>;
119
+ /**
120
+ * Termine la requête : hook `onClose`, puis fin de la réponse si l'envoi ne
121
+ * l'a pas déjà faite. Synchrone sans écouteur `onClose` (#505).
122
+ *
123
+ * @returns la réponse, ou sa promesse quand le hook attend.
124
+ */
125
+ close(): MaybePromise<Http2Response | HttpResponse>;
126
+ /** Fin de la réponse, sauf si l'envoi l'a déjà terminée. */
127
+ private endResponse;
71
128
  redirect(Url: string, status?: number | string, headers?: Record<string, string | number>): HttpResponse;
72
129
  redirectHttps(status?: number | string, headers?: Record<string, string | number>): HttpResponse;
73
130
  redirectHttp(status?: number | string, headers?: Record<string, string | number>): HttpResponse;
@@ -39,7 +39,7 @@ declare class HttpRequest {
39
39
  host: string | undefined;
40
40
  method: HTTPMethod;
41
41
  contentType: string | null;
42
- rawContentType: Record<string, string>;
42
+ rawContentType: Record<string, string | undefined>;
43
43
  domain: string;
44
44
  remoteAddress: string | null | undefined;
45
45
  hostname: string;
@@ -116,7 +116,37 @@ declare class HttpRequest {
116
116
  * `body` dans tout le framework, celui de l'écosystème et d'`IAdminRequest`.
117
117
  */
118
118
  private fireRequestEnd;
119
- initialize(): Promise<unknown>;
119
+ /**
120
+ * Lit le corps de la requête, s'il y en a un, puis émet `onRequestEnd`.
121
+ *
122
+ * Le contrat de retour dit ce qui reste à ATTENDRE :
123
+ * - `undefined` — la méthode ne porte pas de corps (GET, HEAD, OPTIONS…) et
124
+ * aucun écouteur `onRequestEnd` ne rend de promesse : tout est fait, de
125
+ * façon SYNCHRONE ;
126
+ * - une `Promise` — le corps arrive du socket (POST, PUT, PATCH, DELETE), ou
127
+ * un écouteur `onRequestEnd` est à attendre. Sa valeur n'est consommée par
128
+ * personne : elle ne sert qu'au séquençage.
129
+ *
130
+ * L'appelant n'attend donc que ce qui attend vraiment
131
+ * (`const pending = request.initialize(); if (pending) await pending;`) : un
132
+ * `await` inconditionnel reportait le pipeline d'une micro-tâche, et la
133
+ * chaîne de promesses coûtait ~6 évènements asynchrones à un GET qui n'avait
134
+ * rien à lire (#505).
135
+ *
136
+ * Ne lève jamais de façon synchrone : un corps refusé (413) ou illisible rompt
137
+ * la promesse rendue, comme avant.
138
+ *
139
+ * @returns `undefined` quand tout est fait, sinon la promesse à attendre.
140
+ */
141
+ initialize(): Promise<unknown> | undefined;
142
+ /**
143
+ * Fin d'une requête SANS corps à lire : la marque terminée et émet
144
+ * `onRequestEnd`. Synchrone — ne rend une promesse que lorsqu'un écouteur
145
+ * `onRequestEnd` est à attendre, ou quand l'émission lève (celle d'`onError`).
146
+ *
147
+ * @returns `undefined` quand tout est fait, sinon la promesse à attendre.
148
+ */
149
+ private endWithoutBody;
120
150
  /**
121
151
  * B1 — Pré-contrôle de taille du corps via l'en-tête `Content-Length` :
122
152
  * rejette en 413 (RFC 9110 §15.5.14 « Content Too Large ») AVANT toute lecture
@@ -126,6 +156,13 @@ declare class HttpRequest {
126
156
  *
127
157
  * @throws {HttpError} 413 si `Content-Length` dépasse `maxBodySize`.
128
158
  */
159
+ /**
160
+ * Lit le plafond du corps et les limites d'envoi tels que les voit CETTE
161
+ * requête : la configuration http, surchargée par le calque que la requête a
162
+ * pu poser avant la lecture du corps (`overlayConfig`, #494). Hors pipeline
163
+ * (aucun scope ouvert), la configuration du module telle quelle.
164
+ */
165
+ private readBodyLimits;
129
166
  private enforceBodyLimit;
130
167
  parseRequest(): Promise<ParserType | null>;
131
168
  /**
@@ -2,6 +2,7 @@ import http, { OutgoingHttpHeaders } from "node:http";
2
2
  import http2 from "node:http2";
3
3
  import HttpContext from "../http/HttpContext.js";
4
4
  import { Pci, Pdu, Message, Severity, Msgid } from "nodefony";
5
+ import type { MaybePromise } from "nodefony";
5
6
  import Cookie from "../../cookies/cookie.js";
6
7
  declare class HttpResponse {
7
8
  #private;
@@ -11,10 +12,9 @@ declare class HttpResponse {
11
12
  statusMessage: string;
12
13
  flushing: boolean;
13
14
  encoding: BufferEncoding;
14
- body: Buffer | null;
15
15
  contentType: string;
16
16
  headers: http.OutgoingHttpHeaders;
17
- timeout?: number;
17
+ timeout?: number | undefined;
18
18
  cookies: Record<string, Cookie>;
19
19
  constructor(response: http.ServerResponse | http2.Http2ServerResponse, context: HttpContext);
20
20
  /**
@@ -24,6 +24,20 @@ declare class HttpResponse {
24
24
  * l'ancienne pose au constructeur, sans le ping-pong set/remove/re-set.
25
25
  */
26
26
  protected ensureContentTypeHeader(): void;
27
+ /**
28
+ * Corps de la réponse en octets. Un corps posé en texte n'est encodé qu'à la
29
+ * première lecture de cette propriété — l'envoi, lui, écrit le texte tel quel.
30
+ */
31
+ get body(): Buffer | null;
32
+ set body(value: Buffer | null);
33
+ /**
34
+ * Corps tel qu'il part sur le fil : le texte s'il n'a jamais été encodé, sinon
35
+ * les octets — vide légal si rien n'a été posé (`write(null)` jetterait
36
+ * ERR_STREAM_NULL_VALUES).
37
+ */
38
+ protected get payload(): string | Buffer;
39
+ /** Encodage à passer avec {@link payload} (ignoré par Node pour des octets). */
40
+ protected get payloadEncoding(): BufferEncoding;
27
41
  clean(): void;
28
42
  isHeaderSent(): boolean;
29
43
  isHtml(): boolean;
@@ -50,15 +64,41 @@ declare class HttpResponse {
50
64
  };
51
65
  getStatusCode(): number;
52
66
  getStatusMessage(code?: number | string): string;
53
- setBody(ele: unknown, encoding?: BufferEncoding | undefined): Buffer;
67
+ /**
68
+ * Pose le corps de la réponse : texte gardé tel quel, octets copiés sur leur
69
+ * seule fenêtre, tout autre valeur sérialisée en JSON.
70
+ *
71
+ * @param ele - corps à envoyer.
72
+ * @param encoding - encodage d'un corps texte (défaut : `this.encoding`).
73
+ */
74
+ setBody(ele: unknown, encoding?: BufferEncoding): void;
54
75
  setLength(body?: string | NodeJS.ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
55
76
  writeHead(statusCode?: number, headers?: http.OutgoingHttpHeaders | http.OutgoingHttpHeader[]): void;
56
77
  addTrailers(headers: http.OutgoingHttpHeaders): void;
57
- flush(chunk: unknown, encoding: BufferEncoding): Promise<HttpResponse>;
58
- send(chunk?: unknown, encoding?: BufferEncoding, _flush?: boolean): Promise<HttpResponse>;
59
- write(chunk?: unknown, encoding?: BufferEncoding): Promise<HttpResponse>;
78
+ flush(chunk: unknown, encoding: BufferEncoding): MaybePromise<HttpResponse>;
79
+ /**
80
+ * Écrit le corps sur le flux et, pour une réponse unique, la termine.
81
+ *
82
+ * Synchrone pour une réponse unique ou une redirection : `end(corps)` est un
83
+ * appel synchrone de Node, rien n'y est à attendre (#505). Une promesse
84
+ * seulement en streaming chunké (`flush()`), réglée quand le flux accepte
85
+ * l'écriture suivante (`drain`) — c'est la contre-pression.
86
+ *
87
+ * @param chunk - le corps (posé par `setBody`) ; absent, le corps déjà posé.
88
+ * @param encoding - l'encodage d'un corps texte.
89
+ * @returns la réponse, ou sa promesse en streaming chunké.
90
+ * @throws de façon SYNCHRONE quand la réponse Node n'existe plus.
91
+ */
92
+ send(chunk?: unknown, encoding?: BufferEncoding, _flush?: boolean): MaybePromise<HttpResponse>;
93
+ write(chunk?: unknown, encoding?: BufferEncoding): MaybePromise<HttpResponse>;
60
94
  writeContinue(): void | undefined;
61
- end(chunk?: string | Buffer, encoding?: BufferEncoding): Promise<http.ServerResponse | http2.ServerHttp2Stream>;
95
+ /**
96
+ * Termine la réponse Node — un appel synchrone, rien n'y est à attendre (#505).
97
+ *
98
+ * @returns le flux terminé (valeur de `ServerResponse.end`).
99
+ * @throws de façon SYNCHRONE quand la réponse Node n'existe plus.
100
+ */
101
+ end(chunk?: string | Buffer, encoding?: BufferEncoding): MaybePromise<http.ServerResponse | http2.ServerHttp2Stream>;
62
102
  getHeader(name: string): string | number | string[] | undefined;
63
103
  hasHeader(name: string): boolean;
64
104
  getHeaders(): http.OutgoingHttpHeaders;
@@ -58,7 +58,7 @@ declare class ParserJson extends Parser {
58
58
  type AcceptEntry = {
59
59
  type: RegExp;
60
60
  subtype: RegExp;
61
- q?: number;
61
+ q?: number | undefined;
62
62
  [key: string]: RegExp | number | string | undefined;
63
63
  };
64
64
  declare const acceptParser: (acc?: string) => AcceptEntry[];
@@ -1,7 +1,24 @@
1
1
  import http2 from "node:http2";
2
+ import type { MaybePromise } from "nodefony";
2
3
  import http from "node:http";
3
4
  import HttpContext from "../http/HttpContext.js";
4
5
  import HttpResponse from "../http/Response.js";
6
+ /**
7
+ * Compose les en-têtes d'une réponse HTTP/2 : ceux posés sur la réponse, puis
8
+ * ceux passés à `writeHead`, qui gagnent.
9
+ *
10
+ * Les clés sont ramenées en MINUSCULES : `getHeaders()` rend `content-type`,
11
+ * un contrôleur passe `Content-Type` — fusionnées telles quelles, les deux
12
+ * clés partaient ensemble et `stream.respond()` refusait la réponse
13
+ * (`ERR_HTTP2_HEADER_SINGLE_VALUE`), soit une 500 sur tout média servi en
14
+ * HTTP/2. HTTP/2 n'a de toute façon que des noms en minuscules (RFC 9113 §8.2.1).
15
+ *
16
+ * @param statusMessage - message de statut, recopié en `x-status-message`
17
+ * @param current - en-têtes déjà posés sur la réponse
18
+ * @param extra - en-têtes passés à `writeHead` (objet, ou liste plate clé/valeur)
19
+ * @returns les en-têtes à transmettre à `respond()`
20
+ */
21
+ export declare function mergeResponseHeaders(statusMessage: string, current: http.OutgoingHttpHeaders, extra?: http.OutgoingHttpHeaders | http.OutgoingHttpHeader[]): http.OutgoingHttpHeaders;
5
22
  declare class Http2Response extends HttpResponse {
6
23
  statusCode: number;
7
24
  stream: http2.ServerHttp2Stream | null;
@@ -9,8 +26,21 @@ declare class Http2Response extends HttpResponse {
9
26
  constructor(response: http2.Http2ServerResponse, context: HttpContext);
10
27
  isHeaderSent(): boolean;
11
28
  writeHead(statusCode?: number, headers?: http.OutgoingHttpHeaders | http.OutgoingHttpHeader[]): void;
12
- send(chunk?: unknown, encoding?: BufferEncoding, _flush?: boolean): Promise<Http2Response>;
13
- end(chunk?: string | Buffer, encoding?: BufferEncoding): Promise<http.ServerResponse | http2.ServerHttp2Stream>;
29
+ /**
30
+ * Écrit le corps sur le flux HTTP/2. Synchrone pour une redirection, un flux
31
+ * fermé ou le repli HTTP/1 ; une promesse quand le corps est écrit sur le
32
+ * flux, réglée par le rappel d'écriture (#505).
33
+ *
34
+ * @returns la réponse, ou sa promesse.
35
+ */
36
+ send(chunk?: unknown, encoding?: BufferEncoding, _flush?: boolean): MaybePromise<Http2Response>;
37
+ /**
38
+ * Termine le flux HTTP/2 (ou la réponse de repli) — synchrone (#505).
39
+ *
40
+ * @returns le flux terminé.
41
+ * @throws de façon SYNCHRONE ce que lève la fin du flux.
42
+ */
43
+ end(chunk?: string | Buffer, encoding?: BufferEncoding): MaybePromise<http.ServerResponse | http2.ServerHttp2Stream>;
14
44
  getStatusMessage(code?: number | string): string;
15
45
  getStatus(): {
16
46
  code: number;
@@ -11,13 +11,13 @@ export type { IWsCookie } from "../../../interfaces/ICookie.js";
11
11
  */
12
12
  export declare function isPeerGoneError(error: unknown): boolean;
13
13
  declare class WebsocketResponse {
14
- private context;
14
+ private readonly context;
15
15
  statusCode: number;
16
16
  body: Buffer | null;
17
17
  encoding: BufferEncoding;
18
18
  connection: Ws | null;
19
19
  statusMessage: string;
20
- webSocketVersion?: number;
20
+ webSocketVersion?: number | undefined;
21
21
  cookies: Record<string, Cookie>;
22
22
  constructor(connection: Ws | null, context: WebsocketContext);
23
23
  log(pci: Pci, severity?: Severity, msgid?: Msgid, msg?: Message): import("nodefony").Pdu | undefined;
@@ -30,8 +30,8 @@ declare class WebsocketResponse {
30
30
  */
31
31
  private logFirstDrop;
32
32
  setBody(ele: string | NodeJS.ArrayBufferView | ArrayBuffer | SharedArrayBuffer, encoding?: BufferEncoding): Buffer<ArrayBufferLike> | null;
33
- drop(reasonCode: number, description: string): void;
34
- close(reasonCode: number, description: string): void;
33
+ drop(reasonCode?: number, description?: string): void;
34
+ close(reasonCode?: number, description?: string): void;
35
35
  getStatus(): {
36
36
  code: number;
37
37
  message: string;
@@ -40,7 +40,7 @@ export default class WebsocketContext extends Context implements IWebsocketConte
40
40
  #private;
41
41
  request: WsIncomingMessage | null;
42
42
  response: WebsocketResponse | null;
43
- acceptedProtocol?: string;
43
+ acceptedProtocol?: string | undefined;
44
44
  port: number | string;
45
45
  rejected: boolean;
46
46
  teardownWired: boolean;
@@ -59,7 +59,7 @@ export default class WebsocketContext extends Context implements IWebsocketConte
59
59
  handle(data?: unknown[]): Promise<this>;
60
60
  render(chunk: unknown, encoding?: BufferEncoding): Promise<WebsocketResponse>;
61
61
  send(data?: string | Buffer | null, encoding?: BufferEncoding): Promise<WebsocketResponse>;
62
- broadcast(data?: string | Buffer | null, encoding?: BufferEncoding): void | null;
62
+ broadcast(data?: string | Buffer | null, encoding?: BufferEncoding): null | undefined;
63
63
  /**
64
64
  * Logge le CONTENU d'un message WS (corrélé `requestId` via l'override `log`),
65
65
  * gaté hors prod et borné — le Suivi de requête (Studio) le surface alors par
@@ -121,5 +121,14 @@ export default class WebsocketContext extends Context implements IWebsocketConte
121
121
  clean(): void;
122
122
  close(reasonCode: number | undefined | null, description: string): void;
123
123
  drop(reasonCode: number, description: string): void;
124
+ /**
125
+ * Relit `rejected` sans le rétrécissement de TypeScript : après la garde
126
+ * d'entrée de {@link handle}, le compilateur le croit `false` pour toute la
127
+ * méthode, alors qu'un écouteur `onRequest` (pare-feu) ou le `match` du
128
+ * resolver peut avoir rejeté entre-temps.
129
+ *
130
+ * @returns `true` si la connexion a été rejetée.
131
+ */
132
+ private isRejected;
124
133
  reject(code: number | string | undefined, message?: string): void;
125
134
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Taille maximale, en octets UTF-8, de la raison d'une trame Close (RFC 6455
3
+ * §5.5 : charge d'une trame de contrôle ≤ 125 octets, dont 2 pour le code).
4
+ */
5
+ export declare const MAX_WS_CLOSE_REASON_BYTES = 123;
6
+ /**
7
+ * Borne la raison d'une fermeture WebSocket à 123 octets UTF-8, coupée sur une
8
+ * frontière de caractère.
9
+ *
10
+ * `ws.close(code, reason)` LÈVE une `RangeError` au-delà de 123 octets, et la
11
+ * connexion n'est alors PAS fermée : jusqu'en `ws` 8.21 elle restait en
12
+ * `CLOSING` sans trame Close ni minuterie — hors de portée de toute garde
13
+ * `readyState === OPEN` et du heartbeat, donc ouverte jusqu'à la mort TCP ;
14
+ * depuis 8.22 elle reste `OPEN`. Or la raison porte souvent un message
15
+ * d'erreur, dont rien ne borne la longueur.
16
+ *
17
+ * @param reason - raison candidate (message d'erreur, libellé…)
18
+ * @returns la raison telle quelle si elle tient, sinon tronquée et terminée
19
+ * par « … » ; chaîne vide si elle n'est pas une chaîne.
20
+ */
21
+ export declare function toWsCloseReason(reason: string | undefined | null): string;
@@ -24,17 +24,17 @@ declare function cookiesParser(context: ContextType): void;
24
24
  declare class Cookie implements ICookieInterface {
25
25
  options: CookieOptionsType;
26
26
  name: string;
27
- signed?: boolean;
27
+ signed?: boolean | undefined;
28
28
  value: unknown;
29
- originalMaxAge?: number;
30
- expires?: Date;
31
- maxAge?: number;
32
- path?: string;
29
+ originalMaxAge?: number | undefined;
30
+ expires?: Date | undefined;
31
+ maxAge?: number | undefined;
32
+ path?: string | undefined;
33
33
  domain: string | undefined;
34
- httpOnly?: boolean;
35
- secure?: boolean;
36
- sameSite?: SameSiteType;
37
- priority?: string;
34
+ httpOnly?: boolean | undefined;
35
+ secure?: boolean | undefined;
36
+ sameSite?: SameSiteType | undefined;
37
+ priority?: string | undefined;
38
38
  constructor(cookies: Cookie);
39
39
  constructor(name: string, value: unknown, options?: CookieOptionsType);
40
40
  clearCookie(): void;
@@ -2,13 +2,13 @@ import { nodefonyError as NodefonyError } from "nodefony";
2
2
  import { ContextType } from "../../service/http-kernel.js";
3
3
  import { HttpRequestType, HttpRsponseType } from "../context/http/HttpContext.js";
4
4
  declare class HttpError extends NodefonyError {
5
- context?: ContextType;
6
- response?: HttpRsponseType;
7
- request?: HttpRequestType;
8
- url?: string;
9
- controller?: string;
10
- action?: string;
11
- jsonResponse?: string;
5
+ context?: ContextType | undefined;
6
+ response?: HttpRsponseType | undefined;
7
+ request?: HttpRequestType | undefined;
8
+ url?: string | undefined;
9
+ controller?: string | undefined;
10
+ action?: string | undefined;
11
+ jsonResponse?: string | undefined;
12
12
  constructor(message?: unknown, code?: number, context?: ContextType);
13
13
  toString(): string;
14
14
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Message lisible d'une valeur LEVÉE (`catch`, rejet) dont on ignore le type.
3
+ *
4
+ * Même rendu que `String(value)` pour une `Error` (son `message`) et pour les
5
+ * primitives ; un objet quelconque rend son étiquette (`[object Object]`) sans
6
+ * appeler un `toString` arbitraire ni sérialiser son contenu — ce texte peut
7
+ * finir dans une réponse ou un journal, il ne doit rien divulguer.
8
+ *
9
+ * @param value - la valeur levée.
10
+ * @returns un texte court, jamais vide pour une valeur définie.
11
+ */
12
+ export declare function thrownMessage(value: unknown): string;
@@ -38,13 +38,13 @@ export interface ProfileQuery {
38
38
  /** Requête (SQL ou commande NoSQL), tronquée si volumineuse. */
39
39
  sql: string;
40
40
  /** Début relatif (`performance.now()`) — même horloge que les phases. */
41
- startMs?: number;
41
+ startMs?: number | undefined;
42
42
  /** Durée d'exécution en ms. */
43
43
  durationMs: number;
44
44
  /** Lignes affectées/retournées, si connu. */
45
- rows?: number;
45
+ rows?: number | undefined;
46
46
  /** Connecteur émetteur (`drizzle`, `mongoose`…). */
47
- connector?: string;
47
+ connector?: string | undefined;
48
48
  }
49
49
  /**
50
50
  * Traversée du firewall par cette requête — **ce qui était possible** (la zone :
@@ -97,9 +97,9 @@ export interface ProfileEntry {
97
97
  error: string | null;
98
98
  phases: ProfilePhase[];
99
99
  /** Requêtes ORM (SEAM futur — `undefined` tant qu'aucun adapter ne pushe). */
100
- queries?: ProfileQuery[];
100
+ queries?: ProfileQuery[] | undefined;
101
101
  /** Traversée du firewall — `undefined` si la requête n'a croisé aucune zone. */
102
- security?: ProfileSecurity;
102
+ security?: ProfileSecurity | undefined;
103
103
  }
104
104
  /** Résumé léger d'un profil pour la liste `recent` (sans les phases). */
105
105
  export interface ProfileSummary {
@@ -115,49 +115,49 @@ export interface ProfileSummary {
115
115
  }
116
116
  /** Forme structurelle minimale lue sur un `Context` (lecture défensive). */
117
117
  interface ProfilableContext {
118
- requestId?: string;
119
- type?: string;
120
- scheme?: string;
121
- method?: string | null;
122
- url?: string;
123
- remoteAddress?: string | null;
124
- traceparent?: string | null;
118
+ requestId?: string | undefined;
119
+ type?: string | undefined;
120
+ scheme?: string | undefined;
121
+ method?: string | null | undefined;
122
+ url?: string | undefined;
123
+ remoteAddress?: string | null | undefined;
124
+ traceparent?: string | null | undefined;
125
125
  error?: {
126
126
  message?: string;
127
- } | null;
127
+ } | null | undefined;
128
128
  response?: {
129
129
  statusCode?: number;
130
- } | null;
130
+ } | null | undefined;
131
131
  phases?: ReadonlyArray<{
132
132
  name: string;
133
133
  startMs: number;
134
- durationMs?: number;
135
- }>;
134
+ durationMs?: number | undefined;
135
+ }> | undefined;
136
136
  resolver?: {
137
137
  route?: {
138
- name?: string;
138
+ name?: string | undefined;
139
139
  } | null;
140
140
  controller?: {
141
- name?: string;
142
- } | null;
143
- actionName?: string;
144
- } | null;
141
+ name?: string | undefined;
142
+ } | null | undefined;
143
+ actionName?: string | undefined;
144
+ } | null | undefined;
145
145
  /**
146
146
  * Buffer de requêtes ORM rempli pendant la requête (dev-only). Même tableau
147
147
  * que la payload ALS — les adapters ORM y poussent via `RequestContext`.
148
148
  * `null`/absent hors profiling.
149
149
  */
150
- profilerQueries?: ProfileQuery[] | null;
150
+ profilerQueries?: ProfileQuery[] | null | undefined;
151
151
  /**
152
152
  * Zone firewall capturée (`SecuredArea`, posée par `Firewall.isSecure`). Lue
153
153
  * en structurel : `@nodefony/http` ne peut pas importer `@nodefony/security`.
154
154
  */
155
155
  security?: {
156
- name?: string;
157
- security?: boolean;
158
- mode?: string;
159
- authenticators?: readonly string[];
160
- } | null;
156
+ name?: string | undefined;
157
+ security?: boolean | undefined;
158
+ mode?: string | undefined;
159
+ authenticators?: readonly string[] | undefined;
160
+ } | null | undefined;
161
161
  /** Décision du firewall sur cette requête (dev-only, cf `ISecurityTrace`). */
162
162
  securityTrace?: {
163
163
  authenticator: string | null;
@@ -165,7 +165,7 @@ interface ProfilableContext {
165
165
  reason: string | null;
166
166
  user: string | null;
167
167
  roles: string[] | null;
168
- } | null;
168
+ } | null | undefined;
169
169
  }
170
170
  export declare class Profiler {
171
171
  private readonly _buf;
@@ -0,0 +1,20 @@
1
+ import type { ServersConfig } from "nodefony";
2
+ /**
3
+ * Rétrécit la section `servers` de la configuration du kernel (lue dans le sac
4
+ * de config non typé du cœur) en {@link ServersConfig}, sans propager `any`.
5
+ *
6
+ * Seule lecture de `kernel.options.servers` dans `@nodefony/http` : les serveurs
7
+ * et le `HttpKernel` passent tous par ici.
8
+ *
9
+ * @param servers - la valeur brute de `kernel.options.servers`.
10
+ * @returns la section typée, ou `undefined` si elle est absente ou n'est pas un objet.
11
+ */
12
+ export declare function asServersConfig(servers: unknown): ServersConfig | undefined;
13
+ /**
14
+ * Port DEMANDÉ pour un serveur (`servers.<type>.port`), ou `undefined` si le
15
+ * serveur est désactivé, absent, ou sans port numérique.
16
+ *
17
+ * @param servers - section `servers` déjà rétrécie ({@link asServersConfig}).
18
+ * @param type - le serveur visé.
19
+ */
20
+ export declare function configuredServerPort(servers: ServersConfig | undefined, type: "http" | "https"): number | undefined;