@nodefony/http 10.0.0-alpha.9 → 10.0.0-beta.2
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/README.md +12 -3
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +6 -4
- package/dist/nodefony/command/assetsPublishCommand.js +1 -1
- package/dist/nodefony/command/certificatesCommand.js +1 -2
- package/dist/nodefony/command/networkCommand.js +1 -1
- package/dist/nodefony/command/proxyGenerateCommand.js +1 -1
- package/dist/nodefony/config/config.js +26 -4
- package/dist/nodefony/interfaces/IRouting.js +1 -0
- package/dist/nodefony/interfaces/ISecurity.js +1 -0
- package/dist/nodefony/service/ProfilerAdminApi.js +2 -1
- package/dist/nodefony/service/audit-logger.js +16 -13
- package/dist/nodefony/service/certificates.js +87 -144
- package/dist/nodefony/service/error-renderer.js +11 -8
- package/dist/nodefony/service/http-kernel.js +342 -165
- package/dist/nodefony/service/pretty-request-logger.js +3 -10
- package/dist/nodefony/service/servers/clientError.js +2 -2
- package/dist/nodefony/service/servers/server-http.js +23 -23
- package/dist/nodefony/service/servers/server-https.js +41 -42
- package/dist/nodefony/service/servers/server-static.js +13 -17
- package/dist/nodefony/service/servers/server-websocket-secure.js +11 -10
- package/dist/nodefony/service/servers/server-websocket.js +11 -10
- package/dist/nodefony/service/servers/wsHeartbeat.js +2 -2
- package/dist/nodefony/service/sessions/sessions-service.js +22 -15
- package/dist/nodefony/service/trace.js +3 -2
- package/dist/nodefony/service/upload/upload-service.js +11 -10
- package/dist/nodefony/service/x509.js +324 -0
- package/dist/nodefony/src/assets/prebuiltUi.js +1 -1
- package/dist/nodefony/src/context/Context.js +34 -19
- package/dist/nodefony/src/context/forwarded.js +4 -4
- package/dist/nodefony/src/context/http/HttpContext.js +133 -88
- package/dist/nodefony/src/context/http/Request.js +108 -46
- package/dist/nodefony/src/context/http/Response.js +134 -54
- package/dist/nodefony/src/context/http/parser.js +9 -10
- package/dist/nodefony/src/context/http/urlFastPath.js +35 -11
- package/dist/nodefony/src/context/http2/Response.js +53 -25
- package/dist/nodefony/src/context/metaData.js +1 -1
- package/dist/nodefony/src/context/trustProxy.js +4 -3
- package/dist/nodefony/src/context/websocket/Response.js +13 -6
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +59 -26
- package/dist/nodefony/src/context/websocket/wsCloseReason.js +41 -0
- package/dist/nodefony/src/cookies/cookie.js +3 -3
- package/dist/nodefony/src/errors/httpError.js +6 -6
- package/dist/nodefony/src/errors/thrownMessage.js +27 -0
- package/dist/nodefony/src/profiler/FrameProfile.js +4 -3
- package/dist/nodefony/src/profiler/Profiler.js +4 -4
- package/dist/nodefony/src/servers/kernelServers.js +29 -0
- package/dist/nodefony/src/servers/portBinder.js +65 -23
- package/dist/nodefony/src/session/session.js +18 -10
- package/dist/nodefony/src/session/sessionStoreFailure.js +2 -1
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +24 -9
- package/dist/types/index.d.ts +16 -0
- package/dist/types/nodefony/config/config.d.ts +23 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +24 -7
- package/dist/types/nodefony/interfaces/ICookie.d.ts +18 -18
- package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -4
- package/dist/types/nodefony/interfaces/IResponse.d.ts +1 -1
- package/dist/types/nodefony/interfaces/IRouting.d.ts +87 -0
- package/dist/types/nodefony/interfaces/ISecurity.d.ts +45 -0
- package/dist/types/nodefony/interfaces/ISession.d.ts +8 -8
- package/dist/types/nodefony/interfaces/IUpload.d.ts +9 -9
- package/dist/types/nodefony/interfaces/index.d.ts +2 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +14 -10
- package/dist/types/nodefony/service/certificates.d.ts +42 -57
- package/dist/types/nodefony/service/http-kernel.d.ts +192 -42
- package/dist/types/nodefony/service/servers/server-http.d.ts +4 -1
- package/dist/types/nodefony/service/servers/server-https.d.ts +4 -1
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +4 -1
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +4 -1
- package/dist/types/nodefony/service/sessions/sessions-service.d.ts +6 -2
- package/dist/types/nodefony/service/upload/upload-service.d.ts +1 -1
- package/dist/types/nodefony/service/x509.d.ts +86 -0
- package/dist/types/nodefony/src/context/Context.d.ts +21 -10
- package/dist/types/nodefony/src/context/forwarded.d.ts +3 -3
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +75 -18
- package/dist/types/nodefony/src/context/http/Request.d.ts +39 -2
- package/dist/types/nodefony/src/context/http/Response.d.ts +47 -7
- package/dist/types/nodefony/src/context/http/parser.d.ts +1 -1
- package/dist/types/nodefony/src/context/http2/Response.d.ts +32 -2
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +4 -4
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +11 -2
- package/dist/types/nodefony/src/context/websocket/wsCloseReason.d.ts +21 -0
- package/dist/types/nodefony/src/cookies/cookie.d.ts +9 -9
- package/dist/types/nodefony/src/errors/httpError.d.ts +7 -7
- package/dist/types/nodefony/src/errors/thrownMessage.d.ts +12 -0
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +28 -28
- package/dist/types/nodefony/src/servers/kernelServers.d.ts +20 -0
- package/dist/types/nodefony/src/servers/portBinder.d.ts +16 -65
- package/dist/types/nodefony/src/session/session.d.ts +12 -12
- package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +1 -1
- package/docs/cookies.md +25 -25
- package/docs/index.md +4 -2
- package/docs/observabilite.md +27 -27
- package/docs/rate-limit.md +20 -20
- package/docs/servers.md +82 -79
- package/docs/session.md +74 -69
- package/docs/upload.md +49 -43
- package/package.json +9 -12
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ pipeline de contexte/controller.
|
|
|
6
6
|
|
|
7
7
|
> Docs IA (dans le dépôt) : [`CLAUDE.md`](https://github.com/nodefony/nodefony-core/blob/main/src/packages/@nodefony/http/CLAUDE.md) · [`MEMORY.md`](https://github.com/nodefony/nodefony-core/blob/main/src/packages/@nodefony/http/MEMORY.md) · [`docs/`](./docs).
|
|
8
8
|
> Règle dure : ce module ne peut PAS importer `@nodefony/framework` (cycle) — accès au resolver
|
|
9
|
-
> via `
|
|
9
|
+
> via `context.resolver`, typé par le contrat `IRouteResolver` que ce module définit.
|
|
10
10
|
|
|
11
11
|
## Serveurs
|
|
12
12
|
|
|
@@ -26,14 +26,23 @@ serveur → http-kernel.handle() → rate-limit IP → createContext()
|
|
|
26
26
|
→ Router.resolve() (match avant le parse du body)
|
|
27
27
|
→ Firewall.applySecurityHeaders() → fallback statique si aucune route
|
|
28
28
|
→ parse du body
|
|
29
|
-
→
|
|
29
|
+
→ prepareFrontController() (route + zone armées, AUCUNE instanciation)
|
|
30
30
|
→ Firewall.enforceCsrf() → startSession() → Firewall.handleSecurity()
|
|
31
|
-
→ action du controller → Response.send()
|
|
31
|
+
→ @IsGranted → instanciation DI + initialize() → action du controller → Response.send()
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
En WebSocket, le contrôleur est instancié au handshake (`handleFrontController()`), avant le pare-feu.
|
|
35
|
+
|
|
34
36
|
Chaque contexte (HTTP + WS) porte un `requestId` (UUID v4, ou `X-Request-Id` client),
|
|
35
37
|
réinjecté dans la réponse et corrélé dans les logs via AsyncLocalStorage.
|
|
36
38
|
|
|
39
|
+
Chaque requête HTTP — et chaque connexion WebSocket — reçoit aussi **son propre conteneur de
|
|
40
|
+
services** : un scope ouvert à l'entrée (`enterScope`), dont le prototype est le conteneur du kernel,
|
|
41
|
+
refermé après les hooks `onAfterResponse`/`onFinish` (`leaveScope`), y compris quand un hook lève.
|
|
42
|
+
Le serveur le pose dans le contexte asynchrone de la requête : `RequestContext.getScope()` le rend
|
|
43
|
+
depuis n'importe quel code, et les services `@injectable({ scope: "request" })` y vivent. Détail :
|
|
44
|
+
[Injection & portées](https://github.com/nodefony/nodefony-core/blob/main/docs/architecture/injection-portees.md).
|
|
45
|
+
|
|
37
46
|
## Rate-limit général par IP
|
|
38
47
|
|
|
39
48
|
Plafond de trafic **par IP cliente** sur toutes les routes HTTP — protège la capacité du serveur
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.152.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);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorateMetadata.js
|
|
2
2
|
function __decorateMetadata(k, v) {
|
|
3
3
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4
4
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import config, { httpConfigSchema } from "./nodefony/config/config.js";
|
|
1
|
+
import config, { httpConfigSchema, httpOverlaySchema } from "./nodefony/config/config.js";
|
|
2
2
|
import { defineHttpConfig, httpConfigJsonSchema } from "./nodefony/config/defineModuleConfig.js";
|
|
3
3
|
import { createHttpAdminApi } from "./nodefony/service/HttpAdminApi.js";
|
|
4
4
|
import { createProfilerAdminApi } from "./nodefony/service/ProfilerAdminApi.js";
|
|
@@ -21,8 +21,8 @@ import DefaultErrorRenderer, { schemaMismatchOf } from "./nodefony/service/error
|
|
|
21
21
|
import DefaultRequestLogger from "./nodefony/service/request-logger.js";
|
|
22
22
|
import JsonAuditLogger from "./nodefony/service/audit-logger.js";
|
|
23
23
|
import PrettyRequestLogger from "./nodefony/service/pretty-request-logger.js";
|
|
24
|
-
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.
|
|
25
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.
|
|
24
|
+
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
|
|
25
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
|
|
26
26
|
import http_kernel_default from "./nodefony/service/http-kernel.js";
|
|
27
27
|
import server_http_default from "./nodefony/service/servers/server-http.js";
|
|
28
28
|
import server_https_default from "./nodefony/service/servers/server-https.js";
|
|
@@ -42,6 +42,7 @@ import { PrebuiltUi, resolveUiDelivery } from "./nodefony/src/assets/prebuiltUi.
|
|
|
42
42
|
import { Kernel, Module, services } from "nodefony";
|
|
43
43
|
//#region index.ts
|
|
44
44
|
let Http = class Http extends Module {
|
|
45
|
+
overlaySchema = httpOverlaySchema;
|
|
45
46
|
constructor(kernel) {
|
|
46
47
|
super("http", kernel, import.meta.url, config);
|
|
47
48
|
this.addCommand(Network);
|
|
@@ -68,7 +69,8 @@ let Http = class Http extends Module {
|
|
|
68
69
|
* certificats `serialNumber`) — cf `defineHttpConfig`.
|
|
69
70
|
*/
|
|
70
71
|
async onKernelRegister() {
|
|
71
|
-
|
|
72
|
+
const input = this.options;
|
|
73
|
+
this.options = defineHttpConfig(input ?? {}, this.kernel);
|
|
72
74
|
return this;
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
@@ -80,7 +80,7 @@ var AssetsPublish = class extends Command {
|
|
|
80
80
|
}
|
|
81
81
|
/** Sources = mounts natifs (`server-static`) + bundles `@nodefony/frontend`. */
|
|
82
82
|
collectSources() {
|
|
83
|
-
const modules = this.kernel?.getModules
|
|
83
|
+
const modules = this.kernel?.getModules() ?? {};
|
|
84
84
|
const sources = [];
|
|
85
85
|
const staticSvc = modules["http"]?.get("server-static");
|
|
86
86
|
staticSvc?.mountModulePublics?.();
|
|
@@ -21,12 +21,11 @@ var Certificates = class extends Command {
|
|
|
21
21
|
this.addOption("-j, --json", "output the certificate summary as JSON");
|
|
22
22
|
}
|
|
23
23
|
async generate(opts) {
|
|
24
|
-
const service = (this.kernel?.getModules()
|
|
24
|
+
const service = (this.kernel?.getModules()["http"])?.get("certificates") ?? null;
|
|
25
25
|
if (!service) {
|
|
26
26
|
this.log("Service 'certificates' introuvable (module @nodefony/http non chargé).", "ERROR");
|
|
27
27
|
return this;
|
|
28
28
|
}
|
|
29
|
-
await service.loadForge();
|
|
30
29
|
await service.generateServerCertificates(Boolean(opts.force));
|
|
31
30
|
const info = await service.describe();
|
|
32
31
|
if (opts.json) process.stdout.write(`${JSON.stringify(info, void 0, 2)}\n`);
|
|
@@ -16,7 +16,7 @@ var Network = class extends Command {
|
|
|
16
16
|
let network = this.kernel?.getNetwork();
|
|
17
17
|
let result = {};
|
|
18
18
|
if (arg) {
|
|
19
|
-
if (network?.interfaces[arg]) result = network
|
|
19
|
+
if (network?.interfaces[arg]) result = network.interfaces[arg];
|
|
20
20
|
} else result = network?.interfaces;
|
|
21
21
|
if (opts.json) process.stdout.write(`${JSON.stringify(result, void 0, " ")}\n`);
|
|
22
22
|
else process.stdout.write(`${inspect(result, { colors: process.stdout.isTTY })}\n`);
|
|
@@ -43,7 +43,7 @@ var ProxyGenerate = class extends Command {
|
|
|
43
43
|
}
|
|
44
44
|
/** Construit le modèle d'introspection depuis le kernel + le service statique. */
|
|
45
45
|
buildIntrospection(opts) {
|
|
46
|
-
const module = this.kernel?.getModules()
|
|
46
|
+
const module = this.kernel?.getModules()["http"];
|
|
47
47
|
const httpOpts = module?.options ?? {};
|
|
48
48
|
const servers = (this.kernel?.options)?.servers;
|
|
49
49
|
const staticSvc = module?.get("server-static");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { overlayField } from "nodefony";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
//#region nodefony/config/config.ts
|
|
3
4
|
/**
|
|
@@ -158,8 +159,9 @@ const http3Schema = z.looseObject({});
|
|
|
158
159
|
const selfSignedAttrSchema = z.looseObject({
|
|
159
160
|
name: z.string().optional(),
|
|
160
161
|
value: z.string().optional(),
|
|
161
|
-
shortName: z.string().optional()
|
|
162
|
-
|
|
162
|
+
shortName: z.string().optional(),
|
|
163
|
+
type: z.string().optional()
|
|
164
|
+
}).describe("Attribut du sujet/émetteur : `name` (commonName…), `shortName` (CN…) ou `type` (OID), et `value`.");
|
|
163
165
|
const selfSignedSchema = z.strictObject({
|
|
164
166
|
size: z.number().int().positive().default(2048).describe("Taille de la clé RSA (bits). 2048 minimum, 4096 recommandé en prod."),
|
|
165
167
|
hash: z.enum([
|
|
@@ -173,7 +175,7 @@ const selfSignedSchema = z.strictObject({
|
|
|
173
175
|
kernelDerived: true,
|
|
174
176
|
description: "Attributs du certificat (commonName, organizationName…). Vide (défaut) = dérivés du kernel par le builder (commonName ← domain)."
|
|
175
177
|
})
|
|
176
|
-
}).describe("Génération du certificat AUTO-SIGNÉ (node
|
|
178
|
+
}).describe("Génération du certificat AUTO-SIGNÉ (node:crypto — aucun binaire externe ni dépendance tierce). Ces réglages ne valent QUE pour la stratégie `selfsigned` : sous `mkcert` (défaut en développement) ils sont ignorés — mkcert ne reçoit que les noms d'hôtes — et sous `strategy: \"explicit\"` le certificat est fourni, donc rien n'est généré.");
|
|
177
179
|
const sanSchema = z.strictObject({
|
|
178
180
|
dns: z.array(z.string()).default([]).meta({
|
|
179
181
|
kernelDerived: true,
|
|
@@ -327,9 +329,29 @@ const httpConfigSchema = z.strictObject({
|
|
|
327
329
|
})
|
|
328
330
|
}).describe("Configuration de @nodefony/http.");
|
|
329
331
|
/**
|
|
332
|
+
* LISTE BLANCHE des clés qu'une requête peut surcharger pour elle seule
|
|
333
|
+
* (`overlayConfig("@nodefony/http", …)`, #494) : les quotas de corps et
|
|
334
|
+
* d'envoi, lus au MOMENT de lire le corps (`Request.readBodyLimits`) — un
|
|
335
|
+
* calque posé avant la lecture du corps (écouteur `onRequestScope`, organisation
|
|
336
|
+
* reconnue à son nom d'hôte) les voit. Tout le reste est refusé : en-têtes de
|
|
337
|
+
* sécurité, pare-feu, `trustProxy`, sessions, limites de débit, certificats —
|
|
338
|
+
* et `queryString`, lu au constructeur de la requête, avant tout calque.
|
|
339
|
+
* Chaque champ réutilise celui du schéma (mêmes bornes) sans son défaut.
|
|
340
|
+
*/
|
|
341
|
+
const httpOverlaySchema = z.strictObject({
|
|
342
|
+
maxBodySize: overlayField(httpConfigSchema.shape.maxBodySize),
|
|
343
|
+
upload: z.strictObject({
|
|
344
|
+
maxFileSize: overlayField(uploadSchema.shape.maxFileSize),
|
|
345
|
+
maxTotalFileSize: overlayField(uploadSchema.shape.maxTotalFileSize),
|
|
346
|
+
maxFiles: overlayField(uploadSchema.shape.maxFiles),
|
|
347
|
+
maxFields: overlayField(uploadSchema.shape.maxFields),
|
|
348
|
+
maxFieldsSize: overlayField(uploadSchema.shape.maxFieldsSize)
|
|
349
|
+
}).partial()
|
|
350
|
+
}).partial().describe("Clés de @nodefony/http surchargeables par requête.");
|
|
351
|
+
/**
|
|
330
352
|
* Défauts du module, matérialisés depuis le schéma (source unique). Toujours
|
|
331
353
|
* valides par construction ; passés au `super(..., config)` du Module class.
|
|
332
354
|
*/
|
|
333
355
|
const config = httpConfigSchema.parse({});
|
|
334
356
|
//#endregion
|
|
335
|
-
export { config as default, httpConfigSchema };
|
|
357
|
+
export { config as default, httpConfigSchema, httpOverlaySchema };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -51,7 +51,8 @@ function createProfilerAdminApi(profiler) {
|
|
|
51
51
|
path: "{id}",
|
|
52
52
|
summary: "Full profile (phase timeline) for a requestId",
|
|
53
53
|
handler: ({ params }) => {
|
|
54
|
-
const
|
|
54
|
+
const { id = "" } = params;
|
|
55
|
+
const entry = profiler.get(id);
|
|
55
56
|
if (!entry) return {
|
|
56
57
|
status: 404,
|
|
57
58
|
body: {
|
|
@@ -2,6 +2,19 @@ import { RequestContext } from "nodefony";
|
|
|
2
2
|
import { performance } from "node:perf_hooks";
|
|
3
3
|
//#region nodefony/service/audit-logger.ts
|
|
4
4
|
/**
|
|
5
|
+
* Durée totale de la requête, de la première phase (`startMs`) à maintenant.
|
|
6
|
+
* `null` si le chronométrage est coupé (aucune phase enregistrée). Partagée
|
|
7
|
+
* par le journal d'audit et le journal lisible — une seule définition.
|
|
8
|
+
*
|
|
9
|
+
* @param phases - phases chronométrées du contexte
|
|
10
|
+
* @returns durée en millisecondes, ou `null`
|
|
11
|
+
*/
|
|
12
|
+
function computeDurationMs(phases) {
|
|
13
|
+
const first = phases.at(0);
|
|
14
|
+
if (first === void 0 || typeof first.startMs !== "number") return null;
|
|
15
|
+
return performance.now() - first.startMs;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
5
18
|
* Severity derived from HTTP status code — RFC 9110 categories.
|
|
6
19
|
* 1xx/2xx/3xx → INFO ; 4xx → WARNING ; 5xx → ERROR.
|
|
7
20
|
* Unknown/missing status → INFO.
|
|
@@ -77,7 +90,7 @@ var JsonAuditLogger = class {
|
|
|
77
90
|
method: ctx.method,
|
|
78
91
|
url: ctx.url,
|
|
79
92
|
status,
|
|
80
|
-
durationMs:
|
|
93
|
+
durationMs: computeDurationMs(ctx.phases),
|
|
81
94
|
remoteAddress: ctx.remoteAddress ?? null,
|
|
82
95
|
host: ctx.getHost?.() ?? null,
|
|
83
96
|
userAgent: ctx.getUserAgent?.() ?? null,
|
|
@@ -108,7 +121,7 @@ var JsonAuditLogger = class {
|
|
|
108
121
|
method: ctx.method,
|
|
109
122
|
url: ctx.url,
|
|
110
123
|
status,
|
|
111
|
-
durationMs:
|
|
124
|
+
durationMs: computeDurationMs(ctx.phases),
|
|
112
125
|
remoteAddress: ctx.remoteAddress ?? null,
|
|
113
126
|
host: ctx.getHost?.() ?? null,
|
|
114
127
|
userAgent: ctx.getUserAgent?.() ?? null,
|
|
@@ -128,16 +141,6 @@ var JsonAuditLogger = class {
|
|
|
128
141
|
};
|
|
129
142
|
}
|
|
130
143
|
/**
|
|
131
|
-
* Total request duration computed from the first phase startMs to now.
|
|
132
|
-
* Returns null if timing is disabled (no phases recorded).
|
|
133
|
-
*/
|
|
134
|
-
computeDurationMs(phases) {
|
|
135
|
-
if (!phases.length) return null;
|
|
136
|
-
const first = phases[0];
|
|
137
|
-
if (typeof first.startMs !== "number") return null;
|
|
138
|
-
return performance.now() - first.startMs;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
144
|
* Serialise an Error (recursively for `cause` chain) into an AuditErrorEntry.
|
|
142
145
|
* Stack is included only when `includeStack === true` (dev default).
|
|
143
146
|
* Cause chain is capped at `maxCauseDepth`.
|
|
@@ -156,4 +159,4 @@ var JsonAuditLogger = class {
|
|
|
156
159
|
}
|
|
157
160
|
};
|
|
158
161
|
//#endregion
|
|
159
|
-
export { JsonAuditLogger as default, severityFromStatus };
|
|
162
|
+
export { computeDurationMs, JsonAuditLogger as default, severityFromStatus };
|