# Stack technique

Ce que le projet utilise, et pourquoi. Les décisions détaillées sont dans
[decisions/](/decisions) ; cette page sert à s'orienter.

## Socle

| Brique                  | Version | Rôle                                                                                      |
| ----------------------- | ------- | ----------------------------------------------------------------------------------------- |
| Node                    | 22      | Exécution                                                                                 |
| pnpm                    | 10      | Paquets. `pnpm-workspace.yaml` existe **sans clé `packages:`** : ce n'est pas un monorepo |
| Next.js                 | 16.4    | App Router, pages statiques, Turbopack                                                    |
| React                   | 19      | —                                                                                         |
| Mantine                 | 7.17    | Composants d'interface ; thème calculé depuis la charte d'instance                        |
| Pagefind                | 1.4     | Recherche plein texte dans le navigateur, index construit après le build                  |
| Zod                     | —       | Contrats de données, aux frontières (`src/contracts/`)                                    |
| Vitest, Playwright, axe | —       | Tests unitaires, bout en bout, accessibilité                                              |
| Storybook               | 10      | Catalogue de composants ([design-system.md](/interface/design-system))                    |
| Recharts                | 3       | Graphiques du tableau de bord interne                                                     |
| Biome                   | 2.5     | Lint (règles React, Next, TypeScript) ; le formatage reste à Prettier                     |
| Lefthook                | 2       | Contrôles avant commit et avant push                                                      |

Pas de base de données, pas de serveur applicatif : **tout est calculé au build**. Les pages
lisent des JSON sur le disque, la recherche s'exécute chez le visiteur.

## Dossiers

| Dossier              | Contenu                                                                       |
| -------------------- | ----------------------------------------------------------------------------- |
| `instances/<id>/`    | Configuration d'un établissement : charte, périmètre, groupes, thématiques    |
| `scripts/data/`      | Moisson (`extract/`), fusion (`build.ts`), sondes, gel (`freeze.ts`), exports |
| `src/config/`        | Schéma Zod et registre des instances                                          |
| `src/contracts/`     | Modèle publié (`public.ts`) : ce que la chaîne écrit, ce que les pages lisent |
| `src/server/`        | Lecture des données publiées, observabilité. Interdit d'import côté client    |
| `src/app/[lang]/`    | Pages publiques, français sans préfixe, anglais sous `/en`                    |
| `src/app/admin/`     | Tableau de bord interne de couverture, construit seulement sur demande        |
| `src/features/`      | Recherche, analytics, observabilité                                           |
| `src/theme/`         | Thème Mantine calculé, `tokens.css`                                           |
| `packages/`          | Paquets autonomes, réutilisables hors du projet                               |
| `infra/analytics/`   | Pile d'analytics auto-hébergée (Docker Compose)                               |
| `ops/observability/` | Prometheus, Grafana, alertes                                                  |

## Les trois paquets

Autonomes, sans import du projet : copier le dossier suffit à les réutiliser. Résolus par alias
`@workspace/*` dans `tsconfig.json`, **sans** clé `packages:` dans pnpm — le cahier des charges
interdisait la conversion en monorepo.

| Paquet                   | Ce qu'il fait                                                                                   | Ce qu'il ne fait pas |
| ------------------------ | ----------------------------------------------------------------------------------------------- | -------------------- |
| `packages/analytics`     | Collecte : catalogue typé, consentement, files par fournisseur, adaptateurs Umami/Matomo/Rybbit | Aucun rendu          |
| `packages/observability` | OpenTelemetry vers OpenObserve, caviardage                                                      | Aucun rendu          |
| `packages/analytics-ui`  | Visualisation : Mantine + Recharts                                                              | **Aucune collecte**  |

## La chaîne de données

```
  11 sources ouvertes          data/raw/<instance>/        data/public/<instance>/
  RNSR, scanR, HAL, ROR,  ──►  NDJSON bruts, 2,1 Go   ──►  JSON publiés, 18 Mo    ──►  pages
  Wikidata, ORCID, OpenAIRE,   pnpm data:extract            pnpm data:build             statiques
  OpenAlex, theses.fr,         ≈ 20 min                     ≈ 45 s
  annuaire de l'établissement
```

`data/raw/` n'est pas versionné (2,1 Go, et il contient des noms de personnes). **Les JSON
publiés le sont** : 18 Mo, parce que Vercel construit le site à partir d'eux et ne moissonne pas
([exploitation/deploiement-vercel.md](/exploitation/deploiement-vercel)).

## Commandes

| Commande                           | Durée   | Rôle                                              |
| ---------------------------------- | ------- | ------------------------------------------------- |
| `pnpm data:extract`                | ~20 min | Moisson des 11 sources → `data/raw/`              |
| `pnpm data:build`                  | ~45 s   | Fusion et publication → `data/public/`            |
| `pnpm data:freeze` / `data:verify` | < 5 s   | Fige et vérifie la référence (`tests/reference/`) |
| `pnpm data:export`                 | < 5 s   | Tableurs pour les validations externes            |
| `pnpm build:<instance>`            | ~12 min | 23 696 pages (Saclay) puis index Pagefind         |
| `pnpm search:eval <inst> <url>`    | ~2 min  | 20 requêtes de référence → score                  |
| `pnpm a11y <url> [chemins…]`       | ~1 min  | axe, WCAG 2.1 AA                                  |
| `pnpm telemetry:test[:app]`        | ~2 min  | Fumée analytics, paquets puis application         |
| `make check`                       | ~1 min  | Formatage, lint, typage, 75 tests                 |
| `pnpm storybook`                   | —       | Catalogue de composants                           |

## Marque blanche

Une instance = **un fichier de configuration et deux logos**. La charte tient en quatre couleurs
et deux polices ; les échelles, les variantes accessibles et les jetons CSS en sont calculés.
Le périmètre s'exprime avec les identifiants de l'établissement dans chaque source.

Procédure complète : [architecture/marque-blanche.md](/architecture/marque-blanche).
Deux instances sont livrées : Paris-Saclay, et Paris 1 en démonstration.
