crypto.randomUUID n'est exposé par le navigateur qu'en contexte sécurisé
(HTTPS ou localhost). Servie en HTTP simple, l'app plantait dans
generateSecurePassword à l'étape de création de l'arbre.
Repli sur crypto.getRandomValues, disponible partout, qui produit le
même UUID v4 sans tirets.
Signed-off-by: Fabrice Coutadeur <coutadeurf@gmail.com>
- demo_opened (une fois par chargement, visiteur sans compte) et demo_interaction (personne, média, recherche, édition, création d'arbre ; une fois par type, sans contenu)
- Source d'arrivée gardée sur l'appareil (?source=hn) et jointe à account_screen_viewed et account_registered ; la landing transmet la vraie source au CTA et à l'iframe de démo au lieu de source=landing
- Plus de remise à zéro PostHog à chaque ouverture anonyme (effaçait le visiteur de la landing), seulement à la déconnexion
- Landing : un seul événement signup_cta_clicked (hero_cta_clicked retiré, plus utilisé par le funnel), envoyé avant la navigation sans sendBeacon (bloqué par Brave)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- text-wrap-style pretty sur les paragraphes, listes, légendes et chapôs, balance sur les titres
(landing, guides statiques, base pz.css de l'app). Longhand pour ne pas réactiver le retour
à la ligne là où white-space: nowrap est posé. Les noms de l'arbre sont sur le canvas, non touchés.
- Espaces insécables du français dans le texte visible : fine insécable avant ? ! ;,
insécable avant : et dans les guillemets, entre un nombre et son unité (Mo, Go, €, dpi).
Landing (i18n.js, bloc fr hors meta) et pages guides FR (corps de page, hors script).
- App : frenchTypography() appliquée par t() en français, avant le remplacement des variables ;
le dictionnaire anglais reste indexé sur le français tel qu'écrit. Tests mis à jour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Avant le chargement de l'iframe, le bloc #demo n'affichait qu'un fond papier
et le bouton n'apparaissait qu'une fois la démo chargée. Une capture fixe de
l'arbre (FR/EN, grand écran et mobile, WebP 29 à 69 Ko, même origine) sert
maintenant de fond, et le bouton est visible dès l'arrivée sur la section.
Le clic garde le même comportement ; s'il arrive avant la fin du chargement,
le symbole de chargement s'affiche par-dessus la capture.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choix A de Patch le 02/10/2026 sur la planche des trois terracotta : fond
#D2694A (--a500) sur .sec-demo et .final à la place des roses --a50 et
--peche-50, titres et textes en blanc, bouton final blanc à texte encre.
Le cadre de la démo perd son ombre portée, il garde sa bordure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le souvenir « Mariage Simone 1943 » ouvrait Big Buck Bunny sur YouTube.
Remplacé par 25 s des actualités Polygoon « Bruiloft te Giethoorn » (1947,
Beeld en Geluid via Open Beelden, CC BY-SA 3.0 NL), sans le son, en H.264
pour Safari, servi par le site (media-src 'self'). Crédit dans CREDITS.md
et sources.json. Les trois autres vidéos YouTube de remplacement de la démo
(Témoignage filmé 1960, Anniversaire 2020, Interview archive radio) sont
retirées.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Vidéo piégée : l'expression régulière de position était quadratique, un faux MP4 de 2 Mo figeait l'API (bornée, linéaire)
- Sessions émises avant le 01/10/2026 refusées (SESSIONS_NOT_BEFORE) : des sessions ont fuité dans les annotations et les journaux jusqu'à la 1.0
- Contributions : l'« avant » montré au propriétaire vient de la base ; une suppression approuvée part en cascade (unions, liens, souvenirs, portrait, fichiers)
- Mot de passe de partage : essai compté avant bcrypt, comparaisons une par une, changement et rattachement dans une transaction
- Quota mesuré sur le disque, 10 arbres par compte, corps à 1 Mo hors envois, bornes sur annotations, contributions et GEDCOM
- GIF réencodés ; anciennes propositions d'annotation sans adresse stockée ; clé d'exemple refusée sans NODE_ENV
- Déploiement du commit validé par la CI, clé d'hôte obligatoire ; nginx : X-Forwarded-For écrasé, version masquée, journal sans query string
- PostHog verrouillé dans le code, sans script externe ; CSP sans *.posthog.com ; sauvegardes hors serveur chiffrées par défaut
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vite 8 relève sa cible par défaut à Safari 16.4 et Chrome 111. La famille ouvre souvent l'arbre sur un vieil iPad ou un ancien téléphone : la cible reste celle de Vite 7 (Chrome et Edge 107, Firefox 104, Safari 16.0), valeurs lues dans le guide de migration de Vite 8.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vitest 5 demande Vite 6.4 ou plus et Node 22.12 ou plus (Vite 8.3 et Node 24 ici). Aucun test n'utilise vi.mock, bench, describe.sequential, toThrow('') ni d'assertion asynchrone non attendue, les points touchés par le guide de migration ; clearMocks activé par défaut est sans effet sans mock. 97 tests passent, comme avant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La PR montait @eslint/js seul, dont la config recommandée ajoute no-useless-assignment, no-unassigned-vars et preserve-caught-error. eslint passe aussi en 10 (configuration à plat déjà en place, aucun commentaire eslint-env, Node 24) ; eslint-plugin-react-hooks 7.1 et eslint-plugin-react-refresh 0.5 la déclarent prise en charge. Les trois signalements de no-useless-assignment étaient fondés : une valeur initiale jamais lue, chaque branche suivante l'écrase ou quitte la fonction (libellé de parenté, contenu JSON d'une photo ou d'un tracé posé sur l'arbre). Valeur initiale retirée, comportement inchangé.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La PR seule ne pouvait pas s'installer : @vitejs/plugin-react 5 n'accepte pas Vite 8 (ERESOLVE à npm ci, en CI comme dans l'image Docker). Les deux montent ensemble : Vite 8 construit avec Rolldown et Oxc, la 6 du plugin abandonne Babel, dont ce projet n'utilisait aucune option. Rien à migrer dans vite.config.js (ni rollupOptions, ni esbuild, ni manualChunks). Le build garde un seul script module externe dans index.html, sans eval ni script en ligne, compatible avec la CSP ; les polices auto-hébergées sortent avec les mêmes empreintes. Changements à connaître : CSS minifiée par Lightning CSS, cible navigateur par défaut relevée (Safari 16.4, Chrome et Edge 111, Firefox 114), Node 20.19 ou 22.12 minimum (Docker et CI en Node 24).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La version 1 ne publie plus dist/esm/icons/*.js ni __iconNode : renderers.js importait ces fichiers internes pour dessiner les vignettes carte et GPX sur le canvas, d'où l'échec du build (CI Frontend et image Docker). Les tracés viennent maintenant de @lucide/icons, le paquet officiel des données d'icônes (export nommé, propriété node), à la même version que lucide-react. Les 47 icônes utilisées existent toujours sous leur nom (Edit3, HelpCircle, MoreHorizontal, Trash2 restent des alias) et rendent un SVG identique à la 0.564, aria-hidden compris. Les icônes de marque retirées en v1 ne sont pas utilisées ici.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reprend la PR Dependabot #30. La 7.0 adopte les défauts de la 6.0 et rend fatales ses options dépréciées : rien de cela ne touche tsconfig.json, qui fixait déjà rootDir (src), types (["node"]), module et moduleResolution NodeNext, target ES2022, strict, esModuleInterop à true. Le JavaScript produit dans dist/ est identique octet pour octet à celui de TypeScript 5.9.3 (diff -r vide sur les 27 fichiers). La 7.0 n'a plus d'API JavaScript, mais aucun outil du backend ne s'en sert : vitest et tsx ne passent pas par tsc, Prisma ne lit que le numéro de version et tolère son absence. Binaires natifs dans le package-lock pour Linux x64 (CI, Docker, VPS) et Windows. 113 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reprend la PR Dependabot #34 (18.0.5, correctif publié depuis). Depuis la 17.0.0, config() écrit « ◇ injected env (N) from .env » à chaque démarrage, même sans fichier .env (vu avec la 18.0.5 : « injected env (0) »), ce qui glisse une ligne hors format dans les journaux JSON de production : config({ quiet: true }). Les autres ruptures de la 18.0.0 (préchargement -r dotenv/config retiré, .env.vault retiré) ne touchent pas Parenthèse, qui n'appelle que config() dans config/env.ts. La CLI Prisma garde sa propre copie de dotenv 16. 113 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reprend la PR Dependabot #32. Rupture de la 11.0.0 : la pré-vérification CORS n'annonce plus par défaut que GET, HEAD et POST, un navigateur d'une autre origine aurait refusé les PUT, PATCH et DELETE de l'API. La liste d'avant est maintenant écrite dans app.ts, et un test vérifie la réponse à un OPTIONS d'une origine autorisée (il échoue sans la ligne). Option origin inchangée. Les ajouts de la 11.x (logLevel du préflight, cors par route dans config.cors) ne sont pas utilisés. 113 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
React et react-dom 19.3.0, @types/react et @types/react-dom 19.3.0, posthog-js 1.434.15, autoprefixer 10.6.1, eslint-plugin-react-hooks 7.1.1, eslint-plugin-react-refresh 0.5.7. La règle react-hooks/refs de la 7.1 voit maintenant les refs lus ou écrits pendant le rendu, corrigés plutôt que désactivés : le nom de l'arbre partagé affiché par l'écran compte passe par un état (un ref ne redessine rien), la personne de départ de l'arbre est recopiée dans un effet déclaré avant celui du cadrage, l'API de zoom est exposée par useImperativeHandle, l'horodatage des portraits est posé au premier chargement au lieu de Date.now() au rendu (react-hooks/purity). react-refresh 0.5 garde l'export par défaut et la config vite utilisés ici.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nginx 1.31 est la branche mainline (développement), la stable est 1.30 (nginx.org au 01/10/2026). Les montées mineures de nginx sont ignorées, les correctifs de la 1.30 arrivent toujours. Passer à la stable suivante à la main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L'action passe de Node 20 à Node 24, ce qui retire l'avertissement de dépréciation des journaux de déploiement. Empreinte épinglée vérifiée contre le tag v0.10.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Versions mineures. Pages vues à l'écran (accueil FR et EN, téléphone, vie privée), 13 tests, et événements PostHog toujours envoyés (requêtes interceptées, comparées avec main).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reprend la PR Dependabot #29. La 11.2.0 corrige GHSA-grpc-p53c-r64v : la clé par défaut était l'adresse IP telle quelle, un client IPv6 changeait d'adresse dans son /64 (ou réécrivait la même autrement) et repartait à zéro. La clé est maintenant l'adresse normalisée, IPv4 mappée ramenée en IPv4, IPv6 regroupée par /64. Rupture de la 11.0.0 : option whitelist retirée au profit d'allowList, que Parenthèse n'utilise pas. Aucun changement de code (global: false, limites par route dans config.rateLimit, clé par défaut sur request.ip calculée par Fastify avec TRUST_PROXY). 112 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reprend le groupe Dependabot backend-minor-patch (#27). vitest arrive en 5.0.3, correctif publié depuis la PR (5.0.2). Aucun changement de code. Compilation propre, 112 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Les versions de sharp antérieures à 0.35.0 sont touchées quand elles décodent des images non fiables (GHSA-f88m-g3jw-g9cj, libvips), et antérieures à 0.35.4 pour libheif (GHSA-rgj7-g3m4-5g8c). Parenthèse décode les photos envoyées par la famille : montée en 0.35.5 (libvips 8.18.7). Aucun changement d'API utilisé ici (Node 24, failOn, rotate, sorties JPEG/PNG/WebP). 112 tests, recette sécurité et comparaison de rotation relues.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le test cherchait l'ancienne barre (« Contribuer à l'arbre »). Il suit maintenant la carte d'accueil, la barre « Ajouter un souvenir » et le menu « Plus » pour créer un compte. 19/19 en local.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Avec un arbre, la fenêtre « Mon compte » ne menait nulle part : elle a maintenant « Gérer mon compte », qui ouvre « Vos arbres » (déconnexion, suppression du compte), la croix ramène à l'arbre. Sans arbre, « Mon compte » renvoyait aussitôt à l'assistant de création : le repli vers la démo ne se déclenche plus quand l'utilisateur a ouvert l'écran lui-même. Sur le lien d'un arbre supprimé, la session du compte est restaurée, « Créer mon arbre » ouvre le compte connecté au lieu d'un écran de connexion. Page vie privée alignée sur le chemin réel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La page décrivait encore deux mots de passe (visiteur et contributeur). Elle décrit maintenant le mot de passe de partage (haché, et gardé chiffré pour que le propriétaire le relise), la famille qui propose des ajouts, la fermeture des sessions quand il change, le prénom noté sur une contribution, et les photos réenregistrées sans position GPS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Camille et Alex (hero, « Pour qui ») venaient de randomuser.me, sans licence établie pour un site public. Remplacés par deux portraits Pexels recadrés en 400 x 400 (Clay Elliot, Vitaly Gariev), sources dans CREDITS.md. Images d'aperçu refaites depuis le hero actuel, sur le fond de la DA.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pages française et anglaise alignées sur le code. Polices servies sans Google,
vidéos YouTube et cartes chargées seulement à l'ouverture, pas d'enregistrement
de session ni d'adresse d'arbre envoyée à PostHog, arbre entier et export GEDCOM
pour qui a le mot de passe, contributions anonymisées à la suppression du compte.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unsplash pour les souvenirs, Pexels pour Lya, Marc et Jeanne. Les trois
portraits randomuser.me n'ont pas de licence établie pour un site public,
signalé dans le fichier.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Portraits, souvenirs, photos posées sur l'arbre et aperçus de relecture chargeaient leur image avec la session complète dans l'adresse. Ils utilisent maintenant le jeton de lecture des médias fourni avec le graphe (utils/mediaToken.js), jamais ajouté à une adresse externe. L'aperçu d'une photo posée par la famille n'accepte qu'une photo de cet arbre, reconstruite depuis son chemin. Message pour un arbre plein (storage_quota_exceeded).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le cache d'images de l'arbre chargeait l'adresse de chaque média, vidéos YouTube
comprises, sans que personne lance la vidéo. Seules les photos, les seules
dessinées, sont chargées.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L'identifiant doit faire 11 caractères (lettres, chiffres, - et _), sinon rien
n'est intégré. Le lecteur vient de youtube-nocookie.com. La carte OpenStreetMap
n'envoie plus que l'origine en referrer, plus l'adresse de l'arbre.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L'API /api/arbre/:slug ne renvoie ni nom ni email du propriétaire. Les lectures
restaient dans le front sans jamais rien recevoir.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le paramètre ?source=app-shared&return_url= affichait n'importe quelle adresse
dans un lien du site. L'app ne l'envoie plus, le bloc part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Toutes les copies invite_password_* partent au lancement de l'app, plus seulement
celle de l'arbre rouvert. À la déconnexion et à la suppression du compte, les
brouillons d'édition, la fiche « c'est moi » et le profil sont effacés. Le prénom
du contributeur, la langue et les accueils vus restent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Un nom GEDCOM piégé bloquait l'API plusieurs secondes (expression régulière quadratique) : lu par indexOf, lignes et fiches plafonnées. L'export n'injecte plus de lignes. Deux relectures simultanées appliquaient deux fois les ajouts : la session est verrouillée dans la transaction. Au plus 200 changements par envoi, photos refusées effacées du disque.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Les annotations se lisaient avec n'importe quel compte, et une annotation photo enregistrait l'adresse de l'image avec la session de son auteur. Accès vérifié, adresse retirée à l'écriture comme à la lecture. Les images portent un jeton de lecture des médias (12 h, un arbre) au lieu de la session du compte. trustProxy ne croit plus que nginx (TRUST_PROXY), l'API peut n'écouter que la boucle locale (HOST), plafond de 30 échecs par arbre sur le mot de passe de partage, changer ce mot de passe coupe les comptes rattachés, connexion à durée constante, secrets d'exemple refusés en production.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Un SVG ou du HTML déclaré « photo » passait et s'exécutait sur app.parenthese.io à l'ouverture (vol de session du propriétaire). Le type vient maintenant des premiers octets, les photos sont réencodées par sharp (rotation appliquée, EXIF et GPS retirés), les vidéos de téléphone perdent leur position, et chaque fichier est servi avec nosniff et une CSP sandbox (téléchargement pour tout ce qui n'est pas image, son, vidéo ou PDF). Fichiers supprimés effacés du disque, quota par arbre, limite de débit sur les envois.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VITE_HELP_URL et VITE_PRIVACY_URL remplacent les pages de parenthese.io,
qui restent la valeur par défaut. Documenté dans les deux README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
before_send réécrit /arbre/<slug> en /arbre/:slug dans toutes les propriétés
(URL, chemin, referrer, propriétés initiales de personne). Enregistrement de
session coupé explicitement, appel /flags coupé (aucun feature flag utilisé,
il envoyait les propriétés de personne sans passer par before_send).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>