@nodefony/http 10.0.0-alpha.6 → 10.0.0-alpha.7

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.
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
1
+ //#region \0@oxc-project+runtime@0.150.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.149.0/helpers/esm/decorateMetadata.js
1
+ //#region \0@oxc-project+runtime@0.150.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
  }
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorateParam.js
1
+ //#region \0@oxc-project+runtime@0.150.0/helpers/esm/decorateParam.js
2
2
  function __decorateParam(paramIndex, decorator) {
3
3
  return function(target, key) {
4
4
  decorator(target, key, paramIndex);
package/dist/index.js CHANGED
@@ -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.149.0/helpers/esm/decorateMetadata.js";
25
- import __decorate from "./_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
24
+ import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
25
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.150.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";
@@ -12,9 +12,9 @@ import DefaultRequestLogger from "./request-logger.js";
12
12
  import JsonAuditLogger from "./audit-logger.js";
13
13
  import PrettyRequestLogger from "./pretty-request-logger.js";
14
14
  import { resolveTraceparent } from "./trace.js";
15
- import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
16
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
17
- import { GcScheduler, Module, RequestContext, Service, injectable, logColor, nodefonyError, writeRuntimeState } from "nodefony";
15
+ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
16
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
17
+ import { GcScheduler, Module, RequestContext, Service, injectable, logColor, nodefonyError, servedUrl, writeRuntimeState } from "nodefony";
18
18
  import cluster from "node:cluster";
19
19
  import Ws from "ws";
20
20
  //#region nodefony/service/http-kernel.ts
@@ -597,6 +597,10 @@ let HttpKernel = class HttpKernel extends Service {
597
597
  * Le port DÉSIRÉ est publié à côté du port obtenu : un outil peut ainsi dire
598
598
  * « tu voulais 5151, tu écoutes sur 5153 » sans deviner.
599
599
  *
600
+ * L'ADRESSE complète l'est aussi, et pour la même raison : un port ne dit pas
601
+ * son protocole. Le serveur est le seul à savoir lequel de ses ports parle TLS ;
602
+ * un lecteur qui le devine se trompe le jour où la topologie change.
603
+ *
600
604
  * **Publié dans TOUS les environnements, production comprise.** Le glissement de
601
605
  * port (`auto`) n'est pas la seule façon de sortir de la convention : une app qui
602
606
  * déclare son port (PaaS `PORT`, ingress, `servers.http.port`) écoute ailleurs
@@ -615,13 +619,16 @@ let HttpKernel = class HttpKernel extends Service {
615
619
  publishRuntimePorts(serverHttp, serverHttps) {
616
620
  const ports = [];
617
621
  const desired = [];
622
+ const urls = [];
618
623
  if (serverHttp?.active && serverHttp.port > 0) {
619
624
  ports.push(serverHttp.port);
625
+ urls.push(servedUrl("http", serverHttp.domain, serverHttp.port));
620
626
  const d = this.kernel?.options.servers?.http;
621
627
  if (d && d.port) desired.push(d.port);
622
628
  }
623
629
  if (serverHttps?.active && serverHttps.port > 0) {
624
630
  ports.push(serverHttps.port);
631
+ urls.push(servedUrl("https", serverHttps.domain, serverHttps.port));
625
632
  const d = this.kernel?.options.servers?.https;
626
633
  if (d && d.port) desired.push(d.port);
627
634
  }
@@ -629,7 +636,8 @@ let HttpKernel = class HttpKernel extends Service {
629
636
  writeRuntimeState(process.cwd(), {
630
637
  pid: cluster.isWorker ? process.ppid ?? process.pid : process.pid,
631
638
  ports,
632
- desiredPorts: desired.length > 0 ? desired : void 0
639
+ desiredPorts: desired.length > 0 ? desired : void 0,
640
+ urls
633
641
  });
634
642
  }
635
643
  /**
@@ -1,10 +1,10 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
4
  import { handleClientError } from "./clientError.js";
5
5
  import { createDrainTerminator } from "./serverShutdown.js";
6
6
  import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
7
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
7
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateParam.js";
8
8
  import { Module, Service, extend, inject, nodefonyError } from "nodefony";
9
9
  import http from "node:http";
10
10
  //#region nodefony/service/servers/server-http.ts
@@ -1,10 +1,10 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
4
  import { handleClientError } from "./clientError.js";
5
5
  import { createDrainTerminator } from "./serverShutdown.js";
6
6
  import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
7
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
7
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateParam.js";
8
8
  import { Module, Service, extend, inject, nodefonyError } from "nodefony";
9
9
  import http2 from "node:http2";
10
10
  import https from "node:https";
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateParam.js";
5
5
  import { startHeartbeat, trackPong } from "./wsHeartbeat.js";
6
6
  import { Module, Service, inject } from "nodefony";
7
7
  import Ws, { WebSocketServer } from "ws";
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateParam.js";
5
5
  import { startHeartbeat, trackPong } from "./wsHeartbeat.js";
6
6
  import { Module, Service, inject } from "nodefony";
7
7
  import Ws, { WebSocketServer } from "ws";
@@ -1,8 +1,8 @@
1
1
  import { SESSION_FACETS } from "../../src/session/storage/sessionFilters.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
5
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateParam.js";
6
6
  import Session from "../../src/session/session.js";
7
7
  import MemorySessionStorage from "../../src/session/storage/MemorySessionStorage.js";
8
8
  import RevocationGuardStorage from "../../src/session/storage/RevocationGuardStorage.js";
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateParam.js";
5
5
  import { Cli, FileClass, Module, Service, inject } from "nodefony";
6
6
  import fs from "node:fs";
7
7
  import fsp from "node:fs/promises";
@@ -6,6 +6,7 @@ import HttpRequest from "./Request.js";
6
6
  import HttpResponse from "./Response.js";
7
7
  import Http2Request from "../http2/Request.js";
8
8
  import Http2Response from "../http2/Response.js";
9
+ import { describeSessionStoreFailure } from "../../session/sessionStoreFailure.js";
9
10
  import { extend, typeOf } from "nodefony";
10
11
  import url, { URL } from "node:url";
11
12
  //#region nodefony/src/context/http/HttpContext.ts
@@ -156,7 +157,10 @@ var HttpContext = class extends Context {
156
157
  return this.send(data, encoding);
157
158
  }
158
159
  async end() {
159
- return this.saveSession().then(async (_session) => {
160
+ return this.saveSession().catch((e) => {
161
+ this.log(describeSessionStoreFailure(e).message, "CRITIC", "SESSION-STORE");
162
+ return null;
163
+ }).then(async (_session) => {
160
164
  return this.close().catch((e) => {
161
165
  throw e;
162
166
  });
@@ -180,8 +184,16 @@ var HttpContext = class extends Context {
180
184
  }
181
185
  async #doSend(chunk, encoding) {
182
186
  try {
183
- if (this.session != null) await this.saveSession();
184
- if (chunk) this.response?.setBody(chunk);
187
+ let body = chunk;
188
+ if (this.session != null) try {
189
+ await this.saveSession();
190
+ } catch (e) {
191
+ const failure = describeSessionStoreFailure(e);
192
+ this.log(failure.message, "CRITIC", "SESSION-STORE");
193
+ this.response.statusCode = failure.statusCode;
194
+ body = failure.body;
195
+ }
196
+ if (body) this.response?.setBody(body);
185
197
  if (this.listenerCount("onSend") > 0) await this.fireAsync("onSend", this.response, this);
186
198
  try {
187
199
  this.writeHead();
@@ -189,7 +201,7 @@ var HttpContext = class extends Context {
189
201
  this.log(e, "WARNING");
190
202
  }
191
203
  if (this.isRedirect) return await this.close();
192
- return await this.write(chunk, encoding);
204
+ return await this.write(body, encoding);
193
205
  } catch (error) {
194
206
  this.log(error, "ERROR");
195
207
  throw error;
@@ -222,6 +234,7 @@ var HttpContext = class extends Context {
222
234
  }
223
235
  async close() {
224
236
  if (this.listenerCount("onClose") > 0) await this.fireAsync("onClose", this);
237
+ if (this.response?.response?.writableEnded) return this.response;
225
238
  return this.response.end().then(() => this.response).catch((e) => {
226
239
  throw e;
227
240
  });
@@ -297,6 +297,10 @@ var HttpResponse = class {
297
297
  if (!this.response) throw new Error(`Http Response not found`);
298
298
  if (this.body === null) this.body = Buffer.alloc(0);
299
299
  const res = this.response;
300
+ if (!_flush && !this.flushing && !res.writableEnded) {
301
+ res.end(this.body, encoding || this.encoding);
302
+ return this;
303
+ }
300
304
  return new Promise((resolve) => {
301
305
  let settled = false;
302
306
  const done = () => {
@@ -0,0 +1,29 @@
1
+ //#region nodefony/src/session/sessionStoreFailure.ts
2
+ /**
3
+ * Reconnaît une table de session absente, quel que soit le dialecte.
4
+ *
5
+ * Chaque moteur formule le même défaut à sa façon, et aucun ne porte de code
6
+ * commun : SQLite dit « no such table », PostgreSQL « relation ... does not
7
+ * exist », MySQL « doesn't exist ». On reste volontairement large — le seul
8
+ * enjeu est de savoir s'il faut PARLER DE MIGRATIONS, jamais de trancher une
9
+ * branche de code.
10
+ */
11
+ function looksLikeMissingTable(text) {
12
+ return /no such table/i.test(text) || /does ?n[o']?t exist/i.test(text) || /undefined table/i.test(text) || /unknown table/i.test(text);
13
+ }
14
+ /**
15
+ * Compose la réponse et le journal d'une panne du stockage de session.
16
+ *
17
+ * @param error - l'erreur remontée par le store.
18
+ * @returns le statut, le corps à servir, et la ligne de journal.
19
+ */
20
+ function describeSessionStoreFailure(error) {
21
+ const cause = error instanceof Error ? error.message : String(error ?? "cause inconnue");
22
+ return {
23
+ statusCode: 500,
24
+ body: "Internal Server Error — session store unavailable",
25
+ message: `La session n'a pas pu être persistée — la requête est servie en 500 plutôt que laissée sans réponse. Cause : ${cause}.${looksLikeMissingTable(cause) ? " La table de session est probablement absente : appliquer les migrations (`nodefony orm:migrate`) avant de servir, ou choisir un autre stockage (`http.session.store`)." : " Vérifier que le stockage de session déclaré sous `http.session.store` est joignable ; à défaut, appliquer les migrations (`nodefony orm:migrate`)."}`
26
+ };
27
+ }
28
+ //#endregion
29
+ export { describeSessionStoreFailure };
@@ -314,6 +314,10 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
314
314
  * Le port DÉSIRÉ est publié à côté du port obtenu : un outil peut ainsi dire
315
315
  * « tu voulais 5151, tu écoutes sur 5153 » sans deviner.
316
316
  *
317
+ * L'ADRESSE complète l'est aussi, et pour la même raison : un port ne dit pas
318
+ * son protocole. Le serveur est le seul à savoir lequel de ses ports parle TLS ;
319
+ * un lecteur qui le devine se trompe le jour où la topologie change.
320
+ *
317
321
  * **Publié dans TOUS les environnements, production comprise.** Le glissement de
318
322
  * port (`auto`) n'est pas la seule façon de sortir de la convention : une app qui
319
323
  * déclare son port (PaaS `PORT`, ingress, `servers.http.port`) écoute ailleurs
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Ce qu'on répond quand le stockage de session est inutilisable.
3
+ *
4
+ * Une requête doit TOUJOURS recevoir une réponse. La sauvegarde de session a
5
+ * lieu juste avant l'écriture des en-têtes : une exception qui remonte de là
6
+ * laisse la socket ouverte, et le client attend jusqu'à son propre délai. Rien
7
+ * dans ses journaux ne nomme la cause — l'exploitant cherche du côté du réseau
8
+ * ou de la charge, et le banc de tenue dans la durée est resté rouge dix jours
9
+ * pour cette seule raison.
10
+ *
11
+ * Le cas de loin le plus courant est une application mise en ligne **sans ses
12
+ * migrations** : la table de session n'existe pas, et `session.store: "auto"`
13
+ * retombe sur SQLite dès que l'ORM est chargé. C'est un oubli banal que le
14
+ * framework doit rendre BRUYANT.
15
+ *
16
+ * Pourquoi 500 et non une réponse normale : une session qu'on ne sait pas
17
+ * persister est une **dégradation**, pas une indisponibilité passagère. Servir
18
+ * 200 laisserait croire que la connexion a été retenue alors que rien n'a été
19
+ * écrit — c'est exactement la dégradation silencieuse que ce framework refuse.
20
+ */
21
+ /** La conduite à tenir : ce qu'on répond, et ce qu'on écrit dans le journal. */
22
+ export interface ISessionStoreFailure {
23
+ /** Le statut à servir — toujours 500 : le serveur n'a pas tenu sa part. */
24
+ statusCode: number;
25
+ /** Le corps servi au client : sobre, il ne divulgue aucun détail interne. */
26
+ body: string;
27
+ /** La ligne de journal côté serveur — elle NOMME la cause et le remède. */
28
+ message: string;
29
+ }
30
+ /**
31
+ * Compose la réponse et le journal d'une panne du stockage de session.
32
+ *
33
+ * @param error - l'erreur remontée par le store.
34
+ * @returns le statut, le corps à servir, et la ligne de journal.
35
+ */
36
+ export declare function describeSessionStoreFailure(error: unknown): ISessionStoreFailure;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodefony/http",
3
- "version": "10.0.0-alpha.6",
3
+ "version": "10.0.0-alpha.7",
4
4
  "description": "Serveurs HTTP, HTTPS, HTTP/2 et WebSocket natifs pour Nodefony : sessions, contextes de requête, certificats TLS",
5
5
  "author": "Christophe CAMENSULI <ccamensuli@gmail.com>",
6
6
  "main": "./dist/index.js",
@@ -66,26 +66,26 @@
66
66
  "@types/chai": "5.2.3",
67
67
  "@types/mime-types": "3.0.1",
68
68
  "@types/ms": "2.1.0",
69
- "@types/node": "26.5.1",
69
+ "@types/node": "26.6.1",
70
70
  "@types/node-forge": "1.3.14",
71
71
  "@types/qs": "6.15.1",
72
72
  "@types/serve-static": "2.2.0",
73
73
  "@types/websocket": "1.0.10",
74
74
  "@types/ws": "8.18.1",
75
75
  "@types/xml2js": "0.4.14",
76
- "@vitest/coverage-v8": "5.0.0",
76
+ "@vitest/coverage-v8": "5.0.1",
77
77
  "chai": "6.2.2",
78
- "nodefony": "^10.0.0-alpha.6",
78
+ "nodefony": "^10.0.0-alpha.7",
79
79
  "rimraf": "6.1.3",
80
80
  "tsx": "4.23.13",
81
- "vitest": "5.0.0"
81
+ "vitest": "5.0.1"
82
82
  },
83
83
  "license": "Apache-2.0",
84
84
  "readmeFilename": "README.md",
85
85
  "contributors": [],
86
86
  "peerDependencies": {
87
- "nodefony": "^10.0.0-alpha.6",
88
- "zod": "^4.6.1"
87
+ "nodefony": "^10.0.0-alpha.7",
88
+ "zod": "^4.6.5"
89
89
  },
90
90
  "files": [
91
91
  "dist",