[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)
// Le paquet livré n'emporte rien de l'outillage d'épreuve (§ 14.9, § 14.15) :
// ni fichier de test, ni le lanceur, ni donnée d'épreuve, ni paquet d'un outil
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// d'épreuve, ni le client du serveur de développement ; et il ne charge rien
// du réseau. Le test construit la page par l'API de Vite, avec la
[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
// configuration et le mode de la livraison, dans un répertoire temporaire ;
// il relit les modules que contient le paquet, les noms d'origine de chaque
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// actif, puis le chemin et le contenu de chaque fichier écrit. Le paquet du
// travailleur de génération, que « ?worker&inline » construit à part et
// incruste en chaîne dans le module principal, n'apparaît pas dans les
// moduleIds de celui-ci : un greffon posé par l'option worker.plugins relève
// les modules et le code de ses morceaux, qui passent par les mêmes relevés.
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
//
// Les sorties (§ 11.1, § 14.15) : la police embarquée voyage dans deux
// morceaux différés, un par graisse, incrustée octet pour octet ; pdf-lib et
// fontkit, quand la page les importe, tiennent chacun leur morceau, sous le
// seuil ; aucun de ces morceaux n'est chargé ni préchargé au lancement ; les
// avis de licence sont copiés dans www/licences/, et chaque paquet des
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
// morceaux construits y a le sien, sous la licence de son package.json. Les
// deux fichiers de la police sont aussi émis dans www/, octet pour octet,
// et nommés par la feuille construite : jetons.css en fait la police de
// l'interface.
[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
import assert from 'node:assert/strict' ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
import { createHash } from 'node:crypto' ;
[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
import { mkdtempSync , readdirSync , readFileSync , rmSync } from 'node:fs' ;
import { tmpdir } from 'node:os' ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
import { basename , dirname , extname , join , relative , resolve , sep } from 'node:path' ;
[IMP] build: chunk size warning at 1000 kB, a failing test beyond it
Vite warned on every build because its 500 kB threshold targets pages
fetched over a network; this page is read from the local disk once, and
one chunk holds the whole application with the generation worker inlined.
The threshold now sits at 1000 kB, so it only flags abnormal growth, and
the build test fails when any chunk exceeds it, naming the file and size.
Checked: the build is 658 kB with no warning; a 600 kB threshold turns the
test red.
--- FR ---
[IMP] construction : seuil d'avertissement à 1000 ko, épreuve au-delà
Vite avertissait à chaque construction, car son seuil de 500 ko vise les
pages chargées par le réseau ; cette page se lit sur le disque local, une
fois, et un seul morceau porte l'application entière, travailleur de
génération incrusté compris. Le seuil passe à 1000 ko et ne signale plus
qu'une croissance anormale ; l'épreuve de construction tombe quand un
morceau le dépasse, en nommant le fichier et sa taille. Vérifié : 658 ko
sans avertissement ; un seuil à 600 ko rend l'épreuve rouge.
Assisted-by: Claude Opus 5.5
2026-10-08 03:08:48 -04:00
import { fileURLToPath , pathToFileURL } from 'node:url' ;
[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
import { build } from 'vite' ;
import { describe , test } from './lanceur.js' ;
const RACINE = fileURLToPath ( new URL ( '..' , import . meta . url ) ) ;
// Paquets qui ne servent qu'aux épreuves.
const OUTILS _EPREUVE =
/[\\/]node_modules[\\/](?:vitest|@vitest|playwright|playwright-core|fast-check)[\\/]/ ;
// Vrai quand un module vient de l'outillage d'épreuve : l'arbre test/
// (lanceur, données d'épreuve), un fichier *.test.js rangé à côté de son
// module, un paquet d'épreuve, ou un module de Node que Vite remplace pour le
// navigateur, comme node:assert.
function moduleDEpreuve ( id ) {
return (
id . startsWith ( join ( RACINE , 'test' ) + sep ) ||
/\.test\.[cm]?js(?:\?|$)/ . test ( id ) ||
OUTILS _EPREUVE . test ( id ) ||
id . includes ( '__vite-browser-external' )
) ;
}
// Traces de l'outillage dans le chemin ou le contenu d'un fichier écrit. Les
// motifs portent l'extension : le domaine emploie le mot « lanceur » (lanceur
// portable, § 8.6), et un code minifié peut écrire « x.test.call ».
const TRACES _EPREUVE = [ /vitest/i , /node:assert/ , /lanceur\.js/ , /\.test\.[cm]?js/ ] ;
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// Le client du serveur de développement de Vite : une page qui le nomme
// attend un serveur qui n'existe pas chez l'opérateur.
const CLIENT _DE _DEVELOPPEMENT = '/@vite/client' ;
// Un littéral de chaîne JavaScript, entre guillemets doubles, simples ou
// accents graves sans interpolation ; son contenu est dans l'un des trois
// groupes.
const LITTERAL = String . raw ` (?:"([^" \n ]*)"|'([^' \n ]*)'| \x 60([^ \x 60 $ ]*) \x 60) ` ;
// Les positions où un fichier construit charge une adresse, par nature :
// attributs src et href du HTML ; url() et @import des feuilles ; argument
// littéral d'import(), de fetch, d'importScripts et des constructeurs de
// travailleur, et spécificateur d'un import statique ou d'une réexportation
// en JavaScript. Un composant Svelte compile son balisage en chaînes du
// JavaScript : les attributs src, href, xlink:href et srcset et les url()
// d'un attribut style y sont relevés dans tout le texte, sans apparier les
// littéraux, qu'une expression régulière ou un guillemet échappé du code
// minifié décalerait ; un guillemet échappé (\") y ouvre la valeur comme un
// guillemet nu, et la valeur s'arrête au premier guillemet ou à la première
// barre oblique inverse. Le balisage compilé écrit en entité HTML un
// guillemet de la valeur d'un attribut — url("…") —, que le texte
// JavaScript voit décodé avant les motifs. url( n'y compte qu'en minuscules,
// hors d'un nom plus long : new URL(…) n'est pas une feuille. Chaque
// candidat d'un srcset compte, l'adresse avant son descripteur de largeur
// ou de densité. Une chaîne hors de ces positions — l'adresse
// https://svelte.dev/e/… d'une erreur de Svelte, un espace de noms XML
// http://www.w3.org/… — ne charge rien et n'est pas relevée. Les
// commentaires du HTML et des feuilles sont retirés d'abord : une balise
// commentée ne charge rien.
//
// Par nature : commentaires, le motif des commentaires retirés d'abord ;
// entites, vrai quand les guillemets écrits en entité HTML se décodent
// avant les motifs ; motifs, dont chaque correspondance porte une adresse
// dans l'un de ses groupes ; listes, dont chaque correspondance porte la
// valeur d'un srcset, liste de candidats séparés par des virgules.
const POSITIONS = {
html : {
commentaires : /<!--[\s\S]*?-->/g ,
entites : false ,
motifs : [ /\s(?:src|(?:xlink:)?href)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/gi ] ,
listes : [ /\ssrcset\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/gi ] ,
} ,
css : {
commentaires : /\/\*[\s\S]*?\*\//g ,
entites : false ,
motifs : [ /url\(\s*(?:"([^"]*)"|'([^']*)'|([^)\s]*))\s*\)/gi , /@import\s+(?:"([^"]*)"|'([^']*)')/gi ] ,
listes : [ ] ,
} ,
js : {
commentaires : null ,
entites : true ,
motifs : [
new RegExp ( String . raw ` (?: \b import| \b fetch| \b importScripts| \b Worker| \b SharedWorker) \s * \( \s * ${ LITTERAL } ` , 'g' ) ,
new RegExp ( String . raw ` (?: \b import| \b from) \s * ${ LITTERAL } ` , 'g' ) ,
/\s(?:src|(?:xlink:)?href)\s*=\s*\\?(?:"([^"\\]*)|'([^'\\]*)|([^\s"'>\\]+))/gi ,
/(?<![\w$.])url\(\s*\\?["']?([^)"'\\\s]*)/g ,
] ,
listes : [ /\ssrcset\s*=\s*\\?(?:"([^"\\]*)|'([^'\\]*)|([^\s"'>\\]+))/gi ] ,
} ,
} ;
// Les guillemets qu'une entité HTML écrit, nommée ou numérique, décimale ou
// hexadécimale, zéros de tête et casse indifférents.
const GUILLEMETS _EN _ENTITE = /&(?:(quot)|apos|#0*(34)|#0*39|#x0*(22)|#x0*27);/gi ;
// Le texte où chaque guillemet écrit en entité est rendu à son caractère.
const decoderGuillemets = ( texte ) =>
texte . replace ( GUILLEMETS _EN _ENTITE , ( _entite , quot , decimal , hexadecimal ) =>
quot || decimal || hexadecimal ? '"' : "'" ,
) ;
// Les adresses d'une valeur de srcset : le premier mot de chaque candidat.
const candidatsDe = ( valeur ) =>
valeur
. split ( ',' )
. map ( ( candidat ) => candidat . trim ( ) . split ( /\s+/ ) [ 0 ] )
. filter ( ( adresse ) => adresse !== '' ) ;
// La nature d'un fichier construit, d'après son extension ; null pour un
// fichier qu'aucune position ne concerne.
function natureDe ( nom ) {
const extension = extname ( nom ) . toLowerCase ( ) ;
if ( extension === '.html' ) return 'html' ;
if ( extension === '.css' ) return 'css' ;
if ( [ '.js' , '.mjs' , '.cjs' ] . includes ( extension ) ) return 'js' ;
return null ;
}
// Les adresses qu'un texte de la nature donnée charge, dans l'ordre du
// texte, motif par motif, puis celles des srcset.
function adressesChargees ( nature , texte ) {
const { commentaires , entites , motifs , listes } = POSITIONS [ nature ] ;
const sansCommentaires = commentaires === null ? texte : texte . replace ( commentaires , ' ' ) ;
const nettoye = entites ? decoderGuillemets ( sansCommentaires ) : sansCommentaires ;
const valeurs = ( motif ) => [ ... nettoye . matchAll ( motif ) ] . map ( ( m ) => m . slice ( 1 ) . find ( ( groupe ) => groupe !== undefined ) ) ;
return [ ... motifs . flatMap ( valeurs ) , ... listes . flatMap ( ( motif ) => valeurs ( motif ) . flatMap ( candidatsDe ) ) ] ;
}
// Vrai pour une adresse qui sort de la machine : http, https, ou relative au
// protocole (« //hôte/… »).
const distante = ( adresse ) => / ^ ( ? : https ? : ) ? \ / \ //i.test(adresse.trim());
// Le relevé du travailleur : un greffon de construction des travailleurs,
// qui garde le nom, les modules et le code de chacun de leurs morceaux.
function releveDuTravailleur ( ) {
const morceaux = [ ] ;
const greffon = {
name : 'releve-du-travailleur' ,
generateBundle ( _options , paquet ) {
for ( const sortie of Object . values ( paquet ) ) {
if ( sortie . type === 'chunk' ) {
morceaux . push ( { nom : sortie . fileName , moduleIds : sortie . moduleIds , code : sortie . code } ) ;
}
}
} ,
} ;
return { morceaux , plugins : ( ) => [ greffon ] } ;
}
// Les balises script d'un HTML, dans l'ordre : leur src, si elles portent
// type="module", et si elles portent async ou defer. Les commentaires sont
// retirés d'abord.
function scriptsDe ( html ) {
return [ ... html . replace ( POSITIONS . html . commentaires , ' ' ) . matchAll ( /<script\b([^>]*)>/gi ) ] . map ( ( [ , attributs ] ) => ( {
src : /\ssrc\s*=\s*["']?([^"'\s>]+)/i . exec ( attributs ) ? . [ 1 ] ? ? null ,
module : /\stype\s*=\s*["']?module\b/i . test ( attributs ) ,
differe : /\s(?:async|defer)\b/i . test ( attributs ) ,
} ) ) ;
}
[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
// Construit la page comme la livraison : vite.config.js, mode livraison,
// NODE_ENV=production. Vitest pose NODE_ENV=test, dont Vite déduirait une
// construction de développement — composants compilés en mode dev, paquets
// résolus sous la condition « development ».
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
async function construireLivraison ( sortie , travailleur ) {
[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 nodeEnv = process . env . NODE _ENV ;
process . env . NODE _ENV = 'production' ;
try {
return await build ( {
root : RACINE ,
configFile : join ( RACINE , 'vite.config.js' ) ,
mode : 'livraison' ,
logLevel : 'error' ,
// Sans incrustation, chaque actif sort en fichier et garde ses noms
// d'origine ; incrusté en data:, il ne laisserait aucune trace lisible.
build : { outDir : sortie , emptyOutDir : true , assetsInlineLimit : 0 } ,
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
worker : { plugins : travailleur . plugins } ,
[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
} ) ;
} finally {
if ( nodeEnv === undefined ) delete process . env . NODE _ENV ;
else process . env . NODE _ENV = nodeEnv ;
}
}
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
describe ( 'construction : le relevé des adresses chargées' , ( ) => {
test ( 'chaque position relève son adresse ; commentaires, espaces de noms et adresses d’ erreur ne comptent pas' , ( ) => {
const html = [
'<!-- <script src="https://commente.invalid/a.js"></script> -->' ,
'<script src="https://exemple.invalid/a.js"></script>' ,
'<script src="http://exemple.invalid/k.js"></script>' ,
"<link rel=stylesheet href='//exemple.invalid/b.css'>" ,
'<img src=./local.png>' ,
'<svg><image xlink:href="https://exemple.invalid/q.png"/></svg>' ,
'<img srcset="./r.png 1x, https://exemple.invalid/r2.png 2x">' ,
] . join ( '\n' ) ;
assert . deepEqual ( adressesChargees ( 'html' , html ) , [
'https://exemple.invalid/a.js' ,
'http://exemple.invalid/k.js' ,
'//exemple.invalid/b.css' ,
'./local.png' ,
'https://exemple.invalid/q.png' ,
'./r.png' ,
'https://exemple.invalid/r2.png' ,
] ) ;
const css = [
'/* url(https://commente.invalid/f.png) */' ,
'a { background: url( "https://exemple.invalid/c.png" ) }' ,
"@import 'https://exemple.invalid/d.css';" ,
'b { background: url(./e.png) }' ,
] . join ( '\n' ) ;
assert . deepEqual ( adressesChargees ( 'css' , css ) , [
'https://exemple.invalid/c.png' ,
'./e.png' ,
'https://exemple.invalid/d.css' ,
] ) ;
const js = [
'fetch("https://exemple.invalid/e.json");' ,
'fetch("HTTP://exemple.invalid/l");' ,
"import('https://exemple.invalid/f.js');" ,
'importScripts(`https://exemple.invalid/g.js`);' ,
'new Worker("./h.js");' ,
'import"https://exemple.invalid/i.js";' ,
'export{a}from"https://exemple.invalid/j.js";' ,
'const n="http://www.w3.org/2000/svg",l="https://svelte.dev/e/effect_orphan";' ,
'fetch(u);import(`./${m}.js`);' ,
// Le balisage qu'un composant compilé porte en chaîne, guillemets nus
// ou échappés, gabarit interpolé compris ; l'espace de noms d'une
// balise ne charge rien.
` var t= $ .from_html('<img src="https://exemple.invalid/k.png">'); ` ,
'var a="<a href=\\"https://exemple.invalid/m.html\\">x</a>";' ,
"var d=`<div style=\"background: url('https://exemple.invalid/n.png')\">${x}</div>`;" ,
'var s="<svg xmlns=\\"http://www.w3.org/2000/svg\\"><use href=\\"#a\\"/></svg>";' ,
'var o=new URL(e,import.meta.url);' ,
[FIX] interface: ghost labels, keyboard, valid HTML, closed data-* list
The drag ghost names what releasing at the point changes, floor included.
Escape on a seat returns to its table, a refused Tab stays put, the legend
toggle exposes aria-expanded, and the header markup is valid HTML. Every
data-* attribute the interface sets belongs to one closed list that a guard
reads. The tree guards follow helpers a test imports, and the reporter says
the whole series stays watched under coverage on a small machine.
Checked: every new test red first; 3001 node, 635 browser and 63 long tests
green from the index alone.
--- FR ---
[FIX] interface : fantôme nommé, clavier, HTML valide, data-* fermés
Le fantôme du glisser nomme ce que relâcher au point modifie, sol compris.
Échap sur un siège remonte à sa table, un Tab refusé reste en place, la
bascule de la légende expose aria-expanded, et la tête est du HTML valide.
Chaque attribut data-* que l'interface pose appartient à une liste fermée
qu'une garde lit. Les gardes de l'arbre suivent les aides qu'une épreuve
importe, et le rapporteur dit la série entière surveillée sous couverture.
Vérifié : chaque épreuve rouge d'abord ; 3001 node, 635 navigateur et 63
longues vertes depuis l'index seul.
Assisted-by: Claude Opus 5.5
2026-10-09 04:01:57 -04:00
// url( précédé d'un point, d'un mot ou d'un $ est un appel de méthode
// ou de fonction, non une feuille : rien à relever.
'var y=x.url("https://exemple.invalid/u.png"),z=$url("https://exemple.invalid/v.png"),c=curl(\'https://exemple.invalid/w.png\');' ,
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// Le guillemet d'une valeur d'attribut écrit en entité HTML, comme
// Svelte compile style='background: url("…")'.
'var p=$.from_html(`<div style="background: url("https://exemple.invalid/o.png")"></div>`);' ,
'var q="<div style=\\"background: url('https://exemple.invalid/p.png')\\"></div>";' ,
'var r=\'<i style="background:url('https://exemple.invalid/q.png')"></i>\';' ,
// xlink:href et srcset, dont chaque candidat compte.
'var v=$.from_html(`<svg><image xlink:href="https://exemple.invalid/s.png"></image></svg>`);' ,
'var w="<img srcset=\\"./t.png 1x, https://exemple.invalid/t2.png 2x\\">";' ,
] . join ( '' ) ;
assert . deepEqual ( adressesChargees ( 'js' , js ) , [
'https://exemple.invalid/e.json' ,
'HTTP://exemple.invalid/l' ,
'https://exemple.invalid/f.js' ,
'https://exemple.invalid/g.js' ,
'./h.js' ,
'https://exemple.invalid/i.js' ,
'https://exemple.invalid/j.js' ,
'https://exemple.invalid/k.png' ,
'https://exemple.invalid/m.html' ,
'#a' ,
'https://exemple.invalid/s.png' ,
'https://exemple.invalid/n.png' ,
'https://exemple.invalid/o.png' ,
'https://exemple.invalid/p.png' ,
'https://exemple.invalid/q.png' ,
'./t.png' ,
'https://exemple.invalid/t2.png' ,
] ) ;
const distantes = [ html , css , js ] . flatMap ( ( texte , rang ) =>
adressesChargees ( [ 'html' , 'css' , 'js' ] [ rang ] , texte ) . filter ( distante ) ,
) ;
assert . equal ( distantes . length , 21 ) ;
assert . ok ( ! distantes . some ( ( a ) => a . includes ( 'svelte.dev' ) || a . includes ( 'w3.org' ) || a . includes ( 'commente' ) ) ) ;
} ) ;
test ( 'scriptsDe lit les balises script dans l’ ordre, commentaires retirés' , ( ) => {
const html = [
'<!-- <script type="module" src="./x.js"></script> -->' ,
'<script src="./c.js"></script>' ,
'<script type="module" crossorigin src="./m.js"></script>' ,
"<script defer src='./d.js'></script>" ,
] . join ( '' ) ;
assert . deepEqual ( scriptsDe ( html ) , [
{ src : './c.js' , module : false , differe : false } ,
{ src : './m.js' , module : true , differe : false } ,
{ src : './d.js' , module : false , differe : true } ,
] ) ;
} ) ;
} ) ;
[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
describe ( 'construction' , ( ) => {
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
test ( "le paquet livré n'emporte aucun fichier ni module d'épreuve et ne charge rien du réseau (§ 14.9, § 14.15)" , async ( ) => {
[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 sortie = mkdtempSync ( join ( tmpdir ( ) , 'construction-' ) ) ;
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
const travailleur = releveDuTravailleur ( ) ;
[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
try {
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
const resultat = await construireLivraison ( sortie , travailleur ) ;
[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 sorties = ( Array . isArray ( resultat ) ? resultat : [ resultat ] ) . flatMap (
( r ) => r . output ,
) ;
const modules = sorties
. filter ( ( s ) => s . type === 'chunk' )
. flatMap ( ( s ) => s . moduleIds ) ;
assert . ok (
modules . some ( ( id ) => id . startsWith ( join ( RACINE , 'src' ) + sep ) ) ,
` aucun module de src/ parmi ${ modules . length } modules examinés ` ,
) ;
assert . deepEqual ( modules . filter ( moduleDEpreuve ) , [ ] ) ;
[IMP] build: chunk size warning at 1000 kB, a failing test beyond it
Vite warned on every build because its 500 kB threshold targets pages
fetched over a network; this page is read from the local disk once, and
one chunk holds the whole application with the generation worker inlined.
The threshold now sits at 1000 kB, so it only flags abnormal growth, and
the build test fails when any chunk exceeds it, naming the file and size.
Checked: the build is 658 kB with no warning; a 600 kB threshold turns the
test red.
--- FR ---
[IMP] construction : seuil d'avertissement à 1000 ko, épreuve au-delà
Vite avertissait à chaque construction, car son seuil de 500 ko vise les
pages chargées par le réseau ; cette page se lit sur le disque local, une
fois, et un seul morceau porte l'application entière, travailleur de
génération incrusté compris. Le seuil passe à 1000 ko et ne signale plus
qu'une croissance anormale ; l'épreuve de construction tombe quand un
morceau le dépasse, en nommant le fichier et sa taille. Vérifié : 658 ko
sans avertissement ; un seuil à 600 ko rend l'épreuve rouge.
Assisted-by: Claude Opus 5.5
2026-10-08 03:08:48 -04:00
// La taille de chaque morceau tient sous le seuil d'avertissement de
// vite.config.js, en kilooctets de 1 000 octets comme Vite les compte :
// un paquet qui grossit au-delà fait tomber cette épreuve, et non
// seulement avertir la construction.
const { default : vite } = await import ( pathToFileURL ( join ( RACINE , 'vite.config.js' ) ) . href ) ;
const seuil = vite . build ? . chunkSizeWarningLimit ;
assert . equal ( typeof seuil , 'number' , 'vite.config.js ne pose aucun build.chunkSizeWarningLimit' ) ;
const morceaux = sorties . filter ( ( s ) => s . type === 'chunk' ) ;
assert . ok ( morceaux . length > 0 , ` aucun morceau parmi ${ sorties . length } sorties ` ) ;
assert . deepEqual (
morceaux
. filter ( ( m ) => Buffer . byteLength ( m . code , 'utf8' ) > seuil * 1000 )
. map ( ( m ) => ` ${ m . fileName } : ${ Math . ceil ( Buffer . byteLength ( m . code , 'utf8' ) / 1000 ) } ko > ${ seuil } ko ` ) ,
[ ] ,
) ;
[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
// Un actif n'est pas un module : moduleIds ne le voit pas. Une donnée
// jointe par new URL(…, import.meta.url) n'apparaît que là.
const actifs = sorties
. filter ( ( s ) => s . type === 'asset' )
. flatMap ( ( s ) => ( s . originalFileNames ? ? [ ] ) . map ( ( n ) => resolve ( RACINE , n ) ) ) ;
assert . ok ( actifs . length > 0 , ` aucun actif d'origine connue parmi ${ sorties . length } sorties ` ) ;
assert . deepEqual ( actifs . filter ( moduleDEpreuve ) , [ ] ) ;
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// Le paquet du travailleur : ses modules, dont le point d'entrée de la
// génération, passent par le même relevé que ceux du module principal.
const modulesDuTravailleur = travailleur . morceaux . flatMap ( ( m ) => m . moduleIds ) ;
assert . ok ( travailleur . morceaux . length > 0 , 'aucun morceau de travailleur relevé' ) ;
assert . ok (
modulesDuTravailleur . includes ( join ( RACINE , 'src' , 'application' , 'generation_travailleur.js' ) ) ,
` generation_travailleur.js absent des ${ modulesDuTravailleur . length } modules du travailleur ` ,
) ;
assert . deepEqual ( modulesDuTravailleur . filter ( moduleDEpreuve ) , [ ] ) ;
[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 fichiers = readdirSync ( sortie , { recursive : true , withFileTypes : true } )
. filter ( ( e ) => e . isFile ( ) )
. map ( ( e ) => join ( e . parentPath , e . name ) ) ;
assert . ok (
fichiers . some ( ( f ) => f . endsWith ( '.js' ) ) ,
` aucun script parmi ${ fichiers . length } fichiers écrits ` ,
) ;
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// Les fichiers écrits, puis le code de chaque morceau du travailleur,
// qui n'est écrit qu'incrusté en chaîne et s'y lirait échappé.
const textes = [
... fichiers . map ( ( f ) => ( { nom : relative ( sortie , f ) , contenu : readFileSync ( f , 'utf8' ) } ) ) ,
... travailleur . morceaux . map ( ( m ) => ( { nom : ` travailleur: ${ m . nom } ` , contenu : m . code } ) ) ,
] ;
const traces = textes . flatMap ( ( { nom , contenu } ) =>
[ ... TRACES _EPREUVE , CLIENT _DE _DEVELOPPEMENT ]
. filter ( ( m ) => ( typeof m === 'string' ? nom . includes ( m ) || contenu . includes ( m ) : m . test ( nom ) || m . test ( contenu ) ) )
. map ( ( m ) => ` ${ nom } : ${ m } ` ) ,
) ;
[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
assert . deepEqual ( traces , [ ] ) ;
[ADD] build: no remote load in the bundle, portable startup trial
The delivery build is scanned for test modules, runner traces, the Vite
client and any loading http(s) URL, the worker bundle included and quote
entities decoded. The startup trial runs the real shell in portable mode
from separate exe, home and temp folders: it creates an event, loads the
small demonstration, generates in the worker, closes, and checks that the
files sit under exe/data and no lock is left. It runs only when launched
as itself, symlinks resolved, so it never exits green without running.
Checked: 2699 node, 62 long tests, make essai_demarrage conforme.
--- FR ---
[ADD] construction : rien de distant, essai de démarrage portable
La construction de livraison est balayée : ni module d'épreuve, ni trace du
lanceur, ni client de Vite, ni URL http(s) qui charge, paquet du travailleur
compris et entités de guillemets décodées. L'essai lance la vraie coquille
en mode portable, depuis des dossiers exe, home et temp distincts : il crée
un événement, charge la petite démonstration, génère dans le travailleur,
ferme, et vérifie les fichiers sous exe/data et aucun verrou restant. Il ne
part que lancé lui-même, liens résolus : jamais un vert sans essai.
Vérifié : 2699 node, 62 longues, make essai_demarrage conforme.
Assisted-by: Claude Opus 5.5
2026-10-07 16:08:15 -04:00
// Les adresses chargées : aucune ne sort de la machine. Le HTML en
// porte au moins une, le collecteur, le module d'entrée et sa feuille.
const releves = textes . flatMap ( ( { nom , contenu } ) => {
const nature = natureDe ( nom . replace ( /^travailleur:/ , '' ) ) ;
return nature === null ? [ ] : adressesChargees ( nature , contenu ) . map ( ( adresse ) => ( { nom , nature , adresse } ) ) ;
} ) ;
assert . ok (
releves . filter ( ( { nature } ) => nature === 'html' ) . length >= 3 ,
` ${ releves . length } adresses relevées, dont trop peu dans le HTML ` ,
) ;
assert . deepEqual (
releves . filter ( ( { adresse } ) => distante ( adresse ) ) . map ( ( { nom , adresse } ) => ` ${ nom } : ${ adresse } ` ) ,
[ ] ,
) ;
// index.html : la langue, puis le collecteur, script classique copié
// tel quel depuis public/, en tête des scripts et sans async ni defer :
// il s'exécute pendant l'analyse du HTML, avant tout module, que le
// navigateur diffère à la fin de l'analyse.
const html = readFileSync ( join ( sortie , 'index.html' ) , 'utf8' ) ;
assert . match ( html , /<html\b[^>]*\slang="fr"/ ) ;
const scripts = scriptsDe ( html ) ;
const modulesDuHtml = scripts . filter ( ( s ) => s . module ) ;
assert . ok ( modulesDuHtml . length > 0 , ` aucun script module parmi ${ scripts . length } scripts ` ) ;
assert . deepEqual ( scripts [ 0 ] , { src : './collecteur.js' , module : false , differe : false } ) ;
assert . equal (
readFileSync ( join ( sortie , 'collecteur.js' ) , 'utf8' ) ,
readFileSync ( join ( RACINE , 'public' , 'collecteur.js' ) , 'utf8' ) ,
) ;
[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
} finally {
rmSync ( sortie , { recursive : true , force : true } ) ;
}
} ) ;
} ) ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
// Le paquet de node_modules d'où vient un module construit : le dernier
// segment node_modules de son chemin, et le répertoire qui porte son
// package.json ; null pour un module du projet.
function paquetDe ( id ) {
const lu = /^(.*[\\/]node_modules[\\/])((?:@[^\\/]+[\\/])?[^\\/]+)/ . exec ( id . replace ( /\?.*$/ , '' ) ) ;
if ( lu === null ) return null ;
return { nom : lu [ 2 ] . split ( sep ) . join ( '/' ) , dossier : lu [ 1 ] + lu [ 2 ] } ;
}
// Les sections « paquet : nom version » de TIERS.txt et leur ligne
// « licence : … », dans l'ordre du texte.
function sectionsDesTiers ( texte ) {
return [ ... texte . matchAll ( /^paquet : (\S+) (\S+)\nlicence : (.+)$/gm ) ] . map ( ( [ , nom , version , licence ] ) => ( {
nom ,
version ,
licence ,
} ) ) ;
}
// Les paquets que vite.config.js range dans un groupe de morceaux, et le
// groupe qui les prend : name et test de chaque groupe, dans leur ordre.
const groupeDe = ( groupes , id ) =>
groupes . find ( ( { test : motif } ) => ( typeof motif === 'function' ? motif ( id ) : new RegExp ( motif ) . test ( id ) ) ) ? . name ? ? null ;
// Modules témoins des deux groupes de la production PDF, chemins comme
// Rolldown les nomme, et un module qui n'est d'aucun groupe.
const TEMOINS _DES _GROUPES = [
[ '/projet/node_modules/@pdf-lib/fontkit/dist/fontkit.es.js' , 'fontkit' ] ,
[ '/projet/node_modules/pdf-lib/es/api/PDFDocument.js' , 'pdf-lib' ] ,
[ '/projet/node_modules/pako/lib/deflate.js' , 'pdf-lib' ] ,
[ '/projet/node_modules/pdf-lib/node_modules/tslib/tslib.es6.js' , 'pdf-lib' ] ,
[ '/projet/node_modules/@pdf-lib/standard-fonts/es/index.js' , 'pdf-lib' ] ,
[ '/projet/node_modules/@pdf-lib/upng/UPNG.js' , 'pdf-lib' ] ,
[ '/projet/node_modules/svelte/src/index-client.js' , null ] ,
[ '/projet/src/pdf/imposition.js' , null ] ,
] ;
const DU _PDF = /[\\/]node_modules[\\/](?:pdf-lib|pako|tslib|@pdf-lib[\\/](?:fontkit|standard-fonts|upng))[\\/]/ ;
describe ( 'construction : les sorties (§ 11.1, § 14.15)' , ( ) => {
test ( 'vite.config.js range fontkit dans un morceau, pdf-lib et ce qu’ il tire dans un autre, et rien d’ autre' , async ( ) => {
const { default : vite } = await import ( pathToFileURL ( join ( RACINE , 'vite.config.js' ) ) . href ) ;
const groupes = vite . build ? . rolldownOptions ? . output ? . codeSplitting ? . groups ? ? [ ] ;
assert . deepEqual ( groupes . map ( ( { name } ) => name ) , [ 'fontkit' , 'pdf-lib' ] ) ;
assert . deepEqual (
TEMOINS _DES _GROUPES . map ( ( [ id ] ) => [ id , groupeDe ( groupes , id ) ] ) ,
TEMOINS _DES _GROUPES ,
) ;
} ) ;
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
test ( 'la police voyage dans deux morceaux différés, octet pour octet, et ses deux fichiers sont émis dans www/, nommés par la feuille construite ; aucun morceau des sorties n’ est chargé ni préchargé au lancement ; les avis de licence sont dans www/licences/, et chaque paquet construit y a le sien' , async ( ) => {
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
const sortie = mkdtempSync ( join ( tmpdir ( ) , 'construction-sorties-' ) ) ;
const travailleur = releveDuTravailleur ( ) ;
try {
const resultat = await construireLivraison ( sortie , travailleur ) ;
const sorties = ( Array . isArray ( resultat ) ? resultat : [ resultat ] ) . flatMap ( ( r ) => r . output ) ;
const morceaux = sorties . filter ( ( s ) => s . type === 'chunk' ) ;
// Une graisse, un morceau : chargé par import() différé, il porte la
// police en URL data:, dont le décodage est le fichier de src/polices.
const sha256 = ( octets ) => createHash ( 'sha256' ) . update ( octets ) . digest ( 'hex' ) ;
const morceauxDePolice = [ ] ;
for ( const [ module , fichier ] of [ [ 'regulier.js' , 'NotoSans-Regular.ttf' ] , [ 'gras.js' , 'NotoSans-Bold.ttf' ] ] ) {
const id = join ( RACINE , 'src' , 'polices' , module ) ;
const porteurs = morceaux . filter ( ( m ) => m . moduleIds . includes ( id ) ) ;
assert . equal ( porteurs . length , 1 , ` ${ module } dans ${ porteurs . length } morceaux ` ) ;
const [ morceau ] = porteurs ;
assert . ok ( morceau . isDynamicEntry && ! morceau . isEntry , ` ${ morceau . fileName } n'est pas un morceau différé ` ) ;
const donnees = [ ... morceau . code . matchAll ( /data:font\/ttf;base64,([A-Za-z0-9+/]+=*)/g ) ] . map ( ( m ) => m [ 1 ] ) ;
assert . equal ( donnees . length , 1 , ` ${ morceau . fileName } : ${ donnees . length } polices incrustées ` ) ;
assert . equal (
sha256 ( Buffer . from ( donnees [ 0 ] , 'base64' ) ) ,
sha256 ( readFileSync ( join ( RACINE , 'src' , 'polices' , fichier ) ) ) ,
fichier ,
) ;
morceauxDePolice . push ( morceau . fileName ) ;
}
assert . notEqual ( morceauxDePolice [ 0 ] , morceauxDePolice [ 1 ] ) ;
// Les morceaux des sorties : la police, et tout morceau qui porte un
// module de pdf-lib, de fontkit ou de ce qu'ils tirent. Quand la page
// les importe, fontkit et pdf-lib tiennent chacun un morceau à part.
const deLaProduction = morceaux . filter ( ( m ) => m . moduleIds . some ( ( id ) => DU _PDF . test ( id ) ) ) ;
for ( const morceau of deLaProduction ) {
const nomsDePaquets = new Set ( morceau . moduleIds . map ( ( id ) => paquetDe ( id ) ? . nom ? ? 'projet' ) ) ;
const attendu = nomsDePaquets . has ( '@pdf-lib/fontkit' ) ? 'fontkit' : 'pdf-lib' ;
assert . ok ( basename ( morceau . fileName ) . startsWith ( ` ${ attendu } - ` ) , ` ${ morceau . fileName } : ${ [ ... nomsDePaquets ] } ` ) ;
assert . ok ( ! ( nomsDePaquets . has ( '@pdf-lib/fontkit' ) && nomsDePaquets . has ( 'pdf-lib' ) ) , morceau . fileName ) ;
}
const desSorties = new Set ( [ ... morceauxDePolice , ... deLaProduction . map ( ( m ) => m . fileName ) ] ) ;
// Au lancement, la page charge les scripts du HTML et les imports
// statiques de son point d'entrée, et précharge les modulepreload.
const entrees = morceaux . filter ( ( m ) => m . isEntry ) ;
assert . ok ( entrees . length > 0 , 'aucun point d’ entrée' ) ;
const statiques = new Set ( ) ;
const aSuivre = entrees . map ( ( m ) => m . fileName ) ;
while ( aSuivre . length > 0 ) {
const nom = aSuivre . pop ( ) ;
if ( statiques . has ( nom ) ) continue ;
statiques . add ( nom ) ;
aSuivre . push ( ... ( morceaux . find ( ( m ) => m . fileName === nom ) ? . imports ? ? [ ] ) ) ;
}
const html = readFileSync ( join ( sortie , 'index.html' ) , 'utf8' ) ;
const prechargements = [ ... html . matchAll ( /<link\b[^>]*\brel=["']?modulepreload\b[^>]*>/gi ) ] . map (
( [ balise ] ) => / \ shref \ s *= \ s * [ "']?\.?\/?([^" '\s>]+)/i.exec(balise)?.[1] ?? ' ' ,
) ;
const auLancement = [ ... statiques , ... prechargements , ... scriptsDe ( html ) . map ( ( s ) => ( s . src ? ? '' ) . replace ( /^\.\// , '' ) ) ] ;
assert . deepEqual ( auLancement . filter ( ( nom ) => desSorties . has ( nom ) ) , [ ] ) ;
// Les avis : copiés tels quels de public/licences/.
for ( const avis of [ 'NotoSans-OFL.txt' , 'TIERS.txt' ] ) {
assert . equal (
readFileSync ( join ( sortie , 'licences' , avis ) , 'utf8' ) ,
readFileSync ( join ( RACINE , 'public' , 'licences' , avis ) , 'utf8' ) ,
avis ,
) ;
}
// Chaque paquet des morceaux construits, travailleur compris, a sa
// section dans TIERS.txt, à la version et sous la licence de son
// package.json.
const tiers = sectionsDesTiers ( readFileSync ( join ( RACINE , 'public' , 'licences' , 'TIERS.txt' ) , 'utf8' ) ) ;
const ids = [ ... morceaux , ... travailleur . morceaux ] . flatMap ( ( m ) => m . moduleIds ) ;
const construits = new Map ( ) ;
for ( const id of ids ) {
const paquet = paquetDe ( id ) ;
if ( paquet !== null ) construits . set ( paquet . nom , paquet . dossier ) ;
}
assert . ok ( construits . has ( 'svelte' ) , ` svelte absent des ${ construits . size } paquets construits ` ) ;
const manquants = [ ... construits ]
. map ( ( [ nom , dossier ] ) => {
const { version , license } = JSON . parse ( readFileSync ( join ( dossier , 'package.json' ) , 'utf8' ) ) ;
return { nom , version , licence : license } ;
} )
. filter ( ( { nom , version , licence } ) => ! tiers . some ( ( s ) => s . nom === nom && s . version === version && s . licence === licence ) )
. map ( ( { nom , version , licence } ) => ` ${ nom } ${ version } — ${ licence } ` ) ;
assert . deepEqual ( manquants , [ ] ) ;
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
// La police de l'interface (jetons.css) : les deux fichiers de
// src/polices sont émis dans www/, chacun une fois, octet pour octet,
// et aucune autre police ; une feuille qui en nomme une la trouve dans
// www/, et les deux sont nommées.
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
const fichiers = readdirSync ( sortie , { recursive : true , withFileTypes : true } )
. filter ( ( e ) => e . isFile ( ) )
. map ( ( e ) => join ( e . parentPath , e . name ) ) ;
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
const sources = [ 'NotoSans-Regular.ttf' , 'NotoSans-Bold.ttf' ] . map ( ( f ) => sha256 ( readFileSync ( join ( RACINE , 'src' , 'polices' , f ) ) ) ) ;
const polices = fichiers . filter ( ( f ) => extname ( f ) === '.ttf' ) ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
assert . deepEqual (
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
polices . map ( ( f ) => sha256 ( readFileSync ( f ) ) ) . sort ( ) ,
[ ... sources ] . sort ( ) ,
` polices émises : ${ polices . map ( ( f ) => relative ( sortie , f ) ) . join ( ', ' ) || 'aucune' } ` ,
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
) ;
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
const nommees = new Set ( ) ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
for ( const feuille of fichiers . filter ( ( f ) => extname ( f ) === '.css' ) ) {
for ( const adresse of adressesChargees ( 'css' , readFileSync ( feuille , 'utf8' ) ) . filter ( ( a ) => / \ . ttf$ / i . test ( a ) ) ) {
const cible = resolve ( dirname ( feuille ) , adresse ) ;
assert . ok ( fichiers . includes ( cible ) , ` ${ relative ( sortie , feuille ) } : ${ adresse } absent de la construction ` ) ;
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
nommees . add ( cible ) ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
}
}
[IMP] interface: Noto Sans everywhere, faces loaded before mounting
The screen must match the screenshots and the text the tests measure. The
interface now uses the embedded Noto Sans for every element, form controls
included, with a 1.3 line height. The entry point and the test mount wait
for both faces before mounting, so no text measurer caches the fallback
width; a face that fails to load is reported and the app mounts anyway.
The screens the wider font overflowed have their own CSS adjusted.
Checked: red first; 3216 node, 655 browser and 71 long tests green from the
index; the Electron start-up check loads the font from file://.
--- FR ---
[IMP] interface : Noto Sans partout, faces chargées avant le montage
L'écran doit ressembler aux captures et au texte que mesurent les épreuves.
L'interface emploie désormais Noto Sans, embarquée, pour tout élément,
contrôles de formulaire compris, avec un interligne de 1,3. Le point
d'entrée et le montage d'épreuve attendent les deux faces avant de monter :
aucun mesureur ne garde la largeur de repli ; une face qui ne se charge pas
est signalée et l'application se monte quand même. Les écrans que la police
plus large débordait ajustent leur propre CSS.
Vérifié : rouge d'abord ; 3216 node, 655 navigateur et 71 longues vertes
depuis l'index ; l'essai de démarrage charge la police par file://.
Assisted-by: Claude Opus 5.5
2026-10-09 13:01:49 -04:00
assert . deepEqual ( [ ... nommees ] . sort ( ) , [ ... polices ] . sort ( ) , 'polices que les feuilles nomment' ) ;
[ADD] outputs: pdf-lib, Noto Sans, paper sizes, badge imposition
Badges and printed views need a PDF writer, an embedded font whose metrics
the tests can measure, and the paper the machine prints on. pdf-lib with
fontkit writes the sheet; Noto Sans (OFL) ships with its provenance and
SHA-256, and travels into www/ with every third-party notice that the
minified bundle drops. The paper is kept per machine in reglages_locaux.json.
A4 and Letter derive the A6 grid from their dimensions; the PDF libraries
load in two deferred chunks, each under the 1000 kB threshold.
Checked: 3048 node, 639 browser and 67 long tests green from the index.
--- FR ---
[ADD] sorties : pdf-lib, Noto Sans, formats de papier, imposition
Les badges et les vues imprimables exigent un producteur de PDF, une police
embarquée que les épreuves mesurent, et le papier de la machine. pdf-lib et
fontkit écrivent la planche ; Noto Sans (OFL) arrive avec sa provenance et
son SHA-256, et voyage dans www/ avec chaque avis de tiers que le paquet
minifié retire. Le papier se garde par machine dans reglages_locaux.json.
A4 et Lettre dérivent la grille A6 de leurs dimensions ; les bibliothèques
PDF se chargent en deux morceaux différés, chacun sous le seuil de 1000 ko.
Vérifié : 3048 node, 639 navigateur et 67 longues vertes depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:49:18 -04:00
} finally {
rmSync ( sortie , { recursive : true , force : true } ) ;
}
} ) ;
test ( 'chaque section de TIERS.txt nomme un paquet du verrou, à sa version, sous la licence que porte son package.json' , ( ) => {
const { packages : paquets } = JSON . parse ( readFileSync ( join ( RACINE , 'package-lock.json' ) , 'utf8' ) ) ;
const tiers = sectionsDesTiers ( readFileSync ( join ( RACINE , 'public' , 'licences' , 'TIERS.txt' ) , 'utf8' ) ) ;
assert . ok ( tiers . length > 0 , 'TIERS.txt ne porte aucune section de paquet' ) ;
const fautes = tiers
. filter ( ( { nom , version , licence } ) => {
const entrees = Object . entries ( paquets ) . filter (
( [ chemin , entree ] ) => chemin . endsWith ( ` node_modules/ ${ nom } ` ) && entree . version === version ,
) ;
return ! entrees . some ( ( [ , entree ] ) => entree . license === licence ) ;
} )
. map ( ( { nom , version , licence } ) => ` ${ nom } ${ version } — ${ licence } ` ) ;
assert . deepEqual ( fautes , [ ] ) ;
// Les paquets de la production PDF ont leur avis, même quand aucune
// page ne les importe encore.
for ( const nom of [ 'pdf-lib' , '@pdf-lib/fontkit' , '@pdf-lib/standard-fonts' , '@pdf-lib/upng' , 'pako' , 'tslib' ] ) {
assert . ok ( tiers . some ( ( s ) => s . nom === nom ) , ` ${ nom } sans avis ` ) ;
}
} ) ;
// La construction minifiée retire les commentaires ordinaires : l'avis
// qu'un code incorporé porte en tête n'arrive dans www/ que par TIERS.txt.
// Chaque ligne « Copyright … » d'un commentaire de la construction publiée
// de fontkit y figure donc telle quelle, et une section « incorporé » qui
// nomme Apache porte le texte entier de l'Apache License 2.0. Une ligne
// « Copyright » est le texte du titulaire : aucune incise française n'y
// entre, elle va dans l'en-tête de la section.
test ( 'TIERS.txt porte mot pour mot les avis que les commentaires de fontkit incorporent, et le texte de leur licence' , ( ) => {
const tiers = readFileSync ( join ( RACINE , 'public' , 'licences' , 'TIERS.txt' ) , 'utf8' ) ;
const lignes = new Set ( tiers . split ( '\n' ) . map ( ( l ) => l . trim ( ) ) ) ;
const dossier = join ( RACINE , 'node_modules' , '@pdf-lib' , 'fontkit' ) ;
const { module : entree } = JSON . parse ( readFileSync ( join ( dossier , 'package.json' ) , 'utf8' ) ) ;
const code = readFileSync ( join ( dossier , entree ) , 'utf8' ) ;
const avis = [
... code . matchAll ( /^\s*(?:\/\/|\/\*|\*)\s*(Copyright\b.*\S)\s*$/gm ) ,
] . map ( ( [ , ligne ] ) => ligne ) ;
assert . ok ( avis . length > 0 , ` aucun avis dans ${ entree } ` ) ;
assert . deepEqual ( [ ... new Set ( avis ) ] . filter ( ( ligne ) => ! lignes . has ( ligne ) ) , [ ] ) ;
const incorporees = tiers . split ( /^=+$/m ) . filter ( ( section ) => / ^ \ s * incorporé à / . test ( section ) ) ;
assert . ok ( incorporees . length > 0 , 'aucune section « incorporé »' ) ;
const TERMES _APACHE = [
'Version 2.0, January 2004' ,
'TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION' ,
... [ 'Definitions' , 'Grant of Copyright License' , 'Grant of Patent License' , 'Redistribution' , 'Submission of Contributions' , 'Trademarks' , 'Disclaimer of Warranty' , 'Limitation of Liability' , 'Accepting Warranty or Additional Liability' ] . map (
( titre , i ) => ` ${ i + 1 } . ${ titre } . ` ,
) ,
'END OF TERMS AND CONDITIONS' ,
] ;
const sansTexteApache = incorporees
. filter ( ( section ) => / Apache / . test ( section ) )
. filter ( ( section ) => TERMES _APACHE . some ( ( terme ) => ! section . includes ( terme ) ) )
. map ( ( section ) => section . trim ( ) . split ( '\n' ) [ 0 ] ) ;
assert . deepEqual ( sansTexteApache , [ ] ) ;
if ( /Licensed under the Apache License/ . test ( code ) ) {
assert . ok ( incorporees . some ( ( section ) => / Apache / . test ( section ) ) , 'du code Apache-2.0 sans section qui le nomme' ) ;
}
const incises = tiers . split ( '\n' ) . filter ( ( l ) => / ^ Copyright \ b / . test ( l ) && /[àâçéèêëîïôûùüœ]/ . test ( l ) ) ;
assert . deepEqual ( incises , [ ] ) ;
} ) ;
} ) ;