@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,4 +1,5 @@
1
1
  import { Kernel, Module } from "nodefony";
2
+ import { type IHttpOverlay } from "./nodefony/config/config.js";
2
3
  import type { IHttpConfig, IHttpConfigInput } from "./nodefony/interfaces/IHttpConfig.js";
3
4
  import { Profiler } from "./nodefony/src/profiler/Profiler.js";
4
5
  import HttpKernel from "./nodefony/service/http-kernel.js";
@@ -22,8 +23,21 @@ declare module "nodefony" {
22
23
  interface NodefonyModuleConfig {
23
24
  "@nodefony/http": IHttpConfigInput;
24
25
  }
26
+ interface NodefonyModuleOverlay {
27
+ "@nodefony/http": IHttpOverlay;
28
+ }
25
29
  }
26
30
  declare class Http extends Module<IHttpConfig> {
31
+ overlaySchema: import("zod").ZodObject<{
32
+ maxBodySize: import("zod").ZodOptional<import("zod").ZodType<number, number, import("zod/v4/core").$ZodTypeInternals<number, number>>>;
33
+ upload: import("zod").ZodOptional<import("zod").ZodObject<{
34
+ maxFileSize: import("zod").ZodOptional<import("zod").ZodType<number, number, import("zod/v4/core").$ZodTypeInternals<number, number>>>;
35
+ maxTotalFileSize: import("zod").ZodOptional<import("zod").ZodType<number, number, import("zod/v4/core").$ZodTypeInternals<number, number>>>;
36
+ maxFiles: import("zod").ZodOptional<import("zod").ZodType<number, number, import("zod/v4/core").$ZodTypeInternals<number, number>>>;
37
+ maxFields: import("zod").ZodOptional<import("zod").ZodType<number, number, import("zod/v4/core").$ZodTypeInternals<number, number>>>;
38
+ maxFieldsSize: import("zod").ZodOptional<import("zod").ZodType<number, number, import("zod/v4/core").$ZodTypeInternals<number, number>>>;
39
+ }, import("zod/v4/core").$strict>>;
40
+ }, import("zod/v4/core").$strict>;
27
41
  constructor(kernel: Kernel);
28
42
  /**
29
43
  * Expose le JSON Schema de la config http (dérivé du schéma Zod, flags `meta()`
@@ -75,6 +89,8 @@ export { SESSION_SORTABLE_FIELDS, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_S
75
89
  export type { IHttpRequest, IHttp2Request, IWsRequest, HTTPMethodType, } from "./nodefony/interfaces/IRequest.js";
76
90
  export type { IHttpResponse, IWebsocketResponse, } from "./nodefony/interfaces/IResponse.js";
77
91
  export type { IHttpKernel } from "./nodefony/interfaces/IHttpKernel.js";
92
+ export type { IResolvedRoute, IRouteResolver, IRequestRouter, } from "./nodefony/interfaces/IRouting.js";
93
+ export type { ISecurityZone, IFirewallGate, } from "./nodefony/interfaces/ISecurity.js";
78
94
  export type { IUploadedFile, IUploadService, IParsedUploadFile, IUploadOptions, } from "./nodefony/interfaces/IUpload.js";
79
95
  export type { IErrorRenderer, IErrorHttpResult, IErrorWebsocketResult, } from "./nodefony/interfaces/IErrorRenderer.js";
80
96
  export type { IRequestLogger, IRequestLogEntry, } from "./nodefony/interfaces/IRequestLogger.js";
@@ -87,6 +87,7 @@ export declare const httpConfigSchema: z.ZodObject<{
87
87
  name: z.ZodOptional<z.ZodString>;
88
88
  value: z.ZodOptional<z.ZodString>;
89
89
  shortName: z.ZodOptional<z.ZodString>;
90
+ type: z.ZodOptional<z.ZodString>;
90
91
  }, z.core.$loose>>>;
91
92
  }, z.core.$strict>>;
92
93
  }, z.core.$strict>>;
@@ -189,6 +190,28 @@ export declare const httpConfigSchema: z.ZodObject<{
189
190
  export type IHttpConfig = z.infer<typeof httpConfigSchema>;
190
191
  /** Type d'entrée (toutes sections omissibles — défauts du schéma). */
191
192
  export type IHttpConfigInput = z.input<typeof httpConfigSchema>;
193
+ /**
194
+ * LISTE BLANCHE des clés qu'une requête peut surcharger pour elle seule
195
+ * (`overlayConfig("@nodefony/http", …)`, #494) : les quotas de corps et
196
+ * d'envoi, lus au MOMENT de lire le corps (`Request.readBodyLimits`) — un
197
+ * calque posé avant la lecture du corps (écouteur `onRequestScope`, organisation
198
+ * reconnue à son nom d'hôte) les voit. Tout le reste est refusé : en-têtes de
199
+ * sécurité, pare-feu, `trustProxy`, sessions, limites de débit, certificats —
200
+ * et `queryString`, lu au constructeur de la requête, avant tout calque.
201
+ * Chaque champ réutilise celui du schéma (mêmes bornes) sans son défaut.
202
+ */
203
+ export declare const httpOverlaySchema: z.ZodObject<{
204
+ maxBodySize: z.ZodOptional<z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>>;
205
+ upload: z.ZodOptional<z.ZodObject<{
206
+ maxFileSize: z.ZodOptional<z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>>;
207
+ maxTotalFileSize: z.ZodOptional<z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>>;
208
+ maxFiles: z.ZodOptional<z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>>;
209
+ maxFields: z.ZodOptional<z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>>;
210
+ maxFieldsSize: z.ZodOptional<z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>>;
211
+ }, z.core.$strict>>;
212
+ }, z.core.$strict>;
213
+ /** Calque accepté par `overlayConfig("@nodefony/http", …)`. */
214
+ export type IHttpOverlay = z.input<typeof httpOverlaySchema>;
192
215
  /**
193
216
  * Défauts du module, matérialisés depuis le schéma (source unique). Toujours
194
217
  * valides par construction ; passés au `super(..., config)` du Module class.
@@ -1,4 +1,5 @@
1
1
  import type { URL } from "node:url";
2
+ import type { MaybePromise } from "nodefony";
2
3
  import type { ICookie } from "./ICookie.js";
3
4
  import type { ISession } from "./ISession.js";
4
5
  import type { HTTPMethodType } from "./IRequest.js";
@@ -10,8 +11,8 @@ export type PhaseName = "parse" | "resolve" | "firewall" | "initialize" | "actio
10
11
  export interface PhaseTiming {
11
12
  name: PhaseName;
12
13
  startMs: number;
13
- endMs?: number;
14
- durationMs?: number;
14
+ endMs?: number | undefined;
15
+ durationMs?: number | undefined;
15
16
  }
16
17
  /** Issue de la traversée d'une zone firewall. */
17
18
  export type SecurityOutcome =
@@ -110,9 +111,25 @@ export interface IContext {
110
111
  export interface IHttpContext extends IContext {
111
112
  proxy: object | null;
112
113
  isRedirect: boolean;
113
- handle(): Promise<object>;
114
- render(chunk: unknown, encoding?: BufferEncoding, status?: string | number, headers?: Record<string, string | number>): Promise<object>;
115
- send(chunk?: unknown, encoding?: BufferEncoding): Promise<object>;
114
+ /**
115
+ * Sert la requête — synchrone quand l'action et son rendu le sont.
116
+ *
117
+ * @returns le résultat du rendu, ou sa promesse dès qu'une étape attend.
118
+ */
119
+ handle(): MaybePromise<object>;
120
+ /**
121
+ * Sérialise puis envoie — synchrone quand l'envoi l'est.
122
+ *
123
+ * @returns la réponse, ou sa promesse dès qu'une étape attend.
124
+ */
125
+ render(chunk: unknown, encoding?: BufferEncoding, status?: string | number, headers?: Record<string, string | number>): MaybePromise<object>;
126
+ /**
127
+ * Envoie la réponse — synchrone sans session à sauver, sans hook `onSend`,
128
+ * pour une réponse unique.
129
+ *
130
+ * @returns la réponse, ou sa promesse dès qu'une étape attend.
131
+ */
132
+ send(chunk?: unknown, encoding?: BufferEncoding): MaybePromise<object>;
116
133
  end(): Promise<object>;
117
134
  redirect(url: string, status?: number | string, headers?: Record<string, string | number>): unknown;
118
135
  getHostName(): string | undefined;
@@ -121,8 +138,8 @@ export interface IHttpContext extends IContext {
121
138
  getUserAgent(): string | undefined;
122
139
  }
123
140
  export interface IWebsocketContext extends IContext {
124
- acceptedProtocol?: string;
125
- connection: unknown | null;
141
+ acceptedProtocol?: string | undefined;
142
+ connection: unknown;
126
143
  rejected: boolean;
127
144
  port: number | string;
128
145
  wsUrl: URL | null;
@@ -1,15 +1,15 @@
1
1
  export type SameSiteType = "Strict" | "Lax" | "None";
2
2
  export type PriorityType = "High" | "Medium" | "Low" | undefined;
3
3
  export interface ICookieOptions {
4
- maxAge?: number;
5
- path?: string;
6
- domain?: string;
7
- secure?: boolean;
8
- expires?: Date | string | number;
9
- sameSite?: SameSiteType;
10
- httpOnly?: boolean;
11
- signed?: boolean;
12
- secret?: string;
4
+ maxAge?: number | undefined;
5
+ path?: string | undefined;
6
+ domain?: string | undefined;
7
+ secure?: boolean | undefined;
8
+ expires?: Date | string | number | undefined;
9
+ sameSite?: SameSiteType | undefined;
10
+ httpOnly?: boolean | undefined;
11
+ signed?: boolean | undefined;
12
+ secret?: string | undefined;
13
13
  priority?: PriorityType;
14
14
  }
15
15
  export interface IWsCookie {
@@ -26,16 +26,16 @@ export interface ICookie {
26
26
  name: string;
27
27
  value: unknown;
28
28
  options: ICookieOptions;
29
- signed?: boolean;
30
- originalMaxAge?: number;
31
- expires?: Date;
32
- maxAge?: number;
33
- path?: string;
29
+ signed?: boolean | undefined;
30
+ originalMaxAge?: number | undefined;
31
+ expires?: Date | undefined;
32
+ maxAge?: number | undefined;
33
+ path?: string | undefined;
34
34
  domain?: string | undefined;
35
- httpOnly?: boolean;
36
- secure?: boolean;
37
- sameSite?: SameSiteType;
38
- priority?: string;
35
+ httpOnly?: boolean | undefined;
36
+ secure?: boolean | undefined;
37
+ sameSite?: SameSiteType | undefined;
38
+ priority?: string | undefined;
39
39
  setValue(value: unknown): unknown;
40
40
  toString(): string;
41
41
  serialize(): string;
@@ -1,9 +1,15 @@
1
+ import type { IServerKernel, MaybePromise } from "nodefony";
1
2
  import type { ServerType } from "./IContext.js";
2
- export interface IHttpKernel {
3
+ export interface IHttpKernel extends IServerKernel {
3
4
  domain: string;
4
- httpPort?: number;
5
- httpsPort?: number;
6
- handle(request: unknown, response: unknown, type: ServerType): Promise<unknown>;
5
+ httpPort?: number | undefined;
6
+ httpsPort?: number | undefined;
7
+ /**
8
+ * Pipeline public d'une requête (appelé par les serveurs HTTP/HTTPS).
9
+ *
10
+ * @returns le contexte servi, ou sa promesse dès qu'une étape attend.
11
+ */
12
+ handle(request: unknown, response: unknown, type: ServerType): MaybePromise<unknown>;
7
13
  handleFrontController(context: object, checkFirewall?: boolean): Promise<unknown>;
8
14
  onError(error: unknown, context?: object, extraHeaders?: object): Promise<object>;
9
15
  isValidDomain(context: object): boolean;
@@ -31,7 +31,7 @@ export interface IHttpResponse extends IResponse {
31
31
  setCookie(cookie: ICookie): void;
32
32
  }
33
33
  export interface IWebsocketResponse extends IResponse {
34
- connection: unknown | null;
34
+ connection: unknown;
35
35
  webSocketVersion?: number;
36
36
  setConnection(connection: unknown): unknown;
37
37
  broadcast(data: Buffer | string): void;
@@ -0,0 +1,87 @@
1
+ import type { MaybePromise } from "nodefony";
2
+ import type { ContextType } from "../service/http-kernel.js";
3
+ /**
4
+ * Ce que le pipeline HTTP/WS lit d'une route résolue — et rien de plus.
5
+ *
6
+ * Le routage vit dans `@nodefony/framework`, qui dépend de `@nodefony/http` :
7
+ * `http` ne peut donc pas nommer ses classes sans recréer un cycle. C'est le
8
+ * LECTEUR qui définit le contrat ; `Route` (framework) le satisfait par
9
+ * structure, et le typecheck de `framework` garantit l'alignement.
10
+ */
11
+ export interface IResolvedRoute {
12
+ /** Nom de la route (journalisation, diagnostic). */
13
+ readonly name: string;
14
+ /** Corps laissé en flux (`@Body({ stream: true })`) : le parse est sauté. */
15
+ readonly bodyStream?: boolean | undefined;
16
+ }
17
+ /**
18
+ * Ce que le pipeline HTTP/WS attend du résolveur d'une requête, posé sur
19
+ * `context.resolver` par le routeur.
20
+ *
21
+ * Implémenté par `Resolver` (`@nodefony/framework`), qui l'étend de ses propres
22
+ * membres — un code de `framework` qui a besoin de la classe entière la tient
23
+ * de son routeur, pas de ce contrat.
24
+ */
25
+ export interface IRouteResolver {
26
+ /** Route matchée, `null` tant qu'aucune ne l'est. */
27
+ readonly route: IResolvedRoute | null;
28
+ /** `true` si une route a matché la requête. */
29
+ resolve: boolean;
30
+ /** Erreur levée pendant la résolution, relue par le pipeline. */
31
+ exception?: Error | null | undefined;
32
+ /** Classe du controller résolu — seul son nom est lu (erreurs, journal). */
33
+ readonly controller?: {
34
+ readonly name: string;
35
+ } | null | undefined;
36
+ /** Nom de l'action résolue. */
37
+ actionName?: string | undefined;
38
+ /**
39
+ * Rejoue le match d'une route sur un contexte (WS : handshake puis frames).
40
+ *
41
+ * @param route - la route à matcher, lue sur {@link IRouteResolver.route}
42
+ * @param context - le contexte courant
43
+ * @param cleanPath - chemin imposé (pont WS-RPC), sinon l'URL du contexte
44
+ */
45
+ match(route: IResolvedRoute, context: ContextType, cleanPath?: string): unknown;
46
+ /**
47
+ * Instancie le controller résolu — le pipeline ne fait que le transmettre.
48
+ *
49
+ * @param context - le contexte de la requête
50
+ * @returns l'instance du controller — directement quand elle existe déjà ou
51
+ * se construit sans rien attendre, sinon la promesse de sa création
52
+ */
53
+ newController(context?: ContextType): MaybePromise<object>;
54
+ /**
55
+ * Exécute l'action résolue, puis rend sa valeur sur le transport.
56
+ *
57
+ * @param data - arguments supplémentaires de l'action
58
+ * @param reload - rejouer depuis l'instance en cache (forward)
59
+ * @returns le résultat du rendu — synchrone quand ni l'action ni le rendu
60
+ * n'attendent, sinon sa promesse
61
+ */
62
+ callController(data?: unknown[], reload?: boolean): MaybePromise<unknown>;
63
+ }
64
+ /**
65
+ * Ce que le pipeline HTTP/WS attend du service `router` — implémenté par
66
+ * `Router` (`@nodefony/framework`).
67
+ */
68
+ export interface IRequestRouter {
69
+ /**
70
+ * Résout la route d'un contexte.
71
+ *
72
+ * @param context - le contexte HTTP/WS courant
73
+ * @param cleanPathOverride - chemin porté par un message (pont WS-RPC)
74
+ * @param methodOverride - méthode logique exigée en plus du transport WS
75
+ * @returns le résolveur (`resolve === true` si une route a matché)
76
+ */
77
+ resolve(context: ContextType, cleanPathOverride?: string, methodOverride?: string): IRouteResolver;
78
+ /**
79
+ * Instance partagée d'un controller `scope: "singleton"`, créée une seule fois.
80
+ *
81
+ * @param ctor - la classe du controller (clé du cache)
82
+ * @param create - fabrique exécutée au premier appel
83
+ * @returns l'instance partagée une fois créée ; la promesse de sa création
84
+ * tant qu'elle est en cours
85
+ */
86
+ getSingletonController<T extends object>(ctor: new (...args: never[]) => T, create: () => MaybePromise<T>): MaybePromise<T>;
87
+ }
@@ -0,0 +1,45 @@
1
+ import type { ContextType } from "../service/http-kernel.js";
2
+ /**
3
+ * Ce que le pipeline HTTP/WS — et le résolveur de framework — lit de la zone de sécurité posée sur
4
+ * `context.security` — et rien de plus.
5
+ *
6
+ * Le pare-feu vit dans `@nodefony/security`, qui dépend de `@nodefony/http` :
7
+ * `http` ne peut donc pas nommer ses classes sans recréer un cycle. C'est le
8
+ * LECTEUR qui définit le contrat ; `ISecuredArea` (security) l'étend, et le
9
+ * typecheck de `security` garantit l'alignement.
10
+ */
11
+ export interface ISecurityZone {
12
+ /** Nom de la zone (journalisation, profiler). */
13
+ readonly name: string;
14
+ /** `true` si la zone exige une authentification. */
15
+ readonly security: boolean;
16
+ /** Zone sans session : aucune session n'est ouverte ni lue. */
17
+ readonly stateless: boolean;
18
+ /** Stratégie des authenticators : le premier qui répond, ou tous. */
19
+ readonly mode: "first" | "all";
20
+ /** Noms des authenticators candidats, dans l'ordre. */
21
+ readonly authenticators: readonly string[];
22
+ /**
23
+ * Rôles exigés par la zone, en OU (un seul suffit) ; `null` = aucune exigence
24
+ * de rôle. Lu par le résolveur de `@nodefony/framework` pour l'autorisation.
25
+ */
26
+ readonly roles: readonly string[] | null;
27
+ }
28
+ /**
29
+ * Ce que le noyau HTTP appelle du pare-feu, service `firewall` du conteneur.
30
+ *
31
+ * Implémenté par `Firewall` (`@nodefony/security`), dont `IFirewall` étend ce
32
+ * contrat de ses propres membres (CSP, authenticators, description).
33
+ */
34
+ export interface IFirewallGate {
35
+ /** Match rapide de zone : pose `context.security`, `true` si une zone capture. */
36
+ isSecure(context: ContextType): boolean;
37
+ /** Authentifie la requête d'une zone protégée ; rejette si elle est refusée. */
38
+ handleSecurity(context: ContextType): Promise<ContextType>;
39
+ /** Refuse une requête qui ne présente pas le jeton CSRF attendu. */
40
+ enforceCsrf(context: ContextType): void;
41
+ /** Répond au preflight CORS ; `204` quand la réponse est déjà envoyée. */
42
+ handleCors(context: ContextType): number | undefined;
43
+ /** Pose les en-têtes de sécurité (CSP, HSTS…) de la réponse. */
44
+ applySecurityHeaders(context: ContextType): void;
45
+ }
@@ -13,7 +13,7 @@ export type MetaBagType = Record<string, unknown>;
13
13
  * - `readOnly` : la session est lue/reprise mais **jamais persistée** (0 write storage).
14
14
  */
15
15
  export interface SessionIntent {
16
- readOnly?: boolean;
16
+ readOnly?: boolean | undefined;
17
17
  }
18
18
  /**
19
19
  * Données de session **sérialisées** échangées avec un {@link ISessionStorage}
@@ -25,8 +25,8 @@ export interface ISerializedSession {
25
25
  metaBag: Record<string, unknown>;
26
26
  flashBag: Record<string, unknown>;
27
27
  user: string;
28
- createdAt?: Date;
29
- updatedAt?: Date;
28
+ createdAt?: Date | undefined;
29
+ updatedAt?: Date | undefined;
30
30
  }
31
31
  /**
32
32
  * Projection **redactée** d'une session pour l'ADMINISTRATION (data plane Studio,
@@ -100,7 +100,7 @@ export interface ISessionListFilter {
100
100
  /** Restreint aux sessions d'un utilisateur (pour « déconnecter partout »). */
101
101
  user?: string;
102
102
  /** Slot multi-tenant — non scopé aujourd'hui (réserve coût-0). */
103
- tenantId?: string | null;
103
+ tenantId?: string | null | undefined;
104
104
  }
105
105
  /**
106
106
  * Requête d'énumération **paginée** des sessions — le {@link IPageQuery} standard
@@ -253,10 +253,10 @@ export interface ISession {
253
253
  cookieSession: ICookie | null | undefined;
254
254
  flashBag: FlashBagType;
255
255
  strategy: SessionStrategyType;
256
- created?: Date;
257
- updated?: Date;
258
- user?: string;
259
- lifetime?: number;
256
+ created?: Date | undefined;
257
+ updated?: Date | undefined;
258
+ user?: string | undefined;
259
+ lifetime?: number | undefined;
260
260
  storage: ISessionStorage;
261
261
  start(context: unknown): Promise<ISession>;
262
262
  save(user?: string): Promise<ISession>;
@@ -28,21 +28,21 @@ export interface IParsedUploadFile {
28
28
  */
29
29
  export interface IUploadOptions {
30
30
  /** Répertoire de dépôt des fichiers temporaires. */
31
- uploadDir?: string;
31
+ uploadDir?: string | undefined;
32
32
  /** Taille max d'UN fichier (octets) — `limits.fileSize` busboy. */
33
- maxFileSize?: number;
33
+ maxFileSize?: number | undefined;
34
34
  /** Taille max CUMULÉE des fichiers d'une requête (octets) — appliquée par Nodefony. */
35
- maxTotalFileSize?: number;
35
+ maxTotalFileSize?: number | undefined;
36
36
  /** Nombre max de fichiers — `limits.files` busboy. */
37
- maxFiles?: number;
37
+ maxFiles?: number | undefined;
38
38
  /** Nombre max de champs texte — `limits.fields` busboy. */
39
- maxFields?: number;
39
+ maxFields?: number | undefined;
40
40
  /** Taille max d'un champ texte (octets) — `limits.fieldSize` busboy. */
41
- maxFieldsSize?: number;
41
+ maxFieldsSize?: number | undefined;
42
42
  /** Hash calculé pendant le stream (défaut `false` = aucun). */
43
- hashAlgorithm?: false | "sha1" | "md5" | "sha256";
43
+ hashAlgorithm?: false | "sha1" | "md5" | "sha256" | undefined;
44
44
  /** Encodage par défaut des parts texte. */
45
- encoding?: string;
45
+ encoding?: string | undefined;
46
46
  }
47
47
  export interface IUploadedFile {
48
48
  filename: string;
@@ -60,7 +60,7 @@ export interface IUploadedFile {
60
60
  getPrettySize(): string;
61
61
  }
62
62
  export interface IUploadService {
63
- path?: string | unknown;
63
+ path?: unknown;
64
64
  /** Construit un `UploadedFile` en async (stat non bloquant). */
65
65
  createUploadFile(file: unknown, name: string): Promise<IUploadedFile>;
66
66
  }
@@ -5,3 +5,5 @@ export type { IRequest, IHttpRequest, IHttp2Request, IWsRequest, HTTPMethodType,
5
5
  export type { IResponse, IHttpResponse, IWebsocketResponse } from "./IResponse.js";
6
6
  export type { IContext, IHttpContext, IWebsocketContext, ServerType, SchemeType, WebSocketStateType, CookiesMap, } from "./IContext.js";
7
7
  export type { IHttpKernel } from "./IHttpKernel.js";
8
+ export type { IResolvedRoute, IRouteResolver, IRequestRouter, } from "./IRouting.js";
9
+ export type { ISecurityZone, IFirewallGate } from "./ISecurity.js";
@@ -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 };