@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,9 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// \0@oxc-project+runtime@0.155.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);
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
}
|
|
8
|
-
//#endregion
|
|
9
8
|
export { __decorate as default };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/decorateParam.js
|
|
2
2
|
function __decorateParam(paramIndex, decorator) {
|
|
3
3
|
return function(target, key) {
|
|
4
4
|
decorator(target, key, paramIndex);
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
|
-
//#endregion
|
|
8
7
|
export { __decorateParam as default };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import config, { httpConfigSchema, httpOverlaySchema } from "./nodefony/config/c
|
|
|
3
3
|
import { defineHttpConfig, httpConfigJsonSchema } from "./nodefony/config/defineModuleConfig.js";
|
|
4
4
|
import { createHttpAdminApi } from "./nodefony/service/HttpAdminApi.js";
|
|
5
5
|
import { createProfilerAdminApi } from "./nodefony/service/ProfilerAdminApi.js";
|
|
6
|
+
import { ACTION_SELF_PHASE, NO_ROUTE, N_PLUS_ONE_MIN_REPEATS, TOOLING_URL_PREFIX, aggregateProfiles, createProfileAggregator, isFailedProfile, sqlShape } from "./nodefony/src/profiler/aggregate.js";
|
|
6
7
|
import { Profiler } from "./nodefony/src/profiler/Profiler.js";
|
|
7
8
|
import HttpError from "./nodefony/src/errors/httpError.js";
|
|
8
9
|
import { MemoryRateLimitStore } from "./nodefony/src/rateLimit/MemoryRateLimitStore.js";
|
|
@@ -19,15 +20,15 @@ import Http2Request from "./nodefony/src/context/http2/Request.js";
|
|
|
19
20
|
import Http2Response from "./nodefony/src/context/http2/Response.js";
|
|
20
21
|
import { SSE_HEARTBEAT_MS, SseStream, acceptsEventStream, openSseStream } from "./nodefony/src/context/http/SseStream.js";
|
|
21
22
|
import HttpContext from "./nodefony/src/context/http/HttpContext.js";
|
|
22
|
-
import DefaultErrorRenderer, { schemaMismatchOf } from "./nodefony/service/error-renderer.js";
|
|
23
|
-
import DefaultRequestLogger from "./nodefony/service/request-logger.js";
|
|
23
|
+
import DefaultErrorRenderer, { schemaMismatchOf, toValidationFields } from "./nodefony/service/error-renderer.js";
|
|
24
24
|
import JsonAuditLogger from "./nodefony/service/audit-logger.js";
|
|
25
|
+
import DefaultRequestLogger from "./nodefony/service/request-logger.js";
|
|
25
26
|
import PrettyRequestLogger from "./nodefony/service/pretty-request-logger.js";
|
|
26
|
-
import
|
|
27
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.js";
|
|
27
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
28
28
|
import http_kernel_default from "./nodefony/service/http-kernel.js";
|
|
29
29
|
import server_http_default from "./nodefony/service/servers/server-http.js";
|
|
30
30
|
import server_https_default from "./nodefony/service/servers/server-https.js";
|
|
31
|
+
import server_h2c_default from "./nodefony/service/servers/server-h2c.js";
|
|
31
32
|
import server_websocket_default from "./nodefony/service/servers/server-websocket.js";
|
|
32
33
|
import server_websocket_secure_default from "./nodefony/service/servers/server-websocket-secure.js";
|
|
33
34
|
import Statics from "./nodefony/service/servers/server-static.js";
|
|
@@ -41,9 +42,10 @@ import { SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQ
|
|
|
41
42
|
import sessions_service_default from "./nodefony/service/sessions/sessions-service.js";
|
|
42
43
|
import Certificate, { trustOutboundAuthorities } from "./nodefony/service/certificates.js";
|
|
43
44
|
import upload_service_default from "./nodefony/service/upload/upload-service.js";
|
|
45
|
+
import { isWebsocketContext } from "./nodefony/src/context/websocket/isWebsocketContext.js";
|
|
44
46
|
import { PrebuiltUi, resolveUiDelivery } from "./nodefony/src/assets/prebuiltUi.js";
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
+
import { Module, services } from "nodefony";
|
|
48
|
+
// index.ts
|
|
47
49
|
let Http = class Http extends Module {
|
|
48
50
|
overlaySchema = httpOverlaySchema;
|
|
49
51
|
constructor(kernel) {
|
|
@@ -75,7 +77,7 @@ let Http = class Http extends Module {
|
|
|
75
77
|
const input = this.options;
|
|
76
78
|
const options = defineHttpConfig(input ?? {}, this.kernel);
|
|
77
79
|
this.options = options;
|
|
78
|
-
await trustOutboundAuthorities(options.certificates.extraCaFiles, this.kernel?.path ?? process.cwd(), (message, severity) => this.log(message, severity));
|
|
80
|
+
await trustOutboundAuthorities(options.certificates.extraCaFiles, this.kernel?.path ?? process.cwd(), (message, severity) => this.log(message, severity), { development: this.kernel?.environment === "development" });
|
|
79
81
|
return this;
|
|
80
82
|
}
|
|
81
83
|
/**
|
|
@@ -107,10 +109,10 @@ Http = __decorate([services([
|
|
|
107
109
|
ReverseProxy,
|
|
108
110
|
server_http_default,
|
|
109
111
|
server_https_default,
|
|
112
|
+
server_h2c_default,
|
|
110
113
|
server_websocket_default,
|
|
111
114
|
server_websocket_secure_default,
|
|
112
115
|
upload_service_default
|
|
113
|
-
])
|
|
116
|
+
])], Http);
|
|
114
117
|
var http_default = Http;
|
|
115
|
-
|
|
116
|
-
export { Context, Cookie, DefaultErrorRenderer, DefaultRequestLogger, FrameProfile, Http2Request, Http2Response, HttpContext, HttpError, http_kernel_default as HttpKernel, HttpRequest, HttpResponse, HttpResponse as Response, JsonAuditLogger, MemoryRateLimitStore, PrebuiltUi, PrettyRequestLogger, Profiler, ReverseProxy, SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_SORTABLE_FIELDS, SSE_HEARTBEAT_MS, Session, sessions_service_default as SessionsService, SseStream, WebsocketContext, WebsocketResponse, WebsocketResponse as wsResponse, acceptsEventStream, compileDomainPattern, compileDomainPatterns, compileTrustedHosts, decideSend, http_default as default, defineHttpConfig, httpConfigJsonSchema, httpConfigSchema, isDomainAllowed, normalizePrefix, openSseStream, parseProxyOrigin, proxyMountProblems, readBackpressureOptions, resolveUiDelivery, schemaMismatchOf };
|
|
118
|
+
export { ACTION_SELF_PHASE, Context, Cookie, DefaultErrorRenderer, DefaultRequestLogger, FrameProfile, Http2Request, Http2Response, HttpContext, HttpError, http_kernel_default as HttpKernel, HttpRequest, HttpResponse, HttpResponse as Response, JsonAuditLogger, MemoryRateLimitStore, NO_ROUTE, N_PLUS_ONE_MIN_REPEATS, PrebuiltUi, PrettyRequestLogger, Profiler, ReverseProxy, SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQL, SESSION_SORTABLE_FIELDS, SSE_HEARTBEAT_MS, Session, sessions_service_default as SessionsService, SseStream, TOOLING_URL_PREFIX, WebsocketContext, WebsocketResponse, WebsocketResponse as wsResponse, acceptsEventStream, aggregateProfiles, compileDomainPattern, compileDomainPatterns, compileTrustedHosts, createProfileAggregator, decideSend, http_default as default, defineHttpConfig, httpConfigJsonSchema, httpConfigSchema, isDomainAllowed, isFailedProfile, isWebsocketContext, normalizePrefix, openSseStream, parseProxyOrigin, proxyMountProblems, readBackpressureOptions, resolveUiDelivery, schemaMismatchOf, sqlShape, toValidationFields };
|
|
@@ -3,7 +3,7 @@ import { Command } from "nodefony";
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import fsp from "node:fs/promises";
|
|
5
5
|
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
-
|
|
6
|
+
// nodefony/command/assetsPublishCommand.ts
|
|
7
7
|
const options = {
|
|
8
8
|
helpGroup: "FRONT ET RÉSEAU",
|
|
9
9
|
showBanner: false,
|
|
@@ -35,7 +35,10 @@ async function countFiles(dir) {
|
|
|
35
35
|
*/
|
|
36
36
|
var AssetsPublish = class extends Command {
|
|
37
37
|
constructor(cli) {
|
|
38
|
-
super("assets:publish", "rassemble les fichiers statiques pour un CDN", cli,
|
|
38
|
+
super("assets:publish", "rassemble les fichiers statiques pour un CDN", cli, {
|
|
39
|
+
...options,
|
|
40
|
+
examples: ["nodefony assets:publish", "nodefony assets:publish --out dist-assets --clean"]
|
|
41
|
+
});
|
|
39
42
|
this.addOption("-o, --out <dir>", "output directory (default ./dist-assets)");
|
|
40
43
|
this.addOption("--clean", "remove the output directory first");
|
|
41
44
|
this.addOption("--json", "print a machine-readable summary");
|
|
@@ -96,5 +99,4 @@ var AssetsPublish = class extends Command {
|
|
|
96
99
|
return sources;
|
|
97
100
|
}
|
|
98
101
|
};
|
|
99
|
-
//#endregion
|
|
100
102
|
export { AssetsPublish as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "nodefony";
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
|
-
|
|
3
|
+
// nodefony/command/certificatesCommand.ts
|
|
4
4
|
const options = {
|
|
5
5
|
helpGroup: "FRONT ET RÉSEAU",
|
|
6
6
|
showBanner: false,
|
|
@@ -16,7 +16,10 @@ const options = {
|
|
|
16
16
|
*/
|
|
17
17
|
var Certificates = class extends Command {
|
|
18
18
|
constructor(cli) {
|
|
19
|
-
super("http:certificates", "engendre ou inspecte le certificat TLS de dev", cli,
|
|
19
|
+
super("http:certificates", "engendre ou inspecte le certificat TLS de dev", cli, {
|
|
20
|
+
...options,
|
|
21
|
+
examples: ["nodefony http:certificates", "nodefony http:certificates --force"]
|
|
22
|
+
});
|
|
20
23
|
this.addOption("-f, --force", "regenerate even if a valid certificate already exists");
|
|
21
24
|
this.addOption("-j, --json", "output the certificate summary as JSON");
|
|
22
25
|
}
|
|
@@ -42,5 +45,4 @@ Requête vérifiée (NE PAS utiliser -k / rejectUnauthorized:false) :
|
|
|
42
45
|
return this;
|
|
43
46
|
}
|
|
44
47
|
};
|
|
45
|
-
//#endregion
|
|
46
48
|
export { Certificates as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "nodefony";
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
|
-
|
|
3
|
+
// nodefony/command/networkCommand.ts
|
|
4
4
|
const options = {
|
|
5
5
|
helpGroup: "FRONT ET RÉSEAU",
|
|
6
6
|
showBanner: false,
|
|
@@ -8,7 +8,10 @@ const options = {
|
|
|
8
8
|
};
|
|
9
9
|
var Network = class extends Command {
|
|
10
10
|
constructor(cli) {
|
|
11
|
-
super("http:network", "les interfaces réseau et adresses de cette machine", cli,
|
|
11
|
+
super("http:network", "les interfaces réseau et adresses de cette machine", cli, {
|
|
12
|
+
...options,
|
|
13
|
+
examples: ["nodefony http:network", "nodefony http:network eth0 --json"]
|
|
14
|
+
});
|
|
12
15
|
this.addArgument("[interface]", "Selection interface example eth0 ");
|
|
13
16
|
this.addOption("-j, --json", "get json");
|
|
14
17
|
}
|
|
@@ -23,5 +26,4 @@ var Network = class extends Command {
|
|
|
23
26
|
return this;
|
|
24
27
|
}
|
|
25
28
|
};
|
|
26
|
-
//#endregion
|
|
27
29
|
export { Network as default };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { resolveTrustedHostNames } from "../src/context/domainMatcher.js";
|
|
2
|
+
import { asServersConfig, configuredServerPort } from "../src/servers/kernelServers.js";
|
|
2
3
|
import { defaultIntrospection, generateHaproxyConfig, generateNginxConfig } from "../src/proxy/generateProxyConfig.js";
|
|
3
4
|
import { planAssetPublish } from "../src/assets/collectAssets.js";
|
|
4
5
|
import { Command } from "nodefony";
|
|
5
6
|
import fsp from "node:fs/promises";
|
|
6
7
|
import { isAbsolute, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
+
// nodefony/command/proxyGenerateCommand.ts
|
|
8
9
|
const options = {
|
|
9
10
|
helpGroup: "FRONT ET RÉSEAU",
|
|
10
11
|
showBanner: false,
|
|
@@ -18,7 +19,10 @@ const options = {
|
|
|
18
19
|
*/
|
|
19
20
|
var ProxyGenerate = class extends Command {
|
|
20
21
|
constructor(cli) {
|
|
21
|
-
super("proxy:generate", "engendre une configuration nginx ou haproxy", cli,
|
|
22
|
+
super("proxy:generate", "engendre une configuration nginx ou haproxy", cli, {
|
|
23
|
+
...options,
|
|
24
|
+
examples: ["nodefony proxy:generate nginx", "nodefony proxy:generate haproxy --out haproxy.cfg"]
|
|
25
|
+
});
|
|
22
26
|
this.addArgument("<target>", "nginx | haproxy");
|
|
23
27
|
this.addOption("-o, --out <file>", "write to file instead of stdout");
|
|
24
28
|
this.addOption("-b, --backend <host>", "backend host the proxy connects to (default 127.0.0.1)");
|
|
@@ -67,12 +71,14 @@ var ProxyGenerate = class extends Command {
|
|
|
67
71
|
backendHost: opts.backend ?? "127.0.0.1",
|
|
68
72
|
httpPort: Number(servers?.http?.port) || defaultIntrospection.httpPort,
|
|
69
73
|
httpsPort: Number(servers?.https?.port) || defaultIntrospection.httpsPort,
|
|
74
|
+
h2cPort: configuredServerPort(asServersConfig(this.kernel?.options.servers), "h2c") ?? null,
|
|
70
75
|
staticRoots,
|
|
71
76
|
mounts,
|
|
72
77
|
listen: opts.listen ? Number(opts.listen) : defaultIntrospection.listen,
|
|
73
78
|
reencrypt: Boolean(opts.reencrypt),
|
|
74
79
|
maxBodyBytes: Number(httpOpts.maxBodySize) || 0,
|
|
75
80
|
keepaliveIntervalMs: Number(httpOpts.websocket?.keepaliveInterval) || 0,
|
|
81
|
+
readinessPath: httpOpts.health?.enabled === false ? null : httpOpts.health?.readinessPath ?? "/readyz",
|
|
76
82
|
tls: opts.tlsCert && opts.tlsKey ? {
|
|
77
83
|
certPath: opts.tlsCert,
|
|
78
84
|
keyPath: opts.tlsKey,
|
|
@@ -81,5 +87,4 @@ var ProxyGenerate = class extends Command {
|
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
};
|
|
84
|
-
//#endregion
|
|
85
90
|
export { ProxyGenerate as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizePrefix, proxyMountProblems } from "../src/proxy/rules.js";
|
|
2
2
|
import { overlayField } from "nodefony";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
4
|
+
// nodefony/config/config.ts
|
|
5
5
|
/**
|
|
6
6
|
* @nodefony/http — CONFIGURATION DU MODULE (schéma Zod = source unique).
|
|
7
7
|
*
|
|
@@ -393,5 +393,4 @@ const httpOverlaySchema = z.strictObject({
|
|
|
393
393
|
* valides par construction ; passés au `super(..., config)` du Module class.
|
|
394
394
|
*/
|
|
395
395
|
const config = httpConfigSchema.parse({});
|
|
396
|
-
//#endregion
|
|
397
396
|
export { config as default, httpConfigSchema, httpOverlaySchema };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { httpConfigSchema } from "./config.js";
|
|
2
2
|
import { parseModuleConfig } from "nodefony";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
4
|
+
// nodefony/config/defineModuleConfig.ts
|
|
5
5
|
/**
|
|
6
6
|
* Injecte les défauts dérivés du kernel APRÈS le parse (le schéma reste pur).
|
|
7
7
|
*
|
|
@@ -89,5 +89,4 @@ function defineHttpConfig(config = {}, kernel) {
|
|
|
89
89
|
function httpConfigJsonSchema() {
|
|
90
90
|
return z.toJSONSchema(httpConfigSchema);
|
|
91
91
|
}
|
|
92
|
-
//#endregion
|
|
93
92
|
export { defineHttpConfig, httpConfigJsonSchema };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SESSION_FACETS, SESSION_FILTERS, SESSION_STATS_FILTERS } from "../src/session/storage/sessionFilters.js";
|
|
2
2
|
import { RATE_LIMIT_FILTERS } from "../src/rateLimit/rateLimitFilters.js";
|
|
3
3
|
import { parseFilters, parsePageQuery } from "nodefony";
|
|
4
|
-
|
|
4
|
+
// nodefony/service/HttpAdminApi.ts
|
|
5
5
|
/** Nom du service HttpKernel dans le container (source unique). */
|
|
6
6
|
const HTTP_KERNEL_SERVICE = "HttpKernel";
|
|
7
7
|
/**
|
|
@@ -62,6 +62,7 @@ function createHttpAdminApi(module) {
|
|
|
62
62
|
const serverServices = [
|
|
63
63
|
"server-http",
|
|
64
64
|
"server-https",
|
|
65
|
+
"server-h2c",
|
|
65
66
|
"server-websocket",
|
|
66
67
|
"server-websocket-secure",
|
|
67
68
|
"server-static"
|
|
@@ -371,5 +372,4 @@ function createHttpAdminApi(module) {
|
|
|
371
372
|
adminEndpoints: () => endpoints
|
|
372
373
|
};
|
|
373
374
|
}
|
|
374
|
-
//#endregion
|
|
375
375
|
export { createHttpAdminApi };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// nodefony/service/ProfilerAdminApi.ts
|
|
2
2
|
/**
|
|
3
3
|
* Producteur `IAdminApi` du **profiler** — exposé sous `/nodefony/profiler/api/*`.
|
|
4
4
|
*
|
|
@@ -70,5 +70,4 @@ function createProfilerAdminApi(profiler) {
|
|
|
70
70
|
adminEndpoints: () => endpoints
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
-
//#endregion
|
|
74
73
|
export { createProfilerAdminApi, createProfilerAdminApi as default };
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
1
2
|
import { performance } from "node:perf_hooks";
|
|
2
|
-
|
|
3
|
+
// nodefony/service/audit-logger.ts
|
|
4
|
+
const statusOf = (context) => {
|
|
5
|
+
return context.response?.statusCode ?? null;
|
|
6
|
+
};
|
|
7
|
+
const headersOf = (context) => {
|
|
8
|
+
return context.request?.headers ?? {};
|
|
9
|
+
};
|
|
3
10
|
/**
|
|
4
11
|
* Durée totale de la requête, de la première phase (`startMs`) à maintenant.
|
|
5
12
|
* `null` si le chronométrage est coupé (aucune phase enregistrée). Partagée
|
|
@@ -65,20 +72,20 @@ var JsonAuditLogger = class {
|
|
|
65
72
|
const ctx = context;
|
|
66
73
|
if (!this.nominalEnabled) {
|
|
67
74
|
if (error ?? ctx.error) return true;
|
|
68
|
-
const status = ctx
|
|
75
|
+
const status = statusOf(ctx);
|
|
69
76
|
return status !== null && status >= 400;
|
|
70
77
|
}
|
|
71
78
|
if (this.sampleRate <= 1) return true;
|
|
72
79
|
if (error ?? ctx.error) return true;
|
|
73
|
-
const status = ctx
|
|
80
|
+
const status = statusOf(ctx);
|
|
74
81
|
if (status !== null && status >= 400) return true;
|
|
75
82
|
this.sampleCounter = (this.sampleCounter + 1) % this.sampleRate;
|
|
76
83
|
return this.sampleCounter === 0;
|
|
77
84
|
}
|
|
78
85
|
renderHttp(context, error) {
|
|
79
86
|
const ctx = context;
|
|
80
|
-
const status = ctx
|
|
81
|
-
const headers = ctx
|
|
87
|
+
const status = statusOf(ctx);
|
|
88
|
+
const headers = headersOf(ctx);
|
|
82
89
|
const err = error ?? ctx.error ?? null;
|
|
83
90
|
const entry = {
|
|
84
91
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -91,8 +98,8 @@ var JsonAuditLogger = class {
|
|
|
91
98
|
status,
|
|
92
99
|
durationMs: computeDurationMs(ctx.phases),
|
|
93
100
|
remoteAddress: ctx.remoteAddress ?? null,
|
|
94
|
-
host: ctx.getHost
|
|
95
|
-
userAgent: ctx.getUserAgent
|
|
101
|
+
host: ctx.getHost() ?? null,
|
|
102
|
+
userAgent: ctx.getUserAgent() ?? null,
|
|
96
103
|
hasAuthorization: Boolean(headers["authorization"]),
|
|
97
104
|
hasCookie: Boolean(headers["cookie"]),
|
|
98
105
|
phases: ctx.phases.length ? ctx.phases.map((p) => ({
|
|
@@ -109,8 +116,9 @@ var JsonAuditLogger = class {
|
|
|
109
116
|
}
|
|
110
117
|
renderWebsocket(context, error, acceptedProtocol) {
|
|
111
118
|
const ctx = context;
|
|
112
|
-
const
|
|
113
|
-
const
|
|
119
|
+
const refused = error ? handshakeRefusalStatus(ctx, error) : null;
|
|
120
|
+
const status = refused ?? statusOf(ctx);
|
|
121
|
+
const headers = headersOf(ctx);
|
|
114
122
|
const entry = {
|
|
115
123
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
116
124
|
requestId: ctx.requestId,
|
|
@@ -122,8 +130,8 @@ var JsonAuditLogger = class {
|
|
|
122
130
|
status,
|
|
123
131
|
durationMs: computeDurationMs(ctx.phases),
|
|
124
132
|
remoteAddress: ctx.remoteAddress ?? null,
|
|
125
|
-
host: ctx.getHost
|
|
126
|
-
userAgent: ctx.getUserAgent
|
|
133
|
+
host: ctx.getHost() ?? null,
|
|
134
|
+
userAgent: ctx.getUserAgent() ?? null,
|
|
127
135
|
hasAuthorization: Boolean(headers["authorization"]),
|
|
128
136
|
hasCookie: Boolean(headers["cookie"]),
|
|
129
137
|
phases: ctx.phases.length ? ctx.phases.map((p) => ({
|
|
@@ -135,7 +143,7 @@ var JsonAuditLogger = class {
|
|
|
135
143
|
};
|
|
136
144
|
return {
|
|
137
145
|
text: JSON.stringify(entry),
|
|
138
|
-
severity: error ? "ERROR" : "INFO",
|
|
146
|
+
severity: refused !== null ? severityFromStatus(refused) : error ? "ERROR" : "INFO",
|
|
139
147
|
msgid: "audit"
|
|
140
148
|
};
|
|
141
149
|
}
|
|
@@ -157,5 +165,4 @@ var JsonAuditLogger = class {
|
|
|
157
165
|
return entry;
|
|
158
166
|
}
|
|
159
167
|
};
|
|
160
|
-
//#endregion
|
|
161
168
|
export { computeDurationMs, JsonAuditLogger as default, severityFromStatus };
|
|
@@ -7,7 +7,7 @@ import path, { resolve } from "node:path";
|
|
|
7
7
|
import tls from "node:tls";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { execFile } from "node:child_process";
|
|
10
|
-
|
|
10
|
+
// nodefony/service/certificates.ts
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
const generateKeyPairAsync = promisify(generateKeyPair);
|
|
13
13
|
/** Compare deux PEM sans dépendre des fins de ligne ni des espaces. */
|
|
@@ -66,12 +66,16 @@ function splitPemCertificates(pem) {
|
|
|
66
66
|
*
|
|
67
67
|
* @param file - chemin du fichier, absolu ou relatif à `root`
|
|
68
68
|
* @param root - racine de l'application
|
|
69
|
+
* Chaque refus dit le problème, d'où vient le chemin quand on le sait, puis le
|
|
70
|
+
* geste qui répare ({@link caFileRemedy}).
|
|
71
|
+
*
|
|
69
72
|
* @param store - accès aux ancres ; `node:tls` par défaut
|
|
73
|
+
* @param origin - phrase qui dit d'où vient le chemin (` Valeur venue de …`), insérée dans un refus
|
|
70
74
|
* @returns le bilan de l'ajout
|
|
71
75
|
* @throws BootConfigurationError - fichier illisible, sans certificat, ou Node
|
|
72
76
|
* incapable d'élargir ses ancres en cours d'exécution
|
|
73
77
|
*/
|
|
74
|
-
async function trustCaFile(file, root, store = tls) {
|
|
78
|
+
async function trustCaFile(file, root, store = tls, origin = "") {
|
|
75
79
|
const absolute = path.resolve(root, file);
|
|
76
80
|
let content;
|
|
77
81
|
try {
|
|
@@ -79,10 +83,10 @@ async function trustCaFile(file, root, store = tls) {
|
|
|
79
83
|
} catch (error) {
|
|
80
84
|
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
81
85
|
const reason = typeof code === "string" ? code : error instanceof Error ? error.message : String(error);
|
|
82
|
-
throw new BootConfigurationError(
|
|
86
|
+
throw new BootConfigurationError(`${unreadableProblem(absolute, reason)}${origin} ${caFileRemedy(reason)}`);
|
|
83
87
|
}
|
|
84
88
|
const certificates = splitPemCertificates(content);
|
|
85
|
-
if (certificates.length === 0) throw new BootConfigurationError(`http.certificates.extraCaFiles : « ${absolute} » ne contient aucun certificat PEM (-----BEGIN CERTIFICATE-----)
|
|
89
|
+
if (certificates.length === 0) throw new BootConfigurationError(`http.certificates.extraCaFiles : « ${absolute} » ne contient aucun certificat PEM (-----BEGIN CERTIFICATE-----).${origin} ${caFileRemedy("NOPEM")}`);
|
|
86
90
|
let added = 0;
|
|
87
91
|
let present = 0;
|
|
88
92
|
for (const pem of certificates) {
|
|
@@ -97,27 +101,107 @@ async function trustCaFile(file, root, store = tls) {
|
|
|
97
101
|
present
|
|
98
102
|
};
|
|
99
103
|
}
|
|
104
|
+
/** Première phrase d'un refus de lecture : le fichier et la cause en un mot. */
|
|
105
|
+
function unreadableProblem(absolute, reason) {
|
|
106
|
+
return `http.certificates.extraCaFiles : « ${absolute} » est illisible (${reason}).`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Le geste qui répare une autorité déclarée qu'on ne peut pas reconnaître.
|
|
110
|
+
*
|
|
111
|
+
* Un refus au démarrage qui ne dit que la cause envoie chercher ; celui-ci dit
|
|
112
|
+
* quoi faire. Pure : éprouvée cause par cause, y compris celles qu'une machine
|
|
113
|
+
* de test ne sait pas produire (un droit refusé ne l'est pas à `root`, ni
|
|
114
|
+
* sous Windows).
|
|
115
|
+
*
|
|
116
|
+
* @param cause - code système (`ENOENT`, `EACCES`, `EPERM`, `EISDIR`),
|
|
117
|
+
* `NOPEM` pour un fichier lu sans certificat, ou tout autre motif
|
|
118
|
+
* @returns la phrase du geste
|
|
119
|
+
*/
|
|
120
|
+
function caFileRemedy(cause) {
|
|
121
|
+
switch (cause) {
|
|
122
|
+
case "ENOENT": return "Poser le fichier à ce chemin (absolu, ou relatif à la racine de l'application), ou retirer la déclaration.";
|
|
123
|
+
case "EACCES":
|
|
124
|
+
case "EPERM": return "Le compte qui lance l'application n'a pas le droit de le lire : lui donner ce droit (sous Kubernetes, monter le ConfigMap ou le Secret lisible par l'utilisateur du pod).";
|
|
125
|
+
case "EISDIR": return "C'est un dossier : désigner le fichier PEM qu'il contient.";
|
|
126
|
+
case "NOPEM": return "Le fichier doit porter le CERTIFICAT de l'autorité (pas sa clé privée), au format PEM ; un fichier DER se convertit par `openssl x509 -inform der -in autorite.der -out autorite.pem`.";
|
|
127
|
+
default: return "Vérifier le chemin et les droits du fichier, ou retirer la déclaration.";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Dossier où Nodefony range les certificats qu'il fabrique en développement,
|
|
132
|
+
* relatif au répertoire courant.
|
|
133
|
+
*
|
|
134
|
+
* @returns le chemin absolu de `nodefony/config/certificates`
|
|
135
|
+
*/
|
|
136
|
+
function certificatesDirectory() {
|
|
137
|
+
return resolve(".", "nodefony", "config", "certificates");
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Chemin de l'autorité de DÉVELOPPEMENT : le certificat racine que Nodefony
|
|
141
|
+
* fabrique lui-même pour signer le certificat du serveur local. Généré, jamais
|
|
142
|
+
* versionné — une seule convention, que le service et la confiance sortante lisent.
|
|
143
|
+
*
|
|
144
|
+
* @param directory - dossier des certificats ; {@link certificatesDirectory} par défaut
|
|
145
|
+
* @returns le chemin absolu du fichier PEM de l'autorité
|
|
146
|
+
*/
|
|
147
|
+
function developmentAuthorityPath(directory = certificatesDirectory()) {
|
|
148
|
+
return resolve(directory, "ca", "nodefony-root-ca.crt.pem");
|
|
149
|
+
}
|
|
150
|
+
/** Vrai si le fichier n'existe pas (et seulement dans ce cas). */
|
|
151
|
+
async function isMissing(file) {
|
|
152
|
+
try {
|
|
153
|
+
await fsp.stat(file);
|
|
154
|
+
return false;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
100
159
|
/**
|
|
101
160
|
* Ajoute aux ancres TLS du processus les autorités déclarées
|
|
102
161
|
* (`certificates.extraCaFiles`), au plus tôt : appelé à l'enregistrement du
|
|
103
162
|
* module, avant tout service — toute requête sortante, commande console
|
|
104
163
|
* comprise, les trouve en place.
|
|
105
164
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
165
|
+
* L'autorité de DÉVELOPPEMENT déclarée mais pas encore engendrée est ignorée en
|
|
166
|
+
* développement, et le journal le dit : elle n'a signé aucun certificat, il n'y
|
|
167
|
+
* a donc rien à lui faire confiance, et le serveur de développement la pose
|
|
168
|
+
* lui-même quand il la fabrique ({@link Certificate.generateServerCertificates}).
|
|
169
|
+
* Rien n'est fabriqué ici : une commande console n'écrit pas de certificat.
|
|
170
|
+
* Une autre autorité absente, ou n'importe laquelle hors développement, reste
|
|
171
|
+
* un refus — qui nomme la variable d'origine et le geste qui répare
|
|
172
|
+
* ({@link caFileRemedy}).
|
|
108
173
|
*
|
|
109
174
|
* @param files - `certificates.extraCaFiles`
|
|
110
175
|
* @param root - racine de l'application
|
|
111
176
|
* @param log - journal du module
|
|
177
|
+
* @param context - décor (environnement, autorité de dev, ancres)
|
|
112
178
|
* @throws BootConfigurationError - cf {@link trustCaFile}
|
|
113
179
|
*/
|
|
114
|
-
async function trustOutboundAuthorities(files, root, log) {
|
|
180
|
+
async function trustOutboundAuthorities(files, root, log, context = {}) {
|
|
181
|
+
const developmentAuthority = context.developmentAuthority ?? developmentAuthorityPath();
|
|
115
182
|
for (const file of files) {
|
|
116
|
-
const
|
|
183
|
+
const absolute = path.resolve(root, file);
|
|
184
|
+
const isDevelopmentAuthority = absolute === developmentAuthority;
|
|
185
|
+
if (isDevelopmentAuthority && context.development === true && await isMissing(absolute)) {
|
|
186
|
+
log(`autorité de développement ${absolute} pas encore engendrée : ignorée. Elle naît au premier \`npx nodefony development\` (ou \`npx nodefony http:certificates\`), et le serveur de développement lui fait alors confiance de lui-même.`, "INFO");
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const origin = originOf(absolute, root, context.env ?? process.env);
|
|
190
|
+
if (isDevelopmentAuthority && await isMissing(absolute)) throw new BootConfigurationError(`${unreadableProblem(absolute, "ENOENT")}${origin} C'est l'autorité de DÉVELOPPEMENT : Nodefony la fabrique au premier \`npx nodefony development\`, jamais hors développement. Pour essayer la production sur ce poste, l'engendrer une fois (\`npx nodefony http:certificates\`) puis relancer ; sinon retirer la déclaration — une production réelle n'en a pas besoin.`);
|
|
191
|
+
const trust = await trustCaFile(file, root, context.store, origin);
|
|
117
192
|
log(`autorité sortante ${trust.file} : ${trust.added} ajoutée(s), ${trust.present} déjà présente(s)`, "INFO");
|
|
118
193
|
}
|
|
119
194
|
}
|
|
120
195
|
/**
|
|
196
|
+
* Nomme la variable d'où vient le chemin, quand c'est `NF_TLS_CA_FILE` — la
|
|
197
|
+
* déclaration que pose le gabarit d'application. Constaté sur la valeur, jamais
|
|
198
|
+
* supposé : une configuration écrite à la main n'est pas attribuée à la variable.
|
|
199
|
+
*/
|
|
200
|
+
function originOf(absolute, root, env) {
|
|
201
|
+
const declared = env.NF_TLS_CA_FILE;
|
|
202
|
+
return declared && path.resolve(root, declared) === absolute ? " Valeur venue de NF_TLS_CA_FILE (`.env` ou environnement du processus)." : "";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
121
205
|
* Vrai si `host` est une IP littérale (IPv4 `n.n.n.n` ou IPv6 — contient `:`).
|
|
122
206
|
* Une IP doit aller en SAN `iPAddress`, jamais en `dNSName` (RFC 9525).
|
|
123
207
|
*/
|
|
@@ -166,9 +250,9 @@ var Certificate = class Certificate extends Service {
|
|
|
166
250
|
publicKeyPem = "";
|
|
167
251
|
/** CAROOT résolu de mkcert (null tant que non détecté / indisponible). */
|
|
168
252
|
mkcertCaRoot = null;
|
|
169
|
-
path =
|
|
253
|
+
path = certificatesDirectory();
|
|
170
254
|
serverPath = resolve(this.path, "server");
|
|
171
|
-
caPath =
|
|
255
|
+
caPath = developmentAuthorityPath(this.path);
|
|
172
256
|
publicKeyPath = resolve(this.path, "server", "publickey.pem");
|
|
173
257
|
privateKeyPath = resolve(this.path, "server", "privkey.pem");
|
|
174
258
|
certPath = resolve(this.path, "server", "cert.pem");
|
|
@@ -480,7 +564,8 @@ var Certificate = class Certificate extends Service {
|
|
|
480
564
|
return Buffer.from(await fsp.readFile(value, "utf8"));
|
|
481
565
|
}
|
|
482
566
|
createFullChain() {
|
|
483
|
-
|
|
567
|
+
const pem = this.cert?.toString().trim() ?? "";
|
|
568
|
+
return pem === "" ? "" : `${pem}\n`;
|
|
484
569
|
}
|
|
485
570
|
async readCerticates() {
|
|
486
571
|
for (const file of this.files) try {
|
|
@@ -634,5 +719,4 @@ var Certificate = class Certificate extends Service {
|
|
|
634
719
|
}
|
|
635
720
|
}
|
|
636
721
|
};
|
|
637
|
-
|
|
638
|
-
export { addDefaultCaCertificate, Certificate as default, splitPemCertificates, trustCaFile, trustOutboundAuthorities };
|
|
722
|
+
export { addDefaultCaCertificate, caFileRemedy, certificatesDirectory, Certificate as default, developmentAuthorityPath, splitPemCertificates, trustCaFile, trustOutboundAuthorities };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import HttpError from "../src/errors/httpError.js";
|
|
2
2
|
import { toWsCloseCode } from "../src/context/websocket/WebsocketContext.js";
|
|
3
3
|
import { Nodefony, nodefonyError } from "nodefony";
|
|
4
|
-
|
|
4
|
+
// nodefony/service/error-renderer.ts
|
|
5
5
|
/** Une donnée rejetée par la validation n'est pas une requête malformée : 422, pas 400. */
|
|
6
6
|
const VALIDATION_STATUS = 422;
|
|
7
7
|
/**
|
|
@@ -74,8 +74,15 @@ const INTERNAL_ERROR_KEYS = [
|
|
|
74
74
|
* deux tests — un gate qu'on ne peut plus voir mordre.
|
|
75
75
|
*/
|
|
76
76
|
const isProduction = () => Nodefony.getKernel()?.environment === "production";
|
|
77
|
+
/** Une propriété d'une anomalie de forme inconnue, lue sans rien supposer. */
|
|
78
|
+
function readIssueProperty(issue, key) {
|
|
79
|
+
return typeof issue === "object" && issue !== null ? Reflect.get(issue, key) : void 0;
|
|
80
|
+
}
|
|
81
|
+
/** Erreurs de validation reconnues : Zod, et celle des routes à schéma (cœur). */
|
|
82
|
+
const VALIDATION_ERROR_NAMES = /* @__PURE__ */ new Set(["ZodError", "SchemaValidationError"]);
|
|
77
83
|
/**
|
|
78
|
-
* Reconnaît une erreur de validation
|
|
84
|
+
* Reconnaît une erreur de validation — `ZodError`, ou `SchemaValidationError`
|
|
85
|
+
* d'une route à schéma — et en extrait les champs fautifs.
|
|
79
86
|
*
|
|
80
87
|
* Reconnaissance **structurelle** (`name` + `issues`), pas `instanceof ZodError` :
|
|
81
88
|
* une application peut embarquer sa propre copie de zod (résolutions npm multiples),
|
|
@@ -88,13 +95,21 @@ const isProduction = () => Nodefony.getKernel()?.environment === "production";
|
|
|
88
95
|
* @returns les champs fautifs, ou `null` si ce n'est pas une erreur de validation.
|
|
89
96
|
*/
|
|
90
97
|
function toValidationFields(error) {
|
|
91
|
-
const
|
|
92
|
-
if (error.name
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
message
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
const issues = Reflect.get(error, "issues");
|
|
99
|
+
if (!VALIDATION_ERROR_NAMES.has(error.name) || !Array.isArray(issues)) return null;
|
|
100
|
+
return issues.map((issue) => {
|
|
101
|
+
const path = readIssueProperty(issue, "path");
|
|
102
|
+
const message = readIssueProperty(issue, "message");
|
|
103
|
+
const code = readIssueProperty(issue, "code");
|
|
104
|
+
const part = readIssueProperty(issue, "in");
|
|
105
|
+
const field = {
|
|
106
|
+
field: Array.isArray(path) ? path.map(String).join(".") : "",
|
|
107
|
+
message: typeof message === "string" ? message : "invalide",
|
|
108
|
+
rule: typeof code === "string" ? code : void 0
|
|
109
|
+
};
|
|
110
|
+
if (typeof part === "string") field.in = part;
|
|
111
|
+
return field;
|
|
112
|
+
});
|
|
98
113
|
}
|
|
99
114
|
/**
|
|
100
115
|
* Construit l'erreur **422** exposée au client à partir des champs fautifs.
|
|
@@ -109,7 +124,10 @@ function toValidationFields(error) {
|
|
|
109
124
|
* reçoit `error.fields` et sait QUEL champ corriger — pas seulement que « ça a échoué ».
|
|
110
125
|
*/
|
|
111
126
|
function toValidationError(error, fields) {
|
|
112
|
-
const summary = fields.map((f) =>
|
|
127
|
+
const summary = fields.map((f) => {
|
|
128
|
+
const where = f.in && f.field ? `${f.in}.${f.field}` : f.in || f.field;
|
|
129
|
+
return where ? `${where}: ${f.message}` : f.message;
|
|
130
|
+
}).join(" · ");
|
|
113
131
|
const validation = new nodefonyError(`Validation failed — ${summary}`, VALIDATION_STATUS);
|
|
114
132
|
if (error.stack !== void 0) validation.stack = error.stack;
|
|
115
133
|
validation.fields = fields;
|
|
@@ -429,5 +447,4 @@ var DefaultErrorRenderer = class {
|
|
|
429
447
|
return code;
|
|
430
448
|
}
|
|
431
449
|
};
|
|
432
|
-
|
|
433
|
-
export { DefaultErrorRenderer as default, malformedIdentifierOf, schemaMismatchOf };
|
|
450
|
+
export { DefaultErrorRenderer as default, malformedIdentifierOf, schemaMismatchOf, toValidationFields };
|