@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.
Files changed (124) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  2. package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
  3. package/dist/index.js +12 -10
  4. package/dist/nodefony/command/assetsPublishCommand.js +5 -3
  5. package/dist/nodefony/command/certificatesCommand.js +5 -3
  6. package/dist/nodefony/command/networkCommand.js +5 -3
  7. package/dist/nodefony/command/proxyGenerateCommand.js +8 -3
  8. package/dist/nodefony/config/config.js +1 -2
  9. package/dist/nodefony/config/defineModuleConfig.js +1 -2
  10. package/dist/nodefony/service/HttpAdminApi.js +2 -2
  11. package/dist/nodefony/service/ProfilerAdminApi.js +1 -2
  12. package/dist/nodefony/service/audit-logger.js +20 -13
  13. package/dist/nodefony/service/certificates.js +97 -13
  14. package/dist/nodefony/service/error-renderer.js +29 -12
  15. package/dist/nodefony/service/http-kernel.js +118 -43
  16. package/dist/nodefony/service/pretty-request-logger.js +5 -4
  17. package/dist/nodefony/service/request-logger.js +24 -16
  18. package/dist/nodefony/service/reverse-proxy.js +3 -3
  19. package/dist/nodefony/service/servers/clientError.js +22 -3
  20. package/dist/nodefony/service/servers/server-h2c.js +126 -0
  21. package/dist/nodefony/service/servers/server-http.js +5 -8
  22. package/dist/nodefony/service/servers/server-https.js +12 -24
  23. package/dist/nodefony/service/servers/server-static.js +3 -3
  24. package/dist/nodefony/service/servers/server-websocket-secure.js +10 -18
  25. package/dist/nodefony/service/servers/server-websocket.js +10 -18
  26. package/dist/nodefony/service/servers/serverShutdown.js +78 -18
  27. package/dist/nodefony/service/servers/wsHeartbeat.js +1 -2
  28. package/dist/nodefony/service/sessions/sessions-service.js +7 -14
  29. package/dist/nodefony/service/trace.js +1 -2
  30. package/dist/nodefony/service/upload/upload-service.js +5 -8
  31. package/dist/nodefony/service/x509.js +1 -2
  32. package/dist/nodefony/src/assets/collectAssets.js +1 -2
  33. package/dist/nodefony/src/assets/prebuiltUi.js +1 -2
  34. package/dist/nodefony/src/context/Context.js +23 -31
  35. package/dist/nodefony/src/context/domainMatcher.js +1 -2
  36. package/dist/nodefony/src/context/forwarded.js +1 -2
  37. package/dist/nodefony/src/context/http/HttpContext.js +7 -6
  38. package/dist/nodefony/src/context/http/Request.js +33 -29
  39. package/dist/nodefony/src/context/http/Response.js +23 -6
  40. package/dist/nodefony/src/context/http/SseStream.js +1 -2
  41. package/dist/nodefony/src/context/http/parser.js +36 -10
  42. package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
  43. package/dist/nodefony/src/context/http2/Request.js +4 -8
  44. package/dist/nodefony/src/context/http2/Response.js +22 -9
  45. package/dist/nodefony/src/context/metaData.js +1 -2
  46. package/dist/nodefony/src/context/requestId.js +1 -2
  47. package/dist/nodefony/src/context/responseEnded.js +1 -2
  48. package/dist/nodefony/src/context/transport.js +45 -0
  49. package/dist/nodefony/src/context/trustProxy.js +1 -2
  50. package/dist/nodefony/src/context/websocket/Response.js +33 -6
  51. package/dist/nodefony/src/context/websocket/WebsocketContext.js +92 -17
  52. package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
  53. package/dist/nodefony/src/context/websocket/websocketUrl.js +1 -2
  54. package/dist/nodefony/src/context/websocket/wsBackpressure.js +1 -2
  55. package/dist/nodefony/src/context/websocket/wsCloseReason.js +1 -2
  56. package/dist/nodefony/src/context/websocket/wsLogContent.js +1 -2
  57. package/dist/nodefony/src/cookies/cookie.js +3 -7
  58. package/dist/nodefony/src/errors/httpError.js +1 -2
  59. package/dist/nodefony/src/errors/thrownMessage.js +1 -2
  60. package/dist/nodefony/src/profiler/FrameProfile.js +1 -2
  61. package/dist/nodefony/src/profiler/Profiler.js +102 -20
  62. package/dist/nodefony/src/profiler/aggregate.js +422 -0
  63. package/dist/nodefony/src/proxy/forward.js +29 -5
  64. package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
  65. package/dist/nodefony/src/proxy/rules.js +12 -6
  66. package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +1 -2
  67. package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +1 -2
  68. package/dist/nodefony/src/rateLimit/rateLimitFilters.js +1 -2
  69. package/dist/nodefony/src/servers/http2Limits.js +50 -0
  70. package/dist/nodefony/src/servers/kernelServers.js +12 -3
  71. package/dist/nodefony/src/servers/portBinder.js +41 -10
  72. package/dist/nodefony/src/servers/upgradeDispatch.js +86 -7
  73. package/dist/nodefony/src/session/session.js +2 -3
  74. package/dist/nodefony/src/session/sessionStoreFailure.js +1 -2
  75. package/dist/nodefony/src/session/storage/MemorySessionStorage.js +1 -2
  76. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +1 -2
  77. package/dist/nodefony/src/session/storage/sessionFilters.js +1 -2
  78. package/dist/nodefony/src/session/storage/sessionSort.js +1 -2
  79. package/dist/types/index.d.ts +7 -3
  80. package/dist/types/nodefony/interfaces/IContext.d.ts +63 -4
  81. package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
  82. package/dist/types/nodefony/interfaces/IRouting.d.ts +10 -0
  83. package/dist/types/nodefony/interfaces/index.d.ts +2 -2
  84. package/dist/types/nodefony/service/certificates.d.ts +55 -4
  85. package/dist/types/nodefony/service/error-renderer.d.ts +31 -0
  86. package/dist/types/nodefony/service/http-kernel.d.ts +46 -10
  87. package/dist/types/nodefony/service/servers/clientError.d.ts +16 -0
  88. package/dist/types/nodefony/service/servers/server-h2c.d.ts +51 -0
  89. package/dist/types/nodefony/service/servers/server-http.d.ts +3 -3
  90. package/dist/types/nodefony/service/servers/server-https.d.ts +4 -4
  91. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +1 -3
  92. package/dist/types/nodefony/service/servers/server-websocket.d.ts +1 -3
  93. package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
  94. package/dist/types/nodefony/src/context/Context.d.ts +13 -4
  95. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +1 -1
  96. package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
  97. package/dist/types/nodefony/src/context/http/Response.d.ts +11 -0
  98. package/dist/types/nodefony/src/context/http/parser.d.ts +18 -2
  99. package/dist/types/nodefony/src/context/http2/Request.d.ts +1 -1
  100. package/dist/types/nodefony/src/context/http2/Response.d.ts +15 -5
  101. package/dist/types/nodefony/src/context/transport.d.ts +53 -0
  102. package/dist/types/nodefony/src/context/websocket/Response.d.ts +18 -2
  103. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +57 -7
  104. package/dist/types/nodefony/src/context/websocket/isWebsocketContext.d.ts +15 -0
  105. package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +13 -12
  106. package/dist/types/nodefony/src/profiler/Profiler.d.ts +68 -15
  107. package/dist/types/nodefony/src/profiler/aggregate.d.ts +215 -0
  108. package/dist/types/nodefony/src/proxy/forward.d.ts +14 -2
  109. package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
  110. package/dist/types/nodefony/src/proxy/rules.d.ts +3 -0
  111. package/dist/types/nodefony/src/servers/http2Limits.d.ts +37 -0
  112. package/dist/types/nodefony/src/servers/kernelServers.d.ts +10 -1
  113. package/dist/types/nodefony/src/servers/portBinder.d.ts +29 -5
  114. package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +33 -3
  115. package/docs/cookies.md +85 -78
  116. package/docs/observabilite.md +80 -80
  117. package/docs/rate-limit.md +83 -83
  118. package/docs/reverse-proxy.md +54 -54
  119. package/docs/servers.md +338 -224
  120. package/docs/session.md +179 -179
  121. package/docs/sse.md +24 -20
  122. package/docs/upload.md +120 -117
  123. package/package.json +8 -9
  124. package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js +0 -6
