@nodefony/http 10.0.0-beta.3 → 10.0.0-beta.5

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 (138) 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 +13 -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 +3 -3
  9. package/dist/nodefony/config/defineModuleConfig.js +1 -2
  10. package/dist/nodefony/interfaces/IRequestTrace.js +1 -0
  11. package/dist/nodefony/service/HttpAdminApi.js +2 -2
  12. package/dist/nodefony/service/ProfilerAdminApi.js +13 -3
  13. package/dist/nodefony/service/audit-logger.js +23 -30
  14. package/dist/nodefony/service/certificates.js +97 -13
  15. package/dist/nodefony/service/error-renderer.js +29 -12
  16. package/dist/nodefony/service/http-kernel.js +198 -50
  17. package/dist/nodefony/service/pretty-request-logger.js +15 -13
  18. package/dist/nodefony/service/request-logger.js +24 -16
  19. package/dist/nodefony/service/reverse-proxy.js +3 -3
  20. package/dist/nodefony/service/servers/clientError.js +22 -3
  21. package/dist/nodefony/service/servers/server-h2c.js +126 -0
  22. package/dist/nodefony/service/servers/server-http.js +5 -8
  23. package/dist/nodefony/service/servers/server-https.js +12 -24
  24. package/dist/nodefony/service/servers/server-static.js +3 -3
  25. package/dist/nodefony/service/servers/server-websocket-secure.js +10 -18
  26. package/dist/nodefony/service/servers/server-websocket.js +10 -18
  27. package/dist/nodefony/service/servers/serverShutdown.js +78 -18
  28. package/dist/nodefony/service/servers/wsHeartbeat.js +1 -2
  29. package/dist/nodefony/service/sessions/sessions-service.js +7 -14
  30. package/dist/nodefony/service/trace.js +1 -2
  31. package/dist/nodefony/service/upload/upload-service.js +5 -8
  32. package/dist/nodefony/service/x509.js +1 -2
  33. package/dist/nodefony/src/assets/collectAssets.js +1 -2
  34. package/dist/nodefony/src/assets/prebuiltUi.js +1 -2
  35. package/dist/nodefony/src/context/Context.js +78 -43
  36. package/dist/nodefony/src/context/domainMatcher.js +1 -2
  37. package/dist/nodefony/src/context/forwarded.js +1 -2
  38. package/dist/nodefony/src/context/http/HttpContext.js +28 -6
  39. package/dist/nodefony/src/context/http/Request.js +33 -29
  40. package/dist/nodefony/src/context/http/Response.js +23 -6
  41. package/dist/nodefony/src/context/http/SseStream.js +1 -2
  42. package/dist/nodefony/src/context/http/parser.js +36 -10
  43. package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
  44. package/dist/nodefony/src/context/http2/Request.js +4 -8
  45. package/dist/nodefony/src/context/http2/Response.js +41 -9
  46. package/dist/nodefony/src/context/metaData.js +1 -2
  47. package/dist/nodefony/src/context/requestId.js +1 -2
  48. package/dist/nodefony/src/context/requestTrace.js +104 -0
  49. package/dist/nodefony/src/context/responseEnded.js +1 -2
  50. package/dist/nodefony/src/context/timingMode.js +21 -0
  51. package/dist/nodefony/src/context/transport.js +45 -0
  52. package/dist/nodefony/src/context/trustProxy.js +1 -2
  53. package/dist/nodefony/src/context/unsentResponse.js +30 -0
  54. package/dist/nodefony/src/context/websocket/Response.js +33 -6
  55. package/dist/nodefony/src/context/websocket/WebsocketContext.js +104 -25
  56. package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
  57. package/dist/nodefony/src/context/websocket/websocketUrl.js +1 -2
  58. package/dist/nodefony/src/context/websocket/wsBackpressure.js +1 -2
  59. package/dist/nodefony/src/context/websocket/wsCloseReason.js +1 -2
  60. package/dist/nodefony/src/context/websocket/wsLogContent.js +1 -2
  61. package/dist/nodefony/src/cookies/cookie.js +3 -7
  62. package/dist/nodefony/src/errors/httpError.js +1 -2
  63. package/dist/nodefony/src/errors/thrownMessage.js +1 -2
  64. package/dist/nodefony/src/profiler/FrameProfile.js +23 -2
  65. package/dist/nodefony/src/profiler/Profiler.js +104 -32
  66. package/dist/nodefony/src/profiler/aggregate.js +442 -0
  67. package/dist/nodefony/src/proxy/forward.js +29 -5
  68. package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
  69. package/dist/nodefony/src/proxy/rules.js +12 -6
  70. package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +1 -2
  71. package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +1 -2
  72. package/dist/nodefony/src/rateLimit/rateLimitFilters.js +1 -2
  73. package/dist/nodefony/src/servers/http2Limits.js +50 -0
  74. package/dist/nodefony/src/servers/kernelServers.js +12 -3
  75. package/dist/nodefony/src/servers/portBinder.js +41 -10
  76. package/dist/nodefony/src/servers/upgradeDispatch.js +86 -7
  77. package/dist/nodefony/src/session/session.js +2 -3
  78. package/dist/nodefony/src/session/sessionStoreFailure.js +1 -2
  79. package/dist/nodefony/src/session/storage/MemorySessionStorage.js +1 -2
  80. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +1 -2
  81. package/dist/nodefony/src/session/storage/sessionFilters.js +1 -2
  82. package/dist/nodefony/src/session/storage/sessionSort.js +1 -2
  83. package/dist/types/index.d.ts +9 -3
  84. package/dist/types/nodefony/config/config.d.ts +4 -0
  85. package/dist/types/nodefony/interfaces/IContext.d.ts +72 -4
  86. package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
  87. package/dist/types/nodefony/interfaces/IRequestTrace.d.ts +56 -0
  88. package/dist/types/nodefony/interfaces/IReverseProxy.d.ts +8 -0
  89. package/dist/types/nodefony/interfaces/IRouting.d.ts +26 -0
  90. package/dist/types/nodefony/interfaces/index.d.ts +3 -2
  91. package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +3 -0
  92. package/dist/types/nodefony/service/audit-logger.d.ts +3 -11
  93. package/dist/types/nodefony/service/certificates.d.ts +55 -4
  94. package/dist/types/nodefony/service/error-renderer.d.ts +31 -0
  95. package/dist/types/nodefony/service/http-kernel.d.ts +96 -10
  96. package/dist/types/nodefony/service/servers/clientError.d.ts +16 -0
  97. package/dist/types/nodefony/service/servers/server-h2c.d.ts +51 -0
  98. package/dist/types/nodefony/service/servers/server-http.d.ts +3 -3
  99. package/dist/types/nodefony/service/servers/server-https.d.ts +4 -4
  100. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +1 -3
  101. package/dist/types/nodefony/service/servers/server-websocket.d.ts +1 -3
  102. package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
  103. package/dist/types/nodefony/src/context/Context.d.ts +38 -5
  104. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +16 -2
  105. package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
  106. package/dist/types/nodefony/src/context/http/Response.d.ts +11 -0
  107. package/dist/types/nodefony/src/context/http/parser.d.ts +18 -2
  108. package/dist/types/nodefony/src/context/http2/Request.d.ts +1 -1
  109. package/dist/types/nodefony/src/context/http2/Response.d.ts +27 -5
  110. package/dist/types/nodefony/src/context/requestTrace.d.ts +64 -0
  111. package/dist/types/nodefony/src/context/timingMode.d.ts +23 -0
  112. package/dist/types/nodefony/src/context/transport.d.ts +53 -0
  113. package/dist/types/nodefony/src/context/unsentResponse.d.ts +16 -0
  114. package/dist/types/nodefony/src/context/websocket/Response.d.ts +18 -2
  115. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +58 -8
  116. package/dist/types/nodefony/src/context/websocket/isWebsocketContext.d.ts +15 -0
  117. package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +28 -12
  118. package/dist/types/nodefony/src/profiler/Profiler.d.ts +79 -111
  119. package/dist/types/nodefony/src/profiler/aggregate.d.ts +132 -0
  120. package/dist/types/nodefony/src/proxy/forward.d.ts +14 -2
  121. package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
  122. package/dist/types/nodefony/src/proxy/rules.d.ts +3 -0
  123. package/dist/types/nodefony/src/servers/http2Limits.d.ts +37 -0
  124. package/dist/types/nodefony/src/servers/kernelServers.d.ts +10 -1
  125. package/dist/types/nodefony/src/servers/portBinder.d.ts +29 -5
  126. package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +33 -3
  127. package/docs/cookies.md +85 -78
  128. package/docs/diagnostics-channel.md +303 -0
  129. package/docs/index.md +6 -1
  130. package/docs/observabilite.md +159 -81
  131. package/docs/rate-limit.md +83 -83
  132. package/docs/reverse-proxy.md +72 -70
  133. package/docs/servers.md +338 -224
  134. package/docs/session.md +179 -179
  135. package/docs/sse.md +24 -20
  136. package/docs/upload.md +120 -117
  137. package/package.json +8 -9
  138. 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
