@nodefony/http 10.0.0-beta.2 → 10.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  2. package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
  3. package/dist/index.js +19 -11
  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 +45 -6
  9. package/dist/nodefony/config/defineModuleConfig.js +2 -3
  10. package/dist/nodefony/interfaces/IReverseProxy.js +1 -0
  11. package/dist/nodefony/service/HttpAdminApi.js +2 -3
  12. package/dist/nodefony/service/ProfilerAdminApi.js +1 -2
  13. package/dist/nodefony/service/audit-logger.js +22 -16
  14. package/dist/nodefony/service/certificates.js +228 -19
  15. package/dist/nodefony/service/error-renderer.js +30 -13
  16. package/dist/nodefony/service/http-kernel.js +331 -82
  17. package/dist/nodefony/service/pretty-request-logger.js +5 -4
  18. package/dist/nodefony/service/request-logger.js +24 -16
  19. package/dist/nodefony/service/reverse-proxy.js +336 -0
  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 +7 -10
  25. package/dist/nodefony/service/servers/server-websocket-secure.js +18 -18
  26. package/dist/nodefony/service/servers/server-websocket.js +18 -18
  27. package/dist/nodefony/service/servers/serverShutdown.js +78 -18
  28. package/dist/nodefony/service/servers/wsHeartbeat.js +32 -17
  29. package/dist/nodefony/service/sessions/sessions-service.js +34 -20
  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 +51 -32
  36. package/dist/nodefony/src/context/domainMatcher.js +21 -4
  37. package/dist/nodefony/src/context/forwarded.js +3 -4
  38. package/dist/nodefony/src/context/http/HttpContext.js +98 -46
  39. package/dist/nodefony/src/context/http/Request.js +34 -38
  40. package/dist/nodefony/src/context/http/Response.js +39 -7
  41. package/dist/nodefony/src/context/http/SseStream.js +349 -0
  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 +22 -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/responseEnded.js +26 -0
  49. package/dist/nodefony/src/context/transport.js +45 -0
  50. package/dist/nodefony/src/context/trustProxy.js +1 -2
  51. package/dist/nodefony/src/context/websocket/Response.js +42 -6
  52. package/dist/nodefony/src/context/websocket/WebsocketContext.js +94 -20
  53. package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
  54. package/dist/nodefony/src/context/websocket/websocketUrl.js +20 -0
  55. package/dist/nodefony/src/context/websocket/wsBackpressure.js +1 -2
  56. package/dist/nodefony/src/context/websocket/wsCloseReason.js +1 -2
  57. package/dist/nodefony/src/context/websocket/wsLogContent.js +1 -2
  58. package/dist/nodefony/src/cookies/cookie.js +3 -7
  59. package/dist/nodefony/src/errors/httpError.js +1 -2
  60. package/dist/nodefony/src/errors/thrownMessage.js +1 -2
  61. package/dist/nodefony/src/profiler/FrameProfile.js +1 -2
  62. package/dist/nodefony/src/profiler/Profiler.js +108 -30
  63. package/dist/nodefony/src/profiler/aggregate.js +422 -0
  64. package/dist/nodefony/src/proxy/forward.js +500 -0
  65. package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
  66. package/dist/nodefony/src/proxy/rules.js +188 -0
  67. package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +1 -2
  68. package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +1 -2
  69. package/dist/nodefony/src/rateLimit/rateLimitFilters.js +1 -2
  70. package/dist/nodefony/src/servers/http2Limits.js +50 -0
  71. package/dist/nodefony/src/servers/kernelServers.js +12 -3
  72. package/dist/nodefony/src/servers/portBinder.js +41 -10
  73. package/dist/nodefony/src/servers/upgradeDispatch.js +128 -0
  74. package/dist/nodefony/src/session/session.js +30 -15
  75. package/dist/nodefony/src/session/sessionStoreFailure.js +1 -2
  76. package/dist/nodefony/src/session/storage/MemorySessionStorage.js +1 -2
  77. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +1 -2
  78. package/dist/nodefony/src/session/storage/sessionFilters.js +1 -2
  79. package/dist/nodefony/src/session/storage/sessionSort.js +1 -2
  80. package/dist/types/index.d.ts +12 -3
  81. package/dist/types/nodefony/config/config.d.ts +16 -0
  82. package/dist/types/nodefony/interfaces/IContext.d.ts +65 -4
  83. package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +1 -1
  84. package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
  85. package/dist/types/nodefony/interfaces/IReverseProxy.d.ts +67 -0
  86. package/dist/types/nodefony/interfaces/IRouting.d.ts +26 -0
  87. package/dist/types/nodefony/interfaces/ISecurity.d.ts +6 -0
  88. package/dist/types/nodefony/interfaces/index.d.ts +3 -2
  89. package/dist/types/nodefony/service/audit-logger.d.ts +2 -1
  90. package/dist/types/nodefony/service/certificates.d.ts +147 -5
  91. package/dist/types/nodefony/service/error-renderer.d.ts +32 -1
  92. package/dist/types/nodefony/service/http-kernel.d.ts +157 -15
  93. package/dist/types/nodefony/service/reverse-proxy.d.ts +156 -0
  94. package/dist/types/nodefony/service/servers/clientError.d.ts +16 -0
  95. package/dist/types/nodefony/service/servers/server-h2c.d.ts +51 -0
  96. package/dist/types/nodefony/service/servers/server-http.d.ts +3 -3
  97. package/dist/types/nodefony/service/servers/server-https.d.ts +4 -4
  98. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +2 -3
  99. package/dist/types/nodefony/service/servers/server-websocket.d.ts +2 -3
  100. package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
  101. package/dist/types/nodefony/service/servers/wsHeartbeat.d.ts +26 -0
  102. package/dist/types/nodefony/service/sessions/sessions-service.d.ts +19 -1
  103. package/dist/types/nodefony/src/context/Context.d.ts +35 -6
  104. package/dist/types/nodefony/src/context/domainMatcher.d.ts +11 -1
  105. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +48 -1
  106. package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
  107. package/dist/types/nodefony/src/context/http/Response.d.ts +14 -0
  108. package/dist/types/nodefony/src/context/http/SseStream.d.ts +155 -0
  109. package/dist/types/nodefony/src/context/http/parser.d.ts +18 -2
  110. package/dist/types/nodefony/src/context/http2/Request.d.ts +1 -1
  111. package/dist/types/nodefony/src/context/http2/Response.d.ts +15 -5
  112. package/dist/types/nodefony/src/context/responseEnded.d.ts +20 -0
  113. package/dist/types/nodefony/src/context/transport.d.ts +53 -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 +57 -7
  116. package/dist/types/nodefony/src/context/websocket/isWebsocketContext.d.ts +15 -0
  117. package/dist/types/nodefony/src/context/websocket/websocketUrl.d.ts +16 -0
  118. package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +13 -12
  119. package/dist/types/nodefony/src/profiler/Profiler.d.ts +69 -15
  120. package/dist/types/nodefony/src/profiler/aggregate.d.ts +215 -0
  121. package/dist/types/nodefony/src/proxy/forward.d.ts +168 -0
  122. package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
  123. package/dist/types/nodefony/src/proxy/rules.d.ts +88 -0
  124. package/dist/types/nodefony/src/servers/http2Limits.d.ts +37 -0
  125. package/dist/types/nodefony/src/servers/kernelServers.d.ts +10 -1
  126. package/dist/types/nodefony/src/servers/portBinder.d.ts +29 -5
  127. package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +69 -0
  128. package/dist/types/nodefony/src/session/session.d.ts +2 -0
  129. package/docs/cookies.md +85 -78
  130. package/docs/index.md +5 -1
  131. package/docs/observabilite.md +81 -81
  132. package/docs/rate-limit.md +83 -83
  133. package/docs/reverse-proxy.md +522 -0
  134. package/docs/servers.md +378 -230
  135. package/docs/session.md +180 -180
  136. package/docs/sse.md +287 -0
  137. package/docs/upload.md +120 -117
  138. package/package.json +10 -11
  139. package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
