[ADD] skeleton: Capacitor web app that shows its dated version
The engine to come must be proven against oracles under node in seconds,
so the skeleton sets three test projects: node, watched; node-long; and a
real Chromium that reads computed styles in both themes. The version
lives in version.json alone; a check refuses eight divergences — technical
form, generated module, changelog head, stray version strings — and a
build guard keeps test files and fixture assets out of the bundle.
Checked: on this commit alone, 73 node tests, 7 browser tests and the
version check pass; the guard fails on a fixture joined as an asset.
--- FR ---
[ADD] squelette : application web Capacitor qui affiche sa version datée
Le moteur à venir doit s'éprouver contre des oracles sous node en
quelques secondes : le squelette pose trois projets d'épreuve — node,
sous surveillance ; node-long ; et un vrai Chromium qui lit le style
calculé dans les deux thèmes. La version vit dans version.json seul ; un
contrôle refuse huit divergences — forme technique, module engendré,
tête du changelog, chaînes de version égarées — et une garde de
construction tient fichiers et données d'épreuve hors du paquet.
Vérifié : sur ce commit seul, 73 épreuves node, 7 navigateur et le
contrôle de version passent ; la garde échoue sur une donnée jointe.
Assisted-by: Claude Opus 5.5
2026-10-05 13:13:13 -04:00
// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// La version datée (§ 18). version.json en est l'unique source ; ce script en
// dérive tout le reste et contrôle que rien n'en diverge.
//
// Une version s'écrit sous quatre formes :
// affichée AAAA.MM.JJ.NN, quatre champs à longueur fixe, rang de 01 à 99 ;
// son ordre alphabétique est l'ordre chronologique ;
// technique AAAA.(MM× 100+JJ).NN, trois entiers sans zéro de tête, la forme
// qu'exigent package.json et electron-builder ;
// Windows la forme technique suivie de « .0 » : les quatre champs de la
// ressource de version de l'exécutable, de 16 bits chacun ;
// nom gestion_table_tournante_libre_v<AAAA>_<MM>_<JJ>_<NN>.exe, les
// points devenus soulignés, seul point restant celui de
// l'extension.
// Le jour étant inférieur à 100, MM× 100+JJ se redécompose d'une seule façon :
// la dérivation est monotone et bijective.
//
// En ligne de commande, la racine du projet est le parent du répertoire du
// script :
// node scripts/version.js engendrer réécrit src/version.genere.js, le
// champ version de package.json et les
// deux champs de version du paquet
// racine de package-lock.json ;
// node scripts/version.js controler applique les huit points du § 18.5 à
// la date civile locale de la machine ;
// imprime les échecs et sort en code 1,
// ou sort en code 0.
// Ce script n'appartient ni au moteur ni au générateur de démonstrations, et
// l'interdit de lecture d'horloge du § 14.7 ne le vise pas : dateCivileLocale,
// qu'appelle la commande « controler », lit l'horloge de la machine.
import { mkdirSync , readdirSync , readFileSync , realpathSync , writeFileSync } from 'node:fs' ;
import { dirname , join } from 'node:path' ;
import { fileURLToPath } from 'node:url' ;
// Le nom du livrable (§ 18.2) ne s'écrit qu'ici : le produit, « _v », la
// version dont les points deviennent des soulignés, l'extension. nomDuLivrable
// sert à deriver pour une version et au gabarit pour les champs nommés ; le
// point 7 compare chaque nom cité à ce calcul.
const PRODUIT = 'gestion_table_tournante_libre' ;
const PREFIXE _NOM = ` ${ PRODUIT } _v ` ;
const EXTENSION = '.exe' ;
const nomDuLivrable = ( version ) => ` ${ PREFIXE _NOM } ${ version . replaceAll ( '.' , '_' ) } ${ EXTENSION } ` ;
const GABARIT _NOM = nomDuLivrable ( '<AAAA>.<MM>.<JJ>.<NN>' ) ;
const UN _JOUR _MS = 86_400_000 ;
const MOIS = [
'janvier' , 'février' , 'mars' , 'avril' , 'mai' , 'juin' ,
'juillet' , 'août' , 'septembre' , 'octobre' , 'novembre' , 'décembre' ,
] ;
// Forme affichée AAAA.MM.JJ.NN. Le rang s'y lit aussi sur trois chiffres ou
// plus sans zéro de tête : la forme est alors reconnue, et le refus nomme le
// plafond du rang. Un rang « 001 » reste une faute de forme.
const FORME _AFFICHEE = /^(\d{4})\.(\d{2})\.(\d{2})\.(\d{2}|[1-9]\d{2,})$/ ;
// Trois entiers sans zéro de tête ; MM× 100+JJ s'écrit sur trois ou quatre
// chiffres, le rang sur un ou deux.
const FORME _TECHNIQUE = /^(\d{4})\.([1-9]\d{2,3})\.([1-9]\d?)$/ ;
const deuxChiffres = ( n ) => String ( n ) . padStart ( 2 , '0' ) ;
function estBissextile ( annee ) {
return ( annee % 4 === 0 && annee % 100 !== 0 ) || annee % 400 === 0 ;
}
function joursDuMois ( annee , mois ) {
if ( mois === 2 ) return estBissextile ( annee ) ? 29 : 28 ;
return mois === 4 || mois === 6 || mois === 9 || mois === 11 ? 30 : 31 ;
}
// Lève quand { annee, mois, jour } n'est pas une date du calendrier
// grégorien dont l'année s'écrit sur quatre chiffres sans zéro de tête — la
// forme technique écrit l'année en entier, et un zéro de tête s'y perdrait.
// « contexte » ouvre le message.
function exigerDate ( { annee , mois , jour } , contexte ) {
if ( ! Number . isInteger ( annee ) || annee < 1000 || annee > 9999 ) {
throw new RangeError ( ` ${ contexte } : année ${ annee } hors de 1000 à 9999 ` ) ;
}
if ( ! Number . isInteger ( mois ) || mois < 1 || mois > 12 ) {
throw new RangeError ( ` ${ contexte } : mois ${ mois } hors de 1 à 12 ` ) ;
}
const dernier = joursDuMois ( annee , mois ) ;
if ( ! Number . isInteger ( jour ) || jour < 1 || jour > dernier ) {
throw new RangeError (
` ${ contexte } : jour ${ jour } hors de 1 à ${ dernier } en ${ MOIS [ mois - 1 ] } ${ annee } ` ,
) ;
}
}
/ * *
* Formes dérivées d ' une version affichée AAAA . MM . JJ . NN : { affichee ,
* technique , windows , nomFichier , annee , mois , jour , rang } , les quatre
* derniers en entiers . Lève sur une forme non conforme , une date civile
* invalide , un rang hors de 1 à 99.
* /
export function deriver ( versionAffichee ) {
if ( typeof versionAffichee !== 'string' ) {
throw new TypeError ( ` version attendue sous forme de chaîne, reçu ${ typeof versionAffichee } ` ) ;
}
const contexte = ` version « ${ versionAffichee } » ` ;
const champs = FORME _AFFICHEE . exec ( versionAffichee ) ;
if ( champs === null ) {
throw new Error (
` ${ contexte } : forme attendue AAAA.MM.JJ.NN, quatre champs à longueur fixe (§ 18.1) ` ,
) ;
}
const [ annee , mois , jour , rang ] = champs . slice ( 1 ) . map ( Number ) ;
exigerDate ( { annee , mois , jour } , contexte ) ;
if ( rang > 99 ) throw new RangeError ( ` ${ contexte } : rang ${ rang } , le rang plafonne à 99 (§ 18.1) ` ) ;
if ( rang < 1 ) throw new RangeError ( ` ${ contexte } : rang 00 hors de 01 à 99 ` ) ;
const technique = ` ${ annee } . ${ mois * 100 + jour } . ${ rang } ` ;
return {
affichee : versionAffichee ,
technique ,
windows : ` ${ technique } .0 ` ,
nomFichier : nomDuLivrable ( versionAffichee ) ,
annee ,
mois ,
jour ,
rang ,
} ;
}
/ * *
* Version affichée d ' une version technique AAAA . ( MM × 100 + JJ ) . NN . Lève quand
* la chaîne ne désigne pas exactement une version : forme non conforme , zéro
* de tête , date civile invalide , rang hors de 1 à 99. Sur une chaîne
* acceptée , deriver ( redecomposer ( t ) ) . technique === t .
* /
export function redecomposer ( versionTechnique ) {
if ( typeof versionTechnique !== 'string' ) {
throw new TypeError (
` version technique attendue sous forme de chaîne, reçu ${ typeof versionTechnique } ` ,
) ;
}
const contexte = ` version technique « ${ versionTechnique } » ` ;
const champs = FORME _TECHNIQUE . exec ( versionTechnique ) ;
if ( champs === null ) {
throw new Error (
` ${ contexte } : forme attendue AAAA.(MM× 100+JJ).NN, trois entiers sans zéro de tête, rang de 1 à 99 (§ 18.1) ` ,
) ;
}
const [ annee , moisJour , rang ] = champs . slice ( 1 ) . map ( Number ) ;
const mois = Math . floor ( moisJour / 100 ) ;
const jour = moisJour % 100 ;
exigerDate ( { annee , mois , jour } , contexte ) ;
return ` ${ annee } . ${ deuxChiffres ( mois ) } . ${ deuxChiffres ( jour ) } . ${ deuxChiffres ( rang ) } ` ;
}
/ * *
* Date en toutes lettres du titre d ' une section du changelog : « 9 mars
* 2031 » , « 1 er janvier 2027 » . Lève sur une date hors du calendrier .
* /
export function dateEnLettres ( { annee , mois , jour } ) {
exigerDate ( { annee , mois , jour } , 'date' ) ;
return ` ${ jour === 1 ? '1er' : jour } ${ MOIS [ mois - 1 ] } ${ annee } ` ;
}
// --- Le module engendré et les recopies ------------------------------------
const MODULE = 'src/version.genere.js' ;
const CONSEIL _ENGENDRER = 'lancer « node scripts/version.js engendrer »' ;
const EN _TETE _LICENCE = [
'// © 2026 TechnoLibre (http://www.technolibre.ca)' ,
'// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)' ,
] ;
/ * *
* Contenu exact de src / version . genere . js pour une version affichée :
* l ' en - tête de licence , une ligne vide , puis la constante VERSION sous ses
[ADD] interface: application shell — banner, sections, theme, about
The window showed only a banner; it now holds the application. App mounts
the session, the facade and the generation, settles the working folder
and lists its events before it reports ready, re-reading the list once a
question is answered or a refused determination succeeds. The banner
carries version, mode and the event list command; an open event shows its
sections in their first form; the theme follows the system at each start
and the operator's choice lasts the session; À propos names the build.
Errors are collected for a report, and Electron removes the session's
locks at exit.
Checked: 2189 node, 193 browser, 60 node-long; the Electron start check.
--- FR ---
[ADD] interface : coquille — bandeau, sections, thème, À propos
La fenêtre ne montrait qu'un bandeau ; elle tient désormais l'application.
App monte la séance, la façade et la génération, règle le dossier de
travail et liste ses événements avant de se dire prête, et relit la liste
une fois une question répondue ou une détermination refusée réussie. Le
bandeau porte version, mode et la commande de la liste ; un événement
ouvert montre ses sections dans leur première forme ; le thème suit le
système à chaque démarrage et le choix de l'opérateur vaut pour la séance ;
À propos nomme la construction. Les erreurs se collectent pour un
signalement, et Electron efface à la sortie les verrous de la séance.
Vérifié : 2189 node, 193 navigateur, 60 node-long ; démarrage d'Electron.
Assisted-by: Claude Opus 5.5
2026-10-07 07:22:05 -04:00
* formes affichée , technique et Windows — celle que le panneau « À propos »
* met en regard de l ' affichée ( § 18.6 ) , sans la recalculer . Le module ne
* porte que la version ; la provenance de la construction vient du mode de
* Vite .
[ADD] skeleton: Capacitor web app that shows its dated version
The engine to come must be proven against oracles under node in seconds,
so the skeleton sets three test projects: node, watched; node-long; and a
real Chromium that reads computed styles in both themes. The version
lives in version.json alone; a check refuses eight divergences — technical
form, generated module, changelog head, stray version strings — and a
build guard keeps test files and fixture assets out of the bundle.
Checked: on this commit alone, 73 node tests, 7 browser tests and the
version check pass; the guard fails on a fixture joined as an asset.
--- FR ---
[ADD] squelette : application web Capacitor qui affiche sa version datée
Le moteur à venir doit s'éprouver contre des oracles sous node en
quelques secondes : le squelette pose trois projets d'épreuve — node,
sous surveillance ; node-long ; et un vrai Chromium qui lit le style
calculé dans les deux thèmes. La version vit dans version.json seul ; un
contrôle refuse huit divergences — forme technique, module engendré,
tête du changelog, chaînes de version égarées — et une garde de
construction tient fichiers et données d'épreuve hors du paquet.
Vérifié : sur ce commit seul, 73 épreuves node, 7 navigateur et le
contrôle de version passent ; la garde échoue sur une donnée jointe.
Assisted-by: Claude Opus 5.5
2026-10-05 13:13:13 -04:00
* /
export function engendrerModule ( versionAffichee ) {
[ADD] interface: application shell — banner, sections, theme, about
The window showed only a banner; it now holds the application. App mounts
the session, the facade and the generation, settles the working folder
and lists its events before it reports ready, re-reading the list once a
question is answered or a refused determination succeeds. The banner
carries version, mode and the event list command; an open event shows its
sections in their first form; the theme follows the system at each start
and the operator's choice lasts the session; À propos names the build.
Errors are collected for a report, and Electron removes the session's
locks at exit.
Checked: 2189 node, 193 browser, 60 node-long; the Electron start check.
--- FR ---
[ADD] interface : coquille — bandeau, sections, thème, À propos
La fenêtre ne montrait qu'un bandeau ; elle tient désormais l'application.
App monte la séance, la façade et la génération, règle le dossier de
travail et liste ses événements avant de se dire prête, et relit la liste
une fois une question répondue ou une détermination refusée réussie. Le
bandeau porte version, mode et la commande de la liste ; un événement
ouvert montre ses sections dans leur première forme ; le thème suit le
système à chaque démarrage et le choix de l'opérateur vaut pour la séance ;
À propos nomme la construction. Les erreurs se collectent pour un
signalement, et Electron efface à la sortie les verrous de la séance.
Vérifié : 2189 node, 193 navigateur, 60 node-long ; démarrage d'Electron.
Assisted-by: Claude Opus 5.5
2026-10-07 07:22:05 -04:00
const { affichee , technique , windows } = deriver ( versionAffichee ) ;
[ADD] skeleton: Capacitor web app that shows its dated version
The engine to come must be proven against oracles under node in seconds,
so the skeleton sets three test projects: node, watched; node-long; and a
real Chromium that reads computed styles in both themes. The version
lives in version.json alone; a check refuses eight divergences — technical
form, generated module, changelog head, stray version strings — and a
build guard keeps test files and fixture assets out of the bundle.
Checked: on this commit alone, 73 node tests, 7 browser tests and the
version check pass; the guard fails on a fixture joined as an asset.
--- FR ---
[ADD] squelette : application web Capacitor qui affiche sa version datée
Le moteur à venir doit s'éprouver contre des oracles sous node en
quelques secondes : le squelette pose trois projets d'épreuve — node,
sous surveillance ; node-long ; et un vrai Chromium qui lit le style
calculé dans les deux thèmes. La version vit dans version.json seul ; un
contrôle refuse huit divergences — forme technique, module engendré,
tête du changelog, chaînes de version égarées — et une garde de
construction tient fichiers et données d'épreuve hors du paquet.
Vérifié : sur ce commit seul, 73 épreuves node, 7 navigateur et le
contrôle de version passent ; la garde échoue sur une donnée jointe.
Assisted-by: Claude Opus 5.5
2026-10-05 13:13:13 -04:00
return [
... EN _TETE _LICENCE ,
'' ,
'// Engendré par scripts/version.js depuis version.json — ne pas modifier.' ,
'export const VERSION = Object.freeze({' ,
` affichee: ' ${ affichee } ', ` ,
` technique: ' ${ technique } ', ` ,
[ADD] interface: application shell — banner, sections, theme, about
The window showed only a banner; it now holds the application. App mounts
the session, the facade and the generation, settles the working folder
and lists its events before it reports ready, re-reading the list once a
question is answered or a refused determination succeeds. The banner
carries version, mode and the event list command; an open event shows its
sections in their first form; the theme follows the system at each start
and the operator's choice lasts the session; À propos names the build.
Errors are collected for a report, and Electron removes the session's
locks at exit.
Checked: 2189 node, 193 browser, 60 node-long; the Electron start check.
--- FR ---
[ADD] interface : coquille — bandeau, sections, thème, À propos
La fenêtre ne montrait qu'un bandeau ; elle tient désormais l'application.
App monte la séance, la façade et la génération, règle le dossier de
travail et liste ses événements avant de se dire prête, et relit la liste
une fois une question répondue ou une détermination refusée réussie. Le
bandeau porte version, mode et la commande de la liste ; un événement
ouvert montre ses sections dans leur première forme ; le thème suit le
système à chaque démarrage et le choix de l'opérateur vaut pour la séance ;
À propos nomme la construction. Les erreurs se collectent pour un
signalement, et Electron efface à la sortie les verrous de la séance.
Vérifié : 2189 node, 193 navigateur, 60 node-long ; démarrage d'Electron.
Assisted-by: Claude Opus 5.5
2026-10-07 07:22:05 -04:00
` windows: ' ${ windows } ', ` ,
[ADD] skeleton: Capacitor web app that shows its dated version
The engine to come must be proven against oracles under node in seconds,
so the skeleton sets three test projects: node, watched; node-long; and a
real Chromium that reads computed styles in both themes. The version
lives in version.json alone; a check refuses eight divergences — technical
form, generated module, changelog head, stray version strings — and a
build guard keeps test files and fixture assets out of the bundle.
Checked: on this commit alone, 73 node tests, 7 browser tests and the
version check pass; the guard fails on a fixture joined as an asset.
--- FR ---
[ADD] squelette : application web Capacitor qui affiche sa version datée
Le moteur à venir doit s'éprouver contre des oracles sous node en
quelques secondes : le squelette pose trois projets d'épreuve — node,
sous surveillance ; node-long ; et un vrai Chromium qui lit le style
calculé dans les deux thèmes. La version vit dans version.json seul ; un
contrôle refuse huit divergences — forme technique, module engendré,
tête du changelog, chaînes de version égarées — et une garde de
construction tient fichiers et données d'épreuve hors du paquet.
Vérifié : sur ce commit seul, 73 épreuves node, 7 navigateur et le
contrôle de version passent ; la garde échoue sur une donnée jointe.
Assisted-by: Claude Opus 5.5
2026-10-05 13:13:13 -04:00
'});' ,
'' ,
] . join ( '\n' ) ;
}
// JSON écrit comme npm l'écrit : indentation de deux espaces, saut de ligne
// final.
const enJson = ( valeur ) => ` ${ JSON . stringify ( valeur , null , 2 ) } \n ` ;
// Contenu d'un fichier relatif à la racine, ou null s'il n'existe pas.
function lireTexte ( racine , chemin ) {
try {
return readFileSync ( join ( racine , chemin ) , 'utf8' ) ;
} catch ( erreur ) {
if ( erreur . code === 'ENOENT' ) return null ;
throw erreur ;
}
}
// Valeur JSON d'un fichier relatif à la racine. Lève en nommant le fichier
// quand il est illisible, et quand il manque, avec « raisonSiAbsent » pour
// suite du message.
function lireJson ( racine , chemin , raisonSiAbsent ) {
const texte = lireTexte ( racine , chemin ) ;
if ( texte === null ) throw new Error ( ` ${ chemin } absent : ${ raisonSiAbsent } ` ) ;
try {
return JSON . parse ( texte ) ;
} catch ( erreur ) {
throw new Error ( ` ${ chemin } illisible : ${ erreur . message } ` ) ;
}
}
// Version affichée que porte version.json, non vérifiée. Lève sur un
// fichier absent, illisible ou sans champ « version » textuel.
function lireVersion ( racine ) {
const donnees = lireJson ( racine , 'version.json' , "c'est l'unique source de la version (§ 18.3)" ) ;
if ( typeof donnees ? . version !== 'string' ) {
throw new Error ( 'version.json : champ « version » absent ou non textuel' ) ;
}
return donnees . version ;
}
// Formes dérivées de la version de version.json. Chaque refus nomme
// version.json.
function versionDeLaSource ( racine ) {
const affichee = lireVersion ( racine ) ;
try {
return deriver ( affichee ) ;
} catch ( erreur ) {
throw new Error ( ` version.json : ${ erreur . message } ` , { cause : erreur } ) ;
}
}
// Vrai pour un objet JSON, ni tableau ni null.
const estObjet = ( valeur ) =>
typeof valeur === 'object' && valeur !== null && ! Array . isArray ( valeur ) ;
const RAISON _PAQUET = 'il porte la forme technique de la version (§ 18.3)' ;
const RAISON _VERROU = ` « npm install » le crée, puis ${ CONSEIL _ENGENDRER } ` ;
/ * *
* Réécrit , depuis version . json , le module engendré , le champ version de
* package . json et les champs version et packages [ "" ] . version de
* package - lock . json , en forme technique . Tout est lu et vérifié avant la
* première écriture : une version non conforme , un package . json ou un verrou
* absent font lever sans rien écrire . Un fichier déjà à jour n ' est pas
* réécrit . Rend les chemins réécrits , relatifs à la racine .
* /
export function engendrer ( racine ) {
const { affichee , technique } = versionDeLaSource ( racine ) ;
const paquet = lireJson ( racine , 'package.json' , RAISON _PAQUET ) ;
if ( ! estObjet ( paquet ) ) throw new Error ( 'package.json : un objet JSON est attendu' ) ;
const verrou = lireJson ( racine , 'package-lock.json' , RAISON _VERROU ) ;
if ( ! estObjet ( verrou ) || ! estObjet ( verrou . packages ) || ! estObjet ( verrou . packages [ '' ] ) ) {
throw new Error ( 'package-lock.json : paquet racine packages[""] absent' ) ;
}
const cibles = [
[ MODULE , engendrerModule ( affichee ) ] ,
[ 'package.json' , enJson ( { ... paquet , version : technique } ) ] ,
[
'package-lock.json' ,
enJson ( {
... verrou ,
version : technique ,
packages : { ... verrou . packages , '' : { ... verrou . packages [ '' ] , version : technique } } ,
} ) ,
] ,
] ;
const reecrits = [ ] ;
for ( const [ chemin , contenu ] of cibles ) {
if ( lireTexte ( racine , chemin ) === contenu ) continue ;
mkdirSync ( dirname ( join ( racine , chemin ) ) , { recursive : true } ) ;
writeFileSync ( join ( racine , chemin ) , contenu ) ;
reecrits . push ( chemin ) ;
}
return reecrits ;
}
// --- Le contrôle du § 18.5 ------------------------------------------------
// Les deux lignes obligatoires de la section de tête du changelog (§ 18.4) :
// le libellé en gras, les deux-points, puis une réponse.
const LIGNES _OBLIGATOIRES = [
[ '**Version de format des fichiers**' , /^\*\*Version de format des fichiers\*\*\s*:\s*\S/ ] ,
[ '**En remplaçant**' , /^\*\*En remplaçant\*\*\s*:\s*\S/ ] ,
] ;
// Périmètre du balayage du point 6, posé ici et non après coup : les fichiers
// de la racine, et les arbres des sources, des scripts, de la coquille et des
// épreuves. Seuls ces arbres sont parcourus : les dépendances et les sorties
// de construction (www/, dist*/, android/) n'en font pas partie, et un
// répertoire node_modules n'est jamais descendu. En sont exclus :
// - version.json, package-lock.json, le module engendré et le changelog, où
// la version se recopie par construction et que les points 1 à 3
// contrôlent ;
// - les documents (*.md), dont le point 7 contrôle les noms cités ;
// - les épreuves de la dérivation, qui citent des versions par nécessité ;
[ADD] demo: delivered event files, example CSVs, payload fingerprint
The four demonstrations ship as event files written once by a script,
the source of truth of § 15.5: a test replays the generator and compares
SHA-256 fingerprints of the payload alone, naming the paths that differ,
so a new build version never forces a regeneration. The affiliation
profile is read from the delivered file. The two demonstration CSVs are
the export of those files; the edge-case CSV is written by hand, and
its windows-1252 variant derived. Version and forged-name guards skip
these generated files, as § 18.5 and § 15.6 allow.
Checked: node and long series; the script writes only its seven files.
--- FR ---
[ADD] démo : fichiers d'événement livrés, CSV d'exemple, empreinte
Les quatre démonstrations se livrent en fichiers d'événement qu'un
script écrit une fois, source de vérité du § 15.5 : une épreuve rejoue
le générateur et compare les empreintes SHA-256 de la seule charge, en
nommant les chemins qui diffèrent ; une nouvelle version n'impose donc
aucune régénération. Le profil d'appartenances se lit dans le fichier
livré. Les deux CSV de démonstration sont l'export de ces fichiers ;
celui des cas limites s'écrit à la main, sa variante windows-1252 en
dérive. Les gardes de version et de noms forgés passent ces fichiers.
Vérifié : séries node et longue ; le script n'écrit que ses sept fichiers.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 17:03:15 -04:00
// - les données d'épreuve, sous test/fixtures/ ;
// - les démonstrations livrées, sous src/demo/livrees/, données engendrées
// dont l'en-tête porte la version de la construction qui les a écrites
// (§ 18.5, § 18.6) : une livraison suivante ne les réécrit pas (§ 15.5,
// point 5).
// Un répertoire s'exclut par son chemin exact, et rien n'est lu sous lui ;
// un fichier dont le nom commence comme le sien, src/demo/livrees.js, reste
// balayé.
[ADD] skeleton: Capacitor web app that shows its dated version
The engine to come must be proven against oracles under node in seconds,
so the skeleton sets three test projects: node, watched; node-long; and a
real Chromium that reads computed styles in both themes. The version
lives in version.json alone; a check refuses eight divergences — technical
form, generated module, changelog head, stray version strings — and a
build guard keeps test files and fixture assets out of the bundle.
Checked: on this commit alone, 73 node tests, 7 browser tests and the
version check pass; the guard fails on a fixture joined as an asset.
--- FR ---
[ADD] squelette : application web Capacitor qui affiche sa version datée
Le moteur à venir doit s'éprouver contre des oracles sous node en
quelques secondes : le squelette pose trois projets d'épreuve — node,
sous surveillance ; node-long ; et un vrai Chromium qui lit le style
calculé dans les deux thèmes. La version vit dans version.json seul ; un
contrôle refuse huit divergences — forme technique, module engendré,
tête du changelog, chaînes de version égarées — et une garde de
construction tient fichiers et données d'épreuve hors du paquet.
Vérifié : sur ce commit seul, 73 épreuves node, 7 navigateur et le
contrôle de version passent ; la garde échoue sur une donnée jointe.
Assisted-by: Claude Opus 5.5
2026-10-05 13:13:13 -04:00
// package.json reste balayé : la forme technique y est à sa place, la forme
// affichée non.
const ARBRES _BALAYES = [ 'src' , 'scripts' , 'electron' , 'test' ] ;
const FICHIERS _EXCLUS = new Set ( [
'version.json' ,
'package-lock.json' ,
MODULE ,
'CHANGELOG.md' ,
'scripts/version.test.js' ,
] ) ;
[ADD] demo: delivered event files, example CSVs, payload fingerprint
The four demonstrations ship as event files written once by a script,
the source of truth of § 15.5: a test replays the generator and compares
SHA-256 fingerprints of the payload alone, naming the paths that differ,
so a new build version never forces a regeneration. The affiliation
profile is read from the delivered file. The two demonstration CSVs are
the export of those files; the edge-case CSV is written by hand, and
its windows-1252 variant derived. Version and forged-name guards skip
these generated files, as § 18.5 and § 15.6 allow.
Checked: node and long series; the script writes only its seven files.
--- FR ---
[ADD] démo : fichiers d'événement livrés, CSV d'exemple, empreinte
Les quatre démonstrations se livrent en fichiers d'événement qu'un
script écrit une fois, source de vérité du § 15.5 : une épreuve rejoue
le générateur et compare les empreintes SHA-256 de la seule charge, en
nommant les chemins qui diffèrent ; une nouvelle version n'impose donc
aucune régénération. Le profil d'appartenances se lit dans le fichier
livré. Les deux CSV de démonstration sont l'export de ces fichiers ;
celui des cas limites s'écrit à la main, sa variante windows-1252 en
dérive. Les gardes de version et de noms forgés passent ces fichiers.
Vérifié : séries node et longue ; le script n'écrit que ses sept fichiers.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 17:03:15 -04:00
const REPERTOIRES _EXCLUS = new Set ( [ 'test/fixtures' , 'src/demo/livrees' ] ) ;
[ADD] skeleton: Capacitor web app that shows its dated version
The engine to come must be proven against oracles under node in seconds,
so the skeleton sets three test projects: node, watched; node-long; and a
real Chromium that reads computed styles in both themes. The version
lives in version.json alone; a check refuses eight divergences — technical
form, generated module, changelog head, stray version strings — and a
build guard keeps test files and fixture assets out of the bundle.
Checked: on this commit alone, 73 node tests, 7 browser tests and the
version check pass; the guard fails on a fixture joined as an asset.
--- FR ---
[ADD] squelette : application web Capacitor qui affiche sa version datée
Le moteur à venir doit s'éprouver contre des oracles sous node en
quelques secondes : le squelette pose trois projets d'épreuve — node,
sous surveillance ; node-long ; et un vrai Chromium qui lit le style
calculé dans les deux thèmes. La version vit dans version.json seul ; un
contrôle refuse huit divergences — forme technique, module engendré,
tête du changelog, chaînes de version égarées — et une garde de
construction tient fichiers et données d'épreuve hors du paquet.
Vérifié : sur ce commit seul, 73 épreuves node, 7 navigateur et le
contrôle de version passent ; la garde échoue sur une donnée jointe.
Assisted-by: Claude Opus 5.5
2026-10-05 13:13:13 -04:00
const DOCUMENT = /\.md$/i ;
const ARBRE _DES _SOURCES = 'src/' ;
// Une chaîne de chaque forme, bornée : ni chiffre ni point devant, aucun
// chiffre derrière. La forme Windows contient la forme technique.
const CHAINE _AFFICHEE = /(?<![\d.])\d{4}\.\d{2}\.\d{2}\.\d{2}(?!\d)/g ;
const CHAINE _TECHNIQUE = /(?<![\d.])\d{4}\.\d{3,4}\.\d{1,2}(?!\d)/g ;
// Documents du point 7, à la racine.
const DOCUMENTS _FIXES = [ 'README.md' , 'CHANGELOG.md' ] ;
const GUIDE = /^GUIDE-.*\.md$/ ;
const LITTERAL _NON _SUBSTITUE = /#_#/g ;
// Un nom d'exécutable cité : depuis le nom du produit jusqu'à la première
// extension, sans blanc ni délimiteur de Markdown ou de code. La casse est
// ignorée à la recherche, pas à la conformité. Le préfixe seul, sans
// extension, désigne une famille de fichiers et n'est pas un nom.
const NOM _CITE = new RegExp (
` ${ RegExp . escape ( PRODUIT ) } [^ \\ s \` '"()[ \\ ]{}|*]*? ${ RegExp . escape ( EXTENSION ) } (?! \\ w) ` ,
'gi' ,
) ;
// Vrai quand le nom cité est le gabarit, ou le nom que deriver calcule pour
// la version dont il porte les chiffres, lus après le préfixe. Tout nom admis
// sort ainsi de deriver, à l'octet.
function nomConforme ( nom ) {
if ( nom === GABARIT _NOM ) return true ;
const chiffres = nom . slice ( PREFIXE _NOM . length ) . match ( /\d+/g ) ? ? [ ] ;
return essayer ( ( ) => deriver ( chiffres . join ( '.' ) ) . nomFichier ) === nom ;
}
// Résultat de « calcul », ou null s'il lève.
function essayer ( calcul ) {
try {
return calcul ( ) ;
} catch {
return null ;
}
}
// Ordre de deux versions dérivées, comparées champ par champ comme quatre
// entiers : négatif quand a précède b.
function comparerVersions ( a , b ) {
return a . annee - b . annee || a . mois - b . mois || a . jour - b . jour || a . rang - b . rang ;
}
// Rang d'une date civile dans le calendrier, en jours : la différence de
// deux rangs compte les jours qui séparent les deux dates.
const numeroDeJour = ( { annee , mois , jour } ) => Date . UTC ( annee , mois - 1 , jour ) / UN _JOUR _MS ;
// { annee, mois, jour } d'une date civile AAAA-MM-JJ ; lève sur toute autre
// forme et sur une date hors du calendrier.
function lireDateCivile ( dateCivile ) {
const champs =
typeof dateCivile === 'string' ? /^(\d{4})-(\d{2})-(\d{2})$/ . exec ( dateCivile ) : null ;
if ( champs === null ) {
throw new TypeError ( ` dateCivile attendue sous la forme AAAA-MM-JJ, reçu ${ String ( dateCivile ) } ` ) ;
}
const [ annee , mois , jour ] = champs . slice ( 1 ) . map ( Number ) ;
exigerDate ( { annee , mois , jour } , ` dateCivile « ${ dateCivile } » ` ) ;
return { annee , mois , jour } ;
}
// Sections du changelog : chaque titre « ## », avec son numéro de ligne, son
// texte et les lignes de son corps. Un bloc de code, de la ligne qui l'ouvre
// par ``` ou ~~~ à celle qui le ferme, n'apporte ni titre ni ligne de corps :
// ce qu'il contient s'affiche comme du code, et une ligne obligatoire citée
// là n'est pas écrite.
function sectionsDuChangelog ( texte ) {
const sections = [ ] ;
let dansUnBloc = false ;
texte . split ( /\r?\n/ ) . forEach ( ( ligne , i ) => {
if ( /^\s*(```|~~~)/ . test ( ligne ) ) {
dansUnBloc = ! dansUnBloc ;
return ;
}
if ( dansUnBloc ) return ;
const titre = /^## (.*)$/ . exec ( ligne ) ;
if ( titre !== null ) {
sections . push ( { ligne : i + 1 , titre : titre [ 1 ] . trim ( ) , corps : [ ] } ) ;
} else if ( sections . length > 0 ) {
sections [ sections . length - 1 ] . corps . push ( ligne ) ;
}
} ) ;
return sections ;
}
// Chemins relatifs, en « / », des fichiers d'un arbre, sans descendre dans
// node_modules ni dans un répertoire exclu. Un arbre absent n'ajoute rien.
function parcourir ( racine , arbre , chemins ) {
let entrees ;
try {
entrees = readdirSync ( join ( racine , arbre ) , { withFileTypes : true } ) ;
} catch ( erreur ) {
if ( erreur . code === 'ENOENT' || erreur . code === 'ENOTDIR' ) return ;
throw erreur ;
}
for ( const entree of entrees ) {
const chemin = ` ${ arbre } / ${ entree . name } ` ;
if ( entree . isDirectory ( ) ) {
if ( entree . name !== 'node_modules' && ! REPERTOIRES _EXCLUS . has ( chemin ) ) {
parcourir ( racine , chemin , chemins ) ;
}
} else if ( entree . isFile ( ) ) {
chemins . push ( chemin ) ;
}
}
}
// Fichiers de la racine, triés ; un répertoire ou un lien n'en est pas.
function fichiersDeLaRacine ( racine ) {
return readdirSync ( racine , { withFileTypes : true } )
. filter ( ( entree ) => entree . isFile ( ) )
. map ( ( entree ) => entree . name )
. sort ( ) ;
}
// Fichiers du périmètre du point 6, triés.
function fichiersBalayes ( racine ) {
const chemins = fichiersDeLaRacine ( racine ) ;
for ( const arbre of ARBRES _BALAYES ) parcourir ( racine , arbre , chemins ) ;
return chemins . filter ( ( chemin ) => ! FICHIERS _EXCLUS . has ( chemin ) && ! DOCUMENT . test ( chemin ) ) . sort ( ) ;
}
// Chaque occurrence du motif dans le texte : { ligne, chaine }.
function occurrences ( texte , motif ) {
const trouvees = [ ] ;
texte . split ( /\r?\n/ ) . forEach ( ( ligne , i ) => {
for ( const [ chaine ] of ligne . matchAll ( motif ) ) trouvees . push ( { ligne : i + 1 , chaine } ) ;
} ) ;
return trouvees ;
}
// Point 1 : package.json et les deux champs du verrou portent la forme
// technique dérivée de version.json, et elle se redécompose en la version
// affichée.
function controlerRecopies ( racine , { affichee , technique } , echec ) {
try {
const porte = lireJson ( racine , 'package.json' , RAISON _PAQUET ) ? . version ;
const retour = essayer ( ( ) => redecomposer ( porte ) ) ;
if ( porte !== technique || retour !== affichee ) {
const lue = retour === null ? '' : ` (soit ${ retour } ) ` ;
echec (
1 ,
` package.json porte la version ${ JSON . stringify ( porte ) } ${ lue } , version.json donne ${ affichee } , soit ${ technique } : ${ CONSEIL _ENGENDRER } ` ,
) ;
}
} catch ( erreur ) {
echec ( 1 , erreur . message ) ;
}
try {
const verrou = lireJson ( racine , 'package-lock.json' , RAISON _VERROU ) ;
const champs = [
[ 'version' , verrou ? . version ] ,
[ 'packages[""].version' , verrou ? . packages ? . [ '' ] ? . version ] ,
] ;
for ( const [ champ , porte ] of champs ) {
if ( porte !== technique ) {
echec (
1 ,
` package-lock.json, champ « ${ champ } » : ${ JSON . stringify ( porte ) } , version.json donne ${ technique } : ${ CONSEIL _ENGENDRER } ` ,
) ;
}
}
} catch ( erreur ) {
echec ( 1 , erreur . message ) ;
}
}
// Point 2 : le module engendré reproduit, caractère pour caractère, ce que
// le script engendre ; l'échec cite la première ligne qui diffère.
function controlerModule ( racine , { affichee } , echec ) {
const present = lireTexte ( racine , MODULE ) ;
if ( present === null ) {
echec ( 2 , ` ${ MODULE } absent : ${ CONSEIL _ENGENDRER } ` ) ;
return ;
}
const attendu = engendrerModule ( affichee ) ;
if ( present === attendu ) return ;
const lignesPresentes = present . split ( '\n' ) ;
const lignesAttendues = attendu . split ( '\n' ) ;
const longueur = Math . max ( lignesPresentes . length , lignesAttendues . length ) ;
let i = 0 ;
while ( i < longueur && lignesPresentes [ i ] === lignesAttendues [ i ] ) i += 1 ;
const citer = ( ligne ) => ( ligne === undefined ? 'la fin du fichier' : ` « ${ ligne } » ` ) ;
echec (
2 ,
` ${ MODULE } , ligne ${ i + 1 } : ${ citer ( lignesPresentes [ i ] ) } au lieu de ${ citer ( lignesAttendues [ i ] ) } , que le script engendre : ${ CONSEIL _ENGENDRER } ` ,
) ;
}
// Point 3 : la section de tête porte la version courante, la date en lettres
// que cette version engendre, et les deux lignes obligatoires. Sans version
// valide, seules la forme du titre et les deux lignes se contrôlent.
function controlerTete ( sections , version , echec ) {
const tete = sections [ 0 ] ;
if ( tete === undefined ) {
echec ( 3 , 'CHANGELOG.md : aucune section « ## » ; la section de tête porte la version courante' ) ;
return ;
}
const lieu = ` CHANGELOG.md, ligne ${ tete . ligne } ` ;
const titre = /^(\S+) — (.+)$/ . exec ( tete . titre ) ;
if ( titre === null ) {
echec ( 3 , ` ${ lieu } : le titre « ## ${ tete . titre } » n'a pas la forme « ## <version> — <date en lettres> » ` ) ;
} else if ( version !== null ) {
const [ , porte , date ] = titre ;
const attendue = dateEnLettres ( version ) ;
if ( porte !== version . affichee ) {
echec (
3 ,
` ${ lieu } : la section de tête porte ${ porte } , version.json porte ${ version . affichee } ; la section de cette version s'écrit en tête (§ 18.4) ` ,
) ;
} else if ( date !== attendue ) {
echec ( 3 , ` ${ lieu } : la date « ${ date } » n'est pas « ${ attendue } », que la version ${ porte } engendre ` ) ;
}
}
for ( const [ libelle , motif ] of LIGNES _OBLIGATOIRES ) {
if ( ! tete . corps . some ( ( ligne ) => motif . test ( ligne ) ) ) {
echec ( 3 , ` ${ lieu } : la ligne obligatoire « ${ libelle } : … » manque à la section de tête (§ 18.4) ` ) ;
}
}
}
// Point 4 : chaque titre de section s'ouvre sur une version, aucune version
// n'est portée deux fois, et chaque section porte une version strictement
// antérieure à celle de la section qui la précède.
function controlerOrdre ( sections , echec ) {
const vues = new Map ( ) ;
let precedente = null ;
for ( const section of sections ) {
const version = essayer ( ( ) => deriver ( section . titre . split ( /\s/ ) [ 0 ] ) ) ;
if ( version === null ) {
echec (
4 ,
` CHANGELOG.md, ligne ${ section . ligne } : le titre « ## ${ section . titre } » ne s'ouvre pas sur une version AAAA.MM.JJ.NN ` ,
) ;
continue ;
}
const deja = vues . get ( version . affichee ) ;
if ( deja !== undefined ) {
echec ( 4 , ` CHANGELOG.md, lignes ${ deja } et ${ section . ligne } : deux sections portent la version ${ version . affichee } ` ) ;
} else {
vues . set ( version . affichee , section . ligne ) ;
if ( precedente !== null && comparerVersions ( version , precedente . version ) > 0 ) {
echec (
4 ,
` CHANGELOG.md, ligne ${ section . ligne } : la section ${ version . affichee } suit la section ${ precedente . version . affichee } (ligne ${ precedente . ligne } ) ; les versions vont en ordre strictement décroissant ` ,
) ;
}
}
precedente = { version , ligne : section . ligne } ;
}
}
// Point 5 : la date de la version ne dépasse pas de plus d'un jour la date
// civile de la machine. La tolérance absorbe l'écart de fuseau.
function controlerDate ( version , civile , echec ) {
const avance = numeroDeJour ( version ) - numeroDeJour ( civile ) ;
if ( avance > 1 ) {
echec (
5 ,
` version.json : la version ${ version . affichee } est datée du ${ dateEnLettres ( version ) } , ${ avance } jours après la date civile de la machine, le ${ dateEnLettres ( civile ) } ; la tolérance est d'un jour ` ,
) ;
}
}
// Point 6 : aucune chaîne de forme affichée hors de version.json, du module
// engendré et du changelog ; aucune de forme technique hors de package.json
// et du module engendré. Rend le nombre de fichiers de src/ examinés, que
// contrôle le point 8. Un fichier binaire, qui porte un octet nul, n'est pas
// examiné.
function controlerFormes ( racine , echec ) {
let sourcesExaminees = 0 ;
for ( const chemin of fichiersBalayes ( racine ) ) {
const texte = lireTexte ( racine , chemin ) ;
if ( texte === null || texte . includes ( '\u0000' ) ) continue ;
if ( chemin . startsWith ( ARBRE _DES _SOURCES ) ) sourcesExaminees += 1 ;
for ( const { ligne , chaine } of occurrences ( texte , CHAINE _AFFICHEE ) ) {
echec (
6 ,
` ${ chemin } , ligne ${ ligne } : « ${ chaine } », forme affichée, hors de version.json, du module engendré et du changelog ` ,
) ;
}
if ( chemin === 'package.json' ) continue ;
for ( const { ligne , chaine } of occurrences ( texte , CHAINE _TECHNIQUE ) ) {
echec ( 6 , ` ${ chemin } , ligne ${ ligne } : « ${ chaine } », forme technique, hors de package.json et du module engendré ` ) ;
}
}
return sourcesExaminees ;
}
// Point 7 : aucun document ne porte le littéral #_# ni ne cite un nom
// d'exécutable hors du gabarit du § 18.2. Rend le nombre de documents
// examinés, que contrôle le point 8.
function controlerDocuments ( racine , echec ) {
const documents = fichiersDeLaRacine ( racine ) . filter (
( nom ) => DOCUMENTS _FIXES . includes ( nom ) || GUIDE . test ( nom ) ,
) ;
for ( const document of documents ) {
const texte = lireTexte ( racine , document ) ;
for ( const { ligne , chaine } of occurrences ( texte , LITTERAL _NON _SUBSTITUE ) ) {
echec ( 7 , ` ${ document } , ligne ${ ligne } : littéral « ${ chaine } », un gabarit non substitué ` ) ;
}
for ( const { ligne , chaine } of occurrences ( texte , NOM _CITE ) ) {
if ( ! nomConforme ( chaine ) ) {
echec ( 7 , ` ${ document } , ligne ${ ligne } : « ${ chaine } » ne se conforme pas au gabarit ${ GABARIT _NOM } (§ 18.2) ` ) ;
}
}
}
return documents . length ;
}
/ * *
* Applique à l ' arbre « racine » les huit points du § 18.5 , à la date civile
* « dateCivile » ( AAAA - MM - JJ ) de la machine qui construit . Rend la liste des
* échecs , chacun préfixé du numéro de son point ( « 1. … » ) et nommant
* l ' endroit qui diverge ; liste vide = conforme . Lève sur une dateCivile mal
* formée .
* /
export function controler ( racine , { dateCivile } ) {
const civile = lireDateCivile ( dateCivile ) ;
const echecs = [ ] ;
const echec = ( point , texte ) => echecs . push ( ` ${ point } . ${ texte } ` ) ;
let version = null ;
try {
version = versionDeLaSource ( racine ) ;
} catch ( erreur ) {
echec ( 1 , erreur . message ) ;
}
if ( version !== null ) {
controlerRecopies ( racine , version , echec ) ;
controlerModule ( racine , version , echec ) ;
}
const changelog = lireTexte ( racine , 'CHANGELOG.md' ) ;
if ( changelog === null ) {
echec ( 3 , "CHANGELOG.md absent : la section de la version courante s'écrit à la livraison (§ 18.4)" ) ;
} else {
const sections = sectionsDuChangelog ( changelog ) ;
controlerTete ( sections , version , echec ) ;
controlerOrdre ( sections , echec ) ;
}
if ( version !== null ) controlerDate ( version , civile , echec ) ;
const sourcesExaminees = controlerFormes ( racine , echec ) ;
const documentsExamines = controlerDocuments ( racine , echec ) ;
if ( sourcesExaminees === 0 ) {
echec ( 8 , ` le balayage du point 6 n'examine aucun fichier sous ${ ARBRE _DES _SOURCES } : zéro fichier examiné n'est pas zéro problème (§ 14.2) ` ) ;
}
if ( documentsExamines === 0 ) {
echec ( 8 , 'le balayage du point 7 n\'examine aucun document (README.md, CHANGELOG.md, GUIDE-*.md)' ) ;
}
return echecs ;
}
/ * *
* Date civile locale d ' un instant , sous la forme AAAA - MM - JJ ; par défaut ,
* celle de la machine au moment de l ' appel .
* /
export function dateCivileLocale ( instant = new Date ( ) ) {
return ` ${ instant . getFullYear ( ) } - ${ deuxChiffres ( instant . getMonth ( ) + 1 ) } - ${ deuxChiffres ( instant . getDate ( ) ) } ` ;
}
// --- Ligne de commande ------------------------------------------------------
const USAGE = 'usage : node scripts/version.js engendrer | controler' ;
// Exécute une commande sur l'arbre « racine » et rend le code de sortie : 0
// réussi, 1 échec, 2 commande inconnue.
function executer ( commande , racine ) {
if ( commande === 'engendrer' ) {
try {
const reecrits = engendrer ( racine ) ;
const version = lireVersion ( racine ) ;
console . log (
reecrits . length === 0
? ` Version ${ version } : les recopies sont à jour. `
: ` Version ${ version } : réécrit ${ reecrits . join ( ', ' ) } . ` ,
) ;
return 0 ;
} catch ( erreur ) {
console . error ( ` Version : ${ erreur . message } ` ) ;
return 1 ;
}
}
if ( commande === 'controler' ) {
const echecs = controler ( racine , { dateCivile : dateCivileLocale ( ) } ) ;
if ( echecs . length === 0 ) {
console . log ( ` Version ${ lireVersion ( racine ) } : conforme aux huit points du § 18.5. ` ) ;
return 0 ;
}
console . error ( ` Version : ${ echecs . length } échec(s) au contrôle du § 18.5. ` ) ;
for ( const echec of echecs ) console . error ( echec ) ;
return 1 ;
}
console . error ( USAGE ) ;
return 2 ;
}
// Vrai quand ce fichier est le script que Node a lancé, et non un module
// importé.
function estLanceDirectement ( ) {
if ( process . argv [ 1 ] === undefined ) return false ;
try {
return realpathSync ( process . argv [ 1 ] ) === realpathSync ( fileURLToPath ( import . meta . url ) ) ;
} catch {
return false ;
}
}
if ( estLanceDirectement ( ) ) {
process . exitCode = executer ( process . argv [ 2 ] , fileURLToPath ( new URL ( '..' , import . meta . url ) ) ) ;
}