@@ -2,11 +2,13 @@ import { normalizePrefix, parseProxyOrigin, proxyMountProblems } from "./nodefon
2
2
  import config, { httpConfigSchema, httpOverlaySchema } from "./nodefony/config/config.js";
3
3
  import { defineHttpConfig, httpConfigJsonSchema } from "./nodefony/config/defineModuleConfig.js";
4
4
  import { createHttpAdminApi } from "./nodefony/service/HttpAdminApi.js";
5
+ import { ACTION_SELF_PHASE, NO_ROUTE, N_PLUS_ONE_MIN_REPEATS, TOOLING_URL_PREFIX, aggregateProfiles, createProfileAggregator, isFailedProfile, sqlShape } from "./nodefony/src/profiler/aggregate.js";
5
6
  import { createProfilerAdminApi } from "./nodefony/service/ProfilerAdminApi.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";
9
10
  import { compileDomainPattern, compileDomainPatterns, compileTrustedHosts, isDomainAllowed } from "./nodefony/src/context/domainMatcher.js";
11
+ import { REQUEST_TRACE_CHANNEL } from "./nodefony/src/context/requestTrace.js";
10
12
  import Cookie from "./nodefony/src/cookies/cookie.js";
11
13
  import Context from "./nodefony/src/context/Context.js";