@@ -1,4 +1,4 @@
1
- //#region nodefony/src/context/websocket/wsCloseReason.ts
1
+ // nodefony/src/context/websocket/wsCloseReason.ts
2
2
  /**
3
3
  * Taille maximale, en octets UTF-8, de la raison d'une trame Close (RFC 6455
4
4
  * §5.5 : charge d'une trame de contrôle ≤ 125 octets, dont 2 pour le code).
@@ -37,5 +37,4 @@ function toWsCloseReason(reason) {
37
37
  }
38
38
  return reason.slice(0, end) + ELLIPSIS;
39
39
  }
40
- //#endregion
41
40
  export { MAX_WS_CLOSE_REASON_BYTES, toWsCloseReason };
@@ -1,4 +1,4 @@
1
- //#region nodefony/src/context/websocket/wsLogContent.ts
1
+ // nodefony/src/context/websocket/wsLogContent.ts
2
2
  /**
3
3
  * Formatage **pur** (0 état, 0 dépendance) du CONTENU d'un message WebSocket pour
4
4
  * le log de trace (Suivi de requête Studio). Séparé de `WebsocketContext` pour
@@ -64,5 +64,4 @@ function formatWsLogContent(data, cap = WS_LOG_CONTENT_CAP) {
64
64
  if (typeof s !== "string") return String(data);
65
65
  return s.length > cap ? `${s.slice(0, cap)}…` : s;
66
66
  }
67
- //#endregion
68
67
  export { WS_LOG_CONTENT_CAP, binaryByteLength, formatWsLogContent };
@@ -2,7 +2,7 @@ import { extend } from "nodefony";
2
2
  import crypto from "node:crypto";
3
3
  import { parseCookie } from "cookie";
4
4
  import MS from "ms";
5
- //#region nodefony/src/cookies/cookie.ts
5
+ // nodefony/src/cookies/cookie.ts
6
6
  const encode = encodeURIComponent;
7
7
  const decode = decodeURIComponent;
8
8
  const cookieDefaultSettings = {
@@ -20,11 +20,9 @@ function parser(strToParse) {
20
20
  }
21
21
  function cookiesParser(context) {
22
22
  let cookies = null;
23
- let co = null;
23
+ let co;
24
24
  switch (context.type) {
25
25
  case "http":
26
- case "https":
27
- case "http2":
28
26
  if (context.request.request.headers.cookie) cookies = context.request.request.headers.cookie;
29
27
  if (cookies) {
30
28
  const obj = parser(cookies);
@@ -35,8 +33,7 @@ function cookiesParser(context) {
35
33
  if (context.response) context.request.request.cookies = context.response.cookies;
36
34
  }
37
35
  break;
38
- case "websocket":
39
- case "websocket-secure": {
36
+ case "websocket": {
40
37
  const cookieHeader = context.request?.headers.cookie;
41
38
  if (cookieHeader) {
42
39
  const parsed = parser(cookieHeader);
@@ -254,5 +251,4 @@ var Cookie = class Cookie {
254
251
  return obj;
255
252
  }
256
253
  };
257
- //#endregion
258
254
  export { cookiesParser, Cookie as default, parser };
@@ -1,5 +1,5 @@
1
1
  import { clc, nodefonyError } from "nodefony";
2
- //#region nodefony/src/errors/httpError.ts
2
+ // nodefony/src/errors/httpError.ts
3
3
  const exclude = {
4
4
  context: true,
5
5
  resolver: true,
@@ -65,5 +65,4 @@ var HttpError = class extends nodefonyError {
65
65
  }
66
66
  };
67
67
  Object.defineProperty(HttpError.prototype, "toJSON", jsonHttpError);
68
- //#endregion
69
68
  export { HttpError as default };
@@ -1,4 +1,4 @@
1
- //#region nodefony/src/errors/thrownMessage.ts
1
+ // nodefony/src/errors/thrownMessage.ts
2
2
  /**
3
3
  * Message lisible d'une valeur LEVÉE (`catch`, rejet) dont on ignore le type.
4
4
  *
@@ -23,5 +23,4 @@ function thrownMessage(value) {
23
23
  case "function": return value === null ? "null" : Object.prototype.toString.call(value);
24
24
  }
25
25
  }
26
- //#endregion
27
26
  export { thrownMessage };
@@ -1,5 +1,5 @@
1
1
  import { thrownMessage } from "../errors/thrownMessage.js";
2
- //#region nodefony/src/profiler/FrameProfile.ts
2
+ // nodefony/src/profiler/FrameProfile.ts
3
3
  /**
4
4
  * Profil d'**une invocation** du pont RPC (une frame `api.request`), et non de
5
5
  * la connexion qui la porte.
@@ -92,5 +92,4 @@ var FrameProfile = class {
92
92
  if (error !== void 0 && error !== null) this.error = { message: thrownMessage(error) };
93
93
  }
94
94
  };
95
- //#endregion
96
95
  export { FrameProfile, FrameProfile as default };
@@ -1,12 +1,20 @@
1
- //#region nodefony/src/profiler/Profiler.ts
2
- /** Durée totale = (fin de la dernière phase terminée) − (début de la 1ère). */
1
+ import { profileLane } from "./aggregate.js";
2
+ // nodefony/src/profiler/Profiler.ts
3
+ /**
4
+ * Durée totale = (fin de la dernière phase) − (début de la 1ère).
5
+ *
6
+ * Une phase encore OUVERTE à la collecte (action interrompue par le départ du
7
+ * client : flux SSE, outil MCP annulé) court jusqu'à la collecte — la compter
8
+ * pour zéro publiait 0,3 ms pour un appel coupé à 3 s.
9
+ */
3
10
  function totalDuration(phases) {
4
11
  if (phases.length === 0) return null;
5
12
  const first = phases.at(0);
6
13
  if (first === void 0 || typeof first.startMs !== "number") return null;
7
14
  let end = first.startMs;
15
+ let now = null;
8
16
  for (const p of phases) {
9
- const e = p.startMs + (p.durationMs ?? 0);
17
+ const e = p.durationMs === void 0 ? now ??= performance.now() : p.startMs + p.durationMs;
10
18
  if (e > end) end = e;
11
19
  }
12
20
  const d = end - first.startMs;
@@ -14,11 +22,41 @@ function totalDuration(phases) {
14
22
  }
15
23
  /** Capacité par défaut du ring buffer (assez pour une session de debug). */
16
24
  const DEFAULT_CAP = 500;
25
+ /**
26
+ * Capacité par défaut des deux anneaux annexes : l'OUTILLAGE (console, API
27
+ * d'administration, porte MCP) et les ASSETS relayés de Vite. Un chargement
28
+ * de la console relaie à lui seul quelques centaines de modules : dans un
29
+ * tampon commun, il chassait en quelques secondes les profils de
30
+ * l'application, puis ceux des appels de la console elle-même.
31
+ */
32
+ const DEFAULT_SIDE_CAP = 100;
33
+ /**
34
+ * Collecteur des profils de requête, en un anneau par provenance
35
+ * ({@link profileLane}) : l'application, l'outillage, les assets relayés.
36
+ * Chacun évince ses propres profils — ouvrir la console ne chasse plus celui
37
+ * qu'on y consulte.
38
+ */
17
39
  var Profiler = class {
18
- _buf = /* @__PURE__ */ new Map();
19
- _cap;
20
- constructor(cap = DEFAULT_CAP) {
21
- this._cap = cap > 0 ? cap : DEFAULT_CAP;
40
+ _lanes = {
41
+ app: /* @__PURE__ */ new Map(),
42
+ tooling: /* @__PURE__ */ new Map(),
43
+ asset: /* @__PURE__ */ new Map()
44
+ };
45
+ _caps;
46
+ /** Dernier numéro d'ordre attribué (0 = aucun profil collecté). */
47
+ _seq = 0;
48
+ /**
49
+ * @param cap - capacité de l'anneau de l'application.
50
+ * @param sideCap - capacité de chacun des anneaux de l'outillage et des
51
+ * assets relayés.
52
+ */
53
+ constructor(cap = DEFAULT_CAP, sideCap = DEFAULT_SIDE_CAP) {
54
+ const side = sideCap > 0 ? sideCap : DEFAULT_SIDE_CAP;
55
+ this._caps = {
56
+ app: cap > 0 ? cap : DEFAULT_CAP,
57
+ tooling: side,
58
+ asset: side
59
+ };
22
60
  }
23
61
  /**
24
62
  * Snapshot un `Context` en fin de requête. Tolérant aux champs absents
@@ -32,8 +70,9 @@ var Profiler = class {
32
70
  const phases = ctx.phases ?? [];
33
71
  const entry = {
34
72
  requestId,
73
+ seq: ++this._seq,
35
74
  ts: Date.now(),
36
- kind: ctx.type === "websocket" || ctx.type === "websocket-secure" ? "ws" : "http",
75
+ kind: ctx.type === "websocket" ? "ws" : "http",
37
76
  method: ctx.method ?? null,
38
77
  url: ctx.url ?? "",
39
78
  scheme: ctx.scheme ?? "",
@@ -54,24 +93,30 @@ var Profiler = class {
54
93
  queries: ctx.profilerQueries && ctx.profilerQueries.length > 0 ? ctx.profilerQueries : void 0,
55
94
  security: readSecurity(ctx)
56
95
  };
57
- this._buf.delete(requestId);
58
- this._buf.set(requestId, entry);
59
- if (this._buf.size > this._cap) {
60
- const oldest = this._buf.keys().next().value;
61
- if (oldest !== void 0) this._buf.delete(oldest);
96
+ const { app, tooling, asset } = this._lanes;
97
+ app.delete(requestId);
98
+ tooling.delete(requestId);
99
+ asset.delete(requestId);
100
+ const lane = profileLane(entry.url);
101
+ const buf = this._lanes[lane];
102
+ buf.set(requestId, entry);
103
+ if (buf.size > this._caps[lane]) {
104
+ const oldest = buf.keys().next().value;
105
+ if (oldest !== void 0) buf.delete(oldest);
62
106
  }
63
107
  }
64
108
  /** Profil complet d'une requête, ou `undefined` si évincé/inconnu. */
65
109
  get(requestId) {
66
- return this._buf.get(requestId);
110
+ const { app, tooling, asset } = this._lanes;
111
+ return app.get(requestId) ?? tooling.get(requestId) ?? asset.get(requestId);
67
112
  }
68
113
  /**
69
114
  * Résumés des requêtes les plus récentes (récent → ancien), capés à `limit`.
70
115
  */
71
116
  recent(limit = 60) {
72
117
  const out = [];
73
- const entries = [...this._buf.values()];
74
- for (const e of entries.reverse()) {
118
+ const entries = this.#collectAfter(0).reverse();
119
+ for (const e of entries) {
75
120
  if (out.length >= limit) break;
76
121
  out.push({
77
122
  requestId: e.requestId,
@@ -87,13 +132,51 @@ var Profiler = class {
87
132
  }
88
133
  return out;
89
134
  }
135
+ /** Dernier numéro d'ordre attribué : le point de départ d'une lecture suivie. */
136
+ get cursor() {
137
+ return this._seq;
138
+ }
139
+ /**
140
+ * Profils collectés après `cursor`, tous anneaux confondus, dans l'ordre
141
+ * des numéros.
142
+ *
143
+ * Les pertes se comptent sans estimation : chaque numéro attribué est un
144
+ * profil collecté, donc ce qui manque entre le curseur et le dernier numéro
145
+ * a disparu avant d'être lu. Un curseur venu d'ailleurs (supérieur au
146
+ * dernier numéro) repart de zéro.
147
+ *
148
+ * @param cursor - le `cursor` rendu par l'appel précédent, ou {@link cursor}.
149
+ */
150
+ since(cursor) {
151
+ const from = cursor >= 0 && cursor <= this._seq ? cursor : 0;
152
+ const entries = this.#collectAfter(from);
153
+ return {
154
+ entries,
155
+ cursor: this._seq,
156
+ lost: this._seq - from - entries.length
157
+ };
158
+ }
90
159
  /** Nombre de profils en mémoire. */
91
160
  get size() {
92
- return this._buf.size;
161
+ const { app, tooling, asset } = this._lanes;
162
+ return app.size + tooling.size + asset.size;
93
163
  }
94
- /** Vide le ring buffer. */
164
+ /** Vide les anneaux. */
95
165
  clear() {
96
- this._buf.clear();
166
+ const { app, tooling, asset } = this._lanes;
167
+ app.clear();
168
+ tooling.clear();
169
+ asset.clear();
170
+ }
171
+ /**
172
+ * Profils de numéro supérieur à `from`, tous anneaux confondus, du plus
173
+ * ancien au plus récent. Lecture à la demande (console, outil de l'agent) :
174
+ * jamais sur le chemin d'une requête.
175
+ */
176
+ #collectAfter(from) {
177
+ const out = [];
178
+ for (const lane of Object.values(this._lanes)) for (const e of lane.values()) if (e.seq > from) out.push(e);
179
+ return out.sort((a, b) => a.seq - b.seq);
97
180
  }
98
181
  };
99
182
  /**
@@ -122,18 +205,13 @@ function readSecurity(ctx) {
122
205
  /**
123
206
  * Identité de la requête.
124
207
  *
125
- * `context.user` n'est PAS la source de vérité en zone firewall : le token
126
- * résolu vit dans l'ALS (`RequestContext`), qui n'est plus lisible au teardown
127
- * où `collect()` s'exécute. Sans le repli sur la trace, une requête pleinement
128
- * authentifiée (rôles compris) s'afficherait « anonyme ».
208
+ * Le jeton résolu vit dans l'ALS, qui n'est plus lisible au teardown où
209
+ * `collect()` s'exécute : le pare-feu pose donc l'identifiant de l'appelant
210
+ * authentifié sur `context.userIdentifier` (même source que le journal
211
+ * d'audit). La trace du pare-feu prend le relais pour l'anonyme d'une zone
212
+ * `anonymous`, qu'elle nomme.
129
213
  */
130
214
  function readUser(ctx) {
131
- const u = ctx.user;
132
- if (u && typeof u === "object") {
133
- const username = u.username;
134
- if (typeof username === "string") return username;
135
- }
136
- return ctx.securityTrace?.user ?? null;
215
+ return ctx.userIdentifier ?? ctx.securityTrace?.user ?? null;
137
216
  }
138
- //#endregion
139
217
  export { Profiler, Profiler as default };