@nodefony/http 10.0.0-alpha.1
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/LICENSE +544 -0
- package/README.md +77 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js +8 -0
- package/dist/index.js +108 -0
- package/dist/nodefony/command/assetsPublishCommand.js +102 -0
- package/dist/nodefony/command/certificatesCommand.js +47 -0
- package/dist/nodefony/command/networkCommand.js +27 -0
- package/dist/nodefony/command/proxyGenerateCommand.js +66 -0
- package/dist/nodefony/config/config.js +335 -0
- package/dist/nodefony/config/defineModuleConfig.js +93 -0
- package/dist/nodefony/interfaces/IContext.js +1 -0
- package/dist/nodefony/interfaces/ICookie.js +1 -0
- package/dist/nodefony/interfaces/IErrorRenderer.js +1 -0
- package/dist/nodefony/interfaces/IHttpConfig.js +1 -0
- package/dist/nodefony/interfaces/IHttpKernel.js +1 -0
- package/dist/nodefony/interfaces/IRequest.js +1 -0
- package/dist/nodefony/interfaces/IRequestLogger.js +1 -0
- package/dist/nodefony/interfaces/IResponse.js +1 -0
- package/dist/nodefony/interfaces/ISession.js +1 -0
- package/dist/nodefony/interfaces/IUpload.js +1 -0
- package/dist/nodefony/interfaces/index.js +1 -0
- package/dist/nodefony/service/HttpAdminApi.js +376 -0
- package/dist/nodefony/service/ProfilerAdminApi.js +73 -0
- package/dist/nodefony/service/audit-logger.js +159 -0
- package/dist/nodefony/service/certificates.js +545 -0
- package/dist/nodefony/service/error-renderer.js +320 -0
- package/dist/nodefony/service/http-kernel.js +948 -0
- package/dist/nodefony/service/pretty-request-logger.js +72 -0
- package/dist/nodefony/service/request-logger.js +54 -0
- package/dist/nodefony/service/servers/clientError.js +20 -0
- package/dist/nodefony/service/servers/server-http.js +135 -0
- package/dist/nodefony/service/servers/server-https.js +204 -0
- package/dist/nodefony/service/servers/server-static.js +192 -0
- package/dist/nodefony/service/servers/server-websocket-secure.js +104 -0
- package/dist/nodefony/service/servers/server-websocket.js +104 -0
- package/dist/nodefony/service/servers/serverShutdown.js +31 -0
- package/dist/nodefony/service/servers/wsHeartbeat.js +64 -0
- package/dist/nodefony/service/sessions/sessions-service.js +580 -0
- package/dist/nodefony/service/trace.js +72 -0
- package/dist/nodefony/service/upload/upload-service.js +171 -0
- package/dist/nodefony/src/assets/collectAssets.js +34 -0
- package/dist/nodefony/src/assets/prebuiltUi.js +125 -0
- package/dist/nodefony/src/context/Context.js +415 -0
- package/dist/nodefony/src/context/domainMatcher.js +88 -0
- package/dist/nodefony/src/context/forwarded.js +185 -0
- package/dist/nodefony/src/context/http/HttpContext.js +309 -0
- package/dist/nodefony/src/context/http/Request.js +543 -0
- package/dist/nodefony/src/context/http/Response.js +368 -0
- package/dist/nodefony/src/context/http/parser.js +188 -0
- package/dist/nodefony/src/context/http/urlFastPath.js +103 -0
- package/dist/nodefony/src/context/http2/Request.js +29 -0
- package/dist/nodefony/src/context/http2/Response.js +97 -0
- package/dist/nodefony/src/context/metaData.js +47 -0
- package/dist/nodefony/src/context/requestId.js +41 -0
- package/dist/nodefony/src/context/trustProxy.js +167 -0
- package/dist/nodefony/src/context/websocket/Response.js +181 -0
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +389 -0
- package/dist/nodefony/src/context/websocket/wsBackpressure.js +56 -0
- package/dist/nodefony/src/context/websocket/wsLogContent.js +68 -0
- package/dist/nodefony/src/cookies/cookie.js +258 -0
- package/dist/nodefony/src/errors/httpError.js +69 -0
- package/dist/nodefony/src/profiler/FrameProfile.js +95 -0
- package/dist/nodefony/src/profiler/Profiler.js +139 -0
- package/dist/nodefony/src/proxy/generateProxyConfig.js +157 -0
- package/dist/nodefony/src/rateLimit/IRateLimitStore.js +1 -0
- package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +146 -0
- package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +64 -0
- package/dist/nodefony/src/rateLimit/rateLimitFilters.js +20 -0
- package/dist/nodefony/src/servers/portBinder.js +114 -0
- package/dist/nodefony/src/session/session.js +390 -0
- package/dist/nodefony/src/session/storage/MemorySessionStorage.js +185 -0
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +137 -0
- package/dist/nodefony/src/session/storage/sessionFilters.js +83 -0
- package/dist/nodefony/src/session/storage/sessionSort.js +53 -0
- package/dist/types/index.d.ts +83 -0
- package/dist/types/nodefony/command/assetsPublishCommand.d.ts +23 -0
- package/dist/types/nodefony/command/certificatesCommand.d.ts +17 -0
- package/dist/types/nodefony/command/networkCommand.d.ts +8 -0
- package/dist/types/nodefony/command/proxyGenerateCommand.d.ts +19 -0
- package/dist/types/nodefony/config/config.d.ts +197 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +39 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +138 -0
- package/dist/types/nodefony/interfaces/ICookie.d.ts +47 -0
- package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +55 -0
- package/dist/types/nodefony/interfaces/IHttpConfig.d.ts +12 -0
- package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -0
- package/dist/types/nodefony/interfaces/IRequest.d.ts +35 -0
- package/dist/types/nodefony/interfaces/IRequestLogger.d.ts +31 -0
- package/dist/types/nodefony/interfaces/IResponse.d.ts +39 -0
- package/dist/types/nodefony/interfaces/ISession.d.ts +283 -0
- package/dist/types/nodefony/interfaces/IUpload.d.ts +66 -0
- package/dist/types/nodefony/interfaces/index.d.ts +7 -0
- package/dist/types/nodefony/service/HttpAdminApi.d.ts +18 -0
- package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +23 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +143 -0
- package/dist/types/nodefony/service/certificates.d.ts +246 -0
- package/dist/types/nodefony/service/error-renderer.d.ts +74 -0
- package/dist/types/nodefony/service/http-kernel.d.ts +377 -0
- package/dist/types/nodefony/service/pretty-request-logger.d.ts +25 -0
- package/dist/types/nodefony/service/request-logger.d.ts +18 -0
- package/dist/types/nodefony/service/servers/clientError.d.ts +14 -0
- package/dist/types/nodefony/service/servers/server-http.d.ts +42 -0
- package/dist/types/nodefony/service/servers/server-https.d.ts +41 -0
- package/dist/types/nodefony/service/servers/server-static.d.ts +62 -0
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +29 -0
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +29 -0
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +27 -0
- package/dist/types/nodefony/service/servers/wsHeartbeat.d.ts +46 -0
- package/dist/types/nodefony/service/sessions/sessions-service.d.ts +218 -0
- package/dist/types/nodefony/service/trace.d.ts +39 -0
- package/dist/types/nodefony/service/upload/upload-service.d.ts +61 -0
- package/dist/types/nodefony/src/assets/collectAssets.d.ts +35 -0
- package/dist/types/nodefony/src/assets/prebuiltUi.d.ts +99 -0
- package/dist/types/nodefony/src/context/Context.d.ts +195 -0
- package/dist/types/nodefony/src/context/domainMatcher.d.ts +67 -0
- package/dist/types/nodefony/src/context/forwarded.d.ts +95 -0
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +85 -0
- package/dist/types/nodefony/src/context/http/Request.d.ts +203 -0
- package/dist/types/nodefony/src/context/http/Response.d.ts +68 -0
- package/dist/types/nodefony/src/context/http/parser.d.ts +65 -0
- package/dist/types/nodefony/src/context/http/urlFastPath.d.ts +52 -0
- package/dist/types/nodefony/src/context/http2/Request.d.ts +14 -0
- package/dist/types/nodefony/src/context/http2/Response.d.ts +20 -0
- package/dist/types/nodefony/src/context/metaData.d.ts +58 -0
- package/dist/types/nodefony/src/context/requestId.d.ts +28 -0
- package/dist/types/nodefony/src/context/trustProxy.d.ts +77 -0
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +53 -0
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +125 -0
- package/dist/types/nodefony/src/context/websocket/wsBackpressure.d.ts +73 -0
- package/dist/types/nodefony/src/context/websocket/wsLogContent.d.ts +37 -0
- package/dist/types/nodefony/src/cookies/cookie.d.ts +88 -0
- package/dist/types/nodefony/src/errors/httpError.d.ts +15 -0
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +110 -0
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +192 -0
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +76 -0
- package/dist/types/nodefony/src/rateLimit/IRateLimitStore.d.ts +98 -0
- package/dist/types/nodefony/src/rateLimit/MemoryRateLimitStore.d.ts +40 -0
- package/dist/types/nodefony/src/rateLimit/WsConnectionCounter.d.ts +37 -0
- package/dist/types/nodefony/src/rateLimit/rateLimitFilters.d.ts +18 -0
- package/dist/types/nodefony/src/servers/portBinder.d.ts +102 -0
- package/dist/types/nodefony/src/session/session.d.ts +171 -0
- package/dist/types/nodefony/src/session/storage/MemorySessionStorage.d.ts +77 -0
- package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +81 -0
- package/dist/types/nodefony/src/session/storage/sessionFilters.d.ts +102 -0
- package/dist/types/nodefony/src/session/storage/sessionSort.d.ts +45 -0
- package/docs/cookies.md +365 -0
- package/docs/index.md +163 -0
- package/docs/observabilite.md +460 -0
- package/docs/rate-limit.md +372 -0
- package/docs/servers.md +935 -0
- package/docs/session.md +768 -0
- package/docs/upload.md +460 -0
- package/package.json +101 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import type { IPage, IPageQuery, ISortableSource } from "nodefony";
|
|
2
|
+
import type { ICookie, ICookieOptions } from "./ICookie.js";
|
|
3
|
+
export type SessionStatusType = "none" | "active" | "disabled";
|
|
4
|
+
export type SessionStrategyType = "none" | "migrate" | "invalidate";
|
|
5
|
+
export type FlashBagType = Record<string, unknown>;
|
|
6
|
+
export type MetaBagType = Record<string, unknown>;
|
|
7
|
+
/**
|
|
8
|
+
* Intent d'activation de session déclaré par une route (décorateur `@UseSession`
|
|
9
|
+
* de `@nodefony/framework`, ou présence d'un paramètre `@Session`). Lu au point
|
|
10
|
+
* d'activation **unique** du pipeline (HTTP comme WS) : c'est lui — et non plus un
|
|
11
|
+
* `sessionAutoStart` global « démarre partout » — qui décide d'ouvrir une session.
|
|
12
|
+
*
|
|
13
|
+
* - `readOnly` : la session est lue/reprise mais **jamais persistée** (0 write storage).
|
|
14
|
+
*/
|
|
15
|
+
export interface SessionIntent {
|
|
16
|
+
readOnly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Données de session **sérialisées** échangées avec un {@link ISessionStorage}
|
|
20
|
+
* (blob opaque persisté/restauré). La forme métier riche (ProtoService/bags) est
|
|
21
|
+
* l'affaire de `Session` ; le storage ne manipule que cette projection JSON-safe.
|
|
22
|
+
*/
|
|
23
|
+
export interface ISerializedSession {
|
|
24
|
+
Attributes: Record<string, unknown>;
|
|
25
|
+
metaBag: Record<string, unknown>;
|
|
26
|
+
flashBag: Record<string, unknown>;
|
|
27
|
+
user: string;
|
|
28
|
+
createdAt?: Date;
|
|
29
|
+
updatedAt?: Date;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Projection **redactée** d'une session pour l'ADMINISTRATION (data plane Studio,
|
|
33
|
+
* `/nodefony/http/api/sessions`). Construite par allowlist — **jamais** par `delete`
|
|
34
|
+
* après coup — donc ne porte par construction ni `Attributes` ni `flashBag` (données
|
|
35
|
+
* métier potentiellement sensibles), ni l'**id de session brut** (= la valeur du
|
|
36
|
+
* cookie ; le posséder = être connecté).
|
|
37
|
+
*
|
|
38
|
+
* À la place : {@link ISessionSummary.ref}, pseudonyme HMAC stable et **non
|
|
39
|
+
* réversible** (cf `SessionsService.sessionRef`). Standard « appareils connectés »
|
|
40
|
+
* GitHub/Google : on montre une référence, jamais le jeton de session.
|
|
41
|
+
*/
|
|
42
|
+
export interface ISessionSummary {
|
|
43
|
+
/** Pseudonyme `HMAC(secret, id)` tronqué (préfixe `sess_…`). JAMAIS l'id brut. */
|
|
44
|
+
ref: string;
|
|
45
|
+
/** Identifiant de l'utilisateur porté par la session (chaîne vide = anonyme). */
|
|
46
|
+
user: string;
|
|
47
|
+
/** Vrai si la session porte un utilisateur authentifié (`user` non vide). */
|
|
48
|
+
authenticated: boolean;
|
|
49
|
+
/** IP capturée au login (`metaBag.ip`), ou `null` si non capturée / anonyme. */
|
|
50
|
+
ip: string | null;
|
|
51
|
+
/** User-Agent capturé au login (`metaBag.ua`), ou `null` si non capturé. */
|
|
52
|
+
ua: string | null;
|
|
53
|
+
/** Création de la session (epoch ms), ou `null` si inconnue. */
|
|
54
|
+
createdAt: number | null;
|
|
55
|
+
/** Dernière persistance (epoch ms), ou `null` si inconnue. */
|
|
56
|
+
updatedAt: number | null;
|
|
57
|
+
/** Réserve multi-tenant (toujours `null` en mono-tenant — slot coût-0). */
|
|
58
|
+
tenantId: string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Vrai si cette entrée est **la session qui porte la requête en cours** — le
|
|
61
|
+
* « cet appareil » des consoles d'appareils connectés.
|
|
62
|
+
*
|
|
63
|
+
* Sans elle, un client qui liste ses sessions ne peut désigner AUCUNE ligne :
|
|
64
|
+
* ni celle qu'il ne doit pas fermer, ni celle qu'il veut fermer. Deux
|
|
65
|
+
* conséquences vécues, la même cause : la console d'administration marquait
|
|
66
|
+
* « vous appartient » toutes les lignes (comparaison sur l'UTILISATEUR, jamais
|
|
67
|
+
* sur la session), et un banc révoquait « la sienne » en prenant la première
|
|
68
|
+
* de la liste — c'est-à-dire la plus récente du COMPTE, celle d'un voisin dès
|
|
69
|
+
* que deux clients partagent l'identité.
|
|
70
|
+
*
|
|
71
|
+
* `false` quand la requête ne porte pas de session (invocation CLI, appel
|
|
72
|
+
* interne) : l'énumération est alors faite par personne, aucune ligne n'est
|
|
73
|
+
* « celle-ci ».
|
|
74
|
+
*/
|
|
75
|
+
current: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Entrée **brute** d'énumération renvoyée par {@link ISessionStorage.listAll} :
|
|
79
|
+
* l'id opaque RÉEL + le blob sérialisé. Usage strictement **interne au process**
|
|
80
|
+
* (le service en a besoin pour calculer le `ref` et révoquer par id) — n'est
|
|
81
|
+
* JAMAIS sérialisée vers une réponse HTTP : `SessionsService` la projette en
|
|
82
|
+
* {@link ISessionSummary} (redaction par construction).
|
|
83
|
+
*
|
|
84
|
+
* Optimisation côté stores SQL/NoSQL : `data.Attributes`/`data.flashBag` peuvent
|
|
85
|
+
* être renvoyés vides (les secrets ne quittent alors jamais la base) — seuls
|
|
86
|
+
* `user`/`metaBag`/timestamps sont nécessaires à la projection.
|
|
87
|
+
*/
|
|
88
|
+
export interface ISessionRecord {
|
|
89
|
+
/** Identifiant opaque réel de la session (interne — jamais exposé via l'API). */
|
|
90
|
+
id: string;
|
|
91
|
+
data: ISerializedSession;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Filtre d'énumération admin de {@link ISessionStorage.listAll}. Tous les champs
|
|
95
|
+
* sont optionnels. Un store SQL peut honorer `user` (WHERE indexable) ; les autres
|
|
96
|
+
* peuvent l'ignorer — `SessionsService.listAllSessions` ré-applique le filtre de
|
|
97
|
+
* façon défensive. `tenantId` = slot multi-tenant (ignoré en mono-tenant).
|
|
98
|
+
*/
|
|
99
|
+
export interface ISessionListFilter {
|
|
100
|
+
/** Restreint aux sessions d'un utilisateur (pour « déconnecter partout »). */
|
|
101
|
+
user?: string;
|
|
102
|
+
/** Slot multi-tenant — non scopé aujourd'hui (réserve coût-0). */
|
|
103
|
+
tenantId?: string | null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Requête d'énumération **paginée** des sessions — le {@link IPageQuery} standard
|
|
107
|
+
* de Nodefony étendu des filtres propres au store de session. C'est la forme que
|
|
108
|
+
* consomme {@link ISessionStorage.listPage} ; `ISessionListFilter` reste la forme
|
|
109
|
+
* non paginée du dump {@link ISessionStorage.listAll}.
|
|
110
|
+
*
|
|
111
|
+
* **Filtres portables par construction** (`user` = égalité, `authenticated` =
|
|
112
|
+
* `user` non vide) : ils s'expriment dans tous les backends — `WHERE` SQL/Mongo
|
|
113
|
+
* indexable, prédicat mémoire — donc aucun n'oblige un store à matérialiser la
|
|
114
|
+
* collection pour filtrer.
|
|
115
|
+
*
|
|
116
|
+
* **Tri** : l'ordre du contrat est `updatedAt` DESC (session la plus récemment
|
|
117
|
+
* active d'abord), départagé par l'id pour rester **déterministe** à horodatage
|
|
118
|
+
* égal (deux sessions écrites dans la même milliseconde). Un backend curseur
|
|
119
|
+
* (Redis `SCAN`) n'a pas d'ordre global — il l'annonce, il ne le simule pas.
|
|
120
|
+
*/
|
|
121
|
+
export interface ISessionListQuery extends IPageQuery, ISessionListFilter {
|
|
122
|
+
/**
|
|
123
|
+
* Restreint aux sessions **authentifiées** (`true` : `user` non vide) ou
|
|
124
|
+
* **anonymes** (`false`). Omis = les deux. Sert les KPI de la console admin
|
|
125
|
+
* sans jamais énumérer (cf {@link ISessionStorage.countSessions}).
|
|
126
|
+
*/
|
|
127
|
+
authenticated?: boolean;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Contrat **unique** d'un backend de stockage de session (File, Redis, SQL/Drizzle…).
|
|
131
|
+
* Source de vérité unifiée — l'ex-doublon `sessionStorageInterface` (any) n'est plus
|
|
132
|
+
* qu'un alias transitionnel. Enregistré dans le registre IoC `SessionsService.registerStorage`.
|
|
133
|
+
*/
|
|
134
|
+
export interface ISessionStorage extends ISortableSource {
|
|
135
|
+
read(id: string): Promise<ISerializedSession>;
|
|
136
|
+
write(id: string, data: ISerializedSession): Promise<ISerializedSession>;
|
|
137
|
+
start(id: string): Promise<ISerializedSession>;
|
|
138
|
+
open(): Promise<number>;
|
|
139
|
+
close(): boolean;
|
|
140
|
+
destroy(id: string): Promise<boolean>;
|
|
141
|
+
/**
|
|
142
|
+
* Purge les sessions expirées sur les **deux bornes** NIST/OWASP : idle
|
|
143
|
+
* (inactivité depuis `updatedAt`) ET absolute (âge depuis `createdAt`, jamais
|
|
144
|
+
* prolongé). `absoluteSeconds` omis/0 → seul l'idle s'applique. Hors hot-path
|
|
145
|
+
* (timer `GcScheduler`). Un store à TTL natif (Redis) peut le laisser no-op
|
|
146
|
+
* pour l'idle — l'absolute restant honoré à la lecture (`isValidSession`).
|
|
147
|
+
*/
|
|
148
|
+
gc(idleSeconds?: number, absoluteSeconds?: number): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* **Prolonge l'idle timeout** d'une session active (timeout glissant) SANS
|
|
151
|
+
* réécrire son blob — `UPDATE updatedAt` (SQL), `EXPIRE` (Redis TTL), `utimes`
|
|
152
|
+
* (fichier). Capacité **optionnelle** : un store qui ne l'implémente pas voit
|
|
153
|
+
* son idle prolongé uniquement par un vrai `write` (mutation) → dégradation
|
|
154
|
+
* gracieuse, jamais un breaking.
|
|
155
|
+
*
|
|
156
|
+
* Appelé de façon **throttlée** (1 fois par tranche d'idle, jamais par requête)
|
|
157
|
+
* sur l'activité HTTP/WS — y compris en lecture seule — pour qu'une session
|
|
158
|
+
* réellement utilisée n'expire pas. N'affecte **jamais** l'absolute timeout
|
|
159
|
+
* (borné à la création).
|
|
160
|
+
*
|
|
161
|
+
* @param id - identifiant opaque de la session.
|
|
162
|
+
* @param idleSeconds - idle courant (les stores à TTL natif, ex. Redis, en ont
|
|
163
|
+
* besoin pour repositionner l'expiration).
|
|
164
|
+
*/
|
|
165
|
+
touch?(id: string, idleSeconds?: number): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Énumère les sessions persistées — capacité d'**ADMINISTRATION** (gouvernance,
|
|
168
|
+
* « déconnecter partout »), **optionnelle**. Un backend incapable de lister
|
|
169
|
+
* (KV sans scan, edge…) l'omet : l'endpoint admin répond alors **501** (refus
|
|
170
|
+
* honnête), jamais une liste vide trompeuse.
|
|
171
|
+
*
|
|
172
|
+
* Renvoie des {@link ISessionRecord} bruts (id réel + blob) — le service les
|
|
173
|
+
* redacte. Coût **O(N)** assumé (admin, faible fréquence, pas de hot-path) ;
|
|
174
|
+
* un index inverse `user → [id]` reste une optimisation future.
|
|
175
|
+
*
|
|
176
|
+
* @param filter - restriction optionnelle (ex. `user`) ; un store peut l'honorer
|
|
177
|
+
* (WHERE SQL) ou l'ignorer (le service ré-applique).
|
|
178
|
+
*/
|
|
179
|
+
listAll?(filter?: ISessionListFilter): Promise<ISessionRecord[]>;
|
|
180
|
+
/**
|
|
181
|
+
* Énumère **une page** de sessions — la capacité d'administration NORMALE
|
|
182
|
+
* (console, « mes appareils », révocation). Contrairement à {@link listAll},
|
|
183
|
+
* un store conforme ne matérialise **jamais** plus d'une page : `LIMIT/OFFSET`
|
|
184
|
+
* SQL, `skip/limit` Mongo, `SCAN` par curseur Redis, tranche mémoire. C'est ce
|
|
185
|
+
* qui rend le coût d'une requête admin indépendant du nombre de sessions.
|
|
186
|
+
*
|
|
187
|
+
* Optionnelle, comme {@link listAll} : un backend incapable d'énumérer l'omet
|
|
188
|
+
* → l'endpoint admin répond **501** (refus honnête), jamais une page vide
|
|
189
|
+
* trompeuse.
|
|
190
|
+
*
|
|
191
|
+
* **Redaction par construction — garantie du contrat, pas une optimisation** :
|
|
192
|
+
* les records rendus portent `Attributes` et `flashBag` **vides**. Les données
|
|
193
|
+
* métier d'une session (potentiellement des secrets) n'ont aucune raison de
|
|
194
|
+
* traverser la couche d'administration : les stores SQL/NoSQL ne les
|
|
195
|
+
* sélectionnent pas, les stores mémoire ne les recopient pas. Un appelant qui
|
|
196
|
+
* oublierait la projection en {@link ISessionSummary} ne peut donc pas les
|
|
197
|
+
* faire fuiter. (Le dump {@link listAll}, lui, reste libre de les porter.)
|
|
198
|
+
*
|
|
199
|
+
* **Deux modes, déclarés par le store dans sa réponse** :
|
|
200
|
+
* - **offset** — `total` exact (sauf `withTotal:false`) et ordre `updatedAt`
|
|
201
|
+
* DESC déterministe ; `nextCursor` absent.
|
|
202
|
+
* - **curseur** — `nextCursor` à repasser en {@link IPageQuery.cursor}, pas de
|
|
203
|
+
* `total` ni d'ordre global, taille de page variable. Le client boucle
|
|
204
|
+
* jusqu'à `nextCursor === null`. Capacité réduite **assumée**, pas simulée.
|
|
205
|
+
*
|
|
206
|
+
* @param query - page + filtres ({@link ISessionListQuery}).
|
|
207
|
+
* @returns la page de {@link ISessionRecord} bruts — le service les redacte.
|
|
208
|
+
*/
|
|
209
|
+
listPage?(query: ISessionListQuery): Promise<IPage<ISessionRecord>>;
|
|
210
|
+
/**
|
|
211
|
+
* Compte les sessions correspondant aux filtres, **sans les énumérer** (`COUNT`
|
|
212
|
+
* natif SQL/Mongo). Alimente les KPI de la console (total, authentifiées vs
|
|
213
|
+
* anonymes) sans jamais charger de collection.
|
|
214
|
+
*
|
|
215
|
+
* @param query - les **filtres** seuls. Un comptage n'a pas de fenêtre : `limit`
|
|
216
|
+
* et `offset` n'y ont aucun sens, d'où le `Partial` (le contrat de page rend
|
|
217
|
+
* `limit` obligatoire, ce qui obligerait l'appelant à inventer une valeur
|
|
218
|
+
* ignorée).
|
|
219
|
+
* @returns le total exact, ou **`-1`** si le backend ne sait pas compter à coût
|
|
220
|
+
* raisonnable (Redis : compter = re-`SCAN` tout le keyspace). `-1` est un
|
|
221
|
+
* « je ne sais pas » explicite — l'appelant affiche l'inconnu, il ne l'invente pas.
|
|
222
|
+
*/
|
|
223
|
+
countSessions?(query?: Partial<ISessionListQuery>): Promise<number>;
|
|
224
|
+
/**
|
|
225
|
+
* Compte les utilisateurs **distincts** portant au moins une session active.
|
|
226
|
+
*
|
|
227
|
+
* Ce n'est pas un {@link countSessions} avec un filtre de plus : dédupliquer
|
|
228
|
+
* exige de regrouper, là où compter n'exige que de parcourir. C'est pourquoi
|
|
229
|
+
* la capacité vit ici, sur le store, et non dans la table de facettes de la
|
|
230
|
+
* ressource — un `COUNT(DISTINCT …)` est trivial en SQL, direct en Mongo, et
|
|
231
|
+
* hors de portée d'un backend en curseur.
|
|
232
|
+
*
|
|
233
|
+
* Sépare deux nombres que la console confondrait sinon : « 400 sessions » et
|
|
234
|
+
* « 12 personnes connectées » ne racontent pas la même chose sur un parc.
|
|
235
|
+
*
|
|
236
|
+
* @param query - mêmes filtres que {@link countSessions} ; le décompte porte
|
|
237
|
+
* sur le sous-ensemble filtré.
|
|
238
|
+
* @returns le nombre d'utilisateurs distincts, ou **`-1`** si le backend ne
|
|
239
|
+
* sait pas agréger (même convention que {@link countSessions}).
|
|
240
|
+
*/
|
|
241
|
+
countDistinctUsers?(query?: Partial<ISessionListQuery>): Promise<number>;
|
|
242
|
+
}
|
|
243
|
+
export interface ISession {
|
|
244
|
+
id: string;
|
|
245
|
+
name: string;
|
|
246
|
+
status: SessionStatusType;
|
|
247
|
+
saved: boolean;
|
|
248
|
+
/** Vrai si la session a été mutée sans être encore persistée (dirty-tracking). */
|
|
249
|
+
dirty: boolean;
|
|
250
|
+
/** Lecture seule : la session est reprise mais jamais persistée (0 write storage). */
|
|
251
|
+
readOnly: boolean;
|
|
252
|
+
migrated: boolean;
|
|
253
|
+
cookieSession: ICookie | null | undefined;
|
|
254
|
+
flashBag: FlashBagType;
|
|
255
|
+
strategy: SessionStrategyType;
|
|
256
|
+
created?: Date;
|
|
257
|
+
updated?: Date;
|
|
258
|
+
user?: string;
|
|
259
|
+
lifetime?: number;
|
|
260
|
+
storage: ISessionStorage;
|
|
261
|
+
start(context: unknown): Promise<ISession>;
|
|
262
|
+
save(user?: string): Promise<ISession>;
|
|
263
|
+
invalidate(lifetime?: number, id?: string, options?: ICookieOptions): Promise<ISession>;
|
|
264
|
+
destroy(cookieDelete?: boolean): Promise<boolean>;
|
|
265
|
+
create(lifetime: number, id?: string, options?: ICookieOptions): ISession;
|
|
266
|
+
/** Régénère un identifiant opaque CSPRNG en conservant l'état (anti session-fixation, appelée au login par `AuthFlow`). */
|
|
267
|
+
regenerateId(): void;
|
|
268
|
+
get(key: string): unknown;
|
|
269
|
+
set(key: string, value: unknown): unknown;
|
|
270
|
+
getAttributes(): unknown;
|
|
271
|
+
getMetaBag(key: string): unknown;
|
|
272
|
+
setMetaBag(key: string, value: unknown): unknown;
|
|
273
|
+
getMetas(): MetaBagType;
|
|
274
|
+
getFlashBag(key: string): unknown;
|
|
275
|
+
setFlashBag(key: string, value: unknown): unknown;
|
|
276
|
+
flashBags(): FlashBagType;
|
|
277
|
+
clearFlashBag(key: string): void;
|
|
278
|
+
clearFlashBags(): void;
|
|
279
|
+
getName(): string;
|
|
280
|
+
checkStatus(): "restart" | boolean;
|
|
281
|
+
serialize(user?: string): ISerializedSession;
|
|
282
|
+
deSerialize(data: ISerializedSession): void;
|
|
283
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forme neutre d'un fichier multipart parsé (temp écrit sur disque), découplée
|
|
3
|
+
* du parser sous-jacent. Remplace l'ex-couplage à `formidable.File` : le moteur
|
|
4
|
+
* de parsing (busboy) n'apparaît plus dans le contrat consommé par
|
|
5
|
+
* `UploadedFile`. Champs alignés sur ce que lit `UploadedFile.create`.
|
|
6
|
+
*/
|
|
7
|
+
export interface IParsedUploadFile {
|
|
8
|
+
/** Chemin absolu du fichier temporaire écrit sur disque. */
|
|
9
|
+
filepath: string;
|
|
10
|
+
/** Nom de fichier temporaire généré (UUID + extension). */
|
|
11
|
+
newFilename: string;
|
|
12
|
+
/** Nom de fichier d'origine déclaré par le client (peut être null). */
|
|
13
|
+
originalFilename: string | null;
|
|
14
|
+
/** Type MIME déclaré dans la part multipart. */
|
|
15
|
+
mimetype: string | null;
|
|
16
|
+
/** Taille réellement écrite sur disque (octets). */
|
|
17
|
+
size: number;
|
|
18
|
+
/** Date d'écriture du temporaire. */
|
|
19
|
+
mtime: Date | null;
|
|
20
|
+
/** Algorithme de hash appliqué pendant le stream, ou `false` si aucun. */
|
|
21
|
+
hashAlgorithm: false | "sha1" | "md5" | "sha256";
|
|
22
|
+
/** Hash hexadécimal du contenu si `hashAlgorithm` configuré, sinon null. */
|
|
23
|
+
hash: string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Options du sous-système d'upload (clé de config `upload`). Mappées sur les
|
|
27
|
+
* `limits` de busboy + la gestion temp/hash propre à Nodefony.
|
|
28
|
+
*/
|
|
29
|
+
export interface IUploadOptions {
|
|
30
|
+
/** Répertoire de dépôt des fichiers temporaires. */
|
|
31
|
+
uploadDir?: string;
|
|
32
|
+
/** Taille max d'UN fichier (octets) — `limits.fileSize` busboy. */
|
|
33
|
+
maxFileSize?: number;
|
|
34
|
+
/** Taille max CUMULÉE des fichiers d'une requête (octets) — appliquée par Nodefony. */
|
|
35
|
+
maxTotalFileSize?: number;
|
|
36
|
+
/** Nombre max de fichiers — `limits.files` busboy. */
|
|
37
|
+
maxFiles?: number;
|
|
38
|
+
/** Nombre max de champs texte — `limits.fields` busboy. */
|
|
39
|
+
maxFields?: number;
|
|
40
|
+
/** Taille max d'un champ texte (octets) — `limits.fieldSize` busboy. */
|
|
41
|
+
maxFieldsSize?: number;
|
|
42
|
+
/** Hash calculé pendant le stream (défaut `false` = aucun). */
|
|
43
|
+
hashAlgorithm?: false | "sha1" | "md5" | "sha256";
|
|
44
|
+
/** Encodage par défaut des parts texte. */
|
|
45
|
+
encoding?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface IUploadedFile {
|
|
48
|
+
filename: string;
|
|
49
|
+
size: number;
|
|
50
|
+
prettySize: string;
|
|
51
|
+
mimeType: string | null | undefined;
|
|
52
|
+
hash: string | null | undefined;
|
|
53
|
+
hashAlgorithm: false | "sha1" | "md5" | "sha256";
|
|
54
|
+
lastModifiedDate: Date | null | undefined;
|
|
55
|
+
move(target: string): IUploadedFile;
|
|
56
|
+
/** Variante non bloquante de `move()` (recommandée dans le pipeline). */
|
|
57
|
+
moveAsync(target: string): Promise<IUploadedFile>;
|
|
58
|
+
getMimeType(): string | null | undefined;
|
|
59
|
+
getSize(): number;
|
|
60
|
+
getPrettySize(): string;
|
|
61
|
+
}
|
|
62
|
+
export interface IUploadService {
|
|
63
|
+
path?: string | unknown;
|
|
64
|
+
/** Construit un `UploadedFile` en async (stat non bloquant). */
|
|
65
|
+
createUploadFile(file: unknown, name: string): Promise<IUploadedFile>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { ICookie, ICookieOptions, IWsCookie, SameSiteType, PriorityType, } from "./ICookie.js";
|
|
2
|
+
export type { ISession, ISessionStorage, ISerializedSession, SessionIntent, SessionStatusType, SessionStrategyType, FlashBagType, MetaBagType, } from "./ISession.js";
|
|
3
|
+
export type { IUploadedFile, IUploadService, IParsedUploadFile, IUploadOptions, } from "./IUpload.js";
|
|
4
|
+
export type { IRequest, IHttpRequest, IHttp2Request, IWsRequest, HTTPMethodType, } from "./IRequest.js";
|
|
5
|
+
export type { IResponse, IHttpResponse, IWebsocketResponse } from "./IResponse.js";
|
|
6
|
+
export type { IContext, IHttpContext, IWebsocketContext, ServerType, SchemeType, WebSocketStateType, CookiesMap, } from "./IContext.js";
|
|
7
|
+
export type { IHttpKernel } from "./IHttpKernel.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Module } from "nodefony";
|
|
2
|
+
import type { IAdminApi } from "nodefony";
|
|
3
|
+
/**
|
|
4
|
+
* Producteur `IAdminApi` du module **http** — exposé sous `/nodefony/http/api/*`.
|
|
5
|
+
*
|
|
6
|
+
* 2ᵉ producteur du data plane admin (le 1er étant le kernel). Démontre le
|
|
7
|
+
* pattern multi-modules : `@nodefony/http` n'importe QUE le contrat core
|
|
8
|
+
* (`IAdminApi`) — jamais `@nodefony/framework` (dépendance circulaire). Il
|
|
9
|
+
* s'enregistre auprès du broker via `IAdminRegistry` récupéré du container.
|
|
10
|
+
*
|
|
11
|
+
* Endpoints :
|
|
12
|
+
* - `GET /nodefony/http/api/servers` → liste des serveurs réseau + leur état
|
|
13
|
+
* - `GET /nodefony/http/api/info` → résumé (serveurs prêts, ports, schemes)
|
|
14
|
+
*
|
|
15
|
+
* @param module - le module http (accès aux services serveur du container).
|
|
16
|
+
* @returns le contrat admin de http, prêt à `registry.register()`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createHttpAdminApi(module: Module): IAdminApi;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IAdminApi } from "nodefony";
|
|
2
|
+
import type { Profiler } from "../src/profiler/Profiler.js";
|
|
3
|
+
/**
|
|
4
|
+
* Producteur `IAdminApi` du **profiler** — exposé sous `/nodefony/profiler/api/*`.
|
|
5
|
+
*
|
|
6
|
+
* Namespace dédié (≠ replié dans `http`) car le profiling par requête est un
|
|
7
|
+
* concern transverse : timing par phase, route, user, futur SQL/audit. Il a sa
|
|
8
|
+
* propre entrée Studio et n'est monté qu'en **dev** (le module n'instancie le
|
|
9
|
+
* {@link Profiler} qu'hors prod).
|
|
10
|
+
*
|
|
11
|
+
* Endpoints :
|
|
12
|
+
* - `GET /nodefony/profiler/api/recent` → derniers profils (résumés, récent → ancien)
|
|
13
|
+
* - `GET /nodefony/profiler/api/{id}` → profil complet (phases) d'un requestId
|
|
14
|
+
* - `DELETE /nodefony/profiler/api/recent` → vide le ring buffer
|
|
15
|
+
*
|
|
16
|
+
* La debug bar (toute page, dev) lit `X-Request-Id` de SON appel AJAX puis
|
|
17
|
+
* fetch `/{id}` — corrélation client↔serveur gratuite.
|
|
18
|
+
*
|
|
19
|
+
* @param profiler - l'instance partagée du ring buffer (même que le hook kernel).
|
|
20
|
+
* @returns le contrat admin du profiler, prêt à `registry.register()`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createProfilerAdminApi(profiler: Profiler): IAdminApi;
|
|
23
|
+
export default createProfilerAdminApi;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Severity } from "nodefony";
|
|
2
|
+
import type { IRequestLogger, IRequestLogEntry } from "../interfaces/IRequestLogger.js";
|
|
3
|
+
import type { IHttpContext, IWebsocketContext } from "../interfaces/IContext.js";
|
|
4
|
+
/**
|
|
5
|
+
* Canonical audit log entry — 1 JSON PDU per request (P3.1).
|
|
6
|
+
*
|
|
7
|
+
* Fed to `context.log()` as a stringified JSON payload. Ingest pipelines
|
|
8
|
+
* (Vision, Loki, ELK, OpenTelemetry...) can parse it directly without
|
|
9
|
+
* regex on a colored line.
|
|
10
|
+
*
|
|
11
|
+
* Includes P3.3 (severity per HTTP status) for free and exposes phase
|
|
12
|
+
* timings (P1.1) for downstream trace tools (P3.7).
|
|
13
|
+
*
|
|
14
|
+
* Header redaction (P3.4) — Authorization / Cookie / Set-Cookie are
|
|
15
|
+
* never serialised here. We log presence-only flags instead.
|
|
16
|
+
*/
|
|
17
|
+
export interface AuditLogEntry {
|
|
18
|
+
ts: string;
|
|
19
|
+
requestId: string;
|
|
20
|
+
userId: string | null;
|
|
21
|
+
type: "http" | "ws";
|
|
22
|
+
scheme: string;
|
|
23
|
+
method: string | null;
|
|
24
|
+
url: string;
|
|
25
|
+
status: number | null;
|
|
26
|
+
durationMs: number | null;
|
|
27
|
+
remoteAddress: string | null;
|
|
28
|
+
host: string | null;
|
|
29
|
+
userAgent: string | null;
|
|
30
|
+
hasAuthorization: boolean;
|
|
31
|
+
hasCookie: boolean;
|
|
32
|
+
phases?: {
|
|
33
|
+
name: string;
|
|
34
|
+
durationMs: number | null;
|
|
35
|
+
}[];
|
|
36
|
+
error?: AuditErrorEntry;
|
|
37
|
+
protocol?: string | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Enriched error description for audit logs (P3.5).
|
|
41
|
+
* Includes optional cause chain (Error.cause) and stack (dev only).
|
|
42
|
+
*/
|
|
43
|
+
export interface AuditErrorEntry {
|
|
44
|
+
name: string;
|
|
45
|
+
message: string;
|
|
46
|
+
code?: number;
|
|
47
|
+
/** nodefonyError's domain classifier when available (P1.5 / Phase 1). */
|
|
48
|
+
errorType?: string;
|
|
49
|
+
/** Multi-line stack — dev/development only, omitted in prod for safety. */
|
|
50
|
+
stack?: string;
|
|
51
|
+
/** Recursive — capped to depth 5 to avoid pathological cycles. */
|
|
52
|
+
cause?: AuditErrorEntry;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Severity derived from HTTP status code — RFC 9110 categories.
|
|
56
|
+
* 1xx/2xx/3xx → INFO ; 4xx → WARNING ; 5xx → ERROR.
|
|
57
|
+
* Unknown/missing status → INFO.
|
|
58
|
+
*/
|
|
59
|
+
declare function severityFromStatus(status: number | null | undefined): Severity;
|
|
60
|
+
export interface JsonAuditLoggerOptions {
|
|
61
|
+
/**
|
|
62
|
+
* Whether to include `error.stack` and recursive `error.cause.stack`.
|
|
63
|
+
* Default `process.env.NODE_ENV !== "production"` — auto-hidden in prod.
|
|
64
|
+
* Override explicitly for stricter security or to enable in staging.
|
|
65
|
+
*/
|
|
66
|
+
includeStack?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Max depth for `Error.cause` chain serialisation.
|
|
69
|
+
* Default `5`. Prevents pathological cycles and oversized log entries.
|
|
70
|
+
*/
|
|
71
|
+
maxCauseDepth?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Sampling rate for nominal (2xx/3xx) audit logs — perf lever on the hot
|
|
74
|
+
* path (L3). `1` (default) logs every request. `N > 1` logs only 1 in N of
|
|
75
|
+
* the 2xx/3xx requests, **but always logs `status >= 400` and errored
|
|
76
|
+
* requests** (you never lose a failure). Counted with a deterministic
|
|
77
|
+
* counter — no RNG (consistent with the L2 entropy amortisation).
|
|
78
|
+
*
|
|
79
|
+
* Skipped requests never reach `renderHttp`, so they cost **zero** object
|
|
80
|
+
* allocation and zero `JSON.stringify`. Configure via
|
|
81
|
+
* `kernel.options.log.requestLogger.sampleRate`.
|
|
82
|
+
*/
|
|
83
|
+
sampleRate?: number;
|
|
84
|
+
/**
|
|
85
|
+
* T1 (profil delta vs Express) — audit du chemin NOMINAL (2xx/3xx).
|
|
86
|
+
* `false` → seules les requêtes en erreur et les `status >= 400` sont
|
|
87
|
+
* auditées (jamais gâtées — OWASP, faible volume, valeur forensique max).
|
|
88
|
+
* Résolu AU BOOT par `HttpKernel.applyRequestLoggerFromConfig` depuis
|
|
89
|
+
* `log.requestLogger.nominal` (`"auto"` défaut = coupé SSI
|
|
90
|
+
* `log.driver === "null"`, où l'entrée d'audit n'atteint AUCUNE destination
|
|
91
|
+
* texte — elle coûtait objet + toISOString + stringify + Pdu ring pour rien,
|
|
92
|
+
* ~5,9 % du profil CPU). Défaut `true` (comportement historique).
|
|
93
|
+
*/
|
|
94
|
+
nominal?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* JSON audit logger — implements IRequestLogger so it slots into
|
|
98
|
+
* `httpKernel.setRequestLogger(new JsonAuditLogger())`.
|
|
99
|
+
*
|
|
100
|
+
* Stateless singleton. Allocates one plain object + one JSON.stringify per
|
|
101
|
+
* request — acceptable since this is the terminal log path (1 per req).
|
|
102
|
+
*/
|
|
103
|
+
declare class JsonAuditLogger implements IRequestLogger {
|
|
104
|
+
private readonly includeStack;
|
|
105
|
+
private readonly maxCauseDepth;
|
|
106
|
+
/** Sampling divisor for 2xx/3xx logs (`1` = log all). Always ≥ 1. */
|
|
107
|
+
private readonly sampleRate;
|
|
108
|
+
/** Deterministic 0-based counter for `1/sampleRate` selection (no RNG). */
|
|
109
|
+
private sampleCounter;
|
|
110
|
+
/** T1 — `false` = audit nominal coupé (erreurs/4xx/5xx toujours audités). */
|
|
111
|
+
private readonly nominalEnabled;
|
|
112
|
+
constructor(opts?: JsonAuditLoggerOptions);
|
|
113
|
+
/**
|
|
114
|
+
* Decide whether the current HTTP request must be logged (audit sampling).
|
|
115
|
+
*
|
|
116
|
+
* Always `true` when `sampleRate <= 1`, on errors, and for `status >= 400`
|
|
117
|
+
* (failures are never sampled out). Otherwise selects 1 in `sampleRate` of
|
|
118
|
+
* the 2xx/3xx requests with a deterministic counter.
|
|
119
|
+
*
|
|
120
|
+
* Called by `Context.logRequest()` **before** `renderHttp`, so a sampled-out
|
|
121
|
+
* request allocates nothing and runs no `JSON.stringify`.
|
|
122
|
+
*
|
|
123
|
+
* @param context - the HTTP context being finalised
|
|
124
|
+
* @param error - error captured for this request, if any
|
|
125
|
+
* @returns `true` to render+log the entry, `false` to skip it
|
|
126
|
+
*/
|
|
127
|
+
shouldSample(context: IHttpContext, error?: Error | null): boolean;
|
|
128
|
+
renderHttp(context: IHttpContext, error?: Error | null): IRequestLogEntry;
|
|
129
|
+
renderWebsocket(context: IWebsocketContext, error?: Error | null, acceptedProtocol?: string | null): IRequestLogEntry;
|
|
130
|
+
/**
|
|
131
|
+
* Total request duration computed from the first phase startMs to now.
|
|
132
|
+
* Returns null if timing is disabled (no phases recorded).
|
|
133
|
+
*/
|
|
134
|
+
private computeDurationMs;
|
|
135
|
+
/**
|
|
136
|
+
* Serialise an Error (recursively for `cause` chain) into an AuditErrorEntry.
|
|
137
|
+
* Stack is included only when `includeStack === true` (dev default).
|
|
138
|
+
* Cause chain is capped at `maxCauseDepth`.
|
|
139
|
+
*/
|
|
140
|
+
private serializeError;
|
|
141
|
+
}
|
|
142
|
+
export default JsonAuditLogger;
|
|
143
|
+
export { severityFromStatus };
|