12
14
  import { decideSend, readBackpressureOptions } from "./nodefony/src/context/websocket/wsBackpressure.js";
@@ -19,15 +21,15 @@ import Http2Request from "./nodefony/src/context/http2/Request.js";
19
21
  import Http2Response from "./nodefony/src/context/http2/Response.js";
20
22
  import { SSE_HEARTBEAT_MS, SseStream, acceptsEventStream, openSseStream } from "./nodefony/src/context/http/SseStream.js";
21
23
  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";
24
+ import DefaultErrorRenderer, { schemaMismatchOf, toValidationFields } from "./nodefony/service/error-renderer.js";
24
25
  import JsonAuditLogger from "./nodefony/service/audit-logger.js";
26
+ import DefaultRequestLogger from "./nodefony/service/request-logger.js";
25
27
  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";
28
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
28
29
  import http_kernel_default from "./nodefony/service/http-kernel.js";
29
30
  import server_http_default from "./nodefony/service/servers/server-http.js";
30
31
  import server_https_default from "./nodefony/service/servers/server-https.js";
32
+ import server_h2c_default from "./nodefony/service/servers/server-h2c.js";
31
33
  import server_websocket_default from "./nodefony/service/servers/server-websocket.js";
32
34
  import server_websocket_secure_default from "./nodefony/service/servers/server-websocket-secure.js";
33
35
  import Statics from "./nodefony/service/servers/server-static.js";
