@nodefony/http 10.0.0-beta.3 → 10.0.0-beta.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.
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
- package/dist/index.js +12 -10
- package/dist/nodefony/command/assetsPublishCommand.js +5 -3
- package/dist/nodefony/command/certificatesCommand.js +5 -3
- package/dist/nodefony/command/networkCommand.js +5 -3
- package/dist/nodefony/command/proxyGenerateCommand.js +8 -3
- package/dist/nodefony/config/config.js +1 -2
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/service/HttpAdminApi.js +2 -2
- package/dist/nodefony/service/ProfilerAdminApi.js +1 -2
- package/dist/nodefony/service/audit-logger.js +20 -13
- package/dist/nodefony/service/certificates.js +97 -13
- package/dist/nodefony/service/error-renderer.js +29 -12
- package/dist/nodefony/service/http-kernel.js +118 -43
- package/dist/nodefony/service/pretty-request-logger.js +5 -4
- package/dist/nodefony/service/request-logger.js +24 -16
- package/dist/nodefony/service/reverse-proxy.js +3 -3
- package/dist/nodefony/service/servers/clientError.js +22 -3
- package/dist/nodefony/service/servers/server-h2c.js +126 -0
- package/dist/nodefony/service/servers/server-http.js +5 -8
- package/dist/nodefony/service/servers/server-https.js +12 -24
- package/dist/nodefony/service/servers/server-static.js +3 -3
- package/dist/nodefony/service/servers/server-websocket-secure.js +10 -18
- package/dist/nodefony/service/servers/server-websocket.js +10 -18
- package/dist/nodefony/service/servers/serverShutdown.js +78 -18
- package/dist/nodefony/service/servers/wsHeartbeat.js +1 -2
- package/dist/nodefony/service/sessions/sessions-service.js +7 -14
- package/dist/nodefony/service/trace.js +1 -2
- package/dist/nodefony/service/upload/upload-service.js +5 -8
- package/dist/nodefony/service/x509.js +1 -2
- package/dist/nodefony/src/assets/collectAssets.js +1 -2
- package/dist/nodefony/src/assets/prebuiltUi.js +1 -2
- package/dist/nodefony/src/context/Context.js +23 -31
- package/dist/nodefony/src/context/domainMatcher.js +1 -2
- package/dist/nodefony/src/context/forwarded.js +1 -2
- package/dist/nodefony/src/context/http/HttpContext.js +7 -6
- package/dist/nodefony/src/context/http/Request.js +33 -29
- package/dist/nodefony/src/context/http/Response.js +23 -6
- package/dist/nodefony/src/context/http/SseStream.js +1 -2
- package/dist/nodefony/src/context/http/parser.js +36 -10
- package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
- package/dist/nodefony/src/context/http2/Request.js +4 -8
- package/dist/nodefony/src/context/http2/Response.js +22 -9
- package/dist/nodefony/src/context/metaData.js +1 -2
- package/dist/nodefony/src/context/requestId.js +1 -2
- package/dist/nodefony/src/context/responseEnded.js +1 -2
- package/dist/nodefony/src/context/transport.js +45 -0
- package/dist/nodefony/src/context/trustProxy.js +1 -2
- package/dist/nodefony/src/context/websocket/Response.js +33 -6
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +92 -17
- package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
- package/dist/nodefony/src/context/websocket/websocketUrl.js +1 -2
- package/dist/nodefony/src/context/websocket/wsBackpressure.js +1 -2
- package/dist/nodefony/src/context/websocket/wsCloseReason.js +1 -2
- package/dist/nodefony/src/context/websocket/wsLogContent.js +1 -2
- package/dist/nodefony/src/cookies/cookie.js +3 -7
- package/dist/nodefony/src/errors/httpError.js +1 -2
- package/dist/nodefony/src/errors/thrownMessage.js +1 -2
- package/dist/nodefony/src/profiler/FrameProfile.js +1 -2
- package/dist/nodefony/src/profiler/Profiler.js +102 -20
- package/dist/nodefony/src/profiler/aggregate.js +422 -0
- package/dist/nodefony/src/proxy/forward.js +29 -5
- package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
- package/dist/nodefony/src/proxy/rules.js +12 -6
- package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +1 -2
- package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +1 -2
- package/dist/nodefony/src/rateLimit/rateLimitFilters.js +1 -2
- package/dist/nodefony/src/servers/http2Limits.js +50 -0
- package/dist/nodefony/src/servers/kernelServers.js +12 -3
- package/dist/nodefony/src/servers/portBinder.js +41 -10
- package/dist/nodefony/src/servers/upgradeDispatch.js +86 -7
- package/dist/nodefony/src/session/session.js +2 -3
- package/dist/nodefony/src/session/sessionStoreFailure.js +1 -2
- package/dist/nodefony/src/session/storage/MemorySessionStorage.js +1 -2
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +1 -2
- package/dist/nodefony/src/session/storage/sessionFilters.js +1 -2
- package/dist/nodefony/src/session/storage/sessionSort.js +1 -2
- package/dist/types/index.d.ts +7 -3
- package/dist/types/nodefony/interfaces/IContext.d.ts +63 -4
- package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
- package/dist/types/nodefony/interfaces/IRouting.d.ts +10 -0
- package/dist/types/nodefony/interfaces/index.d.ts +2 -2
- package/dist/types/nodefony/service/certificates.d.ts +55 -4
- package/dist/types/nodefony/service/error-renderer.d.ts +31 -0
- package/dist/types/nodefony/service/http-kernel.d.ts +46 -10
- package/dist/types/nodefony/service/servers/clientError.d.ts +16 -0
- package/dist/types/nodefony/service/servers/server-h2c.d.ts +51 -0
- package/dist/types/nodefony/service/servers/server-http.d.ts +3 -3
- package/dist/types/nodefony/service/servers/server-https.d.ts +4 -4
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +1 -3
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +1 -3
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
- package/dist/types/nodefony/src/context/Context.d.ts +13 -4
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +1 -1
- package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
- package/dist/types/nodefony/src/context/http/Response.d.ts +11 -0
- package/dist/types/nodefony/src/context/http/parser.d.ts +18 -2
- package/dist/types/nodefony/src/context/http2/Request.d.ts +1 -1
- package/dist/types/nodefony/src/context/http2/Response.d.ts +15 -5
- package/dist/types/nodefony/src/context/transport.d.ts +53 -0
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +18 -2
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +57 -7
- package/dist/types/nodefony/src/context/websocket/isWebsocketContext.d.ts +15 -0
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +13 -12
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +68 -15
- package/dist/types/nodefony/src/profiler/aggregate.d.ts +215 -0
- package/dist/types/nodefony/src/proxy/forward.d.ts +14 -2
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
- package/dist/types/nodefony/src/proxy/rules.d.ts +3 -0
- package/dist/types/nodefony/src/servers/http2Limits.d.ts +37 -0
- package/dist/types/nodefony/src/servers/kernelServers.d.ts +10 -1
- package/dist/types/nodefony/src/servers/portBinder.d.ts +29 -5
- package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +33 -3
- package/docs/cookies.md +85 -78
- package/docs/observabilite.md +80 -80
- package/docs/rate-limit.md +83 -83
- package/docs/reverse-proxy.md +54 -54
- package/docs/servers.md +338 -224
- package/docs/session.md +179 -179
- package/docs/sse.md +24 -20
- package/docs/upload.md +120 -117
- package/package.json +8 -9
- package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// nodefony/src/servers/kernelServers.ts
|
|
2
2
|
/**
|
|
3
3
|
* Rétrécit la section `servers` de la configuration du kernel (lue dans le sac
|
|
4
4
|
* de config non typé du cœur) en {@link ServersConfig}, sans propager `any`.
|
|
@@ -13,6 +13,15 @@ function asServersConfig(servers) {
|
|
|
13
13
|
return servers !== null && typeof servers === "object" ? servers : void 0;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
+
* Le serveur h2c démarre-t-il ? Seul un `enabled: true` EXPLICITE l'allume : la
|
|
17
|
+
* section existe toujours (défauts), un port seul ne vaut pas activation.
|
|
18
|
+
*
|
|
19
|
+
* @param entry - `servers.h2c`.
|
|
20
|
+
*/
|
|
21
|
+
function isH2cEnabled(entry) {
|
|
22
|
+
return entry?.enabled === true;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
16
25
|
* Port DEMANDÉ pour un serveur (`servers.<type>.port`), ou `undefined` si le
|
|
17
26
|
* serveur est désactivé, absent, ou sans port numérique.
|
|
18
27
|
*
|
|
@@ -22,8 +31,8 @@ function asServersConfig(servers) {
|
|
|
22
31
|
function configuredServerPort(servers, type) {
|
|
23
32
|
const entry = servers?.[type];
|
|
24
33
|
if (!entry) return void 0;
|
|
34
|
+
if (type === "h2c" && !isH2cEnabled(servers.h2c)) return void 0;
|
|
25
35
|
const port = entry.port;
|
|
26
36
|
return typeof port === "number" ? port : void 0;
|
|
27
37
|
}
|
|
28
|
-
|
|
29
|
-
export { asServersConfig, configuredServerPort };
|
|
38
|
+
export { asServersConfig, configuredServerPort, isH2cEnabled };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { isH2cEnabled } from "./kernelServers.js";
|
|
1
2
|
import { isPortListening, resolvePortPolicy } from "nodefony";
|
|
2
3
|
import cluster from "node:cluster";
|
|
3
|
-
|
|
4
|
+
// nodefony/src/servers/portBinder.ts
|
|
4
5
|
/**
|
|
5
6
|
* **portBinder** — écoute sur le port désiré, ou sur le prochain port libre.
|
|
6
7
|
*
|
|
@@ -48,27 +49,58 @@ import cluster from "node:cluster";
|
|
|
48
49
|
*/
|
|
49
50
|
/** Nombre de ports essayés après le désiré, en `auto`. */
|
|
50
51
|
const DEFAULT_PORT_RETRY_ATTEMPTS = 20;
|
|
52
|
+
const BOUND_SERVERS = [
|
|
53
|
+
"http",
|
|
54
|
+
"https",
|
|
55
|
+
"h2c"
|
|
56
|
+
];
|
|
51
57
|
/** Port configuré d'un serveur, ou `0` (désactivé / non précisé → choix noyau). */
|
|
52
|
-
function configuredPort(
|
|
58
|
+
function configuredPort(servers, which) {
|
|
59
|
+
if (which === "h2c") {
|
|
60
|
+
const entry = servers?.h2c;
|
|
61
|
+
return isH2cEnabled(entry) ? entry?.port ?? 0 : 0;
|
|
62
|
+
}
|
|
63
|
+
const entry = servers?.[which];
|
|
53
64
|
if (!entry) return 0;
|
|
54
65
|
return entry.port ?? 0;
|
|
55
66
|
}
|
|
56
67
|
/**
|
|
68
|
+
* Serveur actif qui convoite le MÊME port que h2c, ou `null`.
|
|
69
|
+
*
|
|
70
|
+
* Un port partagé avec h2c n'est pas un conflit à contourner : sur ce port, le
|
|
71
|
+
* client HTTP/1.1 et le proxy h2c se marchent dessus, et le repli `auto` ferait
|
|
72
|
+
* glisser l'un des deux là où personne ne l'attend. Le boot le refuse.
|
|
73
|
+
*
|
|
74
|
+
* @param servers - `kernel.options.servers`.
|
|
75
|
+
* @returns `"http"` ou `"https"` en conflit, sinon `null` (h2c inactif, port 0).
|
|
76
|
+
*/
|
|
77
|
+
function h2cPortConflict(servers) {
|
|
78
|
+
const port = configuredPort(servers, "h2c");
|
|
79
|
+
if (port === 0) return null;
|
|
80
|
+
if (configuredPort(servers, "http") === port) return "http";
|
|
81
|
+
if (configuredPort(servers, "https") === port) return "https";
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
57
85
|
* Compose le plan de bind d'un serveur depuis la config du kernel — source UNIQUE
|
|
58
|
-
* (les
|
|
86
|
+
* (les serveurs l'appellent ; la politique n'est décidée qu'ici).
|
|
59
87
|
*
|
|
60
88
|
* @param which - le serveur qu'on borne.
|
|
61
89
|
* @param servers - `kernel.options.servers`.
|
|
62
90
|
* @param environment - `kernel.environment` (arbitre le défaut de la politique).
|
|
63
91
|
*/
|
|
64
92
|
function buildBindPlan(which, servers, environment) {
|
|
65
|
-
const desired = configuredPort(servers
|
|
66
|
-
const
|
|
67
|
-
const
|
|
93
|
+
const desired = configuredPort(servers, which);
|
|
94
|
+
const reserved = [];
|
|
95
|
+
for (const other of BOUND_SERVERS) {
|
|
96
|
+
if (other === which) continue;
|
|
97
|
+
const port = configuredPort(servers, other);
|
|
98
|
+
if (port > 0 && port !== desired) reserved.push(port);
|
|
99
|
+
}
|
|
68
100
|
return {
|
|
69
101
|
desired,
|
|
70
|
-
reserved
|
|
71
|
-
attempts:
|
|
102
|
+
reserved,
|
|
103
|
+
attempts: resolvePortPolicy(environment, servers?.portPolicy) === "auto" ? servers?.portRetryAttempts ?? 20 : 0
|
|
72
104
|
};
|
|
73
105
|
}
|
|
74
106
|
/** Adresses qui désignent « toutes les interfaces » — rien ne s'y connecte. */
|
|
@@ -224,5 +256,4 @@ async function bindWithFallback(server, host, plan, probe = isPortListening, sha
|
|
|
224
256
|
candidate = next;
|
|
225
257
|
}
|
|
226
258
|
}
|
|
227
|
-
|
|
228
|
-
export { DEFAULT_PORT_RETRY_ATTEMPTS, bindWithFallback, buildBindPlan, conflictProbeTargets, detectPortConflict, resolvePortPolicy };
|
|
259
|
+
export { DEFAULT_PORT_RETRY_ATTEMPTS, bindWithFallback, buildBindPlan, conflictProbeTargets, detectPortConflict, h2cPortConflict, resolvePortPolicy };
|
|
@@ -1,6 +1,79 @@
|
|
|
1
|
+
import { websocketHandshakeProblem } from "../proxy/rules.js";
|
|
1
2
|
import { refuseUpgrade } from "../proxy/forward.js";
|
|
2
3
|
import { STATUS_CODES } from "node:http";
|
|
3
|
-
|
|
4
|
+
// nodefony/src/servers/upgradeDispatch.ts
|
|
5
|
+
/** Défaut sans noyau : tout upgrade conforme est accepté puis émis en `connection`. */
|
|
6
|
+
function acceptAll(wss) {
|
|
7
|
+
return (req, admission) => {
|
|
8
|
+
const accepted = admission.accept();
|
|
9
|
+
if (accepted instanceof Promise) accepted.then((ws) => {
|
|
10
|
+
if (ws !== null) wss.emit("connection", ws, req);
|
|
11
|
+
});
|
|
12
|
+
else if (accepted !== null) wss.emit("connection", accepted, req);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Le verdict d'UN upgrade. Pendant que le noyau décide, le socket n'a plus
|
|
17
|
+
* d'écouteur `error` (`node:http` retire les siens à l'émission d'`upgrade`,
|
|
18
|
+
* `ws` ne pose les siens qu'à `handleUpgrade`) : une coupure du client
|
|
19
|
+
* (`ECONNRESET`) y deviendrait une erreur non gérée qui abat le process. D'où
|
|
20
|
+
* l'écouteur de la fenêtre d'attente, retiré quand le verdict tombe.
|
|
21
|
+
*
|
|
22
|
+
* Une classe, pas un objet de fermetures : une allocation par handshake (plus
|
|
23
|
+
* l'écouteur), méthodes partagées par le prototype.
|
|
24
|
+
*/
|
|
25
|
+
var UpgradeAdmission = class {
|
|
26
|
+
wss;
|
|
27
|
+
req;
|
|
28
|
+
socket;
|
|
29
|
+
head;
|
|
30
|
+
#accepted = void 0;
|
|
31
|
+
#settled = false;
|
|
32
|
+
#onPendingError;
|
|
33
|
+
constructor(wss, req, socket, head) {
|
|
34
|
+
this.wss = wss;
|
|
35
|
+
this.req = req;
|
|
36
|
+
this.socket = socket;
|
|
37
|
+
this.head = head;
|
|
38
|
+
this.#onPendingError = () => socket.destroy();
|
|
39
|
+
socket.on("error", this.#onPendingError);
|
|
40
|
+
}
|
|
41
|
+
#settle() {
|
|
42
|
+
if (this.#settled) return false;
|
|
43
|
+
this.#settled = true;
|
|
44
|
+
this.socket.removeListener("error", this.#onPendingError);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
accept() {
|
|
48
|
+
if (this.#accepted !== void 0) return this.#accepted;
|
|
49
|
+
if (!this.#settle()) return this.#accepted = null;
|
|
50
|
+
const { socket } = this;
|
|
51
|
+
if (socket.destroyed || !socket.writable) {
|
|
52
|
+
socket.destroy();
|
|
53
|
+
return this.#accepted = null;
|
|
54
|
+
}
|
|
55
|
+
let ws;
|
|
56
|
+
let late = null;
|
|
57
|
+
const onClose = () => late?.(null);
|
|
58
|
+
this.wss.handleUpgrade(this.req, socket, this.head, (accepted) => {
|
|
59
|
+
if (late === null) {
|
|
60
|
+
ws = accepted;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
socket.removeListener("close", onClose);
|
|
64
|
+
late(accepted);
|
|
65
|
+
});
|
|
66
|
+
if (ws !== void 0) return this.#accepted = ws;
|
|
67
|
+
socket.once("close", onClose);
|
|
68
|
+
return this.#accepted = new Promise((resolve) => {
|
|
69
|
+
late = resolve;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
refuse(status, headers) {
|
|
73
|
+
if (!this.#settle()) return;
|
|
74
|
+
refuseUpgrade(this.socket, status, STATUS_CODES[status] ?? "Refused", headers);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
4
77
|
/**
|
|
5
78
|
* Pose l'UNIQUE écouteur `upgrade` d'un serveur : le proxy inverse d'abord,
|
|
6
79
|
* puis le serveur WebSocket de Nodefony.
|
|
@@ -14,7 +87,10 @@ import { STATUS_CODES } from "node:http";
|
|
|
14
87
|
*
|
|
15
88
|
* Entre les deux, `refusal` tranche ce qui doit être refusé AVANT le `101`
|
|
16
89
|
* (RFC 6455 §4.2.2) : un chemin sans route WebSocket reçoit un statut HTTP,
|
|
17
|
-
* jamais une ouverture suivie d'une fermeture.
|
|
90
|
+
* jamais une ouverture suivie d'une fermeture. Puis la FORME du handshake est
|
|
91
|
+
* contrôlée (`websocketHandshakeProblem`, la règle du proxy inverse) : un
|
|
92
|
+
* handshake malformé reçoit `400` sans jamais atteindre le noyau. Enfin
|
|
93
|
+
* `admit` décide — pare-feu, garde de l'action — et seul son verdict ouvre.
|
|
18
94
|
*
|
|
19
95
|
* Sans montage, le coût par connexion est une lecture de champ.
|
|
20
96
|
*
|
|
@@ -23,9 +99,11 @@ import { STATUS_CODES } from "node:http";
|
|
|
23
99
|
* @param relay - proxy inverse, lu à chaque upgrade (il peut naître après)
|
|
24
100
|
* @param refusal - statut HTTP qui refuse l'upgrade, `null` pour l'admettre
|
|
25
101
|
* (cf `HttpKernel.websocketUpgradeRefusal`)
|
|
102
|
+
* @param admit - décide d'un upgrade admis (cf `HttpKernel.onWebsocketUpgrade`) ;
|
|
103
|
+
* par défaut, tout upgrade conforme est accepté et émis en `connection`
|
|
26
104
|
* @returns la fonction qui retire l'écouteur — à appeler à l'arrêt
|
|
27
105
|
*/
|
|
28
|
-
function attachUpgradeDispatch(server, wss, relay, refusal) {
|
|
106
|
+
function attachUpgradeDispatch(server, wss, relay, refusal, admit = acceptAll(wss)) {
|
|
29
107
|
const onUpgrade = (req, socket, head) => {
|
|
30
108
|
const proxy = relay();
|
|
31
109
|
if (proxy !== null && proxy.mounts !== null) {
|
|
@@ -36,14 +114,15 @@ function attachUpgradeDispatch(server, wss, relay, refusal) {
|
|
|
36
114
|
refuseUpgrade(socket, status, STATUS_CODES[status] ?? "Refused");
|
|
37
115
|
return;
|
|
38
116
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
117
|
+
if (websocketHandshakeProblem(req.method, req.headers) !== null) {
|
|
118
|
+
refuseUpgrade(socket, 400, STATUS_CODES[400] ?? "Bad Request");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
admit(req, new UpgradeAdmission(wss, req, socket, head));
|
|
42
122
|
};
|
|
43
123
|
server.on("upgrade", onUpgrade);
|
|
44
124
|
return () => {
|
|
45
125
|
server.removeListener("upgrade", onUpgrade);
|
|
46
126
|
};
|
|
47
127
|
}
|
|
48
|
-
//#endregion
|
|
49
128
|
export { attachUpgradeDispatch };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Cookie from "../cookies/cookie.js";
|
|
2
2
|
import { extend } from "nodefony";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
|
-
|
|
4
|
+
// nodefony/src/session/session.ts
|
|
5
5
|
const defaultSessionOptions = {
|
|
6
6
|
name: "nodefony",
|
|
7
7
|
strictMode: true,
|
|
@@ -238,7 +238,7 @@ var Session = class {
|
|
|
238
238
|
}
|
|
239
239
|
isValidSession(_data, context) {
|
|
240
240
|
if (this.options.refererCheck) {
|
|
241
|
-
let sameHost
|
|
241
|
+
let sameHost;
|
|
242
242
|
try {
|
|
243
243
|
sameHost = this.checkSecureReferer(context);
|
|
244
244
|
} catch {
|
|
@@ -410,5 +410,4 @@ var Session = class {
|
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
412
|
};
|
|
413
|
-
//#endregion
|
|
414
413
|
export { Session as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { thrownMessage } from "../errors/thrownMessage.js";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/session/sessionStoreFailure.ts
|
|
3
3
|
/**
|
|
4
4
|
* Reconnaît une table de session absente, quel que soit le dialecte.
|
|
5
5
|
*
|
|
@@ -26,5 +26,4 @@ function describeSessionStoreFailure(error) {
|
|
|
26
26
|
message: `La session n'a pas pu être persistée — la requête est servie en 500 plutôt que laissée sans réponse. Cause : ${cause}.${looksLikeMissingTable(cause) ? " La table de session est probablement absente : appliquer les migrations (`nodefony orm:migrate`) avant de servir, ou choisir un autre stockage (`http.session.store`)." : " Vérifier que le stockage de session déclaré sous `http.session.store` est joignable ; à défaut, appliquer les migrations (`nodefony orm:migrate`)."}`
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
//#endregion
|
|
30
29
|
export { describeSessionStoreFailure };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SESSION_DEFAULT_ORDER, SESSION_SORTABLE_FIELDS } from "./sessionSort.js";
|
|
2
2
|
import { assertPageQuery, compareByOrder, pickOrder } from "nodefony";
|
|
3
|
-
|
|
3
|
+
// nodefony/src/session/storage/MemorySessionStorage.ts
|
|
4
4
|
/**
|
|
5
5
|
* Store de sessions **en mémoire** (Map process) — implémentation de référence
|
|
6
6
|
* d'{@link ISessionStorage}, pendant `session` des `Memory*Store` de sécurité.
|
|
@@ -181,5 +181,4 @@ var MemorySessionStorage = class {
|
|
|
181
181
|
return Promise.resolve(users.size);
|
|
182
182
|
}
|
|
183
183
|
};
|
|
184
|
-
//#endregion
|
|
185
184
|
export { MemorySessionStorage as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// nodefony/src/session/storage/RevocationGuardStorage.ts
|
|
2
2
|
/**
|
|
3
3
|
* Durée de vie (ms) d'une pierre tombale de révocation. Couvre la fenêtre
|
|
4
4
|
* pendant laquelle une requête « en vol » — qui avait chargé la session AVANT
|
|
@@ -148,5 +148,4 @@ var RevocationGuardStorage = class {
|
|
|
148
148
|
if (t.size === 0) this.#tombstones = null;
|
|
149
149
|
}
|
|
150
150
|
};
|
|
151
|
-
//#endregion
|
|
152
151
|
export { RevocationGuardStorage as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// nodefony/src/session/storage/sessionFilters.ts
|
|
2
2
|
/**
|
|
3
3
|
* **Le vocabulaire de filtre des sessions**, en noms PUBLICS — celui que la
|
|
4
4
|
* console d'administration écrit dans l'URL (`?user=alice`).
|
|
@@ -79,5 +79,4 @@ const SESSION_FACETS = {
|
|
|
79
79
|
* rouvert du même geste.
|
|
80
80
|
*/
|
|
81
81
|
const SESSION_STATS_FILTERS = { user: "string" };
|
|
82
|
-
//#endregion
|
|
83
82
|
export { SESSION_FACETS, SESSION_FILTERS, SESSION_STATS_FILTERS };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renameOrderFields } from "nodefony";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/session/storage/sessionSort.ts
|
|
3
3
|
/**
|
|
4
4
|
* **Le vocabulaire de tri des sessions**, en noms PUBLICS — ceux qu'un client
|
|
5
5
|
* écrit dans l'URL (`?order=updatedAt:DESC`), jamais des noms de colonne.
|
|
@@ -49,5 +49,4 @@ const SESSION_COLUMN_ALIASES = { id: "session_id" };
|
|
|
49
49
|
* `session_id` — {@link SESSION_DEFAULT_ORDER} déjà traduit.
|
|
50
50
|
*/
|
|
51
51
|
const SESSION_DEFAULT_ORDER_SQL = renameOrderFields(SESSION_DEFAULT_ORDER, SESSION_COLUMN_ALIASES);
|
|
52
|
-
//#endregion
|
|
53
52
|
export { SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_SORTABLE_FIELDS };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export { defineHttpConfig, httpConfigJsonSchema, } from "./nodefony/config/defin
|
|
|
71
71
|
export { httpConfigSchema } from "./nodefony/config/config.js";
|
|
72
72
|
export type { IHttpConfig, IHttpConfigInput, } from "./nodefony/interfaces/IHttpConfig.js";
|
|
73
73
|
export { decideSend, readBackpressureOptions, type WsBackpressurePolicy, type WsSendDecision, type IWsBackpressureOptions, type IBackpressureSocket, type IBackpressureTarget, } from "./nodefony/src/context/websocket/wsBackpressure.js";
|
|
74
|
+
export { isWebsocketContext } from "./nodefony/src/context/websocket/isWebsocketContext.js";
|
|
74
75
|
export { compileDomainPattern, compileDomainPatterns, compileTrustedHosts, isDomainAllowed, } from "./nodefony/src/context/domainMatcher.js";
|
|
75
76
|
export type { DomainPattern, ITrustedHostsConfig, } from "./nodefony/src/context/domainMatcher.js";
|
|
76
77
|
export { MemoryRateLimitStore } from "./nodefony/src/rateLimit/MemoryRateLimitStore.js";
|
|
@@ -79,7 +80,10 @@ export { Context, Session, SessionsService, HttpError, HttpKernel, Response, Htt
|
|
|
79
80
|
export { SseStream, openSseStream, acceptsEventStream, SSE_HEARTBEAT_MS, } from "./nodefony/src/context/http/SseStream.js";
|
|
80
81
|
export type { ISseStreamOptions, ISseSendOptions, } from "./nodefony/src/context/http/SseStream.js";
|
|
81
82
|
export { schemaMismatchOf, type SchemaMismatchConfidence, } from "./nodefony/service/error-renderer.js";
|
|
82
|
-
export
|
|
83
|
+
export { toValidationFields, type IValidationField, } from "./nodefony/service/error-renderer.js";
|
|
84
|
+
export type { ProfileEntry, ProfileSummary, ProfilePhase, ProfileQuery, ProfileSecurity, IProfileBatch, IProfilerReader, } from "./nodefony/src/profiler/Profiler.js";
|
|
85
|
+
export { aggregateProfiles, createProfileAggregator, isFailedProfile, sqlShape, N_PLUS_ONE_MIN_REPEATS, ACTION_SELF_PHASE, NO_ROUTE, TOOLING_URL_PREFIX, } from "./nodefony/src/profiler/aggregate.js";
|
|
86
|
+
export type { IProfileAggregator, IProfileAggregatorOptions, IProfileReport, IRouteStats, IStatusBreakdown, IPhaseTotal, INPlusOne, ISqlShapeStats, IErrorSample, } from "./nodefony/src/profiler/aggregate.js";
|
|
83
87
|
export { FrameProfile } from "./nodefony/src/profiler/FrameProfile.js";
|
|
84
88
|
export type { FrameProfileInit, ProfiledArea, ProfiledResolver, } from "./nodefony/src/profiler/FrameProfile.js";
|
|
85
89
|
export { default as ReverseProxy } from "./nodefony/service/reverse-proxy.js";
|
|
@@ -87,11 +91,11 @@ export { parseProxyOrigin, normalizePrefix, proxyMountProblems, } from "./nodefo
|
|
|
87
91
|
export type { IProxyMount, IProxyMountOptions, ProxyTarget, } from "./nodefony/interfaces/IReverseProxy.js";
|
|
88
92
|
export { resolveUiDelivery, PrebuiltUi, } from "./nodefony/src/assets/prebuiltUi.js";
|
|
89
93
|
export type { UiDeliveryMode, UiDeliveryResolved, IUiDeliveryResolution, IUiDeliveryOptions, IPrebuiltUiOptions, } from "./nodefony/src/assets/prebuiltUi.js";
|
|
90
|
-
export type { IContext, IHttpContext, IWebsocketContext, ServerType, SchemeType, WebSocketStateType, CookiesMap, ISecurityTrace, SecurityOutcome, } from "./nodefony/interfaces/IContext.js";
|
|
94
|
+
export type { IContext, IHttpContext, IWebsocketContext, IWebsocketConnectionState, ServerType, ProtocolName, AlpnProtocol, SchemeType, WebSocketStateType, CookiesMap, ISecurityTrace, SecurityOutcome, } from "./nodefony/interfaces/IContext.js";
|
|
91
95
|
export type { ICookie, ICookieOptions, IWsCookie, SameSiteType, PriorityType, } from "./nodefony/interfaces/ICookie.js";
|
|
92
96
|
export type { ISession, ISessionStorage, ISerializedSession, ISessionSummary, ISessionRecord, ISessionListFilter, ISessionListQuery, SessionIntent, SessionStatusType, SessionStrategyType, FlashBagType, MetaBagType, } from "./nodefony/interfaces/ISession.js";
|
|
93
97
|
export { SESSION_SORTABLE_FIELDS, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_COLUMN_ALIASES, } from "./nodefony/src/session/storage/sessionSort.js";
|
|
94
|
-
export type { IHttpRequest, IHttp2Request, IWsRequest, HTTPMethodType, } from "./nodefony/interfaces/IRequest.js";
|
|
98
|
+
export type { IHttpRequest, RequestBody, IHttp2Request, IWsRequest, HTTPMethodType, } from "./nodefony/interfaces/IRequest.js";
|
|
95
99
|
export type { IHttpResponse, IWebsocketResponse, } from "./nodefony/interfaces/IResponse.js";
|
|
96
100
|
export type { IHttpKernel } from "./nodefony/interfaces/IHttpKernel.js";
|
|
97
101
|
export type { IResolvedRoute, IRouteResolver, IRequestRouter, } from "./nodefony/interfaces/IRouting.js";
|
|
@@ -3,7 +3,35 @@ import type { MaybePromise } from "nodefony";
|
|
|
3
3
|
import type { ICookie } from "./ICookie.js";
|
|
4
4
|
import type { ISession } from "./ISession.js";
|
|
5
5
|
import type { HTTPMethodType } from "./IRequest.js";
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Identité du serveur qui a reçu la requête, nommée par le schéma d'URI qu'il
|
|
8
|
+
* sert en LOCAL (RFC 9110 §4.2, RFC 6455 §3) : elle choisit la section de
|
|
9
|
+
* configuration du serveur (`http`, `https`, `websocket`, `websocketSecure`).
|
|
10
|
+
* Ce n'est PAS un protocole — `https` sert HTTP/1.1 et h2 sur le même port ;
|
|
11
|
+
* le protocole du saut se lit dans {@link AlpnProtocol}.
|
|
12
|
+
*/
|
|
13
|
+
export type ServerType = "http" | "https" | "ws" | "wss";
|
|
14
|
+
/**
|
|
15
|
+
* Famille du protocole que le contrôleur traite, nommée comme l'attribut
|
|
16
|
+
* OpenTelemetry `network.protocol.name`.
|
|
17
|
+
*
|
|
18
|
+
* Union OUVERTE : une valeur peut apparaître en version mineure
|
|
19
|
+
* (`"webtransport"` sur HTTP/3) — pas de `switch` exhaustif sans `default`.
|
|
20
|
+
*/
|
|
21
|
+
export type ProtocolName = "http" | "websocket";
|
|
22
|
+
/**
|
|
23
|
+
* Protocole du saut qui atteint CE processus, nommé par le registre IANA des
|
|
24
|
+
* identifiants ALPN — la valeur même que Node expose (`TLSSocket.alpnProtocol`,
|
|
25
|
+
* `Http2Session.alpnProtocol`, qui vaut `"h2c"` sur une session en clair).
|
|
26
|
+
*
|
|
27
|
+
* Union OUVERTE : `"h3"` s'ajoutera en version mineure, sans rupture.
|
|
28
|
+
*/
|
|
29
|
+
export type AlpnProtocol = "http/1.1" | "h2" | "h2c";
|
|
30
|
+
/**
|
|
31
|
+
* Schéma d'URI vu par le CLIENT (RFC 9110 §4.2, RFC 6455 §3) : derrière un
|
|
32
|
+
* proxy de confiance, c'est le schéma qu'il a annoncé (`Forwarded`), pas
|
|
33
|
+
* celui du saut local. Toute décision de sécurité lit ce champ.
|
|
34
|
+
*/
|
|
7
35
|
export type SchemeType = "http" | "https" | "ws" | "wss";
|
|
8
36
|
export type WebSocketStateType = "handshake" | "connected" | "closed" | "error" | "message" | null;
|
|
9
37
|
export type CookiesMap = Record<string, ICookie>;
|
|
@@ -67,8 +95,14 @@ export interface IContext {
|
|
|
67
95
|
csrfProtect: boolean;
|
|
68
96
|
csrfExempt: boolean;
|
|
69
97
|
csrfToken: string | null;
|
|
70
|
-
|
|
98
|
+
/** Famille traitée : `"http"` ou `"websocket"` (union ouverte). */
|
|
99
|
+
type: ProtocolName;
|
|
100
|
+
/** Schéma vu par le client — seul champ que lit une décision de sécurité. */
|
|
71
101
|
scheme: SchemeType;
|
|
102
|
+
/** Protocole du saut local : `"http/1.1"`, `"h2"`, `"h2c"` (union ouverte). */
|
|
103
|
+
alpnProtocol: AlpnProtocol;
|
|
104
|
+
/** Serveur qui a reçu la requête — choisit la configuration par serveur. */
|
|
105
|
+
serverType: ServerType;
|
|
72
106
|
method: HTTPMethodType | null;
|
|
73
107
|
url: string;
|
|
74
108
|
domain: string;
|
|
@@ -139,13 +173,38 @@ export interface IHttpContext extends IContext {
|
|
|
139
173
|
getHost(): string | undefined;
|
|
140
174
|
getUserAgent(): string | undefined;
|
|
141
175
|
}
|
|
142
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Ce qu'un module attache à une connexion WebSocket pour toute sa durée — interface
|
|
178
|
+
* VIDE par construction, qu'un module AUGMENTE par fusion de déclarations.
|
|
179
|
+
*
|
|
180
|
+
* `@nodefony/http` ignore les protocoles bâtis au-dessus de la socket (le JSON-RPC
|
|
181
|
+
* de `@nodefony/realtime`, par exemple) et ne peut pas les importer. Le module qui
|
|
182
|
+
* en porte un déclare son champ ici : {@link IWebsocketContext} et `WebsocketContext`
|
|
183
|
+
* le reçoivent typé, sans conversion chez le lecteur et sans rien coûter à
|
|
184
|
+
* l'exécution — la lecture reste un accès de champ ordinaire.
|
|
185
|
+
*
|
|
186
|
+
* Chaque champ se déclare OPTIONNEL : il n'existe qu'une fois posé par son module
|
|
187
|
+
* (à la poignée de main, en général), et vaut `undefined` avant, comme sur toute
|
|
188
|
+
* connexion que ce module ne sert pas.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* declare module "@nodefony/http" {
|
|
193
|
+
* interface IWebsocketConnectionState {
|
|
194
|
+
* myProtocol?: IMyProtocolState;
|
|
195
|
+
* }
|
|
196
|
+
* }
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
export interface IWebsocketConnectionState {
|
|
200
|
+
}
|
|
201
|
+
export interface IWebsocketContext extends IContext, IWebsocketConnectionState {
|
|
143
202
|
acceptedProtocol?: string | undefined;
|
|
144
203
|
connection: unknown;
|
|
145
204
|
rejected: boolean;
|
|
146
205
|
port: number | string;
|
|
147
206
|
wsUrl: URL | null;
|
|
148
|
-
|
|
207
|
+
query: Record<string, string>;
|
|
149
208
|
connect(): Promise<unknown>;
|
|
150
209
|
handle(data?: unknown[]): Promise<object>;
|
|
151
210
|
close(reasonCode: number, description: string): unknown;
|
|
@@ -8,12 +8,18 @@ export interface IRequest {
|
|
|
8
8
|
getHeader(name: string): string | string[] | undefined;
|
|
9
9
|
getHeaders(): Record<string, string | string[] | undefined>;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Le corps DÉCODÉ d'une requête : un objet (JSON, formulaire, champs
|
|
13
|
+
* multipart, XML), une chaîne (`text/*`), ou des octets (`Uint8Array` — tout
|
|
14
|
+
* autre type, et un corps sans `Content-Type`). Le même type d'octets que la
|
|
15
|
+
* socket temps réel : un schéma de route valide l'un comme l'autre.
|
|
16
|
+
*/
|
|
17
|
+
export type RequestBody = Record<string, unknown> | string | Uint8Array;
|
|
11
18
|
export interface IHttpRequest extends IRequest {
|
|
12
19
|
url: URL;
|
|
13
20
|
query: Record<string, unknown>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
queryFile: unknown[];
|
|
21
|
+
body: RequestBody;
|
|
22
|
+
files: readonly unknown[];
|
|
17
23
|
remoteAddress: string | null | undefined;
|
|
18
24
|
acceptHtml: boolean;
|
|
19
25
|
getRemoteAddress(): string | null | undefined;
|
|
@@ -29,7 +35,6 @@ export interface IHttp2Request extends IHttpRequest {
|
|
|
29
35
|
export interface IWsRequest extends IRequest {
|
|
30
36
|
url: URL;
|
|
31
37
|
query: Record<string, string>;
|
|
32
|
-
queryGet: Record<string, string>;
|
|
33
38
|
path: string;
|
|
34
39
|
cookies?: Record<string, ICookie>;
|
|
35
40
|
}
|
|
@@ -60,6 +60,16 @@ export interface IRouteResolver {
|
|
|
60
60
|
* n'attendent, sinon sa promesse
|
|
61
61
|
*/
|
|
62
62
|
callController(data?: unknown[], reload?: boolean): MaybePromise<unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Juge les gardes de l'action résolue (`@IsGranted`, `@RequireScope`,
|
|
65
|
+
* step-up, rôle de la zone) SANS l'exécuter — le pipeline WebSocket l'appelle
|
|
66
|
+
* avant d'accepter le handshake, pour qu'un refus parte en statut HTTP. Un
|
|
67
|
+
* verdict favorable vaut pour la PROCHAINE exécution, qui ne le rejuge pas.
|
|
68
|
+
*
|
|
69
|
+
* @returns `undefined` quand l'action n'est pas gardée ; sinon la promesse du
|
|
70
|
+
* verdict, rompue par un refus (401/403)
|
|
71
|
+
*/
|
|
72
|
+
authorizeAction(): Promise<void> | undefined;
|
|
63
73
|
}
|
|
64
74
|
/**
|
|
65
75
|
* Ce que le pipeline HTTP/WS attend du service `router` — implémenté par
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export type { ICookie, ICookieOptions, IWsCookie, SameSiteType, PriorityType, } from "./ICookie.js";
|
|
2
2
|
export type { ISession, ISessionStorage, ISerializedSession, SessionIntent, SessionStatusType, SessionStrategyType, FlashBagType, MetaBagType, } from "./ISession.js";
|
|
3
3
|
export type { IUploadedFile, IUploadService, IParsedUploadFile, IUploadOptions, } from "./IUpload.js";
|
|
4
|
-
export type { IRequest, IHttpRequest, IHttp2Request, IWsRequest, HTTPMethodType, } from "./IRequest.js";
|
|
4
|
+
export type { IRequest, IHttpRequest, RequestBody, IHttp2Request, IWsRequest, HTTPMethodType, } from "./IRequest.js";
|
|
5
5
|
export type { IResponse, IHttpResponse, IWebsocketResponse } from "./IResponse.js";
|
|
6
|
-
export type { IContext, IHttpContext, IWebsocketContext, ServerType, SchemeType, WebSocketStateType, CookiesMap, } from "./IContext.js";
|
|
6
|
+
export type { IContext, IHttpContext, IWebsocketContext, ServerType, ProtocolName, AlpnProtocol, SchemeType, WebSocketStateType, CookiesMap, } from "./IContext.js";
|
|
7
7
|
export type { IHttpKernel } from "./IHttpKernel.js";
|
|
8
8
|
export type { IResolvedRoute, IRouteResolver, IRequestRouter, } from "./IRouting.js";
|
|
9
9
|
export type { ISecurityZone, IFirewallGate } from "./ISecurity.js";
|
|
@@ -61,27 +61,78 @@ export interface ICaFileTrust {
|
|
|
61
61
|
*
|
|
62
62
|
* @param file - chemin du fichier, absolu ou relatif à `root`
|
|
63
63
|
* @param root - racine de l'application
|
|
64
|
+
* Chaque refus dit le problème, d'où vient le chemin quand on le sait, puis le
|
|
65
|
+
* geste qui répare ({@link caFileRemedy}).
|
|
66
|
+
*
|
|
64
67
|
* @param store - accès aux ancres ; `node:tls` par défaut
|
|
68
|
+
* @param origin - phrase qui dit d'où vient le chemin (` Valeur venue de …`), insérée dans un refus
|
|
65
69
|
* @returns le bilan de l'ajout
|
|
66
70
|
* @throws BootConfigurationError - fichier illisible, sans certificat, ou Node
|
|
67
71
|
* incapable d'élargir ses ancres en cours d'exécution
|
|
68
72
|
*/
|
|
69
|
-
export declare function trustCaFile(file: string, root: string, store?: ITlsTrustStore): Promise<ICaFileTrust>;
|
|
73
|
+
export declare function trustCaFile(file: string, root: string, store?: ITlsTrustStore, origin?: string): Promise<ICaFileTrust>;
|
|
74
|
+
/**
|
|
75
|
+
* Le geste qui répare une autorité déclarée qu'on ne peut pas reconnaître.
|
|
76
|
+
*
|
|
77
|
+
* Un refus au démarrage qui ne dit que la cause envoie chercher ; celui-ci dit
|
|
78
|
+
* quoi faire. Pure : éprouvée cause par cause, y compris celles qu'une machine
|
|
79
|
+
* de test ne sait pas produire (un droit refusé ne l'est pas à `root`, ni
|
|
80
|
+
* sous Windows).
|
|
81
|
+
*
|
|
82
|
+
* @param cause - code système (`ENOENT`, `EACCES`, `EPERM`, `EISDIR`),
|
|
83
|
+
* `NOPEM` pour un fichier lu sans certificat, ou tout autre motif
|
|
84
|
+
* @returns la phrase du geste
|
|
85
|
+
*/
|
|
86
|
+
export declare function caFileRemedy(cause: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Dossier où Nodefony range les certificats qu'il fabrique en développement,
|
|
89
|
+
* relatif au répertoire courant.
|
|
90
|
+
*
|
|
91
|
+
* @returns le chemin absolu de `nodefony/config/certificates`
|
|
92
|
+
*/
|
|
93
|
+
export declare function certificatesDirectory(): string;
|
|
94
|
+
/**
|
|
95
|
+
* Chemin de l'autorité de DÉVELOPPEMENT : le certificat racine que Nodefony
|
|
96
|
+
* fabrique lui-même pour signer le certificat du serveur local. Généré, jamais
|
|
97
|
+
* versionné — une seule convention, que le service et la confiance sortante lisent.
|
|
98
|
+
*
|
|
99
|
+
* @param directory - dossier des certificats ; {@link certificatesDirectory} par défaut
|
|
100
|
+
* @returns le chemin absolu du fichier PEM de l'autorité
|
|
101
|
+
*/
|
|
102
|
+
export declare function developmentAuthorityPath(directory?: string): string;
|
|
103
|
+
/** Décor de {@link trustOutboundAuthorities} — injectable pour l'éprouver. */
|
|
104
|
+
export interface IOutboundTrustContext {
|
|
105
|
+
/** Vrai en développement : seul décor où l'autorité de dev absente est tolérée. */
|
|
106
|
+
readonly development?: boolean;
|
|
107
|
+
/** Chemin absolu de l'autorité de développement ; {@link developmentAuthorityPath} par défaut. */
|
|
108
|
+
readonly developmentAuthority?: string;
|
|
109
|
+
/** Environnement lu pour nommer la variable d'origine ; `process.env` par défaut. */
|
|
110
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
111
|
+
/** Accès aux ancres ; `node:tls` par défaut. */
|
|
112
|
+
readonly store?: ITlsTrustStore;
|
|
113
|
+
}
|
|
70
114
|
/**
|
|
71
115
|
* Ajoute aux ancres TLS du processus les autorités déclarées
|
|
72
116
|
* (`certificates.extraCaFiles`), au plus tôt : appelé à l'enregistrement du
|
|
73
117
|
* module, avant tout service — toute requête sortante, commande console
|
|
74
118
|
* comprise, les trouve en place.
|
|
75
119
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
120
|
+
* L'autorité de DÉVELOPPEMENT déclarée mais pas encore engendrée est ignorée en
|
|
121
|
+
* développement, et le journal le dit : elle n'a signé aucun certificat, il n'y
|
|
122
|
+
* a donc rien à lui faire confiance, et le serveur de développement la pose
|
|
123
|
+
* lui-même quand il la fabrique ({@link Certificate.generateServerCertificates}).
|
|
124
|
+
* Rien n'est fabriqué ici : une commande console n'écrit pas de certificat.
|
|
125
|
+
* Une autre autorité absente, ou n'importe laquelle hors développement, reste
|
|
126
|
+
* un refus — qui nomme la variable d'origine et le geste qui répare
|
|
127
|
+
* ({@link caFileRemedy}).
|
|
78
128
|
*
|
|
79
129
|
* @param files - `certificates.extraCaFiles`
|
|
80
130
|
* @param root - racine de l'application
|
|
81
131
|
* @param log - journal du module
|
|
132
|
+
* @param context - décor (environnement, autorité de dev, ancres)
|
|
82
133
|
* @throws BootConfigurationError - cf {@link trustCaFile}
|
|
83
134
|
*/
|
|
84
|
-
export declare function trustOutboundAuthorities(files: readonly string[], root: string, log: (message: string, severity: "INFO") => void): Promise<void>;
|
|
135
|
+
export declare function trustOutboundAuthorities(files: readonly string[], root: string, log: (message: string, severity: "INFO") => void, context?: IOutboundTrustContext): Promise<void>;
|
|
85
136
|
/** Paire de clés RSA du certificat auto-signé. */
|
|
86
137
|
export interface IRsaKeyPair {
|
|
87
138
|
privateKey: KeyObject;
|
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
import type { IErrorRenderer, IErrorHttpResult, IErrorWebsocketResult } from "../interfaces/IErrorRenderer.js";
|
|
2
2
|
import type { IHttpContext, IWebsocketContext } from "../interfaces/IContext.js";
|
|
3
|
+
/** Champ fautif, tel qu'exposé au client dans le corps JSON (`error.fields`). */
|
|
4
|
+
export interface IValidationField {
|
|
5
|
+
/** Chemin pointé du champ (`"author.email"`). */
|
|
6
|
+
field: string;
|
|
7
|
+
/** Message lisible. */
|
|
8
|
+
message: string;
|
|
9
|
+
/** Contrôle qui a échoué (`too_small`…) — utile au client pour réagir finement. */
|
|
10
|
+
rule?: string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Partie de l'entrée qui porte le champ (`body`, `query`, `params`,
|
|
13
|
+
* `headers`) — présente pour une route à schéma, où deux parties peuvent
|
|
14
|
+
* porter le même nom. `field` reste relatif à sa partie : un formulaire le
|
|
15
|
+
* relie à son champ de saisie sans rien retirer.
|
|
16
|
+
*/
|
|
17
|
+
in?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reconnaît une erreur de validation — `ZodError`, ou `SchemaValidationError`
|
|
21
|
+
* d'une route à schéma — et en extrait les champs fautifs.
|
|
22
|
+
*
|
|
23
|
+
* Reconnaissance **structurelle** (`name` + `issues`), pas `instanceof ZodError` :
|
|
24
|
+
* une application peut embarquer sa propre copie de zod (résolutions npm multiples),
|
|
25
|
+
* et `instanceof` échouerait alors en silence — la validation retomberait en 500.
|
|
26
|
+
* Même parti-pris que le duck-typing de `isPromise` dans le framework.
|
|
27
|
+
*
|
|
28
|
+
* Ne coûte rien au chemin nominal : ce code ne tourne que sur une erreur déjà levée.
|
|
29
|
+
*
|
|
30
|
+
* @param error - erreur remontée par le pipeline.
|
|
31
|
+
* @returns les champs fautifs, ou `null` si ce n'est pas une erreur de validation.
|
|
32
|
+
*/
|
|
33
|
+
export declare function toValidationFields(error: Error): IValidationField[] | null;
|
|
3
34
|
/**
|
|
4
35
|
* Reconnaît un identifiant MAL FORMÉ refusé par la base, et rend la valeur fautive.
|
|
5
36
|
*
|