metalfrom.eu/package.json
Nicolas Fryder 5237f3666d
Some checks are pending
CI / javascript (push) Waiting to run
CI / python (push) Waiting to run
CI / mutation (push) Waiting to run
feat: validation syntaxique du SQL + supervision des services de fond
Deux angles morts fermés.

1. Syntaxe SQL sans conteneur (apps/api/test/sqlSyntax.test.js)
Le faux pool vérifiait la FORME du SQL mais ne l'exécutait jamais : une requête
syntaxiquement invalide passait tous les tests et n'échouait qu'en production —
c'est précisément ce qui s'était produit avec crawler_pending.
Chaque requête réellement émise par les 28 routes est désormais parsée avec la
grammaire PostgreSQL (node-sql-parser), y compris les SET dynamiques du PATCH
et les casts par type de resolve-conflict. 48 tests, aucun conteneur.
Limite déclarée explicitement : la sémantique n'est pas validée, et deux
requêtes bâties sur jsonb_build_object ne sont pas parsables — le test échoue
si une route cesse d'avoir la moindre requête vérifiable, pour éviter qu'il
passe au vert à vide.

2. Supervision des services de fond (migration 015)
Le crawler et les workers ne sont pas exposés par Traefik : aucune sonde HTTP
ne peut les atteindre. Un crawler dont FlareSolverr était injoignable, ou un
worker à court de quota Geoapify, restait muet — le seul symptôme était
l'absence de données nouvelles, qu'il fallait remarquer soi-même.

Chaque service écrit un battement de cœur horaire dans service_health :
  - crawler  : base, FlareSolverr joignable, dernier run terminé < 12 h
  - geocoder : base, clé Geoapify présente, API joignable, progression < 6 h
Une ligne par service, écrasée à chaque contrôle. L'API calcule `stale` en SQL
(> 2 h sans écriture) : un service arrêté cesse d'écrire, et son dernier
contrôle réussi le ferait sinon passer pour sain indéfiniment.

Le Pilotage affiche une carte « Services » et remonte chaque service dégradé ou
silencieux en alerte actionnable.

Règle appliquée aux sondes : aucune ne peut interrompre le service qu'elle
surveille. Toute exception devient un échec de sonde, l'écriture du résultat et
la journalisation échouent en silence. Un contrôle de santé qui fait tomber le
crawler serait pire que pas de contrôle.

Tests : 580 JS (+51), 63 Python (+20), 85 Playwright (+3)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 18:20:15 +02:00

53 lines
2.9 KiB
JSON

{
"name": "metalfromeu",
"version": "0.0.1",
"private": true,
"type": "module",
"description": "Monorepo metalfrom.eu — outillage qualité (lint, types, tests). Chaque app garde son propre runtime.",
"workspaces": [
"apps/api"
],
"scripts": {
"lint": "eslint . --cache --cache-location node_modules/.cache/eslint/",
"lint:fix": "eslint . --fix --cache --cache-location node_modules/.cache/eslint/",
"typecheck": "tsc -p jsconfig.json",
"test": "vitest run",
"test:watch": "vitest",
"test:cov": "vitest run --coverage",
"test:mutation": "stryker run",
"test:mutation:full": "stryker run stryker.full.config.json",
"lint:py": "ruff check .",
"lint:py:fix": "ruff check . --fix",
"test:py": "npm run test:py:geocoder && npm run test:py:crawler",
"test:py:geocoder": "cd apps/geocoder && python -m pytest tests -q",
"test:py:crawler": "cd apps/crawler && python -m pytest tests -q",
"audit:js": "npm audit --audit-level=high",
"audit:py": "pip-audit -r apps/api/../crawler/requirements.txt -r apps/geocoder/requirements.txt",
"_comment_check": "`check` = la porte de qualite lancee avant chaque push. Les cinq verifications tournent en parallele (voir scripts/check.mjs). Mutation et audits de dependances en sont exclus : trop lents / dependants du reseau — voir `check:full`.",
"check": "node scripts/check.mjs",
"check:full": "npm run check && npm run test:e2e && npm run audit:js && npm run audit:py && npm run test:mutation:full",
"hooks:install": "git config core.hooksPath .githooks",
"_comment_cache": "lint et typecheck utilisent un cache dans node_modules/.cache : `check` tourne des dizaines de fois par jour, refaire l'analyse complete de fichiers inchanges a chaque fois n'apporte rien. Les caches sont invalides par mtime+contenu ; `npm run check:clean` les purge.",
"check:clean": "node -e \"require('fs').rmSync('node_modules/.cache',{recursive:true,force:true})\"",
"check:sequential": "node scripts/check.mjs --sequential",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:install": "playwright install chromium",
"_comment_e2e": "Les parcours Playwright (~22 s) ne sont PAS dans `check` : celui-ci tourne des dizaines de fois par jour et doit rester sous 10 s. Ils sont lances par le hook pre-push, granularite correcte pour ce cout. `npm run test:e2e:install` recupere Chromium sur un clone neuf."
},
"devDependencies": {
"@axe-core/playwright": "^4.13.0",
"@eslint/js": "^9.17.0",
"@playwright/test": "^1.62.1",
"@stryker-mutator/core": "^8.7.1",
"@stryker-mutator/vitest-runner": "^8.7.1",
"@vitest/coverage-v8": "^2.1.8",
"axe-core": "^4.10.2",
"eslint": "^9.17.0",
"globals": "^15.14.0",
"jsdom": "^25.0.1",
"node-sql-parser": "^5.4.0",
"typescript": "^5.7.2",
"vitest": "^2.1.8"
}
}