@@ -41,9 +43,10 @@ import { SESSION_COLUMN_ALIASES, SESSION_DEFAULT_ORDER, SESSION_DEFAULT_ORDER_SQ
41
43
  import sessions_service_default from "./nodefony/service/sessions/sessions-service.js";
42
44
  import Certificate, { trustOutboundAuthorities } from "./nodefony/service/certificates.js";
43
45
  import upload_service_default from "./nodefony/service/upload/upload-service.js";
46
+ import { isWebsocketContext } from "./nodefony/src/context/websocket/isWebsocketContext.js";
44
47
  import { PrebuiltUi, resolveUiDelivery } from "./nodefony/src/assets/prebuiltUi.js";
45
- import { Kernel, Module, services } from "nodefony";
46
- //#region index.ts
48
+ import { Module, services } from "nodefony";
49
+ // index.ts
47
50
  let Http = class Http extends Module {
48
51
  overlaySchema = httpOverlaySchema;
49
52
  constructor(kernel) {
@@ -75,7 +78,7 @@ let Http = class Http extends Module {
75
78
  const input = this.options;
76
79
  const options = defineHttpConfig(input ?? {}, this.kernel);
77
80
  this.options = options;
78
- await trustOutboundAuthorities(options.certificates.extraCaFiles, this.kernel?.path ?? process.cwd(), (message, severity) => this.log(message, severity));
81
+ await trustOutboundAuthorities(options.certificates.extraCaFiles, this.kernel?.path ?? process.cwd(), (message, severity) => this.log(message, severity), { development: this.kernel?.environment === "development" });
79
82
  return this;
80
83
  }
81
84
  /**
@@ -107,10 +110,10 @@ Http = __decorate([services([
107
110
  ReverseProxy,
108
111
  server_http_default,
109
112
  server_https_default,
113
+ server_h2c_default,
110
114
  server_websocket_default,
111
115
  server_websocket_secure_default,
112
116
  upload_service_default
113
- ]), __decorateMetadata("design:paramtypes", [typeof Kernel === "undefined" ? Object : Kernel])], Http);
117
+ ])], Http);
114
118
  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 };
119
+ 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, REQUEST_TRACE_CHANNEL, 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
  *
@@ -297,7 +297,8 @@ const proxyMountSchema = z.strictObject({
297
297
  preserveHost: z.boolean().default(false).describe("Transmet le `Host` du client au lieu de celui de la cible. Défaut false : l'amont reçoit son propre nom (ce qu'attend un serveur qui filtre les noms d'hôte), le nom d'origine part en `X-Forwarded-Host`."),
298
298
  stripHeaders: z.array(z.string()).default([]).describe("En-têtes de requête à ne PAS transmettre (ex. `[\"cookie\", \"authorization\"]` vers un amont qui n'a pas à voir la session). Les en-têtes de connexion (RFC 9110 §7.6.1) ne le sont jamais."),
299
299
  timeoutMs: z.number().optional().describe("Délai d'inactivité avec l'amont (ms) — dépassé avant la réponse (ou avant le `101` d'un upgrade WebSocket) : 504. Absent = `proxy.timeoutMs`. Ne s'applique pas à un WebSocket établi."),
300
- secure: z.boolean().default(true).describe("Vérifie le certificat d'une cible https. `false` n'est accepté que vers la boucle locale (`127.0.0.0/8`, `localhost`, `::1`) ; ailleurs, fournir l'autorité de l'amont (`certificates.extraCaFiles`, ou `NODE_EXTRA_CA_CERTS`).")
300
+ secure: z.boolean().default(true).describe("Vérifie le certificat d'une cible https. `false` n'est accepté que vers la boucle locale (`127.0.0.0/8`, `localhost`, `::1`) ; ailleurs, fournir l'autorité de l'amont (`certificates.extraCaFiles`, ou `NODE_EXTRA_CA_CERTS`)."),
301
+ accessLog: z.enum(["all", "errors"]).default("all").describe("Ligne d'accès des requêtes relayées : `all` (défaut) les écrit toutes, `errors` seulement celles en échec (statut ≥ 400) — pour un amont qui sert beaucoup de petits fichiers.")
301
302
  }).describe("Un montage du proxy inverse : un préfixe d'URL relayé vers un amont.");
302
303
  const proxySchema = z.strictObject({
303
304
  timeoutMs: z.number().int().positive().default(3e4).describe("Délai d'inactivité par défaut de tous les montages (ms) : un amont muet au-delà rend 504 Gateway Timeout. Défaut 30 s."),
@@ -393,5 +394,4 @@ const httpOverlaySchema = z.strictObject({
393
394
  * valides par construction ; passés au `super(..., config)` du Module class.
394
395
  */
395
396
  const config = httpConfigSchema.parse({});
396
- //#endregion
397
397
  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 };
@@ -0,0 +1 @@
1
+ export {};
@@ -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,5 @@
1
- //#region nodefony/service/ProfilerAdminApi.ts
1
+ import { aggregateProfiles } from "../src/profiler/aggregate.js";
2
+ // nodefony/service/ProfilerAdminApi.ts
2
3
  /**
3
4
  * Producteur `IAdminApi` du **profiler** — exposé sous `/nodefony/profiler/api/*`.
4
5
  *
@@ -9,6 +10,9 @@
9
10
  *
10
11
  * Endpoints :
11
12
  * - `GET /nodefony/profiler/api/recent` → derniers profils (résumés, récent → ancien)
13
+ * `?connection=<requestId>` : les seules frames de cette socket WebSocket
14
+ * - `GET /nodefony/profiler/api/summary` → les profils de l'anneau agrégés par
15
+ * route (percentiles, phases, formes SQL, N+1, erreurs — `aggregateProfiles`)
12
16
  * - `GET /nodefony/profiler/api/{id}` → profil complet (phases) d'un requestId
13
17
  * - `DELETE /nodefony/profiler/api/recent` → vide le ring buffer
14
18
  *
@@ -32,9 +36,11 @@ function createProfilerAdminApi(profiler) {
32
36
  const raw = query.limit;
33
37
  const n = typeof raw === "string" ? parseInt(raw, 10) : NaN;
34
38
  const limit = Number.isFinite(n) && n > 0 ? Math.min(n, 200) : 60;
39
+ const of = query.connection;
40
+ const connection = typeof of === "string" && of.length > 0 && of.length <= 200 ? of : void 0;
35
41
  return {
36
42
  count: profiler.size,
37
- entries: profiler.recent(limit)
43
+ entries: profiler.recent(limit, connection)
38
44
  };
39
45
  }
40
46
  },
@@ -47,6 +53,11 @@ function createProfilerAdminApi(profiler) {
47
53
  return { cleared: true };
48
54
  }
49
55
  },
56
+ {
57
+ path: "summary",
58
+ summary: "Profiles aggregated by route: percentiles, SQL, N+1, errors",
59
+ handler: () => aggregateProfiles(profiler.since(0).entries)
60
+ },
50
61
  {
51
62
  path: "{id}",
52
63
  summary: "Full profile (phase timeline) for a requestId",
@@ -70,5 +81,4 @@ function createProfilerAdminApi(profiler) {
70
81
  adminEndpoints: () => endpoints
71
82
  };
72
83
  }
73
- //#endregion
74
84
  export { createProfilerAdminApi, createProfilerAdminApi as default };
@@ -1,18 +1,11 @@
1
- import { performance } from "node:perf_hooks";
2
- //#region nodefony/service/audit-logger.ts
3
- /**
4
- * Durée totale de la requête, de la première phase (`startMs`) à maintenant.
5
- * `null` si le chronométrage est coupé (aucune phase enregistrée). Partagée
6
- * par le journal d'audit et le journal lisible — une seule définition.
7
- *
8
- * @param phases - phases chronométrées du contexte
9
- * @returns durée en millisecondes, ou `null`
10
- */
11
- function computeDurationMs(phases) {
12
- const first = phases.at(0);
13
- if (first === void 0 || typeof first.startMs !== "number") return null;
14
- return performance.now() - first.startMs;
15
- }
1
+ import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
2
+ // nodefony/service/audit-logger.ts
3
+ const statusOf = (context) => {
4
+ return context.response?.statusCode ?? null;
5
+ };
6
+ const headersOf = (context) => {
7
+ return context.request?.headers ?? {};
8
+ };
16
9
  /**
17
10
  * Severity derived from HTTP status code — RFC 9110 categories.
18
11
  * 1xx/2xx/3xx → INFO ; 4xx → WARNING ; 5xx → ERROR.
@@ -65,20 +58,20 @@ var JsonAuditLogger = class {
65
58
  const ctx = context;
66
59
  if (!this.nominalEnabled) {
67
60
  if (error ?? ctx.error) return true;
68
- const status = ctx.response?.statusCode ?? null;
61
+ const status = statusOf(ctx);
69
62
  return status !== null && status >= 400;
70
63
  }
71
64
  if (this.sampleRate <= 1) return true;
72
65
  if (error ?? ctx.error) return true;
73
- const status = ctx.response?.statusCode ?? null;
66
+ const status = statusOf(ctx);
74
67
  if (status !== null && status >= 400) return true;
75
68
  this.sampleCounter = (this.sampleCounter + 1) % this.sampleRate;
76
69
  return this.sampleCounter === 0;
77
70
  }
78
71
  renderHttp(context, error) {
79
72
  const ctx = context;
80
- const status = ctx.response?.statusCode ?? null;
81
- const headers = ctx.request?.headers ?? {};
73
+ const status = statusOf(ctx);
74
+ const headers = headersOf(ctx);
82
75
  const err = error ?? ctx.error ?? null;
83
76
  const entry = {
84
77
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -89,10 +82,10 @@ var JsonAuditLogger = class {
89
82
  method: ctx.method,
90
83
  url: ctx.url,
91
84
  status,
92
- durationMs: computeDurationMs(ctx.phases),
85
+ durationMs: ctx.stopClock(),
93
86
  remoteAddress: ctx.remoteAddress ?? null,
94
- host: ctx.getHost?.() ?? null,
95
- userAgent: ctx.getUserAgent?.() ?? null,
87
+ host: ctx.getHost() ?? null,
88
+ userAgent: ctx.getUserAgent() ?? null,
96
89
  hasAuthorization: Boolean(headers["authorization"]),
97
90
  hasCookie: Boolean(headers["cookie"]),
98
91
  phases: ctx.phases.length ? ctx.phases.map((p) => ({
@@ -109,8 +102,9 @@ var JsonAuditLogger = class {
109
102
  }
110
103
  renderWebsocket(context, error, acceptedProtocol) {
111
104
  const ctx = context;
112
- const status = ctx.response?.statusCode ?? null;
113
- const headers = ctx.request?.headers ?? {};
105
+ const refused = error ? handshakeRefusalStatus(ctx, error) : null;
106
+ const status = refused ?? statusOf(ctx);
107
+ const headers = headersOf(ctx);
114
108
  const entry = {
115
109
  ts: (/* @__PURE__ */ new Date()).toISOString(),
116
110
  requestId: ctx.requestId,
@@ -120,10 +114,10 @@ var JsonAuditLogger = class {
120
114
  method: ctx.method,
121
115
  url: ctx.url,
122
116
  status,
123
- durationMs: computeDurationMs(ctx.phases),
117
+ durationMs: ctx.stopClock(),
124
118
  remoteAddress: ctx.remoteAddress ?? null,
125
- host: ctx.getHost?.() ?? null,
126
- userAgent: ctx.getUserAgent?.() ?? null,
119
+ host: ctx.getHost() ?? null,
120
+ userAgent: ctx.getUserAgent() ?? null,
127
121
  hasAuthorization: Boolean(headers["authorization"]),
128
122
  hasCookie: Boolean(headers["cookie"]),
129
123
  phases: ctx.phases.length ? ctx.phases.map((p) => ({
@@ -135,7 +129,7 @@ var JsonAuditLogger = class {
135
129
  };
136
130
  return {
137
131
  text: JSON.stringify(entry),
138
- severity: error ? "ERROR" : "INFO",
132
+ severity: refused !== null ? severityFromStatus(refused) : error ? "ERROR" : "INFO",
139
133
  msgid: "audit"
140
134
  };
141
135
  }
@@ -157,5 +151,4 @@ var JsonAuditLogger = class {
157
151
  return entry;
158
152
  }
159
153
  };
160
- //#endregion
161
- export { computeDurationMs, JsonAuditLogger as default, severityFromStatus };
154
+ export { JsonAuditLogger as default, severityFromStatus, statusOf };
@@ -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 };