@@ -1,9 +1,8 @@
1
- //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorate.js
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
- //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
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 __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js";
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 { Kernel, Module, services } from "nodefony";
46
- //#region index.ts
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
- ]), __decorateMetadata("design:paramtypes", [typeof Kernel === "undefined" ? Object : Kernel])], Http);
116
+ ])], Http);
114
117
  var http_default = Http;
115
- //#endregion
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
- //#region nodefony/command/assetsPublishCommand.ts
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, options);
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
- //#region nodefony/command/certificatesCommand.ts
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, options);
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
- //#region nodefony/command/networkCommand.ts
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, options);
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
- //#region nodefony/command/proxyGenerateCommand.ts
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, options);
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
- //#region nodefony/config/config.ts
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
- //#region nodefony/config/defineModuleConfig.ts
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
- //#region nodefony/service/HttpAdminApi.ts
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
- //#region nodefony/service/ProfilerAdminApi.ts
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
- //#region nodefony/service/audit-logger.ts
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.response?.statusCode ?? null;
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.response?.statusCode ?? null;
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.response?.statusCode ?? null;
81
- const headers = ctx.request?.headers ?? {};
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?.() ?? null,
95
- userAgent: ctx.getUserAgent?.() ?? null,
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 status = ctx.response?.statusCode ?? null;
113
- const headers = ctx.request?.headers ?? {};
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?.() ?? null,
126
- userAgent: ctx.getUserAgent?.() ?? null,
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
- //#region nodefony/service/certificates.ts
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(`http.certificates.extraCaFiles : « ${absolute} » est illisible (${reason}).`);
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
- * La CA de DÉVELOPPEMENT n'y figure pas : le service de certificats la pose
107
- * lui-même ({@link Certificate.generateServerCertificates}).
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 trust = await trustCaFile(file, root);
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 = resolve(".", "nodefony", "config", "certificates");
253
+ path = certificatesDirectory();
170
254
  serverPath = resolve(this.path, "server");
171
- caPath = resolve(this.path, "ca", "nodefony-root-ca.crt.pem");
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
- return this.cert?.toString().trim() ?? "";
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
- //#endregion
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
- //#region nodefony/service/error-renderer.ts
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 Zod et en extrait les champs fautifs.
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 candidate = error;
92
- if (error.name !== "ZodError" || !Array.isArray(candidate.issues)) return null;
93
- return candidate.issues.map((issue) => ({
94
- field: Array.isArray(issue.path) ? issue.path.join(".") : "",
95
- message: issue.message ?? "invalide",
96
- rule: issue.code
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) => f.field ? `${f.field}: ${f.message}` : f.message).join(" · ");
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
- //#endregion
433
- export { DefaultErrorRenderer as default, malformedIdentifierOf, schemaMismatchOf };
450
+ export { DefaultErrorRenderer as default, malformedIdentifierOf, schemaMismatchOf, toValidationFields };