@nodefony/http 10.0.0-alpha.3 → 10.0.0-alpha.4

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.
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorate.js
1
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
2
2
  function __decorate(decorators, target, key, desc) {
3
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorateMetadata.js
1
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorateMetadata.js
2
2
  function __decorateMetadata(k, v) {
3
3
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
4
  }
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorateParam.js
1
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorateParam.js
2
2
  function __decorateParam(paramIndex, decorator) {
3
3
  return function(target, key) {
4
4
  decorator(target, key, paramIndex);
package/dist/index.js CHANGED
@@ -21,8 +21,8 @@ import DefaultErrorRenderer, { schemaMismatchOf } from "./nodefony/service/error
21
21
  import DefaultRequestLogger from "./nodefony/service/request-logger.js";
22
22
  import JsonAuditLogger from "./nodefony/service/audit-logger.js";
23
23
  import PrettyRequestLogger from "./nodefony/service/pretty-request-logger.js";
24
- import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
25
- import __decorate from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
24
+ import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
25
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
26
26
  import http_kernel_default from "./nodefony/service/http-kernel.js";
27
27
  import server_http_default from "./nodefony/service/servers/server-http.js";
28
28
  import server_https_default from "./nodefony/service/servers/server-https.js";
@@ -58,7 +58,7 @@ import { z } from "zod";
58
58
  * Ce schéma reste PUR (pas de `Nodefony.getKernel()` ni `process.env`) → sa
59
59
  * sortie est déterministe et sérialisable en JSON Schema (`httpConfigJsonSchema`
60
60
  * dans `defineModuleConfig.ts`). Les défauts dérivés du kernel
61
- * (`upload.uploadDir` ← `kernel.tmpDir`, `certificates.openssl.attrs` ←
61
+ * (`upload.uploadDir` ← `kernel.tmpDir`, `certificates.selfSigned.attrs` ←
62
62
  * `kernel.domain`/`projectName`) sont injectés APRÈS le parse, dans le builder
63
63
  * (kernel disponible à `onKernelRegister`).
64
64
  *
@@ -155,12 +155,12 @@ const http2Schema = z.looseObject({
155
155
  maxSessionMemory: z.number().int().positive().default(10).describe("Mémoire max (Mo) par session HTTP/2 — borne l'amplification mémoire.")
156
156
  }).describe("Serveur HTTP/2 (sur le port HTTPS, allowHTTP1). Loose : options `http2.SecureServerOptions` transmises.");
157
157
  const http3Schema = z.looseObject({});
158
- const opensslAttrSchema = z.looseObject({
158
+ const selfSignedAttrSchema = z.looseObject({
159
159
  name: z.string().optional(),
160
160
  value: z.string().optional(),
161
161
  shortName: z.string().optional()
162
162
  }).describe("Champ de sujet/issuer node-forge (CertificateField).");
163
- const opensslSchema = z.strictObject({
163
+ const selfSignedSchema = z.strictObject({
164
164
  size: z.number().int().positive().default(2048).describe("Taille de la clé RSA (bits). 2048 minimum, 4096 recommandé en prod."),
165
165
  hash: z.enum([
166
166
  "sha256",
@@ -169,11 +169,11 @@ const opensslSchema = z.strictObject({
169
169
  ]).default("sha256").describe("Algorithme de hachage de la signature. SHA-1 INTERDIT (collision SHAttered 2017 ; CA/Browser Forum depuis 2016)."),
170
170
  validityDays: z.number().int().positive().default(365).describe("Durée de validité du certificat (jours). ≤ 398 recommandé (CA/B Forum)."),
171
171
  backdateMinutes: z.number().int().min(0).default(5).describe("Recul de notBefore (minutes) — tolère le décalage d'horloge du client (évite « certificate not yet valid »)."),
172
- attrs: z.array(opensslAttrSchema).default([]).meta({
172
+ attrs: z.array(selfSignedAttrSchema).default([]).meta({
173
173
  kernelDerived: true,
174
174
  description: "Attributs du certificat (commonName, organizationName…). Vide (défaut) = dérivés du kernel par le builder (commonName ← domain)."
175
175
  })
176
- }).describe("Options de génération du certificat auto-signé (node-forge).");
176
+ }).describe("Génération du certificat AUTO-SIGNÉ (node-forge, JavaScript pur — aucun binaire externe n'est invoqué). Ces réglages ne valent QUE pour la stratégie `selfsigned` : sous `mkcert` (défaut en développement) ils sont ignorés — mkcert ne reçoit que les noms d'hôtes — et sous `strategy: \"explicit\"` le certificat est fourni, donc rien n'est généré.");
177
177
  const sanSchema = z.strictObject({
178
178
  dns: z.array(z.string()).default([]).meta({
179
179
  kernelDerived: true,
@@ -195,7 +195,7 @@ const certificatesSchema = z.strictObject({
195
195
  privateKeyMode: z.number().int().default(384).meta({ description: "Permissions POSIX de la clé privée écrite sur disque (0600 = lecture/écriture owner uniquement). Une clé TLS ne doit JAMAIS être world-readable." }),
196
196
  san: sanSchema.default(() => sanSchema.parse({})),
197
197
  dev: certDevSchema.default(() => certDevSchema.parse({})),
198
- openssl: opensslSchema.default(() => opensslSchema.parse({}))
198
+ selfSigned: selfSignedSchema.default(() => selfSignedSchema.parse({}))
199
199
  }).describe("Certificats TLS du serveur HTTPS et WSS.");
200
200
  const websocketSchema = z.looseObject({
201
201
  keepaliveInterval: z.number().int().positive().default(2e4).describe("Intervalle (ms) des pings keep-alive — détecte les connexions zombies."),
@@ -6,7 +6,7 @@ import { z } from "zod";
6
6
  * Injecte les défauts dérivés du kernel APRÈS le parse (le schéma reste pur).
7
7
  *
8
8
  * - `upload.uploadDir` vide → répertoire temporaire du kernel (`kernel.tmpDir`).
9
- * - `certificates.openssl.attrs` vide → sujet du certificat dérivé du kernel
9
+ * - `certificates.selfSigned.attrs` vide → sujet du certificat dérivé du kernel
10
10
  * (`commonName` ← `kernel.domain`, `organizationName` ← `kernel.projectName`).
11
11
  * Sans ce remplissage, le certificat auto-signé n'aurait pas de commonName.
12
12
  * - `certificates.san` vide → Subject Alternative Name dérivé du kernel
@@ -28,7 +28,7 @@ function applyKernelDefaults(config, kernel) {
28
28
  const p = kernel?.tmpDir?.path;
29
29
  config.upload.uploadDir = typeof p === "string" ? p : "/tmp";
30
30
  }
31
- if (config.certificates.openssl.attrs.length === 0) config.certificates.openssl.attrs = [
31
+ if (config.certificates.selfSigned.attrs.length === 0) config.certificates.selfSigned.attrs = [
32
32
  {
33
33
  name: "commonName",
34
34
  value: kernel?.domain ?? "nodefony.com"
@@ -22,7 +22,7 @@ const defaultOptions = {
22
22
  dns: [],
23
23
  ip: []
24
24
  },
25
- openssl: {
25
+ selfSigned: {
26
26
  size: 2048,
27
27
  hash: "sha256",
28
28
  validityDays: 365,
@@ -418,7 +418,7 @@ var Certificate = class Certificate extends Service {
418
418
  }
419
419
  }
420
420
  generateKeys() {
421
- return this.forgeLib.pki.rsa.generateKeyPair(this.certOptions.openssl.size);
421
+ return this.forgeLib.pki.rsa.generateKeyPair(this.certOptions.selfSigned.size);
422
422
  }
423
423
  generatePrivateKeyPem() {
424
424
  if (this.keysPair) return Buffer.from(this.forgeLib.pki.privateKeyToPem(this.keysPair.privateKey));
@@ -434,7 +434,7 @@ var Certificate = class Certificate extends Service {
434
434
  }
435
435
  createCertificate() {
436
436
  if (!this.keysPair) throw new Error(`KeyPair not found`);
437
- const o = this.certOptions.openssl;
437
+ const o = this.certOptions.selfSigned;
438
438
  const cert = this.forgeLib.pki.createCertificate();
439
439
  cert.publicKey = this.keysPair.publicKey;
440
440
  cert.serialNumber = Certificate.generateSerialHex();
@@ -491,7 +491,7 @@ var Certificate = class Certificate extends Service {
491
491
  */
492
492
  sign() {
493
493
  if (!this.certForge || !this.keysPair) throw new Error(`pki.rsa.KeyPair or pki.Certificate not found`);
494
- this.certForge.sign(this.keysPair.privateKey, this.digestFor(this.certOptions.openssl.hash));
494
+ this.certForge.sign(this.keysPair.privateKey, this.digestFor(this.certOptions.selfSigned.hash));
495
495
  }
496
496
  /**
497
497
  * Résumé introspectable du certificat serveur courant — réutilisé par la
@@ -12,8 +12,8 @@ import DefaultRequestLogger from "./request-logger.js";
12
12
  import JsonAuditLogger from "./audit-logger.js";
13
13
  import PrettyRequestLogger from "./pretty-request-logger.js";
14
14
  import { resolveTraceparent } from "./trace.js";
15
- import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
16
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
15
+ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
16
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
17
17
  import { GcScheduler, Module, RequestContext, Service, injectable, logColor, nodefonyError, writeRuntimeState } from "nodefony";
18
18
  import cluster from "node:cluster";
19
19
  import Ws from "ws";
@@ -1,10 +1,10 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
4
  import { handleClientError } from "./clientError.js";
5
5
  import { createDrainTerminator } from "./serverShutdown.js";
6
6
  import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
7
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js";
7
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
8
8
  import { Module, Service, extend, inject, nodefonyError } from "nodefony";
9
9
  import http from "node:http";
10
10
  //#region nodefony/service/servers/server-http.ts
@@ -1,10 +1,10 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
4
  import { handleClientError } from "./clientError.js";
5
5
  import { createDrainTerminator } from "./serverShutdown.js";
6
6
  import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
7
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js";
7
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
8
8
  import { Module, Service, extend, inject, nodefonyError } from "nodefony";
9
9
  import http2 from "node:http2";
10
10
  import https from "node:https";
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
5
5
  import { startHeartbeat, trackPong } from "./wsHeartbeat.js";
6
6
  import { Module, Service, inject } from "nodefony";
7
7
  import Ws, { WebSocketServer } from "ws";
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
5
5
  import { startHeartbeat, trackPong } from "./wsHeartbeat.js";
6
6
  import { Module, Service, inject } from "nodefony";
7
7
  import Ws, { WebSocketServer } from "ws";
@@ -1,12 +1,12 @@
1
1
  import { SESSION_FACETS } from "../../src/session/storage/sessionFilters.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js";
5
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
6
6
  import Session from "../../src/session/session.js";
7
7
  import MemorySessionStorage from "../../src/session/storage/MemorySessionStorage.js";
8
8
  import RevocationGuardStorage from "../../src/session/storage/RevocationGuardStorage.js";
9
- import { AUTO_STORE, EMPTY_INFRA, GcScheduler, Module, Nodefony, RequestContext, Service, countFacets, extend, inject, injectable, readStoreLocation, resolveAutoStore } from "nodefony";
9
+ import { AUTO_STORE, EMPTY_INFRA, GcScheduler, Module, Nodefony, RequestContext, Service, countFacets, extend, inject, injectable, readStoreLocation, resolveAutoStore, runNeedsExternalServices } from "nodefony";
10
10
  import { createHash, createHmac } from "node:crypto";
11
11
  //#region nodefony/service/sessions/sessions-service.ts
12
12
  var _SessionsService;
@@ -142,7 +142,7 @@ let SessionsService = class SessionsService extends Service {
142
142
  const configured = storeName;
143
143
  let reason = `store explicitement configuré ("${configured}")`;
144
144
  if (storeName === AUTO_STORE) {
145
- const auto = resolveAutoStore("session", this.kernel?.infra ?? EMPTY_INFRA, _SessionsService.storageHandlers(), "memory");
145
+ const auto = resolveAutoStore("session", this.kernel?.infra ?? EMPTY_INFRA, _SessionsService.storageHandlers(), "memory", runNeedsExternalServices(this.kernel));
146
146
  storeName = auto.store;
147
147
  reason = auto.reason;
148
148
  this.log(`session.store "auto" → "${storeName}" (${auto.reason})`, "INFO");
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
5
5
  import { Cli, FileClass, Module, Service, inject } from "nodefony";
6
6
  import fs from "node:fs";
7
7
  import fsp from "node:fs/promises";
@@ -54,11 +54,11 @@ declare class Http extends Module<IHttpConfig> {
54
54
  }
55
55
  export default Http;
56
56
  export { defineHttpConfig, httpConfigJsonSchema, } from "./nodefony/config/defineModuleConfig.js";
57
- export { httpConfigSchema, type HttpConfig, type HttpConfigInput, } from "./nodefony/config/config.js";
57
+ export { httpConfigSchema } from "./nodefony/config/config.js";
58
58
  export type { IHttpConfig, IHttpConfigInput, } from "./nodefony/interfaces/IHttpConfig.js";
59
59
  export { decideSend, readBackpressureOptions, type WsBackpressurePolicy, type WsSendDecision, type IWsBackpressureOptions, type IBackpressureSocket, type IBackpressureTarget, } from "./nodefony/src/context/websocket/wsBackpressure.js";
60
60
  export { compileDomainPattern, compileDomainPatterns, compileTrustedHosts, isDomainAllowed, } from "./nodefony/src/context/domainMatcher.js";
61
- export type { DomainPattern, TrustedHostsConfig, } from "./nodefony/src/context/domainMatcher.js";
61
+ export type { DomainPattern, ITrustedHostsConfig, } from "./nodefony/src/context/domainMatcher.js";
62
62
  export { MemoryRateLimitStore } from "./nodefony/src/rateLimit/MemoryRateLimitStore.js";
63
63
  export type { IRateLimitStore, IRateLimitOptions, RateLimitVerdict, } from "./nodefony/src/rateLimit/IRateLimitStore.js";
64
64
  export { Context, Session, SessionsService, HttpError, HttpKernel, Response, Http2Response, Response as HttpResponse, wsResponse, wsResponse as WebsocketResponse, Cookie, HttpContext, WebsocketContext, HttpRequest, Http2Request, DefaultErrorRenderer, DefaultRequestLogger, JsonAuditLogger, PrettyRequestLogger, Profiler, };
@@ -74,7 +74,7 @@ export declare const httpConfigSchema: z.ZodObject<{
74
74
  dev: z.ZodDefault<z.ZodObject<{
75
75
  useMkcert: z.ZodDefault<z.ZodBoolean>;
76
76
  }, z.core.$strict>>;
77
- openssl: z.ZodDefault<z.ZodObject<{
77
+ selfSigned: z.ZodDefault<z.ZodObject<{
78
78
  size: z.ZodDefault<z.ZodNumber>;
79
79
  hash: z.ZodDefault<z.ZodEnum<{
80
80
  sha256: "sha256";
@@ -186,12 +186,12 @@ export declare const httpConfigSchema: z.ZodObject<{
186
186
  wsMaxConnectionsPerIp: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
187
187
  }, z.core.$strict>;
188
188
  /** Type de sortie (config normalisée + défauts appliqués). */
189
- export type HttpConfig = z.infer<typeof httpConfigSchema>;
189
+ export type IHttpConfig = z.infer<typeof httpConfigSchema>;
190
190
  /** Type d'entrée (toutes sections omissibles — défauts du schéma). */
191
- export type HttpConfigInput = z.input<typeof httpConfigSchema>;
191
+ export type IHttpConfigInput = z.input<typeof httpConfigSchema>;
192
192
  /**
193
193
  * Défauts du module, matérialisés depuis le schéma (source unique). Toujours
194
194
  * valides par construction ; passés au `super(..., config)` du Module class.
195
195
  */
196
- declare const config: HttpConfig;
196
+ declare const config: IHttpConfig;
197
197
  export default config;
@@ -1,12 +1,16 @@
1
- import type { HttpConfig, HttpConfigInput } from "../config/config.js";
2
1
  /**
3
- * Configuration validée de `@nodefony/http` (sortie du parse Zod + défauts
4
- * kernel appliqués par le builder). Source de vérité = `../config/config.ts`.
2
+ * Barrel d'interfaces de la configuration de `@nodefony/http`.
3
+ *
4
+ * Ces types sont DÉRIVÉS du schéma Zod — leur source de vérité unique est
5
+ * `../config/config.ts`, et ce fichier ne fait que les republier sous le chemin
6
+ * d'interfaces attendu par la convention du dépôt (`nodefony/interfaces/I*.ts`).
7
+ *
8
+ * ⚠️ Un ré-export, jamais un alias (`export type IHttpConfig = HttpConfig`) :
9
+ * un alias publierait DEUX noms pour le même type, tous deux dans les `.d.ts`
10
+ * et l'autocomplétion du consommateur, sans que rien ne dise lequel est le bon.
5
11
  */
6
- export type IHttpConfig = HttpConfig;
7
- /**
8
- * Configuration brute fournie par l'application (toutes sections optionnelles —
9
- * les sections omises reçoivent leurs défauts). Entrée du builder
10
- * {@link import("../config/defineModuleConfig").defineHttpConfig}.
11
- */
12
- export type IHttpConfigInput = HttpConfigInput;
12
+ export type {
13
+ /** Type de sortie (config normalisée + défauts appliqués). */
14
+ IHttpConfig,
15
+ /** Type d'entrée (toutes sections omissibles défauts du schéma). */
16
+ IHttpConfigInput, } from "../config/config.js";
@@ -10,7 +10,16 @@ type ForgeModule = typeof import("node-forge");
10
10
  export type CertHash = "sha256" | "sha384" | "sha512";
11
11
  /** Stratégie de fourniture du certificat exposée en configuration. */
12
12
  export type CertStrategyConfig = "auto" | "mkcert" | "selfsigned" | "explicit";
13
- export interface OpensslOptions {
13
+ /**
14
+ * Options de génération du certificat AUTO-SIGNÉ (node-forge, JavaScript pur —
15
+ * aucun binaire externe n'est invoqué).
16
+ *
17
+ * ⚠️ Portée : ces réglages ne valent QUE pour la stratégie `selfsigned`. Sous
18
+ * `mkcert` (le défaut en développement) ils sont ignorés — mkcert ne reçoit que
19
+ * les noms d'hôtes — et sous `strategy: "explicit"` le certificat est fourni,
20
+ * donc rien n'est généré.
21
+ */
22
+ export interface SelfSignedOptions {
14
23
  /** Taille de la clé RSA (bits). */
15
24
  size: number;
16
25
  /** Algorithme de hachage de la signature (jamais SHA-1). */
@@ -48,7 +57,7 @@ export interface CertificateOptions {
48
57
  * (Let's Encrypt, ingress, reverse-proxy) : Nodefony n'est pas une CA.
49
58
  */
50
59
  strategy?: CertStrategyConfig;
51
- openssl: OpensslOptions;
60
+ selfSigned: SelfSignedOptions;
52
61
  dev: CertificateDevOptions;
53
62
  san?: CertificateSanOptions;
54
63
  /** Permissions POSIX de la clé privée écrite (0600 = owner-only). */
@@ -4,7 +4,7 @@ import type { Controller } from "@nodefony/framework";
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";
7
- import { type TrustedHostsConfig } from "../src/context/domainMatcher.js";
7
+ import { type ITrustedHostsConfig } from "../src/context/domainMatcher.js";
8
8
  import http from "node:http";
9
9
  import http2 from "node:http2";
10
10
  import type { IncomingMessage } from "node:http";
@@ -79,7 +79,7 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
79
79
  ca: string;
80
80
  serverStatic: Statics | null;
81
81
  domain: string;
82
- trustedHosts?: TrustedHostsConfig;
82
+ trustedHosts?: ITrustedHostsConfig;
83
83
  domainCheck: boolean;
84
84
  regAlias: RegExp[];
85
85
  module: Module;
@@ -28,7 +28,7 @@ export type DomainPattern = string | RegExp;
28
28
  * filtre déjà le `Host`, cf doctrine cloud-native).
29
29
  * - `string` / `string[]` : patterns additionnels (exact ou `*`-wildcard).
30
30
  */
31
- export type TrustedHostsConfig = boolean | DomainPattern | DomainPattern[];
31
+ export type ITrustedHostsConfig = boolean | DomainPattern | DomainPattern[];
32
32
  /**
33
33
  * Compile UN pattern de domaine en `RegExp` selon la politique sûre.
34
34
  *
@@ -56,7 +56,7 @@ export declare function compileDomainPatterns(patterns: DomainPattern | DomainPa
56
56
  * @param isDev - vrai en environnement `development` (ajoute le loopback).
57
57
  * @returns liste de `RegExp` pour {@link isDomainAllowed}.
58
58
  */
59
- export declare function compileTrustedHosts(domain: string, trusted: TrustedHostsConfig | undefined, isDev: boolean): RegExp[];
59
+ export declare function compileTrustedHosts(domain: string, trusted: ITrustedHostsConfig | undefined, isDev: boolean): RegExp[];
60
60
  /**
61
61
  * Teste un `Host` entrant contre une liste de `RegExp` pré-compilée.
62
62
  *
package/docs/servers.md CHANGED
@@ -133,7 +133,7 @@ session et du même firewall, et se ferme avec le même soin qu'une réponse HTT
133
133
 
134
134
  > [!NOTE]
135
135
  > Le serveur HTTP/3 (QUIC) est **réservé, pas implémenté** : la clé `http3` existe dans le schéma,
136
- > marquée `reserved` (`config.ts:1029`). Elle ne fait rien aujourd'hui.
136
+ > marquée `reserved` (`config.ts:1035`). Elle ne fait rien aujourd'hui.
137
137
 
138
138
  ## 🚀 Démarrage rapide
139
139
 
@@ -400,7 +400,7 @@ C'est la distinction la plus utile de cette page, et celle qu'on rate le plus so
400
400
  | Question | Où ça se règle | Source |
401
401
  | ----------------------------------------- | ------------------------------ | --------------------------------------------------------- |
402
402
  | **Quels** serveurs, sur **quels ports** ? | `servers` (config d'app) | `serversSchema` (`src/nodefony/src/config/schema.ts:132`) |
403
- | **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:947`) |
403
+ | **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:953`) |
404
404
 
405
405
  Autrement dit : la **topologie** est une propriété du déploiement (elle change entre le poste du dev,
406
406
  la CI et le cluster) ; le **réglage** est une propriété de l'application.
@@ -451,16 +451,16 @@ Depuis `http2Schema` (`config.ts:332`), appliqué seulement si défini
451
451
 
452
452
  ### Niveau 2 — WebSocket (`websocket` et `websocketSecure`)
453
453
 
454
- Depuis `websocketSchema` (`config.ts:490`). Les deux sections partagent la forme et les défauts ; le WSS
455
- lit `websocketSecure` (`config.ts:1037`).
454
+ Depuis `websocketSchema` (`config.ts:496`). Les deux sections partagent la forme et les défauts ; le WSS
455
+ lit `websocketSecure` (`config.ts:1043`).
456
456
 
457
457
  | Option | Type | Défaut | Effet |
458
458
  | ------------------------ | ------------------- | ------- | ---------------------------------------------------------------------------------- |
459
459
  | `keepaliveInterval` | ms | `20000` | Intervalle des pings — détecte les connexions zombies. |
460
460
  | `keepaliveGracePeriod` | ms | `10000` | Délai de grâce après un ping sans réponse avant fermeture. |
461
461
  | `closeTimeout` | ms | `5000` | Délai de fermeture propre avant destruction de la socket. |
462
- | `maxPayload` | octets | `1 MiB` | Taille max d'un message entrant → au-delà, **close 1009** (`config.ts:516`). |
463
- | `allowedOrigins` | bool \| str \| list | `false` | Allowlist d'`Origin` au handshake — **anti-CSWSH** (`config.ts:525`). |
462
+ | `maxPayload` | octets | `1 MiB` | Taille max d'un message entrant → au-delà, **close 1009** (`config.ts:522`). |
463
+ | `allowedOrigins` | bool \| str \| list | `false` | Allowlist d'`Origin` au handshake — **anti-CSWSH** (`config.ts:531`). |
464
464
  | `perMessageDeflate` | bool \| objet | `false` | Compression RFC 7692. Désactivée par défaut : coût CPU/RAM + risque de _zip bomb_. |
465
465
  | `skipUTF8Validation` | bool | `false` | Désactive la validation UTF-8 des frames texte (RFC 6455 §8.1). À laisser `false`. |
466
466
  | `autoPong` | bool | `true` | Répond automatiquement aux pings entrants (RFC 6455 §5.5.2-3). À laisser `true`. |
@@ -537,12 +537,12 @@ serveur qui, lui, écoute très bien.
537
537
  **Générer un certificat est un confort de développement, pas une fonction de production.** Nodefony
538
538
  n'est pas une autorité de certification : en production, on fournit un vrai certificat (Let's Encrypt,
539
539
  ingress k8s, reverse-proxy). Le service crie un avertissement si ce n'est pas le cas
540
- (`Certificate.resolveStrategy()`, `certificates.ts:409`).
540
+ (`Certificate.resolveStrategy()`, `certificates.ts:418`).
541
541
 
542
542
  ### Les quatre stratégies
543
543
 
544
- Réglées par `certificates.strategy` (`certificatesSchema`, `config.ts:448`), résolues par
545
- `Certificate.resolveStrategy()` (`certificates.ts:370`).
544
+ Réglées par `certificates.strategy` (`certificatesSchema`, `config.ts:475`), résolues par
545
+ `Certificate.resolveStrategy()` (`certificates.ts:379`).
546
546
 
547
547
  | Stratégie | Quand l'utiliser | Ce qui se passe |
548
548
  | --------------- | ------------------------------------------- | --------------------------------------------------------------------------- |
@@ -572,13 +572,13 @@ export default defineConfig(() => ({
572
572
  > [!TIP]
573
573
  > Pour un HTTPS de développement **sans avertissement navigateur** (indispensable au HMR cross-origin
574
574
  > et au WSS) : `brew install mkcert nss && mkcert -install`. Nodefony le détecte tout seul, sinon il
575
- > l'annonce et retombe sur l'auto-signé (`certificates.ts:392`).
575
+ > l'annonce et retombe sur l'auto-signé (`certificates.ts:401`).
576
576
 
577
577
  ### Ce que le chemin `explicit` évite
578
578
 
579
579
  `node-forge` est une grosse dépendance. Elle est chargée **paresseusement**, uniquement sur le chemin
580
- de génération (`Certificate.loadForge()`, `certificates.ts:218`) : en production avec un certificat
581
- fourni, elle n'entre jamais dans le processus (`certificates.ts:325`).
580
+ de génération (`Certificate.loadForge()`, `certificates.ts:227`) : en production avec un certificat
581
+ fourni, elle n'entre jamais dans le processus (`certificates.ts:334`).
582
582
 
583
583
  ### Conformité de l'auto-signé
584
584
 
@@ -587,16 +587,16 @@ invalid »). Celui de Nodefony respecte les règles qui comptent :
587
587
 
588
588
  | Exigence | Norme | Mise en œuvre |
589
589
  | --------------------------------------- | -------------------- | --------------------------------------------------------- |
590
- | Signature SHA-256, **jamais** SHA-1 | RFC 5280, CA/B Forum | `openssl.hash` par défaut `sha256` (`config.ts:379`) |
591
- | Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:255`) |
592
- | Le SAN fait foi, pas le CN | RFC 6125 | SAN dérivé du kernel si non fourni (`config.ts:415`) |
593
- | `notBefore` reculé (décalage d'horloge) | pratique | `openssl.backdateMinutes`, défaut 5 (`config.ts:394`) |
594
- | Clé privée non lisible par tous | hygiène | `privateKeyMode` `0600` (`config.ts:472`) |
590
+ | Signature SHA-256, **jamais** SHA-1 | RFC 5280, CA/B Forum | `selfSigned.hash` par défaut `sha256` (`config.ts:400`) |
591
+ | Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:264`) |
592
+ | Le SAN fait foi, pas le CN | RFC 6125 | SAN dérivé du kernel si non fourni (`config.ts:444`) |
593
+ | `notBefore` reculé (décalage d'horloge) | pratique | `selfSigned.backdateMinutes`, défaut 5 (`config.ts:415`) |
594
+ | Clé privée non lisible par tous | hygiène | `privateKeyMode` `0600` (`config.ts:499`) |
595
595
 
596
596
  ### Régénération automatique
597
597
 
598
598
  Un certificat présent sur disque n'est pas forcément **adéquat**. `Certificate.isCertAdequate()`
599
- (`certificates.ts:533`) le régénère s'il est expiré, s'il est signé en SHA-1, ou si son SAN ne couvre
599
+ (`certificates.ts:542`) le régénère s'il est expiré, s'il est signé en SHA-1, ou si son SAN ne couvre
600
600
  plus les noms requis — le dernier cas est celui qui sauve : changer le domaine d'écoute sans ce
601
601
  contrôle laisserait un certificat obsolète en place indéfiniment.
602
602
 
@@ -608,7 +608,7 @@ Dès qu'un proxy est devant l'application, trois questions se posent — et troi
608
608
 
609
609
  Sans barrière, n'importe quel client peut envoyer `X-Forwarded-For: 1.2.3.4` et usurper son IP :
610
610
  contournement de rate-limit, journaux d'audit falsifiés. Le défaut est donc **`false` — ces en-têtes
611
- sont ignorés** (`config.ts:959`), et l'IP retenue est celle de la socket réelle, non falsifiable.
611
+ sont ignorés** (`config.ts:965`), et l'IP retenue est celle de la socket réelle, non falsifiable.
612
612
 
613
613
  | Valeur | Sens |
614
614
  | -------------------------------------------- | ------------------------------------------------------------------ |
@@ -628,7 +628,7 @@ Barrière testée **avant le routage**, contre l'injection d'en-tête `Host`. Le
628
628
  kernel est toujours accepté, plus le loopback en développement (`HttpKernel.compileAlias()`,
629
629
  `http-kernel.ts:936`). `false` (défaut) = ce socle seul ; une liste ajoute des vhosts (exact ou joker
630
630
  d'un seul niveau, `*.cdn.example.com`) ; `true` désactive la barrière — à réserver au cas où le proxy
631
- filtre déjà le `Host` (`config.ts:968`).
631
+ filtre déjà le `Host` (`config.ts:974`).
632
632
 
633
633
  ### « Cette page a-t-elle le droit d'ouvrir un WebSocket ? » → `allowedOrigins`
634
634
 
@@ -664,7 +664,7 @@ rate-limit**. Un kubelet qui reçoit un `429` croit le pod mort → cascade de r
664
664
 
665
665
  | Option | Type | Défaut | Effet |
666
666
  | --------------- | ------ | --------- | ---------------------------------------------------------------------- |
667
- | `enabled` | bool | `true` | Expose les probes (`healthSchema`, `config.ts:919`). |
667
+ | `enabled` | bool | `true` | Expose les probes (`healthSchema`, `config.ts:925`). |
668
668
  | `livenessPath` | string | `/livez` | Chemin de la sonde de vie (`livenessProbe.httpGet.path` k8s). |
669
669
  | `readinessPath` | string | `/readyz` | Chemin de la sonde de disponibilité. |
670
670
  | `shutdownDelay` | ms | `0` | Délai entre la bascule `503` et le début du drain (propagation du LB). |
@@ -790,7 +790,7 @@ est impossible → la connexion est fermée en **1013 « Try Again Later »**
790
790
  journalisation (un journal par handshake rejeté serait lui-même un amplificateur sous flood).
791
791
 
792
792
  Un second plafond, **désactivé par défaut**, borne le nombre de connexions **simultanées** par IP :
793
- `wsMaxConnectionsPerIp` (`config.ts:1046`). En cloud-native, laisser `null` et déléguer à l'edge —
793
+ `wsMaxConnectionsPerIp` (`config.ts:1052`). En cloud-native, laisser `null` et déléguer à l'edge —
794
794
  nginx `limit_conn`, HAProxy `sc_conn_cur` — qui voit tout le trafic, rejette avant le coût du
795
795
  descripteur et du TLS, et couvre tous les pods. Ne l'activer que sur une machine sans ingress.
796
796
 
@@ -817,7 +817,7 @@ par seconde. Les choix visibles dans le code :
817
817
  - **Fichiers statiques en repli** — depuis la bascule « router d'abord », une requête qui matche une
818
818
  route ne paie plus l'appel disque de `serve-static` (**+28 % de requêtes par seconde** mesurés en
819
819
  production mono-processus).
820
- - **`node-forge` jamais chargé en production** avec un certificat fourni (`certificates.ts:218`).
820
+ - **`node-forge` jamais chargé en production** avec un certificat fourni (`certificates.ts:227`).
821
821
 
822
822
  Ordre de grandeur mesuré : un processus Node saturé sur un cœur tient environ 400 requêtes/s en
823
823
  boucle locale avec dégradation gracieuse (1600 connexions concurrentes, aucun crash) ; côté WebSocket,
@@ -838,15 +838,15 @@ demande le backplane realtime.
838
838
  | HTTP/2 | RFC 9113 | `ServerHttps.createServerH2()` (`server-https.ts:174`) |
839
839
  | HTTP/2 Rapid Reset | CVE-2023-44487 | `maxConcurrentStreams` (`config.ts:334`) |
840
840
  | En-têtes trop volumineux → 431 | RFC 6585 §5 | `handleClientError()` (`clientError.ts:25`) |
841
- | WebSocket — protocole | RFC 6455 | `ws@8` + options (`config.ts:490`) |
841
+ | WebSocket — protocole | RFC 6455 | `ws@8` + options (`config.ts:496`) |
842
842
  | WebSocket — Close 1001 « Going Away » | RFC 6455 §7.4.1 | `Websocket.terminate()` (`server-websocket.ts:134`) |
843
- | WebSocket — 1009 « Message Too Big » | RFC 6455 §7.4.1 | `maxPayload` (`config.ts:516`) |
844
- | WebSocket — validation UTF-8 | RFC 6455 §8.1 | `skipUTF8Validation` (`config.ts:596`) |
845
- | WebSocket — compression | RFC 7692 | `perMessageDeflate` (`config.ts:540`) |
843
+ | WebSocket — 1009 « Message Too Big » | RFC 6455 §7.4.1 | `maxPayload` (`config.ts:522`) |
844
+ | WebSocket — validation UTF-8 | RFC 6455 §8.1 | `skipUTF8Validation` (`config.ts:602`) |
845
+ | WebSocket — compression | RFC 7692 | `perMessageDeflate` (`config.ts:546`) |
846
846
  | CSWSH (Origin au handshake) | OWASP WSTG-CLNT-10 | `HttpKernel.checkWebsocketOrigin()` (`http-kernel.ts:599`) |
847
847
  | En-têtes forwarded | RFC 7239 | `resolveForwarded()` (`forwarded.ts:253`) |
848
- | Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:255`) |
849
- | Certificat — identité par le SAN | RFC 6125 | `sanSchema` (`config.ts:415`) |
848
+ | Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:264`) |
849
+ | Certificat — identité par le SAN | RFC 6125 | `sanSchema` (`config.ts:442`) |
850
850
 
851
851
  ## ⚠️ Pièges (symptôme → cause → correction)
852
852
 
@@ -863,7 +863,7 @@ demande le backplane realtime.
863
863
  | Cascade de redémarrages sous charge | Sonde de santé soumise au rate-limit | Déjà géré : les probes court-circuitent avant le rate-limit (`http-kernel.ts:848`) |
864
864
  | `curl --http2` renvoie du HTTP/1.1 | `servers.https.protocol: "1.1"`, ou client sans ALPN | Passer `protocol: "2.0"` (défaut) et vérifier le client |
865
865
  | Avertissement navigateur en HTTPS de développement | Certificat auto-signé (mkcert absent) | `brew install mkcert nss && mkcert -install`, puis redémarrer |
866
- | Le certificat n'est pas régénéré après un changement de domaine | On croit qu'un fichier présent suffit | Déjà géré : le SAN est vérifié (`certificates.ts:546`) |
866
+ | Le certificat n'est pas régénéré après un changement de domaine | On croit qu'un fichier présent suffit | Déjà géré : le SAN est vérifié (`certificates.ts:555`) |
867
867
  | `strategy: "explicit"` fait échouer le boot | `key`/`cert` absents de la configuration | Fournir les deux chemins — l'échec est volontaire, jamais un repli silencieux |
868
868
  | Une IP falsifiée passe dans les journaux d'audit | `trustProxy` accordé trop largement | Restreindre à l'IP/CIDR du proxy, ou revenir à `false` |
869
869
  | Handshake WebSocket refusé en `1008` | `Origin` non autorisée (anti-CSWSH) | Ajouter l'origine dans `websocket.allowedOrigins` |
@@ -886,7 +886,7 @@ l'origine du transport.
886
886
 
887
887
  `proxy:generate` mérite un mot : la configuration nginx/HAProxy est **dérivée** des domaines de
888
888
  confiance, des ports effectifs et des dossiers statiques montés — donc elle ne diverge pas du code. Le
889
- résumé de certificat vient de `Certificate.describe()` (`certificates.ts:803`), source unique partagée
889
+ résumé de certificat vient de `Certificate.describe()` (`certificates.ts:812`), source unique partagée
890
890
  par la commande, le boot et un futur écran d'administration.
891
891
 
892
892
  **Runtime.** `nodefony status` et `nodefony stop` lisent les ports effectifs publiés au boot ; ils
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodefony/http",
3
- "version": "10.0.0-alpha.3",
3
+ "version": "10.0.0-alpha.4",
4
4
  "description": "Serveurs HTTP, HTTPS, HTTP/2 et WebSocket natifs pour Nodefony : sessions, contextes de requête, certificats TLS",
5
5
  "author": "Christophe CAMENSULI <ccamensuli@gmail.com>",
6
6
  "main": "./dist/index.js",
@@ -75,7 +75,7 @@
75
75
  "@types/xml2js": "0.4.14",
76
76
  "@vitest/coverage-v8": "5.0.0",
77
77
  "chai": "6.2.2",
78
- "nodefony": "^10.0.0-alpha.3",
78
+ "nodefony": "^10.0.0-alpha.4",
79
79
  "rimraf": "6.1.3",
80
80
  "tsx": "4.23.13",
81
81
  "vitest": "5.0.0"
@@ -84,7 +84,7 @@
84
84
  "readmeFilename": "README.md",
85
85
  "contributors": [],
86
86
  "peerDependencies": {
87
- "nodefony": "^10.0.0-alpha.3",
87
+ "nodefony": "^10.0.0-alpha.4",
88
88
  "zod": "^4.4.3"
89
89
  },
90
90
  "files": [