Le dépôt n'avait aucun test, aucun linter, aucune vérification de types. Outillage - ESLint 9 (flat config) sur api + les deux frontends, Ruff sur le Python - tsc --checkJs sur l'API (pas de TypeScript, juste la vérification) - Vitest : 401 tests JS ; pytest : 43 tests Python - Tests de mutation (Stryker), deux profils : logique pure et API complète - Hook pre-push `npm run check` (~17 s) — le déploiement Coolify est sur webhook, c'est donc la seule porte de qualité avant la mise en ligne - Workflow Forgejo Actions prêt (inerte tant qu'aucun runner n'est enregistré) Sécurité - Injection SQL authentifiée dans resolve-conflict : `field` était interpolé dans le SET sans allowlist - timingSafeEqual levait sur un jeton multi-octets (500 au lieu de 401) - setErrorHandler écrasait tous les 4xx en 500 - .env.example : ADMIN_JWT_SECRET et ADMIN_SEED_* n'étaient documentés nulle part alors que leur absence casse toute connexion admin Annulation réelle des crawl_run (migration 014) - L'API posait status='error' sans que le crawler en sache rien : le process continuait, et son UPDATE final ne matchait plus (run réussi affiché en erreur) - Protocole coopératif : drapeau cancel_requested lu à chaque lot, le crawler écrit lui-même status='cancelled' Cohérence géographique (migration 014) - Le trigger 013 supprimait les band_locations sans purger le point dénormalisé - L'édition admin de lat/lon n'atteignait jamais band_locations : la carte ignorait la correction. Override step_order = -1, dans une transaction Corrections - limit/offset NaN → 500 au lieu de 400 - OPTIONS sans `return reply` (Fastify poursuivait le cycle de vie) - listen() sans catch, cast ::text en dur sur les colonnes numériques - /admin/api/logs ne renvoyait pas sa pagination - a11y : sélecteur de langue annoncé comme liste vide (role=option manquant) Nettoyage - apps/web/quizz-site supprimé (sans rapport avec le projet) - Code mort : openModal(), LANG_NAMES, double import, variables inutilisées - .dockerignore ajoutés ; node_modules racine n'était pas gitignoré Co-Authored-By: Claude <noreply@anthropic.com>
103 lines
2.9 KiB
JavaScript
103 lines
2.9 KiB
JavaScript
import js from "@eslint/js";
|
|
import globals from "globals";
|
|
|
|
/**
|
|
* Config ESLint « flat » (ESLint 9).
|
|
*
|
|
* Trois environnements coexistent dans ce repo et n'ont pas les mêmes globales :
|
|
* - apps/api : Node ESM
|
|
* - apps/web/site : scripts classiques navigateur (Leaflet en global)
|
|
* - apps/admin/site : scripts classiques navigateur
|
|
*/
|
|
export default [
|
|
{
|
|
ignores: [
|
|
"**/node_modules/**",
|
|
"apps/web/quizz-site/**",
|
|
"reports/**",
|
|
".stryker-tmp/**",
|
|
"coverage/**",
|
|
],
|
|
},
|
|
|
|
js.configs.recommended,
|
|
|
|
// ---- API : Node, ESM ----
|
|
{
|
|
files: ["apps/api/src/**/*.js"],
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: "module",
|
|
globals: { ...globals.node },
|
|
},
|
|
rules: {
|
|
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
|
|
"no-console": "off",
|
|
eqeqeq: ["error", "smart"],
|
|
"prefer-const": "error",
|
|
"no-var": "error",
|
|
"no-throw-literal": "error",
|
|
"require-await": "warn",
|
|
},
|
|
},
|
|
|
|
// ---- Tests (vitest). Certains tournent sous jsdom → globales navigateur aussi. ----
|
|
{
|
|
files: ["apps/*/test/**/*.js"],
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: "module",
|
|
globals: { ...globals.node, ...globals.browser },
|
|
},
|
|
rules: {
|
|
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
|
|
},
|
|
},
|
|
|
|
// ---- Frontends : scripts classiques navigateur ----
|
|
{
|
|
files: ["apps/web/site/**/*.js", "apps/admin/site/**/*.js"],
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: "script",
|
|
globals: {
|
|
...globals.browser,
|
|
// Chargés via <script> (CDN ou fichier local) avant app.js
|
|
L: "readonly",
|
|
LOCALES: "readonly",
|
|
noUiSlider: "readonly",
|
|
BMPure: "readonly",
|
|
// Footer CJS des modules « purs » partagés navigateur/test
|
|
module: "writable",
|
|
},
|
|
},
|
|
rules: {
|
|
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
|
|
// `try { … } catch {}` volontaire sur les appels best-effort (logout, télémétrie)
|
|
"no-empty": ["error", { allowEmptyCatch: true }],
|
|
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
eqeqeq: ["error", "smart"],
|
|
"prefer-const": "error",
|
|
"no-var": "error",
|
|
// Les frontends déclarent volontairement des globales partagées entre fichiers
|
|
"no-redeclare": ["error", { builtinGlobals: false }],
|
|
},
|
|
},
|
|
|
|
// ---- Traductions : les espaces insécables fines sont de la typographie
|
|
// volontaire (français, tchèque…), pas des caractères parasites. ----
|
|
{
|
|
files: ["apps/web/site/locales.js"],
|
|
rules: { "no-irregular-whitespace": "off" },
|
|
},
|
|
|
|
// ---- Config outillage à la racine ----
|
|
{
|
|
files: ["*.js", "*.config.js"],
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: "module",
|
|
globals: { ...globals.node },
|
|
},
|
|
},
|
|
];
|