@nodefony/http 10.0.0-beta.2 → 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.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
- package/dist/index.js +19 -11
- 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 +45 -6
- package/dist/nodefony/config/defineModuleConfig.js +2 -3
- package/dist/nodefony/interfaces/IReverseProxy.js +1 -0
- package/dist/nodefony/service/HttpAdminApi.js +2 -3
- package/dist/nodefony/service/ProfilerAdminApi.js +1 -2
- package/dist/nodefony/service/audit-logger.js +22 -16
- package/dist/nodefony/service/certificates.js +228 -19
- package/dist/nodefony/service/error-renderer.js +30 -13
- package/dist/nodefony/service/http-kernel.js +331 -82
- 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 +336 -0
- 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 +7 -10
- package/dist/nodefony/service/servers/server-websocket-secure.js +18 -18
- package/dist/nodefony/service/servers/server-websocket.js +18 -18
- package/dist/nodefony/service/servers/serverShutdown.js +78 -18
- package/dist/nodefony/service/servers/wsHeartbeat.js +32 -17
- package/dist/nodefony/service/sessions/sessions-service.js +34 -20
- 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 +51 -32
- package/dist/nodefony/src/context/domainMatcher.js +21 -4
- package/dist/nodefony/src/context/forwarded.js +3 -4
- package/dist/nodefony/src/context/http/HttpContext.js +98 -46
- package/dist/nodefony/src/context/http/Request.js +34 -38
- package/dist/nodefony/src/context/http/Response.js +39 -7
- package/dist/nodefony/src/context/http/SseStream.js +349 -0
- 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 +26 -0
- 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 +42 -6
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +94 -20
- package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
- package/dist/nodefony/src/context/websocket/websocketUrl.js +20 -0
- 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 +108 -30
- package/dist/nodefony/src/profiler/aggregate.js +422 -0
- package/dist/nodefony/src/proxy/forward.js +500 -0
- package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
- package/dist/nodefony/src/proxy/rules.js +188 -0
- 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 +128 -0
- package/dist/nodefony/src/session/session.js +30 -15
- 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 +12 -3
- package/dist/types/nodefony/config/config.d.ts +16 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +65 -4
- package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +1 -1
- package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
- package/dist/types/nodefony/interfaces/IReverseProxy.d.ts +67 -0
- package/dist/types/nodefony/interfaces/IRouting.d.ts +26 -0
- package/dist/types/nodefony/interfaces/ISecurity.d.ts +6 -0
- package/dist/types/nodefony/interfaces/index.d.ts +3 -2
- package/dist/types/nodefony/service/audit-logger.d.ts +2 -1
- package/dist/types/nodefony/service/certificates.d.ts +147 -5
- package/dist/types/nodefony/service/error-renderer.d.ts +32 -1
- package/dist/types/nodefony/service/http-kernel.d.ts +157 -15
- package/dist/types/nodefony/service/reverse-proxy.d.ts +156 -0
- 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 +2 -3
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +2 -3
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
- package/dist/types/nodefony/service/servers/wsHeartbeat.d.ts +26 -0
- package/dist/types/nodefony/service/sessions/sessions-service.d.ts +19 -1
- package/dist/types/nodefony/src/context/Context.d.ts +35 -6
- package/dist/types/nodefony/src/context/domainMatcher.d.ts +11 -1
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +48 -1
- package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
- package/dist/types/nodefony/src/context/http/Response.d.ts +14 -0
- package/dist/types/nodefony/src/context/http/SseStream.d.ts +155 -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/responseEnded.d.ts +20 -0
- 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/context/websocket/websocketUrl.d.ts +16 -0
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +13 -12
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +69 -15
- package/dist/types/nodefony/src/profiler/aggregate.d.ts +215 -0
- package/dist/types/nodefony/src/proxy/forward.d.ts +168 -0
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
- package/dist/types/nodefony/src/proxy/rules.d.ts +88 -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 +69 -0
- package/dist/types/nodefony/src/session/session.d.ts +2 -0
- package/docs/cookies.md +85 -78
- package/docs/index.md +5 -1
- package/docs/observabilite.md +81 -81
- package/docs/rate-limit.md +83 -83
- package/docs/reverse-proxy.md +522 -0
- package/docs/servers.md +378 -230
- package/docs/session.md +180 -180
- package/docs/sse.md +287 -0
- package/docs/upload.md +120 -117
- package/package.json +10 -11
- package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -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,22 +71,31 @@ 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";
|
|
77
78
|
export type { IRateLimitStore, IRateLimitOptions, RateLimitVerdict, } from "./nodefony/src/rateLimit/IRateLimitStore.js";
|
|
78
79
|
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, };
|
|
80
|
+
export { SseStream, openSseStream, acceptsEventStream, SSE_HEARTBEAT_MS, } from "./nodefony/src/context/http/SseStream.js";
|
|
81
|
+
export type { ISseStreamOptions, ISseSendOptions, } from "./nodefony/src/context/http/SseStream.js";
|
|
79
82
|
export { schemaMismatchOf, type SchemaMismatchConfidence, } from "./nodefony/service/error-renderer.js";
|
|
80
|
-
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";
|
|
81
87
|
export { FrameProfile } from "./nodefony/src/profiler/FrameProfile.js";
|
|
82
88
|
export type { FrameProfileInit, ProfiledArea, ProfiledResolver, } from "./nodefony/src/profiler/FrameProfile.js";
|
|
89
|
+
export { default as ReverseProxy } from "./nodefony/service/reverse-proxy.js";
|
|
90
|
+
export { parseProxyOrigin, normalizePrefix, proxyMountProblems, } from "./nodefony/src/proxy/rules.js";
|
|
91
|
+
export type { IProxyMount, IProxyMountOptions, ProxyTarget, } from "./nodefony/interfaces/IReverseProxy.js";
|
|
83
92
|
export { resolveUiDelivery, PrebuiltUi, } from "./nodefony/src/assets/prebuiltUi.js";
|
|
84
93
|
export type { UiDeliveryMode, UiDeliveryResolved, IUiDeliveryResolution, IUiDeliveryOptions, IPrebuiltUiOptions, } from "./nodefony/src/assets/prebuiltUi.js";
|
|
85
|
-
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";
|
|
86
95
|
export type { ICookie, ICookieOptions, IWsCookie, SameSiteType, PriorityType, } from "./nodefony/interfaces/ICookie.js";
|
|
87
96
|
export type { ISession, ISessionStorage, ISerializedSession, ISessionSummary, ISessionRecord, ISessionListFilter, ISessionListQuery, SessionIntent, SessionStatusType, SessionStrategyType, FlashBagType, MetaBagType, } from "./nodefony/interfaces/ISession.js";
|
|
88
97
|
export { SESSION_SORTABLE_FIELDS, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_COLUMN_ALIASES, } from "./nodefony/src/session/storage/sessionSort.js";
|
|
89
|
-
export type { IHttpRequest, IHttp2Request, IWsRequest, HTTPMethodType, } from "./nodefony/interfaces/IRequest.js";
|
|
98
|
+
export type { IHttpRequest, RequestBody, IHttp2Request, IWsRequest, HTTPMethodType, } from "./nodefony/interfaces/IRequest.js";
|
|
90
99
|
export type { IHttpResponse, IWebsocketResponse, } from "./nodefony/interfaces/IResponse.js";
|
|
91
100
|
export type { IHttpKernel } from "./nodefony/interfaces/IHttpKernel.js";
|
|
92
101
|
export type { IResolvedRoute, IRouteResolver, IRequestRouter, } from "./nodefony/interfaces/IRouting.js";
|
|
@@ -67,6 +67,7 @@ export declare const httpConfigSchema: z.ZodObject<{
|
|
|
67
67
|
key: z.ZodDefault<z.ZodString>;
|
|
68
68
|
cert: z.ZodDefault<z.ZodString>;
|
|
69
69
|
privateKeyMode: z.ZodDefault<z.ZodNumber>;
|
|
70
|
+
extraCaFiles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
70
71
|
san: z.ZodDefault<z.ZodObject<{
|
|
71
72
|
dns: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
72
73
|
ip: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -184,6 +185,21 @@ export declare const httpConfigSchema: z.ZodObject<{
|
|
|
184
185
|
readinessPath: z.ZodDefault<z.ZodString>;
|
|
185
186
|
shutdownDelay: z.ZodDefault<z.ZodNumber>;
|
|
186
187
|
}, z.core.$loose>>;
|
|
188
|
+
proxy: z.ZodDefault<z.ZodObject<{
|
|
189
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
190
|
+
connectTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
191
|
+
maxSockets: z.ZodDefault<z.ZodNumber>;
|
|
192
|
+
mounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
193
|
+
target: z.ZodString;
|
|
194
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
195
|
+
websocket: z.ZodDefault<z.ZodBoolean>;
|
|
196
|
+
stripPrefix: z.ZodDefault<z.ZodBoolean>;
|
|
197
|
+
preserveHost: z.ZodDefault<z.ZodBoolean>;
|
|
198
|
+
stripHeaders: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
199
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
200
|
+
secure: z.ZodDefault<z.ZodBoolean>;
|
|
201
|
+
}, z.core.$strict>>>;
|
|
202
|
+
}, z.core.$strict>>;
|
|
187
203
|
wsMaxConnectionsPerIp: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
|
|
188
204
|
}, z.core.$strict>;
|
|
189
205
|
/** Type de sortie (config normalisée + défauts appliqués). */
|
|
@@ -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;
|
|
@@ -86,6 +120,8 @@ export interface IContext {
|
|
|
86
120
|
cookieSession: ICookie | null | undefined;
|
|
87
121
|
cookies: CookiesMap;
|
|
88
122
|
user: unknown;
|
|
123
|
+
/** Identifiant de l'appelant authentifié (pare-feu), lisible au démontage. */
|
|
124
|
+
userIdentifier: string | null;
|
|
89
125
|
error: Error | null | undefined;
|
|
90
126
|
metaData: object;
|
|
91
127
|
readonly phases: PhaseTiming[];
|
|
@@ -137,13 +173,38 @@ export interface IHttpContext extends IContext {
|
|
|
137
173
|
getHost(): string | undefined;
|
|
138
174
|
getUserAgent(): string | undefined;
|
|
139
175
|
}
|
|
140
|
-
|
|
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 {
|
|
141
202
|
acceptedProtocol?: string | undefined;
|
|
142
203
|
connection: unknown;
|
|
143
204
|
rejected: boolean;
|
|
144
205
|
port: number | string;
|
|
145
206
|
wsUrl: URL | null;
|
|
146
|
-
|
|
207
|
+
query: Record<string, string>;
|
|
147
208
|
connect(): Promise<unknown>;
|
|
148
209
|
handle(data?: unknown[]): Promise<object>;
|
|
149
210
|
close(reasonCode: number, description: string): unknown;
|
|
@@ -46,7 +46,7 @@ export interface IErrorWebsocketResult {
|
|
|
46
46
|
*
|
|
47
47
|
* Override via `httpKernel.setErrorRenderer(custom)` to customise:
|
|
48
48
|
* - hide stack traces in prod
|
|
49
|
-
* - emit RFC
|
|
49
|
+
* - emit RFC 9457 problem+json
|
|
50
50
|
* - inject auth challenge headers
|
|
51
51
|
*/
|
|
52
52
|
export interface IErrorRenderer {
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Origine d'un amont relayé : `scheme://hôte[:port]`, sans chemin.
|
|
3
|
+
*
|
|
4
|
+
* Une fonction quand l'origine n'est connue qu'à l'exécution (un serveur
|
|
5
|
+
* enfant qui choisit son port au démarrage) : elle est rappelée à chaque
|
|
6
|
+
* requête, et `undefined` signifie « l'amont n'est pas prêt » — la requête
|
|
7
|
+
* reçoit `503` (`Retry-After: 1`), l'upgrade WebSocket `503` avant tout `101` :
|
|
8
|
+
* le préfixe appartient à l'amont, le routage n'a rien à y répondre.
|
|
9
|
+
*/
|
|
10
|
+
export type ProxyTarget = string | (() => string | undefined);
|
|
11
|
+
/**
|
|
12
|
+
* Réglages d'un montage du proxy inverse (`ReverseProxy.mount`).
|
|
13
|
+
*
|
|
14
|
+
* Les défauts sont ceux d'un relais sans surprise : toutes les méthodes, le
|
|
15
|
+
* chemin transmis tel quel, l'en-tête `Host` réécrit sur la cible, aucun
|
|
16
|
+
* en-tête retiré au-delà des en-têtes de connexion, certificat de la cible
|
|
17
|
+
* vérifié.
|
|
18
|
+
*/
|
|
19
|
+
export interface IProxyMountOptions {
|
|
20
|
+
/** Origine de l'amont (cf {@link ProxyTarget}). */
|
|
21
|
+
target: ProxyTarget;
|
|
22
|
+
/**
|
|
23
|
+
* Méthodes relayées (majuscules). Une autre méthode n'est PAS relayée : la
|
|
24
|
+
* requête poursuit jusqu'au routage. Absent = toutes.
|
|
25
|
+
*/
|
|
26
|
+
methods?: readonly string[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Relaie aussi l'upgrade WebSocket du préfixe. Sans ce drapeau, un upgrade
|
|
29
|
+
* sur le préfixe reste au serveur WebSocket de Nodefony.
|
|
30
|
+
*/
|
|
31
|
+
websocket?: boolean | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Retire le préfixe avant de transmettre le chemin (`/svc/a` → `/a`).
|
|
34
|
+
* Défaut `false` : l'amont reçoit le chemin complet — c'est le cas d'un
|
|
35
|
+
* serveur qui connaît déjà son préfixe (Vite et son `base`).
|
|
36
|
+
*/
|
|
37
|
+
stripPrefix?: boolean | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Garde l'en-tête `Host` du client au lieu de le réécrire sur la cible.
|
|
40
|
+
* Défaut `false` : un amont qui filtre les noms d'hôte (Vite et son
|
|
41
|
+
* `allowedHosts`) reçoit le sien, et le nom d'origine part dans
|
|
42
|
+
* `X-Forwarded-Host`.
|
|
43
|
+
*/
|
|
44
|
+
preserveHost?: boolean | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* En-têtes de requête à ne pas transmettre (insensible à la casse) — ce
|
|
47
|
+
* dont l'amont n'a pas l'usage ne doit pas lui parvenir (`cookie`,
|
|
48
|
+
* `authorization` vers un serveur de sources, par exemple).
|
|
49
|
+
*/
|
|
50
|
+
stripHeaders?: readonly string[] | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Délai d'inactivité de l'échange avec l'amont, en ms. Dépassé avant la
|
|
53
|
+
* réponse : 504. Absent = `proxy.timeoutMs` de la configuration (30 s par
|
|
54
|
+
* défaut). Ne s'applique pas à un WebSocket établi.
|
|
55
|
+
*/
|
|
56
|
+
timeoutMs?: number | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Vérifie le certificat d'une cible `https:`. Défaut `true` — `false` n'est
|
|
59
|
+
* ACCEPTÉ que vers la boucle locale (refus au montage sinon).
|
|
60
|
+
*/
|
|
61
|
+
secure?: boolean | undefined;
|
|
62
|
+
}
|
|
63
|
+
/** Un montage tel que le proxy le garde : préfixe normalisé + réglages. */
|
|
64
|
+
export interface IProxyMount extends IProxyMountOptions {
|
|
65
|
+
/** Préfixe d'URL normalisé (`/` en tête et en fin). */
|
|
66
|
+
readonly prefix: string;
|
|
67
|
+
}
|
|
@@ -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
|
|
@@ -75,6 +85,22 @@ export interface IRequestRouter {
|
|
|
75
85
|
* @returns le résolveur (`resolve === true` si une route a matché)
|
|
76
86
|
*/
|
|
77
87
|
resolve(context: ContextType, cleanPathOverride?: string, methodOverride?: string): IRouteResolver;
|
|
88
|
+
/**
|
|
89
|
+
* Dit si au moins une route déclare le transport `WEBSOCKET` pour ce chemin.
|
|
90
|
+
*
|
|
91
|
+
* Lu AVANT le `101` : une socket vers un chemin sans route se refuse par un
|
|
92
|
+
* statut HTTP (RFC 6455 §4.2.2), jamais par une ouverture suivie d'une
|
|
93
|
+
* fermeture — un client qui sonde par une ouverture conclurait que le
|
|
94
|
+
* serveur l'a acceptée. La réponse est un SUR-ENSEMBLE de ce que
|
|
95
|
+
* {@link IRequestRouter.resolve} acceptera : l'hôte (`@Domain`), le
|
|
96
|
+
* sous-protocole et les exigences de variables ne sont pas jugés ici, ils
|
|
97
|
+
* restent des fermetures après ouverture — refuser à tort coûterait une
|
|
98
|
+
* connexion légitime.
|
|
99
|
+
*
|
|
100
|
+
* @param pathname - chemin de l'URL d'upgrade (WHATWG), barres finales comprises
|
|
101
|
+
* @returns `false` si aucune route WebSocket ne peut répondre à ce chemin
|
|
102
|
+
*/
|
|
103
|
+
servesWebsocket(pathname: string): boolean;
|
|
78
104
|
/**
|
|
79
105
|
* Instance partagée d'un controller `scope: "singleton"`, créée une seule fois.
|
|
80
106
|
*
|
|
@@ -42,4 +42,10 @@ export interface IFirewallGate {
|
|
|
42
42
|
handleCors(context: ContextType): number | undefined;
|
|
43
43
|
/** Pose les en-têtes de sécurité (CSP, HSTS…) de la réponse. */
|
|
44
44
|
applySecurityHeaders(context: ContextType): void;
|
|
45
|
+
/**
|
|
46
|
+
* Le revalidateur de l'identité authentifiée de la requête courante, pour une
|
|
47
|
+
* connexion longue qui la gardera (flux SSE) : la même règle que les sockets
|
|
48
|
+
* WebSocket. `null` pour un anonyme — rien à révoquer.
|
|
49
|
+
*/
|
|
50
|
+
currentRevalidator?(): ((nowMs?: number) => Promise<boolean>) | null;
|
|
45
51
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
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";
|
|
10
|
+
export type { IProxyMount, IProxyMountOptions, ProxyTarget, } from "./IReverseProxy.js";
|
|
@@ -17,7 +17,8 @@ import type { IHttpContext, IWebsocketContext, PhaseTiming } from "../interfaces
|
|
|
17
17
|
export interface AuditLogEntry {
|
|
18
18
|
ts: string;
|
|
19
19
|
requestId: string;
|
|
20
|
-
|
|
20
|
+
/** Identifiant de l'appelant authentifié (`context.userIdentifier`), `null` sinon. */
|
|
21
|
+
userIdentifier: string | null;
|
|
21
22
|
type: "http" | "ws";
|
|
22
23
|
scheme: string;
|
|
23
24
|
method: string | null;
|
|
@@ -1,6 +1,138 @@
|
|
|
1
1
|
import { Service, Module } from "nodefony";
|
|
2
2
|
import { type KeyObject } from "node:crypto";
|
|
3
3
|
import { type ICertificateAttribute } from "./x509.js";
|
|
4
|
+
/**
|
|
5
|
+
* Accès à la liste d'ancres TLS par défaut du processus — injectable pour
|
|
6
|
+
* éprouver la décision sans toucher la confiance du processus de test.
|
|
7
|
+
*/
|
|
8
|
+
export interface ITlsTrustStore {
|
|
9
|
+
getCACertificates?: (type?: "default") => string[];
|
|
10
|
+
setDefaultCACertificates?: (certs: ReadonlyArray<string>) => void;
|
|
11
|
+
}
|
|
12
|
+
/** Issue de {@link addDefaultCaCertificate}, CONSTATÉE à l'exécution. */
|
|
13
|
+
export type CaTrustVerdict = "added" | "present" | "unsupported";
|
|
14
|
+
/**
|
|
15
|
+
* Ajoute une autorité aux ancres TLS par défaut du processus (requêtes sortantes
|
|
16
|
+
* `fetch`, `https`, `tls.connect`), sans en retirer aucune.
|
|
17
|
+
*
|
|
18
|
+
* C'est l'équivalent en cours d'exécution de `NODE_EXTRA_CA_CERTS`, qui n'est lu
|
|
19
|
+
* qu'au lancement du processus — trop tôt pour une CA que le boot vient de
|
|
20
|
+
* générer, et que seul un script de banc posait. La capacité se CONSTATE
|
|
21
|
+
* (`tls.setDefaultCACertificates` n'existe qu'à partir de Node 24.5), elle ne se
|
|
22
|
+
* déduit pas d'un numéro de version. Idempotent : une autorité déjà présente
|
|
23
|
+
* n'est pas ajoutée deux fois.
|
|
24
|
+
*
|
|
25
|
+
* @param pem - autorité au format PEM.
|
|
26
|
+
* @param store - accès aux ancres ; `node:tls` par défaut.
|
|
27
|
+
* @returns `added`, `present` (déjà de confiance), ou `unsupported`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function addDefaultCaCertificate(pem: string, store?: ITlsTrustStore): CaTrustVerdict;
|
|
30
|
+
/**
|
|
31
|
+
* Découpe un fichier PEM en ses certificats — une chaîne d'entreprise est
|
|
32
|
+
* souvent livrée en un seul fichier (racine + intermédiaires).
|
|
33
|
+
*
|
|
34
|
+
* Chaque certificat est ajouté SÉPARÉMENT : c'est ce qui rend l'ajout
|
|
35
|
+
* idempotent certificat par certificat, la liste par défaut du processus
|
|
36
|
+
* n'étant faite que de certificats isolés.
|
|
37
|
+
*
|
|
38
|
+
* @param pem - contenu d'un fichier PEM (un ou plusieurs certificats)
|
|
39
|
+
* @returns les certificats, dans l'ordre du fichier ; vide s'il n'y en a aucun
|
|
40
|
+
*/
|
|
41
|
+
export declare function splitPemCertificates(pem: string): string[];
|
|
42
|
+
/** Bilan de {@link trustCaFile}. */
|
|
43
|
+
export interface ICaFileTrust {
|
|
44
|
+
/** Fichier lu, chemin absolu. */
|
|
45
|
+
readonly file: string;
|
|
46
|
+
/** Certificats ajoutés aux ancres du processus. */
|
|
47
|
+
readonly added: number;
|
|
48
|
+
/** Certificats déjà de confiance. */
|
|
49
|
+
readonly present: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Ajoute les autorités d'un fichier PEM aux ancres TLS du processus
|
|
53
|
+
* (`certificates.extraCaFiles`) — l'équivalent, porté par l'APPLICATION, de
|
|
54
|
+
* `NODE_EXTRA_CA_CERTS`.
|
|
55
|
+
*
|
|
56
|
+
* `NODE_EXTRA_CA_CERTS` n'est lu qu'au lancement de Node, et se pose dans la
|
|
57
|
+
* syntaxe du shell — `VAR=… npm run dev` n'existe pas sous Windows. Une autorité
|
|
58
|
+
* d'entreprise (Keycloak interne, proxy sortant qui ré-signe) se déclare donc
|
|
59
|
+
* dans la configuration, et sert à TOUTES les requêtes sortantes du processus :
|
|
60
|
+
* découverte OpenID, point de jeton, jeu de clés, appels d'administration.
|
|
61
|
+
*
|
|
62
|
+
* @param file - chemin du fichier, absolu ou relatif à `root`
|
|
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
|
+
*
|
|
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
|
|
69
|
+
* @returns le bilan de l'ajout
|
|
70
|
+
* @throws BootConfigurationError - fichier illisible, sans certificat, ou Node
|
|
71
|
+
* incapable d'élargir ses ancres en cours d'exécution
|
|
72
|
+
*/
|
|
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
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Ajoute aux ancres TLS du processus les autorités déclarées
|
|
116
|
+
* (`certificates.extraCaFiles`), au plus tôt : appelé à l'enregistrement du
|
|
117
|
+
* module, avant tout service — toute requête sortante, commande console
|
|
118
|
+
* comprise, les trouve en place.
|
|
119
|
+
*
|
|
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}).
|
|
128
|
+
*
|
|
129
|
+
* @param files - `certificates.extraCaFiles`
|
|
130
|
+
* @param root - racine de l'application
|
|
131
|
+
* @param log - journal du module
|
|
132
|
+
* @param context - décor (environnement, autorité de dev, ancres)
|
|
133
|
+
* @throws BootConfigurationError - cf {@link trustCaFile}
|
|
134
|
+
*/
|
|
135
|
+
export declare function trustOutboundAuthorities(files: readonly string[], root: string, log: (message: string, severity: "INFO") => void, context?: IOutboundTrustContext): Promise<void>;
|
|
4
136
|
/** Paire de clés RSA du certificat auto-signé. */
|
|
5
137
|
export interface IRsaKeyPair {
|
|
6
138
|
privateKey: KeyObject;
|
|
@@ -102,7 +234,7 @@ export interface CertificateInfo {
|
|
|
102
234
|
*
|
|
103
235
|
* Conformité (génération auto-signée) : signature SHA-256 (jamais SHA-1,
|
|
104
236
|
* RFC 5280 / CA-B Forum), numéro de série aléatoire 128 bits unique
|
|
105
|
-
* (RFC 5280 §4.1.2.2), SAN qui fait foi (RFC
|
|
237
|
+
* (RFC 5280 §4.1.2.2), SAN qui fait foi (RFC 9525), `notBefore` reculé,
|
|
106
238
|
* clé privée écrite en `0600`.
|
|
107
239
|
*/
|
|
108
240
|
declare class Certificate extends Service {
|
|
@@ -114,8 +246,6 @@ declare class Certificate extends Service {
|
|
|
114
246
|
cert: Buffer | string | null;
|
|
115
247
|
fullchainPem: Buffer | string | null;
|
|
116
248
|
publicKeyPem: Buffer | string | null;
|
|
117
|
-
rootCertPem: Buffer | string | null;
|
|
118
|
-
intermediateCertPem: Buffer | string | null;
|
|
119
249
|
/** CAROOT résolu de mkcert (null tant que non détecté / indisponible). */
|
|
120
250
|
private mkcertCaRoot;
|
|
121
251
|
path: string;
|
|
@@ -173,6 +303,18 @@ declare class Certificate extends Service {
|
|
|
173
303
|
* @param force - forcer la régénération même si un certificat valide existe
|
|
174
304
|
*/
|
|
175
305
|
generateServerCertificates(force?: boolean): Promise<this>;
|
|
306
|
+
/**
|
|
307
|
+
* En développement, fait confiance à la CA que Nodefony vient de générer ou de
|
|
308
|
+
* relire, pour les requêtes SORTANTES du processus.
|
|
309
|
+
*
|
|
310
|
+
* Sans elle, un service du décor qui sert le certificat de l'application
|
|
311
|
+
* (Keycloak, l'auto-vérification d'un jeton MCP) est injoignable depuis
|
|
312
|
+
* `nodefony development` — `fetch failed` / `SELF_SIGNED_CERT_IN_CHAIN` — alors
|
|
313
|
+
* que `curl -k` répond : la confiance n'existait que sous le script de banc.
|
|
314
|
+
* Réservé au DÉVELOPPEMENT et aux certificats que Nodefony fabrique : un
|
|
315
|
+
* certificat `explicit` (production) n'élargit jamais la confiance du processus.
|
|
316
|
+
*/
|
|
317
|
+
private trustDevelopmentAuthority;
|
|
176
318
|
/**
|
|
177
319
|
* Résout la stratégie effective à partir de `certificates.strategy` :
|
|
178
320
|
* - `explicit` : `key` + `cert` fournis (prod, Let's Encrypt…). Forcé →
|
|
@@ -193,7 +335,7 @@ declare class Certificate extends Service {
|
|
|
193
335
|
/**
|
|
194
336
|
* SAN effectif : config explicite si fournie, sinon dérivé du kernel
|
|
195
337
|
* (localhost + domain en DNS ; loopback en IP). Une IP littérale (ex. domain
|
|
196
|
-
* `127.0.0.1` en dev) est classée en `ip`, pas en `dns` (RFC
|
|
338
|
+
* `127.0.0.1` en dev) est classée en `ip`, pas en `dns` (RFC 9525).
|
|
197
339
|
*/
|
|
198
340
|
private derivedSan;
|
|
199
341
|
/** Hostnames DNS couverts par le SAN. */
|
|
@@ -217,7 +359,7 @@ declare class Certificate extends Service {
|
|
|
217
359
|
* @returns false si absent, illisible ou inadéquat → déclenche la régénération.
|
|
218
360
|
*/
|
|
219
361
|
private isCertAdequate;
|
|
220
|
-
/** Le SAN présent couvre-t-il tous les noms DNS requis (RFC
|
|
362
|
+
/** Le SAN présent couvre-t-il tous les noms DNS requis (RFC 9525) ? */
|
|
221
363
|
private sanCovers;
|
|
222
364
|
/** Charge un certificat fourni en config (chemin fichier ou Buffer). */
|
|
223
365
|
private loadExplicitCert;
|
|
@@ -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
|
*
|
|
@@ -69,7 +100,7 @@ export declare function schemaMismatchOf(error: unknown): SchemaMismatchConfiden
|
|
|
69
100
|
* reason: error.message
|
|
70
101
|
*
|
|
71
102
|
* Stateless singleton — zero per-request allocation. Override via
|
|
72
|
-
* `httpKernel.setErrorRenderer(custom)` for prod hardening or RFC
|
|
103
|
+
* `httpKernel.setErrorRenderer(custom)` for prod hardening or RFC 9457.
|
|
73
104
|
*/
|
|
74
105
|
declare class DefaultErrorRenderer implements IErrorRenderer {
|
|
75
106
|
renderHttp(error: Error, context: IHttpContext): IErrorHttpResult;
|