@nodefony/frontend 10.0.0-alpha.5 → 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.
- package/README.md +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.149.0 → _@oxc-project_runtime@0.150.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.149.0 → _@oxc-project_runtime@0.150.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/index.js +2 -2
- package/dist/nodefony/service/FrontendService.js +5 -4
- package/dist/nodefony/service/ViteProcessSupervisor.js +32 -6
- package/dist/types/nodefony/service/ViteProcessSupervisor.d.ts +24 -3
- package/docs/index.md +13 -9
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -282,7 +282,7 @@ Le `TemplateHelper` injecte automatiquement le preamble React Fast Refresh pour
|
|
|
282
282
|
|
|
283
283
|
### Vite démarre sur un autre port que `devPort`
|
|
284
284
|
|
|
285
|
-
Le port configuré est pris. Le supervisor retry automatiquement sur `port+1`, `port+2` (option `portRetryAttempts`). Vérifie le port résolu via `svc.status().port
|
|
285
|
+
Le port configuré est pris. Le supervisor retry automatiquement sur `port+1`, `port+2` (option `portRetryAttempts`). Vérifie le port résolu via `svc.status().port` — il vaut `null` tant qu'aucun port n'a été résolu, jamais le port demandé : un port qu'on espère n'est pas un port qui sert.
|
|
286
286
|
|
|
287
287
|
### Le browser refuse le cert HTTPS de Vite (`https: true`)
|
|
288
288
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
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.
|
|
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
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,8 +10,8 @@ import { ViteBuilder } from "./nodefony/src/builders/ViteBuilder.js";
|
|
|
10
10
|
import { ViteConfigGenerator } from "./nodefony/service/ViteConfigGenerator.js";
|
|
11
11
|
import { ViteProcessSupervisor } from "./nodefony/service/ViteProcessSupervisor.js";
|
|
12
12
|
import { TemplateHelper } from "./nodefony/src/template/TemplateHelper.js";
|
|
13
|
-
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.
|
|
14
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.
|
|
13
|
+
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
|
|
14
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
|
|
15
15
|
import FrontendService_default from "./nodefony/service/FrontendService.js";
|
|
16
16
|
import { buildFrontendStatus, createFrontendAdminApi } from "./nodefony/src/FrontendAdminApi.js";
|
|
17
17
|
import FrontendBuild from "./nodefony/command/frontend-build.js";
|
|
@@ -5,9 +5,9 @@ import { allowedHostPatternForTemplate, detectRemoteDev, isLoopbackHostname, isV
|
|
|
5
5
|
import { ViteProcessSupervisor } from "./ViteProcessSupervisor.js";
|
|
6
6
|
import { TemplateHelper } from "../src/template/TemplateHelper.js";
|
|
7
7
|
import { familyPortBlocks, familyPortPlan, isolationGroup } from "../src/isolationGroups.js";
|
|
8
|
-
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.
|
|
9
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
10
|
-
import { Module, Service, extend, injectable, stripTrailingSlashes } from "nodefony";
|
|
8
|
+
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorateMetadata.js";
|
|
9
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js";
|
|
10
|
+
import { FRONTEND_CHOICES, Module, Service, extend, injectable, stripTrailingSlashes } from "nodefony";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import fs from "node:fs";
|
|
13
13
|
//#region nodefony/service/FrontendService.ts
|
|
@@ -102,7 +102,8 @@ let FrontendService = class FrontendService extends Service {
|
|
|
102
102
|
const env = this.kernel?.environment;
|
|
103
103
|
if (env === "development" && this.cfg.autoStartInDevelopment) {
|
|
104
104
|
if (this.entries.length === 0) {
|
|
105
|
-
|
|
105
|
+
const engines = FRONTEND_CHOICES.filter((c) => c !== "none").join("|");
|
|
106
|
+
this.log(`aucune interface web n'est déclarée — le serveur de développement Vite ne démarre pas. Pour en poser une : \`nodefony create front [nom] --frontend <${engines}>\``, "WARNING");
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
108
109
|
const names = this.entries.map((e) => e.entryName);
|
|
@@ -49,17 +49,38 @@ const DEFAULTS = {
|
|
|
49
49
|
portRetryAttempts: 3
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
52
|
-
* Un texte (message d'erreur OU sortie brute de Vite) dénonce-t-il un
|
|
52
|
+
* Un texte (message d'erreur OU sortie brute de Vite) dénonce-t-il un conflit de
|
|
53
|
+
* port FATAL — c'est-à-dire un port que vite n'a PAS pu prendre ?
|
|
53
54
|
*
|
|
54
55
|
* **Source UNIQUE** de cette décision. Elle était dupliquée en deux regex qui ont
|
|
55
56
|
* divergé : l'une cherchait `port X is in use`, alors que Vite écrit
|
|
56
57
|
* `Port 5173 is ALREADY in use`. Résultat, le retry de port ne se déclenchait
|
|
57
58
|
* jamais et la seconde app perdait tout son frontend — un conflit de port pourtant
|
|
58
|
-
* parfaitement rattrapable. On
|
|
59
|
-
*
|
|
59
|
+
* parfaitement rattrapable. On ne l'écrit donc qu'ici (deux implémentations d'une
|
|
60
|
+
* même règle = dérive garantie).
|
|
61
|
+
*
|
|
62
|
+
* 🔴 **`already` est OBLIGATOIRE, et c'est tout le sujet.** Vite émet trois textes
|
|
63
|
+
* qui contiennent « in use », et un seul signifie qu'il a échoué — vérifié au
|
|
64
|
+
* SOURCE de `httpServerStart` (vite 8.3.0, `dist/node/chunks/node.js`) :
|
|
65
|
+
*
|
|
66
|
+
* - `Port X is already in use` → `throw` : vite MEURT. Seul cas rattrapable par
|
|
67
|
+
* un repli sur `port + 1`.
|
|
68
|
+
* - `Port X is in use, trying another one…` → `logger.info` : vite cherche
|
|
69
|
+
* lui-même le port suivant et annoncera son `Local:`. Rien à rattraper.
|
|
70
|
+
* - `Port X is in use on a wildcard address, but H:X is available…` →
|
|
71
|
+
* `logger.warn` sur un `listen` **RÉUSSI** : vite sert sur le port demandé.
|
|
72
|
+
*
|
|
73
|
+
* Rendre `already` optionnel confondait les trois. La conséquence ne se voyait
|
|
74
|
+
* pas sur un poste, où vite meurt vite : elle se voyait sur un agent partagé et
|
|
75
|
+
* lent, via `startupTimeoutMessage`. Un démarrage qui avait seulement AVERTI
|
|
76
|
+
* (wildcard) puis dépassé l'échéance sortait sous un libellé `EADDRINUSE:`, donc
|
|
77
|
+
* `spawnWithPortRetry` repliait — et chaque repli repayait l'échéance entière.
|
|
78
|
+
* Mesuré sur la forge : le cas d'intégration du décalage de port tué à 60 s
|
|
79
|
+
* (`4 tentatives × startupTimeoutMs`) quand les cinq autres du même fichier
|
|
80
|
+
* tenaient en 300 à 700 ms.
|
|
60
81
|
*/
|
|
61
82
|
function isPortInUseMessage(text) {
|
|
62
|
-
return /EADDRINUSE/i.test(text) || /address already in use/i.test(text) || /port\s+\d+\s+is\s+
|
|
83
|
+
return /EADDRINUSE/i.test(text) || /address already in use/i.test(text) || /port\s+\d+\s+is\s+already\s+in use/i.test(text);
|
|
63
84
|
}
|
|
64
85
|
/**
|
|
65
86
|
* Message d'échec d'un boot qui n'a jamais rendu la main dans le temps imparti —
|
|
@@ -183,7 +204,7 @@ var ViteProcessSupervisor = class {
|
|
|
183
204
|
state: this.state,
|
|
184
205
|
host: this.opts.devHost,
|
|
185
206
|
origin: this.resolvedOrigin,
|
|
186
|
-
port: this.resolvedPort
|
|
207
|
+
port: this.resolvedPort,
|
|
187
208
|
pid: this.child?.pid ?? null,
|
|
188
209
|
lastError: this.lastError,
|
|
189
210
|
entries: this.entries,
|
|
@@ -201,6 +222,7 @@ var ViteProcessSupervisor = class {
|
|
|
201
222
|
const maxAttempts = this.cfg.portRetryAttempts;
|
|
202
223
|
let lastErr = null;
|
|
203
224
|
this.portRetries = 0;
|
|
225
|
+
this.resolvedPort = null;
|
|
204
226
|
for (let i = 0; i <= maxAttempts; i++) {
|
|
205
227
|
const port = this.opts.devPort + i;
|
|
206
228
|
try {
|
|
@@ -475,7 +497,11 @@ var ViteProcessSupervisor = class {
|
|
|
475
497
|
*/
|
|
476
498
|
pingVite() {
|
|
477
499
|
return new Promise((resolve, reject) => {
|
|
478
|
-
const port = this.resolvedPort
|
|
500
|
+
const port = this.resolvedPort;
|
|
501
|
+
if (port === null) {
|
|
502
|
+
reject(/* @__PURE__ */ new Error("aucun port résolu — rien à sonder"));
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
479
505
|
const req = (this.opts.https ? https : http).request({
|
|
480
506
|
hostname: browserReachableHost(this.opts.devHost),
|
|
481
507
|
port,
|
|
@@ -74,14 +74,35 @@ export interface ViteSupervisorOptions {
|
|
|
74
74
|
readonly portRetryAttempts?: number;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
* Un texte (message d'erreur OU sortie brute de Vite) dénonce-t-il un
|
|
77
|
+
* Un texte (message d'erreur OU sortie brute de Vite) dénonce-t-il un conflit de
|
|
78
|
+
* port FATAL — c'est-à-dire un port que vite n'a PAS pu prendre ?
|
|
78
79
|
*
|
|
79
80
|
* **Source UNIQUE** de cette décision. Elle était dupliquée en deux regex qui ont
|
|
80
81
|
* divergé : l'une cherchait `port X is in use`, alors que Vite écrit
|
|
81
82
|
* `Port 5173 is ALREADY in use`. Résultat, le retry de port ne se déclenchait
|
|
82
83
|
* jamais et la seconde app perdait tout son frontend — un conflit de port pourtant
|
|
83
|
-
* parfaitement rattrapable. On
|
|
84
|
-
*
|
|
84
|
+
* parfaitement rattrapable. On ne l'écrit donc qu'ici (deux implémentations d'une
|
|
85
|
+
* même règle = dérive garantie).
|
|
86
|
+
*
|
|
87
|
+
* 🔴 **`already` est OBLIGATOIRE, et c'est tout le sujet.** Vite émet trois textes
|
|
88
|
+
* qui contiennent « in use », et un seul signifie qu'il a échoué — vérifié au
|
|
89
|
+
* SOURCE de `httpServerStart` (vite 8.3.0, `dist/node/chunks/node.js`) :
|
|
90
|
+
*
|
|
91
|
+
* - `Port X is already in use` → `throw` : vite MEURT. Seul cas rattrapable par
|
|
92
|
+
* un repli sur `port + 1`.
|
|
93
|
+
* - `Port X is in use, trying another one…` → `logger.info` : vite cherche
|
|
94
|
+
* lui-même le port suivant et annoncera son `Local:`. Rien à rattraper.
|
|
95
|
+
* - `Port X is in use on a wildcard address, but H:X is available…` →
|
|
96
|
+
* `logger.warn` sur un `listen` **RÉUSSI** : vite sert sur le port demandé.
|
|
97
|
+
*
|
|
98
|
+
* Rendre `already` optionnel confondait les trois. La conséquence ne se voyait
|
|
99
|
+
* pas sur un poste, où vite meurt vite : elle se voyait sur un agent partagé et
|
|
100
|
+
* lent, via `startupTimeoutMessage`. Un démarrage qui avait seulement AVERTI
|
|
101
|
+
* (wildcard) puis dépassé l'échéance sortait sous un libellé `EADDRINUSE:`, donc
|
|
102
|
+
* `spawnWithPortRetry` repliait — et chaque repli repayait l'échéance entière.
|
|
103
|
+
* Mesuré sur la forge : le cas d'intégration du décalage de port tué à 60 s
|
|
104
|
+
* (`4 tentatives × startupTimeoutMs`) quand les cinq autres du même fichier
|
|
105
|
+
* tenaient en 300 à 700 ms.
|
|
85
106
|
*/
|
|
86
107
|
export declare function isPortInUseMessage(text: string): boolean;
|
|
87
108
|
/**
|
package/docs/index.md
CHANGED
|
@@ -558,7 +558,7 @@ garde le port habituel (`PRIMARY_FAMILY`, `isolationGroups.ts:56`).
|
|
|
558
558
|
|
|
559
559
|
**Les familles démarrent indépendamment.** Si Angular échoue, React continue de fonctionner : le
|
|
560
560
|
démarrage n'échoue que si **aucune** famille n'a pu démarrer (`FrontendService.startDev()`,
|
|
561
|
-
`FrontendService.ts:
|
|
561
|
+
`FrontendService.ts:339`).
|
|
562
562
|
|
|
563
563
|
### Résilience — ce qui se passe quand Vite tombe
|
|
564
564
|
|
|
@@ -567,11 +567,11 @@ les processus meurent.
|
|
|
567
567
|
|
|
568
568
|
| Situation | Réponse |
|
|
569
569
|
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
570
|
-
| Port occupé au lancement | essai sur le port suivant, jusqu'à `portRetryAttempts` (`ViteProcessSupervisor.ts:
|
|
570
|
+
| Port occupé au lancement | essai sur le port suivant, jusqu'à `portRetryAttempts` (`ViteProcessSupervisor.ts:297`) |
|
|
571
571
|
| Vite plante | relance avec délai exponentiel plafonné (`scheduleRestart()`, `ViteProcessSupervisor.ts:674`) |
|
|
572
572
|
| Vite ne répond plus (gelé) | sonde périodique ; après N échecs, Vite est tué pour être relancé (`ViteProcessSupervisor.ts:599`) |
|
|
573
573
|
| Deux `start()` concurrents | la promesse en cours est partagée — jamais deux processus |
|
|
574
|
-
| Ctrl+C au terminal | le signal marque un arrêt **voulu** : pas de relance (`markShutdown`, `ViteProcessSupervisor.ts:
|
|
574
|
+
| Ctrl+C au terminal | le signal marque un arrêt **voulu** : pas de relance (`markShutdown`, `ViteProcessSupervisor.ts:266`) |
|
|
575
575
|
| Arrêt du kernel | `SIGINT`, puis `SIGKILL` après 3 s — aucun zombie ne bloque le port (`ViteProcessSupervisor.ts:26`) |
|
|
576
576
|
|
|
577
577
|
Deux subtilités valent d'être connues, parce qu'elles expliquent des comportements sinon
|
|
@@ -581,12 +581,16 @@ incompréhensibles :
|
|
|
581
581
|
marquage du signal reçu par le processus serveur, un simple Ctrl+C ferait apparaître un
|
|
582
582
|
« redémarrage échoué » en erreur, sur un arrêt parfaitement normal.
|
|
583
583
|
- **La détection d'un port occupé est écrite à un seul endroit** (`isPortInUseMessage()`,
|
|
584
|
-
`ViteProcessSupervisor.ts:
|
|
585
|
-
|
|
586
|
-
|
|
584
|
+
`ViteProcessSupervisor.ts:185`), et elle reconnaît un conflit **FATAL**, pas la simple présence de
|
|
585
|
+
« in use ». Vite écrit trois textes qui contiennent ces mots, et un seul signifie qu'il a échoué :
|
|
586
|
+
« Port X is **already** in use » (il meurt — le seul cas où reprendre sur un autre port a un sens),
|
|
587
|
+
« Port X is in use, **trying another one…** » (il se décale lui-même) et « Port X is in use **on a
|
|
588
|
+
wildcard address**, but H:X is available… » (il a pris le port demandé et se contente d'avertir).
|
|
589
|
+
Les confondre faisait reprendre sur un autre port un démarrage qui n'avait aucun conflit, en
|
|
590
|
+
repayant l'attente de démarrage à chaque essai.
|
|
587
591
|
|
|
588
592
|
Les écouteurs attachés au processus enfant sont suivis puis retirés à chaque mort
|
|
589
|
-
(`cleanupChildListeners()`, `ViteProcessSupervisor.ts:
|
|
593
|
+
(`cleanupChildListeners()`, `ViteProcessSupervisor.ts:971`) : sans cela, les relances successives les
|
|
590
594
|
accumuleraient jusqu'à l'avertissement de fuite.
|
|
591
595
|
|
|
592
596
|
## 🧰 API publique
|
|
@@ -685,7 +689,7 @@ Dans une application générée par `nodefony create app`, tu n'as pas à y pens
|
|
|
685
689
|
**`npm run build` construit l'application entière** — le backend (rolldown) puis le front (il
|
|
686
690
|
chaîne `nodefony frontend:build`). Un seul geste avant `npm start` ou dans un pipeline.
|
|
687
691
|
|
|
688
|
-
`FrontendService.build()` (`FrontendService.ts:
|
|
692
|
+
`FrontendService.build()` (`FrontendService.ts:799`) appelle Vite **entrée par entrée**, et non une
|
|
689
693
|
fois pour toutes. Ce n'est pas un détail : chaque bundle a sa racine, son dossier de sortie, sa base
|
|
690
694
|
et son manifeste — c'est ce qui rend le multi-modules possible et ce qui isole Angular.
|
|
691
695
|
|
|
@@ -821,7 +825,7 @@ Sur le chemin chaud du rendu, trois précautions :
|
|
|
821
825
|
- le **manifeste** est lu une fois par dossier de sortie, jamais par requête ;
|
|
822
826
|
- l'**`index.html`** est mis en cache en production (relu en développement, où la fraîcheur prime) ;
|
|
823
827
|
- les **écouteurs** du processus enfant sont suivis et retirés à chaque mort
|
|
824
|
-
(`trackListener()`, `ViteProcessSupervisor.ts:
|
|
828
|
+
(`trackListener()`, `ViteProcessSupervisor.ts:961`) — sans quoi les relances les accumuleraient.
|
|
825
829
|
|
|
826
830
|
La sonde de vie coûte une requête HTTP toutes les trente secondes par famille. Elle est désactivable
|
|
827
831
|
(`healthCheckIntervalMs: 0`) si ce budget te gêne, au prix de la détection d'un Vite gelé.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodefony/frontend",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.7",
|
|
4
4
|
"description": "Construction et rechargement à chaud des frontends de chaque module Nodefony — Vite intégré, multi-framework (React, Vue, Angular, Svelte)",
|
|
5
5
|
"author": "Christophe CAMENSULI <ccamensuli@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
@@ -36,22 +36,22 @@
|
|
|
36
36
|
"esm"
|
|
37
37
|
],
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@nodefony/framework": "^10.0.0-alpha.
|
|
40
|
-
"@nodefony/http": "^10.0.0-alpha.
|
|
41
|
-
"nodefony": "^10.0.0-alpha.
|
|
42
|
-
"zod": "^4.6.
|
|
39
|
+
"@nodefony/framework": "^10.0.0-alpha.7",
|
|
40
|
+
"@nodefony/http": "^10.0.0-alpha.7",
|
|
41
|
+
"nodefony": "^10.0.0-alpha.7",
|
|
42
|
+
"zod": "^4.6.5"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@nodefony/framework": "^10.0.0-alpha.
|
|
46
|
-
"@nodefony/http": "^10.0.0-alpha.
|
|
45
|
+
"@nodefony/framework": "^10.0.0-alpha.7",
|
|
46
|
+
"@nodefony/http": "^10.0.0-alpha.7",
|
|
47
47
|
"@types/chai": "5.2.3",
|
|
48
|
-
"@types/node": "26.
|
|
49
|
-
"@vitest/coverage-v8": "5.0.
|
|
48
|
+
"@types/node": "26.6.1",
|
|
49
|
+
"@vitest/coverage-v8": "5.0.1",
|
|
50
50
|
"chai": "6.2.2",
|
|
51
|
-
"nodefony": "^10.0.0-alpha.
|
|
51
|
+
"nodefony": "^10.0.0-alpha.7",
|
|
52
52
|
"rimraf": "6.1.3",
|
|
53
53
|
"vite": "8.3.0",
|
|
54
|
-
"vitest": "5.0.
|
|
54
|
+
"vitest": "5.0.1"
|
|
55
55
|
},
|
|
56
56
|
"private": false,
|
|
57
57
|
"engines": {
|