Suivez l'évolution de Boilerplate-Stack avec toutes nos mises à jour, nouvelles fonctionnalités et améliorations.
v1.32Minor
16 juin 2026
Retour visuel à la connexion OAuth, corrections du checkout des licences, pin de Next.js et remédiation de la revue de code v2
Ajoute un retour de chargement en ligne à la connexion OAuth, corrige le flux de checkout des licences de bout en bout (user_id atteint désormais le webhook Stripe, les achats ne sont donc plus silencieusement perdus), rend la carte de facturation de mon compte sensible au modèle de facturation, fige les paquets de la release-train Next.js, et finalise toute la remédiation de la revue de code v2 sur la sécurité, l'architecture et la performance.
Les boutons de connexion OAuth de la page de login affichent désormais un spinner en ligne et un libellé localisé « Connexion à {provider}… », et désactivent tous les fournisseurs pendant qu'une redirection est en cours (components/auth/oauth-buttons.tsx). signInWithOAuth fait un aller-retour réseau pour résoudre l'URL d'autorisation du fournisseur avant que le navigateur ne navigue, si bien que les boutons ne donnaient auparavant aucun retour pendant ce délai et paraissaient inertes. Ajout de aria-busy sur le bouton actif et d'une garde contre les double-clics / le lancement de deux fournisseurs en parallèle ; en cas d'erreur l'état se réinitialise pour permettre une nouvelle tentative, en cas de succès le spinner reste affiché puisque la page navigue déjà
Les sessions de checkout de licence portent désormais user_id dans leurs métadonnées Stripe (createLicenseCheckoutSession dans core/billing/checkout.ts). Le handler checkout.session.completed se protège contre l'absence de account_id/user_id, donc chaque achat de licence était silencieusement abandonné au webhook (« Checkout session missing required metadata ») — la licence n'était jamais créée et les crédits jamais accordés. Cela concernait à la fois le LicensePricingTable du tableau de bord et le flux de la landing. user_id alimente désormais aussi licenses.purchased_by et la ligne d'audit payments (toutes deux auparavant nulles)
Les achats de licence depuis la landing aboutissent désormais de bout en bout. checkout-client.tsx a gagné une branche type: 'license' qui POST vers /api/billing/license-checkout (auparavant les licences passaient par la garde !priceId et levaient « Payment configuration not available for this plan » — les licences portent un productId, pas un priceId Stripe)
landing-licenses.tsx pose désormais le cookie httpOnly bsk_pending_checkout (parité avec le flux d'abonnement, pour que les connexions par magic-link inter-onglets conservent la sélection) et utilise un redirectTo=/checkout sans locale et en liste blanche. L'ancien redirectTo préfixé par la locale et porteur de query était rejeté par sanitizeRedirectTo et renvoyait les acheteurs déconnectés vers /private-dashboard avant qu'ils n'atteignent le checkout
La carte de facturation de /my-account est désormais sensible au modèle de facturation : en mode license elle affiche les libellés « Licence » / « Gérez votre licence et votre accès » / « Aucune licence active » au lieu des chaînes centrées abonnement, et le badge d'accès à la licence affiche le nom de produit localisé (depuis pricingConfig.licenses) au lieu de l'identifiant brut du produit. Les modes subscription et hybrid conservent le libellé d'abonnement
Pin des paquets de la release-train Next.js à ^16.2.9 (depuis ^16.2.6) : next, eslint-config-next et @next/bundle-analyzer. next se résolvait déjà à 16.2.9 (dernière 16.2.x), donc cela ne fait que relever le plancher déclaré pour qu'une installation neuve ne puisse pas retomber sur 16.2.6–16.2.8 ; @next/bundle-analyzer était la seule vraie mise à niveau. Les trois bumpés de concert pour éviter un décalage config-ESLint / analyzer avec le framework. Ne lève pas l'avis postcss (GHSA-qx2v-qp2m-jg93), uniquement au build, que seule une future version de Next pourra résoudre
CRITIQUE — Correction de la politique INSERT obsolète sur admin_logs dans le bloc miroir des migrations en bas de supabase/schema.sql (ARCH-1) : le bloc recréait la politique is_admin WITH CHECK d'avant SEC-M4, donc tout supabase db reset neuf (dev, CI, forks en aval) rouvrait silencieusement la faille de falsification du journal d'audit corrigée par 20260610090002. Le miroir recrée désormais la politique réservée au service_role. Les environnements migrés de façon incrémentale n'ont jamais été affectés
La fusion de comptes OAuth est désormais atomique (SEC-4) : un nouveau RPC SECURITY DEFINER merge_oauth_duplicate_user (EXECUTE réservé au service_role + garde auth.role()) supprime le compte personnel/profil de l'utilisateur en double et reporte les données d'affichage OAuth en une seule transaction. auth.admin.deleteUser ne s'exécute qu'après le commit ; en cas d'échec, le flux se termine quand même en « compte lié » (ligne auth orpheline journalisée) au lieu de recréer des lignes pour l'utilisateur fusionné
Le sameSite du cookie CSRF est passé de 'strict' à 'lax' aux trois points de définition (SEC-3) — 'strict' supprimait le cookie sur les navigations top-level cross-site (liens d'e-mail), désynchronisant la paire double-submit en 403 parasites. Le token n'est pas secret ; la validation Origin/Referer et la comparaison double-submit sont inchangées. Ajout d'un test d'invariants d'architecture (SEC-5) fixant les contraintes porteuses UNIQUE(referred_account_id) et le CHECK d'auto-parrainage pour qu'un refactor ne puisse pas supprimer silencieusement la garantie anti-farming
Consolidation du domaine des licences (ARCH-2/3) : la lecture par lot des licences du contrôle d'accès unifié et la liste admin des licences passent désormais par de nouveaux lecteurs core/licenses/queries.ts ; suppression de 9 fonctions de licence mortes plus des types inutilisés. Suppression du cluster d'exports core/ non câblés sur accounts/affiliates/roles/chat (ARCH-4) ; les lecteurs de chat vivants ont perdu leur suffixe Uncached trompeur. Contrôles de rôle consolidés sur hasRole() et bascule de redirection post-auth unifiée derrière getPostEventRedirect() (ARCH-5/6) ; l'expiration des invitations dérive désormais de workspaceConfig et resendInvitation() a migré vers core avec la validation Zod qui lui manquait (ARCH-7/8/11)
Réécriture du job generate-analytics (PERF-1/2) : le résumé quotidien d'usage IA appelle désormais le RPC d'agrégation get_admin_ai_period_stats au lieu de télécharger chaque ligne ai_requests du jour et de réduire en JS, et ses trois lectures indépendantes s'exécutent en Promise.all. check-low-credits-alerts a déplacé son filtre de solde dans la BDD au seuil configuré le plus large (PERF-3) ; cleanup-push-subscriptions exécute ses trois suppressions indépendantes en parallèle (PERF-4)
v1.31Patch
10 juin 2026
Remédiation de la revue de code complète — sécurité, architecture, performance et base de données
Une large passe de remédiation issue d'une revue de code complète : gardes recent-auth et validation Zod sur les routes admin destructrices, écritures du journal d'audit réservées au service_role, blocklist SSRF étendue et retrait des SVG du bucket public, restauration d'un contrôle RLS admin bicolonne, nettoyage d'index redondants et nouveaux composites de chemin chaud, agrégations d'analytics admin (~100k lignes) déplacées dans des RPC SQL, élimination de la double authentification sur les routes chat/parrainage, et une série de correctifs d'architecture et d'i18n.
Ajout de la validation Zod (union discriminée, licenseId UUID, days borné) et de la garde requireRecentAuth() à la route admin de révocation/extension de licence — révoquer une licence reprend des crédits et un accès payant, donc une session admin détournée et périmée ne peut plus le faire en un seul POST. requireRecentAuth() ajouté aussi aux routes d'annulation de parrainage et de désactivation de code de parrainage, comblant l'écart avec la route structurellement identique d'annulation de conversion d'affilié
INSERT sur admin_logs restreint au service_role (20260610090002) : l'ancien WITH CHECK is_admin permettait à n'importe quel admin de forger des lignes d'audit attribuant des actions à un autre admin via la Data API. Toutes les écritures légitimes passaient déjà par supabaseAdmin ; le SELECT reste lisible par les admins
Arrêt des fuites du texte d'erreur Postgres/RPC brut depuis les quatre routes cron admin — les clients reçoivent désormais une erreur générique avec un code tandis que le message complet reste dans error_logs. L'inscription/désinscription newsletter ne renvoie plus la chaîne d'erreur brute du fournisseur d'e-mail en 500, et le chemin d'échec fournisseur de désinscription est désormais journalisé
Extension de la blocklist SSRF des IP privées (lib/security/ssrf.ts) avec la plage partagée CGNAT 100.64.0.0/10 (RFC 6598), 192.0.0.0/24 (RFC 6890) et 198.18.0.0/15 (RFC 2544) — les URLs de webhook configurées par les admins ne peuvent plus atteindre ces plages
Retrait de image/svg+xml du bucket media public (20260610090006) et de la liste blanche côté application, supprimant avec lui le filtre de contenu SVG par regex contournable. Les SVG peuvent embarquer des scripts et le bucket est public ; utilisez PNG/WebP ou servez des SVG de confiance depuis /public
Correction de user_is_account_admin qui perdait la branche role_slug (20260610090000) : la migration de pin du search_path du 29 mai avait recréé la fonction avec le seul contrôle role hérité, refusant silencieusement la gestion des membres aux admins à rôle personnalisé dont l'adhésion ne renseigne que role_slug. Contrôle bicolonne restauré partout, y compris le CREATE OR REPLACE ultérieur qui l'emporte sur un db reset neuf
Suppression d'index redondants/chevauchants (20260610090005, 20260610090008) pour réduire l'amplification d'écriture, et ajout d'index composites de chemin chaud via des migrations CONCURRENTLY autonomes : api_keys(account_id, created_at DESC), documents(account_id, created_at DESC) et referrals(ip_hash, created_at DESC) soutenant le comptage de rate-limit IP d'apply_referral_code
Remplacement de l'agrégation JS des analytics admin (~100 000 lignes ai_requests par rendu de /admin-dashboard/analytics) par 8 RPC d'agrégation SQL (20260610090001) : get_admin_ai_period_stats, get_admin_active_user_count, get_admin_credit_flow, get_admin_daily_stats, get_admin_top_users, get_admin_model_stats, get_admin_agent_stats, get_admin_org_stats — tous SECURITY DEFINER, EXECUTE réservé au service_role, gardés par auth.role() dans le corps
Suppression du double auth.getUser() + double fetch des memberships sur 8 routes chat-session et parrainage : le nouveau checkPermissionForUser(userId, accountId, permission) réutilise l'utilisateur déjà authentifié par apiSecurity.* — ~60–120 ms gagnées par requête
Bornage des requêtes de liste non bornées (listDocumentsForAccount plafonné à 200 avec metadata retiré de la projection de liste, api_keys plafonné à 100), parallélisation des awaits indépendants du layout du tableau de bord admin, et déplacement d'isomorphic-dompurify (~150 Ko) hors du bundle client du formulaire de page CMS vers un composant de prévisualisation dynamic ssr:false. Plancher de performance Lighthouse CI relevé de 0,7 à 0,85
Remplacement de 7 contrôles d'owner bicolonne en ligne par le helper partagé hasRole() (mon compte, layout admin, pages abonnements/organisations/licences/détail org). Cela a fait surface et corrigé deux bugs latents : la page licences ne vérifiait que role_slug et la page détail-org ne vérifiait que role — les deux requêtes sélectionnent désormais les deux colonnes
Extraction de l'orchestration service_role en ligne des trois pages admin les plus lourdes vers des modules core : users → core/organizations/admin-users.ts, organizations → core/organizations/admin-organizations.ts, subscriptions → core/billing/admin-subscriptions.ts. Sans changement de comportement ; les pages sont désormais de fines coquilles Server Component
Correction du formatage de nombres/dates sans locale sur les KPI + le tableau des conversions du tableau de bord affiliés, ReferralStatsRow et landing-licenses.tsx (désormais via getIntlLocale(locale)). Câblage du producteur createInAppNotification jusqu'alors non branché : le job check-low-credits-alerts crée désormais une notification cloche pour l'owner du compte en plus du push
Mise à jour des règles IA canoniques et de la doc utilisateur pour coller à la nouvelle réalité du code (liste des actions recent-auth, famille de RPC d'analytics, interdiction des SVG, guidance checkPermissionForUser(), cible Lighthouse 70 % → 85 %) et synchronisation des miroirs de règles .codex / .gemini / .opencode
v1.30Patch
29 mai 2026
Garde owner-mint à l'INSERT sur les adhésions, restriction RGPD des clés API et lookup d'authentification résilient
Ferme une voie d'escalade de privilèges intra-tenant sur memberships (un admin non-owner pouvait créer un owner via un insert direct sur la Data API), restreint la section api_keys de l'export RGPD aux owners/admins, et ajoute un wrapper d'authentification edge-safe pour que les sessions Supabase manquantes ou expirées soient traitées comme anonymes au lieu de lever une exception. Plus un correctif du script d'init pour que npm run init ne supprime plus le bloc de configuration CDN.
Fermeture d'une voie d'escalade de privilèges intra-tenant sur memberships : la garde owner-mint (lock_membership_sensitive_columns) était liée uniquement en BEFORE UPDATE, alors que la politique RLS « Admins can insert memberships » vérifie le statut admin du compte sans contraindre le rôle inséré — un admin non-owner pouvait donc faire POST /rest/v1/memberships avec role='owner' pour un utilisateur complice et l'élever au rang d'owner (pouvoirs de facturation + suppression). Le trigger est désormais BEFORE INSERT OR UPDATE et rejette les inserts/updates owner provenant d'appels Data API
Correctif à chaud du périmètre de rôle de la garde : la première version utilisait auth.role() is distinct from 'service_role', ce qui attrapait le trigger d'inscription SECURITY DEFINER handle_new_user_account() (il s'exécute sur la connexion auth-admin GoTrue où auth.role() vaut NULL) et faisait échouer l'inscription avec « Database error saving new user ». Reciblé sur auth.role() in ('authenticated','anon') pour que l'escalade reste bloquée sur les écritures PostgREST directes tandis que les triggers internes et le bootstrap service_role sont exemptés
Restriction de la section api_keys de l'export RGPD aux comptes où le demandeur est owner/admin (via getOrgManagerRoles() + hasRole()), alignée sur le contrôle de l'UI de gestion (requireApiKeyManager) pour qu'un simple membre ne puisse plus exporter les noms/préfixes/scopes de clés qu'il ne peut pas voir autrement
Ajout de lib/auth/safe-get-user.ts (safeGetUser) — un wrapper edge-safe qui traite une session Supabase manquante/expirée/invalide comme anonyme au lieu de laisser @supabase/ssr lever AuthApiError: Invalid Refresh Token (qui apparaissait en 500 non géré / bruit dans les logs). Branché sur chaque appel serveur à auth.getUser() (get-user.ts, proxy.ts, api-security.ts, permissions/check.ts, verify-admin.ts, stripe/actions.ts, ai/stream). Échoue côté fermé — les routes protégées redirigent toujours, les routes authentifiées renvoient toujours 401
Correction de scripts/init-project.js qui régénérait config/app.ts sans le bloc cdn ni les exports isPublicCacheablePath() / PUBLIC_CACHEABLE_PATHS. La fonctionnalité CDN (1.29) les avait ajoutés à la config commitée, au prompt du wizard et à .env.example, mais le template generateAppConfig() n'avait jamais été mis à jour — donc lancer npm run init écrasait config/app.ts en supprimant 57 lignes, cassant proxy.ts
Report du bloc cdn, de PUBLIC_CACHEABLE_PATHS et de isPublicCacheablePath() dans le template generateAppConfig() pour que les futurs inits émettent une config complète, et ajout de l'anti-pattern A15 : tout champ/helper ajouté à un fichier de config régénéré par init-project.js DOIT être reporté dans le template generate*() correspondant dans le même changement — traité comme une unité d'édition, comme le miroir migration↔schema.sql (A1)
v1.29Minor
29 mai 2026
Renforcement sécurité, jobs et performance base de données, rendu des slugs dynamiques, cache CDN optionnel et SEO/localisation
Une large passe de durcissement sur la sécurité, les jobs en arrière-plan et la performance de la base de données : politiques d'adhésion et de suppression de compte resserrées, validation SSRF des webhooks centralisée, synchronisation Stripe réservée au service role, RPC d'agrégation pour les compteurs du tableau de bord, et search_path figé sur les fonctions SECURITY DEFINER. Plus un correctif de rendu des slugs dynamiques blog/CMS en production, une couche de cache CDN edge activable et des améliorations SEO/localisation (html lang rendu côté serveur, URLs du site normalisées, meilleure négociation de locale).
Durcissement des mises à jour de memberships avec WITH CHECK plus un trigger qui bloque la réassignation de compte/utilisateur hors service role et l'auto-élévation du propriétaire via role ou role_slug
Refactorisation de /api/admin/jobs/** en routes légères adossées à core/jobs/*, validation SSRF des webhooks centralisée dans lib/security/ssrf.ts, et documentation des vérifications DNS partagées à la création et à l'exécution
Refonte de sync-stripe pour grouper les mises à jour d'état des abonnements existants via un RPC réservé au service role, tout en gardant la création des lignes manquantes dans le flux du domaine facturation
Ajout de l'usage de RPC d'agrégation pour les statistiques IA admin/org et documentation du pattern « pas de téléchargement de lignes brutes » pour les compteurs du tableau de bord
Mémoïsation des wrappers CMS unstable_cache() par clé canonique et documentation du pattern pour les futurs caches à clé dynamique
Resserrement des échecs d'export RGPD : les messages internes par section sont désormais journalisés côté serveur et le client reçoit une réponse générique EXPORT_FAILED
Fermeture d'une voie d'élévation de privilèges intra-tenant sur account_deletion_requests : la politique UPDATE d'annulation par le propriétaire porte désormais un WITH CHECK, et un trigger BEFORE UPDATE de verrouillage de colonnes restreint les propriétaires hors service role à une transition status → 'cancelled', de sorte qu'un membre ou propriétaire ne peut plus modifier cascade_members (ni aucun autre champ) via la Data API pour forcer la suppression d'un compte membre
Fixation de SET search_path = public sur les helpers d'adhésion SECURITY DEFINER (user_belongs_to_account, user_is_account_admin, user_has_permission) et les triggers lock_membership_sensitive_columns / lock_notification_columns
Ajout d'un WITH CHECK correspondant à la politique storage.objects « Admins can update media » pour qu'un admin ne puisse pas déplacer un objet média hors du bucket media
Restriction du CHECK pending_emails.email_type à l'ensemble pris en charge par le worker (contact, newsletter, notification) afin que les types non supportés échouent dès l'insertion plutôt que d'être silencieusement marqués en échec permanent
Consolidation de la validation SSRF des URLs de jobs admin dans core/admin/settings.ts sur le résolveur canonique validatePublicWebhookUrl (suppression d'une copie regex/DNS divergente)
Remplacement du reduce JS du chiffre d'affaires de l'aperçu admin par un RPC d'agrégation get_admin_revenue_sum et encapsulation de getPlatformStats dans unstable_cache (60s, par locale)
Suppression d'index mono-colonne redondants couverts par des composites (credit_tx_account_idx, subscriptions_account_idx) et ajout de composites pour les chemins critiques du chiffre d'affaires (payments(status, created_at desc)) et de l'historique des jobs (job_runs(job_id, started_at desc))
Déplacement de la route de détail documents/[id] vers un dispatch léger sur un nouveau lecteur core/documents, mémoïsation du wrapper de cache blog par post, suppression d'un appel getUser() redondant à l'onboarding (désormais via SecurityContext.user_metadata), parallélisation des boucles par liste du fournisseur d'emails, suppression d'environ 20 casts as any obsolètes sur des tables désormais typées, et regroupement des helpers de notifications in-app dans core/notifications/
Correction d'un 500 DYNAMIC_SERVER_USAGE en production sur les articles de blog / pages CMS publiés après le dernier build : blog/[slug] et [slug] sont désormais force-dynamic (les données restent mises en cache via unstable_cache), de sorte que l'ISR à la demande n'entre plus en conflit avec la lecture headers() du nonce CSP par requête du layout racine
Ajout d'une couche de cache CDN edge activable (CDN_PUBLIC_CACHE_ENABLED, CDN_PUBLIC_S_MAXAGE, CDN_PUBLIC_SWR) : une fois activée, le middleware annonce un Cache-Control cacheable sur les requêtes GET anonymes vers les pages de contenu public uniquement (sans cookie d'auth, sans Set-Cookie), en préservant le nonce CSP strict par requête tandis que les réponses authentifiées/tableau de bord/API restent private, no-store. Désactivé par défaut ; documenté dans caching.md, .env.example et la doc de cache en production
Rendu côté serveur de la locale réelle de la route sur <html lang> (fr-FR, fr-CH, en-US, en-CA) au lieu de corriger une langue par défaut après hydratation, améliorant la précision pour les crawlers et les outils d'accessibilité
Centralisation de la normalisation de l'URL du site pour les URLs canoniques, les alternates hreflang, OpenGraph/JSON-LD, le sitemap et les métadonnées robots, afin que les slashs finaux dans NEXT_PUBLIC_APP_URL ne produisent plus de doubles slashs
Amélioration de la négociation de locale pour les requêtes sans préfixe afin qu'Accept-Language puisse rediriger vers les locales régionales exactes configurées, comme en-CA
v1.28Patch
27 mai 2026
Synchronisation du widget crédits de la barre latérale privée, grants Data API Supabase explicites et skills d'agent Supabase rafraîchis
Le widget crédits de la barre latérale du tableau de bord privé reste désormais synchronisé avec les props faisant autorité côté serveur après des actions serveur comme Fin d'essai ; une nouvelle migration rend explicites les grants Data API Supabase pour les nouveaux projets et les réinitialisations de base de données, avec la documentation rafraîchie ; les skills locaux de l'agent Supabase et les entrypoints des outils IA sont mis à jour pour invoquer à la fois le skill Supabase produit et le skill des bonnes pratiques Postgres.
Le widget crédits de la barre latérale du tableau de bord privé se met désormais à jour immédiatement après les changements de crédits côté serveur (notamment la confirmation de Fin d'essai) sans nécessiter de rechargement complet de la page
Cause racine corrigée : l'initialiseur paresseux de useState(initialCreditsBalance) ne s'exécutait qu'au montage, donc router.refresh() re-rendait le layout avec une nouvelle prop faisant autorité côté serveur tandis que la barre latérale conservait son état local obsolète — ajout d'un useEffect qui resynchronise creditsBalance chaque fois que la prop initialCreditsBalance change, tout en préservant l'écouteur 'credits-updated' existant pour les décrémentations optimistes de chat
La barre latérale d'organisation lisait déjà workspace.creditsBalance directement depuis les props, aucun changement n'était donc nécessaire de ce côté
Nouvelle migration 20260527225001_explicit_data_api_grants.sql qui rend explicite l'accès à la Data API Supabase pour les nouveaux projets et les réinitialisations de base : les privilèges public-schema par défaut sont révoqués et les grants de table anon/authenticated/service_role prévus sont réappliqués, les miroirs de schéma complet portant la même surface de grants
Modèle de grants Data API documenté dans la doc de configuration Supabase, le guide d'onboarding, les notes de déploiement du README et les règles IA base de données afin que les futures tables publiques, vues, séquences et RPC livrent grants/revokes en même temps que les politiques RLS
Mise à jour des skills locaux de l'agent Supabase depuis supabase/agent-skills à 4e69c80 : ajout du skill général supabase et rafraîchissement de supabase-postgres-best-practices à la disposition references/ amont 1.1.1
Mise à jour des entrypoints des outils IA et des mirrors de skills/règles générés pour que les futurs travaux Supabase invoquent à la fois le skill Supabase produit et le skill des bonnes pratiques Postgres lorsque pertinent
v1.27Minor
27 mai 2026
États de chargement des tableaux de bord, lectures asynchrones des layouts protégés et handlers de jobs durcis
Nouvelle directive d'état de chargement par segment de route pour les tableaux de bord admin plateforme, admin organisation et privé garantissant un retour immédiat (squelette + spinner) pendant la résolution des Server Components ; le motif asynchrone des layouts protégés est documenté ; les handlers de jobs en arrière-plan sont durcis avec un ordre de suppression RGPD plus sûr et la récupération des miroirs d'abonnement Stripe.
Ajout d'une directive d'état de chargement par segment de route pour les pages admin plateforme, admin organisation et tableau de bord privé afin que les futurs travaux conservent un retour immédiat (squelette + spinner) pendant la résolution des données Server Component
Documentation du motif asynchrone des tableaux de bord protégés : lancer les promesses du contexte de tableau de bord et des traductions en parallèle, attendre le contexte pour les redirections, puis attendre les traductions pour le rendu sans déplacer les données utilisateur/compte dans unstable_cache()
Mirrors des règles IA générées synchronisés pour la nouvelle directive de chargement et de lecture asynchrone des tableaux de bord
Durcissement des handlers de jobs en arrière-plan : la suppression RGPD vérifie désormais les erreurs de mutation Supabase, conserve les identifiants de retry jusqu'à ce que le travail destructif réussisse, et supprime les lignes de compte en dernier ; la synchronisation Stripe crée les miroirs d'abonnement manquants lorsque les webhooks de création ont été ratés
v1.26Minor
27 mai 2026
Droits à la vie privée, surfaces de conformité régionale et documentation
Nouvelle page /[locale]/privacy-choices et section vie privée/droits sur les données dans Mon compte exposant le droit d'opposition à la vente/au partage, l'opt-out publicité ciblée, le RGPD, la LPD suisse, les lois US des États, la LPRPDE canadienne et la LCAP ; le consentement cookies honore désormais les signaux Global Privacy Control du navigateur ; règles de sécurité canoniques, mirrors des règles IA et docs produit rafraîchis.
Nouvelle page /[locale]/privacy-choices et lien dans le footer exposant le droit d'opposition à la vente/au partage, l'opt-out publicité ciblée, le RGPD, la LPD suisse, les lois US des États, la LPRPDE canadienne et la LCAP
Nouvelle section vie privée/droits sur les données dans Mon compte avec point d'entrée d'export, gestion du consentement cookies, résumé des droits régionaux, accès à la politique de confidentialité, contact vie privée et contexte de suppression
Privacy Choices référencée depuis les pages CGU, Politique de confidentialité et Mentions légales existantes pour permettre la navigation entre toutes les surfaces légales/vie privée
/privacy-choices ajoutée au sitemap et à la liste des slugs CMS réservés afin que la route soit découvrable et ne puisse pas être masquée par des pages CMS dynamiques
Textes légaux/footer/cookies/compte localisés pour en-US, en-CA, fr-FR et fr-CH
Le consentement cookies honore désormais les signaux Global Privacy Control du navigateur — le consentement marketing/partage/publicité ciblée reste désactivé même quand 'Tout accepter' est cliqué
L'UI des paramètres cookies affiche l'état GPC actif et désactive le consentement marketing tant que ce signal est présent
Règles de sécurité canoniques mises à jour pour préserver /privacy-choices et le comportement Global Privacy Control dans les changements futurs
Mirrors générés des règles IA synchronisés pour Codex, Gemini et OpenCode
Docs produit mises à jour pour RGPD/vie privée, consentement cookies, analytics, Mon compte, onboarding production, README et le changelog
v1.25Minor
25 mai 2026
Hydratation auth marketing, déduplication des requêtes dashboard et docs
Les pages marketing publiques restent anonymes-first et cacheables tandis que l'UI authentifiée s'hydrate côté client via /api/auth/me ; les dashboards privé et org partagent désormais un contexte mis en cache par requête avec des RPCs SQL d'agrégation et des index composites ; règles canoniques caching/auth et docs produit rafraîchies.
Le layout marketing public (app/[locale]/(frontend)/layout.tsx) ne lit plus getUser() / les données de session Supabase côté serveur pour la personnalisation du navbar — reste anonymous-first et cacheable
Nouvel endpoint GET /api/auth/me pour l'hydratation auth côté client de la shell marketing : apiSecurity.public({ rateLimit: 'relaxed' }), getUser() côté serveur, payloads auth/profil/workspace minimaux et sanitisés, et Cache-Control: private, no-store
Nouveaux MarketingAuthProvider et MarketingCommandPalette ; TransparentNavbar et AccountDropdown consomment désormais le contexte auth hydraté côté client — les utilisateurs connectés cliquant sur le lien login anonyme sont toujours redirigés vers leur dashboard par le guard de route auth existant
Nouveau core/accounts/dashboard-context.ts avec helpers React.cache() au niveau de la requête : getPrivateDashboardContext() pour /private-dashboard/* et getOrgDashboardContext() pour /org-dashboard/*
Le layout/pages du dashboard privé (private-dashboard, chat, documents, referrals, affiliates) et le layout/pages du dashboard org (overview/admin, analytics, billing, API keys, members, roles, détail membre, settings) réutilisent désormais le contexte auth/account/workspace partagé au lieu de répéter les lectures getUser(), profil, membership, billing-access et subscription
Les dashboards protégés restent dynamiques et scopés à la requête ; aucune donnée spécifique à l'utilisateur n'a été déplacée vers unstable_cache()
Nouveaux RPCs d'agrégation dashboard pour les counts overview privé, l'analytics org et les résumés d'usage par membre, plus des index composites pour les hot paths dashboard sur ai_requests, chat_sessions et user_access_logs — les pages analytics/members org agrègent désormais en SQL au lieu de tirer les lignes d'usage brutes dans les Server Components
Composants client de purchase de crédits et end-trial chargés en lazy dans les sidebars/actions du dashboard, pour que le code UI modal/paiement ne fasse plus partie du bundle client initial du dashboard
Règles canoniques de caching mises à jour pour documenter l'hydratation auth marketing, les attentes rate-limit / Cache-Control de /api/auth/me, et la déduplication du contexte dashboard
Docs produit sous content/docs rafraîchies pour caching/performance, authentification, référence API et dashboards admin
v1.24Minor
24 mai 2026
Route-thinning, CI + guardrails, durcissement sécurité post-1.23 & docs v2
Suite à 1.23 : passe de route-thinning de l'API déléguant la logique multi-étapes vers core/*, nouveau workflow GitHub Actions CI avec scripts guardrail et lint gate zéro-warning, durcissement sécurité TRUST_CLOUDFLARE_IP / recent-auth / attribution RLS, refonte de la doc multi-pages public/docs/v2/, et refresh des règles et entrypoints des outils IA.
Nouveau gate d'env TRUST_CLOUDFLARE_IP : le rate limiting ne fait plus confiance au header CF-Connecting-IP spoofable — il n'est honoré que si le gate est explicitement activé
Le webhook Stripe lit le body brut en stream avec contrôle de taille au lieu de bufferiser tout arrayBuffer() d'abord
/api/jobs/run sépare proprement le chemin cron à bearer-token du chemin admin-session, avec contrôles rate-limit, CSRF et body-size équivalents
Le désabonnement newsletter cible désormais par défaut uniquement l'utilisateur authentifié — plus de désabonnement anonyme d'email arbitraire
Recent-auth + admin_logs immutables étendus à l'annulation/réactivation d'abonnement, aux purges de logs, aux crédits accordés à des utilisateurs et aux mutations d'organisation
Settings org, révocation d'invitation, écritures de profil onboarding et mises à jour de profil /my-account déplacés derrière des frontières server/API validées ; RLS chat/document scoping par compte durcie pour bloquer toute attribution user_id forgée
Passe de route-thinning de l'API : export GDPR, orchestration AI stream, pages/médias/blocs CMS, settings/users/organizations/subscriptions admin et clés API de compte délèguent désormais la logique multi-étapes à core/gdpr, core/ai, core/cms, core/admin, core/organizations, core/billing et core/accounts — aucun app/api/**/route.ts ne dépasse 250 lignes
Splits de routes à haut risque finalisés : dispatch webhook Stripe dans core/billing/stripe-webhook.ts, création de session de checkout dans core/billing/checkout.ts, auth/exécution job-run dans core/jobs/run-request.ts et helpers body / security-response bornés dans lib/http/request-body.ts — les routes ne sont plus que glue HTTP/sécurité
Nouveau .github/workflows/ci.yml exécuté sur les pull requests et les pushes vers main : typecheck, lint:ci, lint:strict, guardrails projet, couverture pattern API, taille de route API, couverture schéma/policy RLS, couverture des types Supabase, tests unitaires, scan de secrets, build production, accessibilité et checks responsive
Scripts guardrail nouveaux ou étendus : anti-patterns projet, couverture wrapper de sécurité API, taille de route API, couverture RLS sur schema.sql + cms-schema.sql, couverture schéma/types Supabase, sonde staging readiness et load smoke test des APIs critiques
lint:strict est désormais un CI gate zéro-warning ; la visibilité historique des warnings vit dans npm run lint:debt (4 787 warnings mesurés pendant cette passe)
Nouvelle documentation statique multi-pages dans public/docs/v2/ avec navigation partagée, styling responsive, sidebar à icônes groupées, menus de sous-section par page, assets public/docs/screens/ réutilisés, callouts visuels enrichis et un search-index.json global couvrant setup, configuration, architecture, Supabase, auth/B2B, pricing Stripe, AI/RAG, ops, contenu, frontend, sécurité, déploiement, référence API et outils IA
public/docs/index.html refait pour l'onboarding du premier projet : quick-start plus clair, checklist de configuration nouveau projet, guidance d'env par scénario, setup explicite config/pricing.ts / Price IDs Stripe, notes B2B checkout-before-onboarding et auto-bootstrap workspace, comptes de skills AI Blueprint mis à jour, et références à des assets locaux périmés supprimées
Les règles IA canoniques et /docs documentent désormais le split de maintenabilité admin-dashboard / billing (core/admin/*, modules core/billing/* focalisés, hooks de réversion affiliate/referral dans risk-events.ts), le contrat d'env TRUST_CLOUDFLARE_IP (ajouté aussi au .env.local généré par init), le désabonnement authentifié, l'API jobs durcie, les exigences recent-auth admin, les frontières RLS/écriture profil plus strictes, l'expansion privacy GDPR/US/Canada/Suisse (locale fr-CH, pricing CHF, copy légale suisse, seeds CMS légaux) et les contrats d'env du lint gate zéro-warning / lint:debt / couverture RLS / sonde staging / load smoke — les mirrors de règles IA générés sont rafraîchis via scripts/sync-ai-rules.sh
Ajout de .impeccable.md comme handoff de contexte de design projet ; nouveaux scripts CI/guardrail documentés dans les entrypoints des outils IA, les règles canoniques, README.md et la doc de tests produit
v1.23Minor
19 mai 2026
Remédiation d'audit, billing, docs, email & skills IA consolidés
Correctifs issus de l'audit complet du codebase (foundingCodex.md / planFix.md) : tracking privacy-by-default, preuve de suppression GDPR/LPD, complétude des exports, corrections billing/pricing, contrat d'env légal, parité de docs entre outils IA, refonte des emails transactionnels et refresh de /ui-ux-pro-max en v2.5.0.
Opérateurs : appliquer 4 nouvelles migrations sur la base de prod — durcissement DB planfix, index subscriptions (hors transaction), statut des processeurs de suppression, et normalisation des clés de locale CMS (fr/en → fr-FR/en-US) ; mirrorées dans supabase/schema.sql et supabase/cms-schema.sql
Analytics et live-chat désormais privacy-by-default : GTM, gtag/Google Ads, Meta Pixel, X Pixel et Crisp ne se chargent ou ne se déclenchent qu'après le consentement analytics/marketing correspondant ; events purchase/signup gardés au niveau applicatif
Persistance du consentement utilisateur connecté passe désormais par POST /api/user/consents avec apiSecurity.authenticated() + CSRF + Zod + rate limiting ; les composants navigateur n'écrivent plus directement dans user_consents
Uploads CMS WYSIWYG : token CSRF double-submit envoyé et rate limits media-appropriés ; uploads RAG conservent auth + CSRF + body-size sur la limite standard interactive
Remédiation planFix.md : 63/64 findings corrigés — garde fail-closed auth.role() sur les RPCs SECURITY DEFINER referral/affiliate, lectures de hash de clés API révoquées aux rôles anon/authenticated, RLS UPDATE de app_settings dotée d'un WITH CHECK
Vérification admin et escalation durcies : cookie recent-auth 60 s, bucket de rate-limit OTP par email, rejet is_disabled dans verify-OTP et callback magic-link, recent-auth sur admin_email et toggle_admin, audit logs pour les actions destructrices
Export GDPR élargi — profil, memberships, sessions/messages de chat, requêtes IA, consentements, payments, abonnements, licences, invitations, access logs, demandes de suppression, préférences de notification, emails en attente, métadonnées de documents et de clés API (sans secrets ni hashes)
process-account-deletions enregistre le statut des processeurs pour Stripe, newsletter/email, Supabase Storage, cascade DB et Supabase Auth ; échecs rédigés, retry_count incrémenté, requête marquée failed et complétion factice bloquée ; le nettoyage Storage des documents/avatars se fait avant suppression des lignes
Nouveau contrat d'env légal : LEGAL_COMPANY_NAME, LEGAL_ADDRESS, LEGAL_REGISTRATION_NUMBER — npm run init, .env.example, config/app.ts et /docs câblés ; les placeholders d'exemple bloquent intentionnellement les builds publics/indexables
npm run init se termine désormais par un guide Pricing & Stripe — config/pricing.ts comme source de vérité, mapping des Price IDs, choix du modèle de billing, webhooks et moyens de paiement
Fin anticipée d'un trial Stripe synchronise le miroir local des subscriptions avant de répondre (l'org-dashboard retire la bannière trial immédiatement) ; les crédits de conversion de trial soustraient le grant trial configuré de l'allocation du plan au lieu d'ajouter le montant total
Le warning d'expiration de licence utilise la lookup owner en deux étapes documentée au lieu de l'embed invalide profiles!owner_user_id — corrige la régression de zéro email en prod
Détection de patterns d'attaque contact/newsletter câblée ; code helper CSRF non utilisé supprimé ; les checks CSRF Origin/Referer utilisent appConfig.url ; l'allowlist d'iframes HTML est désormais dans DOMPurify
Les tables de pricing affichent les crédits inclus, les limites de projets et les crédits trial depuis config/pricing.ts / variables trial au lieu de duplications de traductions de feature values
Lookup de locale CMS/page/blog/changelog tombe sur des clés langue-seule legacy en fallback ; les écritures admin normalisent les payloads vers les clés BCP-47 ; seeds, sitemap et npm run init utilisent les locales configurées au lieu de fr / en
Docs outils IA synchronisés entre .claude/.codex/.gemini/.opencode/.agents — 22 fichiers par mirror ; les entrypoints des agents incluent matrice de routing, sélection reviewer/skill et pointeurs de vérification par type de changement ; scripts/sync-ai-rules.sh régénère .agents/skills/ depuis .claude/skills/
Extractions thin-route + core/ pour streaming/accounting IA, changelog, mutations chat, billing subscribe-free, admin orgs/users et validation CMS ; core/usage/ et core/memberships/ morts supprimés
CURRENCY_CODES est désormais le tuple canonique des devises (Currency et SUPPORTED_CURRENCIES en dérivent) ; ids de modèles d'agents, sel de referral, bonus signup workspace, seuil RAG, alerte low-credit, fallbacks locale/devise et rôles org-manager utilisent des helpers de config
Les builders d'emails transactionnels partagent lib/email/layout.ts : shell monochrome, layout en tables, CSS dark-mode, fallback CTA bulletproof et helpers d'escape ; gradients par template supprimés ; couverts par __tests__/email/templates-render.test.ts
Le contrôle de body-size de /api/ai/stream lit le body cloné en stream au lieu de bufferiser tout arrayBuffer() d'abord
Traitement RAG, sync Stripe, processeurs GDPR de suppression, lectures d'export, réordonnancement de rôles, revalidation affiliate et purges de logs parallélisés ou batchés ; select('*') remplacés par des projections explicites sur les hot paths flaggés
Next.js mis à jour en 16.2.6 ; npm run audit:bundle utilise next build --webpack car @next/bundle-analyzer n'émet pas de rapports sous Turbopack (rapports dans .next/analyze/)
Clés de rate-limit IPv6 ramenées au préfixe /64 (contournement fermé), X (Twitter) Pixel sous consentement, et passe correctness + responsive sur /docs.
Contournement du rate-limit IPv6 fermé — clés ramenées au préfixe réseau /64 (RFC 4291) en un point unique dans createRateLimiter() ; IPv4 inchangé
normalizeRateLimitIdentifier() ne lève jamais d'erreur et laisse les formes IPv4-mapped intactes ; getClientIP() renvoie toujours le /128 complet pour les chemins contact et IP-hash
Nouveau test unitaire __tests__/security/rate-limit-ip.test.ts : expansion ::, collapse de préfixe, pass-through IPv4 et entrées malformées
X (Twitter) Pixel via components/analytics/x-pixel.tsx — rendu uniquement si consent.marketing est accordé, même gate que le Meta Pixel
xEvents.purchase / xEvents.signUp déclenchés depuis les chemins existants trackPurchase / trackSignUp ; no-op si les variables d'event de conversion sont absentes
Trois variables NEXT_PUBLIC_X_PIXEL_* optionnelles, validées par Zod ; static.ads-twitter.com ajouté à l'allowlist CSP
Page /docs autonome : suppression de deux sections de version obsolètes, ajout d'un lien Changelog vers la source unique de vérité
Tables /docs : clipping mobile corrigé (13 tables wrappées), offset d'ancre, lazy-loading des images, style pre répété dé-inliné
v1.21Patch
14 mai 2026
Moyens de paiement Stripe Checkout
PayPal activé explicitement pour les paiements uniques via une source de config unique ; les abonnements restent en carte seule (restriction géo Stripe).
PayPal apparaît désormais sur Stripe Checkout pour les paiements uniques — payment_method_types demandé explicitement au lieu de l'auto-sélection Stripe
Nouveau pricingConfig.checkoutPaymentMethods dans config/pricing.ts, indexé par mode Stripe (subscription : card, payment : card + paypal)
Union littérale CheckoutPaymentMethod dans lib/pricing/types.ts — une faute de frappe fait échouer npm run build avant le déploiement
Les abonnements restent en carte seule par défaut : Stripe limite les abonnements PayPal aux marchands EEE/UK/CH
Wallets sur carte (Apple Pay / Google Pay) inchangés — ils passent par l'entrée carte
npm run init rappelle d'activer les moyens dans Stripe Dashboard en test et en live
Docs mises à jour : .claude/rules/billing.md et mirrors, registre des sources de config domain-map
v1.20Minor
11 mai 2026
Refonte UX du lien magique
Code à 6 chiffres en repli, redirection cross-onglet, cooldown de renvoi et refonte des textes pour réduire l'abandon du lien magique.
Code OTP à 6 chiffres présent dans l'email du lien magique, à côté du lien ; longueur configurable via OTP_LENGTH (défaut 6, borné 6-10)
Nouvel endpoint POST /api/auth/verify-otp — validé par Zod, apiSecurity.public() + rate limit strict, erreur générique (pas d'énumération)
L'écran de succès affiche l'email de destination, un champ OTP numérique (autoComplete one-time-code) et un bouton retour Mauvais email ?
Redirection cross-onglet : l'écran de succès interroge getUser() toutes les 4s (en pause si l'onglet est masqué) ; cliquer le lien dans un autre onglet redirige l'original
Cooldown de renvoi (compte à rebours 30s) et rappel dossier spam
Refonte des textes : Continuer avec l'email, sous-titre mentionnant lien + code, sujet d'email Votre code de connexion
9 nouvelles clés auth.login.* et 3 email.magicLink.* dans les deux locales (test de parité vert)
Le script d'init demande OTP_LENGTH (validé) ; avertissement opérateur : doit correspondre au réglage Email OTP length du projet Supabase
v1.19Minor
10 mai 2026
Verrou de connexion prelaunch & catalogue OAuth dynamique
Allowlist admin côté serveur verrouillant la connexion en prelaunch, plus un catalogue de boutons OAuth multi-fournisseurs configurable.
La connexion en prelaunch est verrouillée côté serveur par PRELAUNCH_ALLOWED_EMAILS (server-only, séparée par virgules, insensible à la casse ; vide = verrouillé)
Trois points d'application : 403 lien magique avant generateLink, sign-out callback, sign-out OAuth + nettoyage admin.deleteUser
Message de restriction générique — aucun vecteur d'énumération d'email
Catalogue OAuth dynamique : NEXT_PUBLIC_OAUTH_PROVIDERS rend un bouton de marque par id, validé contre les 19 fournisseurs supportés par Supabase
Nouveau lib/auth/oauth-providers.tsx (SVG de marque inlinés, aucune dépendance ajoutée) + components/auth/oauth-buttons.tsx
login-form.tsx : bouton Google en dur supprimé ; le séparateur n'apparaît que si au moins un fournisseur est configuré
i18n : auth.login.continueWithProvider avec placeholder {provider} + auth.login.prelaunchRestricted, deux locales (parité verte)
Le script d'init réhydrate les deux variables sur les re-runs ; sections OAuth + prelaunch de public/docs/index.html réécrites
v1.18Minor
4 mai 2026
Programme d'affiliation & abstraction email
Programme partenaire orienté compte avec commissions en cash (Stripe Connect différé) et une couche email pluggable (Brevo, Mailjet, noop).
Correctif critique pg_net args positionnels (fuite de secret + tempête 401) et nouvelle surface admin pour piloter cron.job.
CRITIQUE : correctif args positionnels pg_net — JOBS_SECRET_KEY était envoyé en query string et fuyait dans les logs d'accès à chaque tick cron. Rotation du secret obligatoire après mise à jour.
Les cron jobs faisaient silencieusement 401 — net.http_post est maintenant appelé en arguments nommés, robuste à toutes les versions de pg_net
Dashboard admin Cron en /admin-dashboard/jobs/cron — list, toggle, unschedule, purge des orphelins
Tokens Bearer présents dans cron.job.command redactés au niveau SQL — jamais exposés à l'API/UI
Garde recent-auth sur les actions destructrices (unschedule, purge-orphans) ; audit trail sur chaque mutation
Script init : blocs de config security + logs restaurés (les init fraîches compilent à nouveau)
Script init : étape de purge des orphelins avant re-sync, filtre ancré (ne touche pas aux entrées cron tierces)
Helper require-recent-auth partagé, factorisé depuis le check inline de promotion admin
v1.15Minor
27 avril 2026
Réalignement de la comptabilité des crédits IA
Les crédits correspondent désormais 1:1 aux tokens LLM réellement consommés. Les forfaits sont recalibrés en conséquence.
1 crédit = 1 token LLM, en chat comme en RAG (fini les divergences réserve/remboursement)
Helper de formatage compact des crédits (1M, 2,5M, 500K) localisé FR/EN
Forfaits recalibrés à l'échelle token : Free 50K, Pro 1M, Business 5M (prix Stripe inchangés)
Champ téléphone migré vers react-international-phone (~250 pays, recherche, drapeaux)
/my-account résout maintenant accountId côté serveur — fini les cartes Abonnement / Parrainage silencieusement cassées
Carte Parrainage sur /my-account avec tuiles de stats et lien vers le tableau de bord complet
Le widget Crisp est désormais limité à la landing page (plus de propagation SPA vers privé/admin)
Visibilité de l'entrée Parrainages dans la sidebar admin corrigée (flag passé côté serveur)
v1.14Minor
21 avril 2026
Système de parrainage
Programme de parrainage end-to-end attaché aux comptes, avec rate limit IP et reversement automatique sur litige.
Codes de parrainage par compte — les deux parties gagnent des crédits sur déclencheur configuré
Garde-fous DB stricts : pas d'auto-parrainage, un parrainage par compte à vie, rate limit IP côté apply
Tableau de bord utilisateur sur /private-dashboard/referrals avec historique paginé
Console admin : KPI, top-10 parrains, liste filtrable, détail timeline, gestion des codes
Bannière d'inscription + auto-attribution depuis le cookie httpOnly à l'onboarding
Hooks webhook Stripe : qualification à l'achat, reversement automatique sur remboursement / litige perdu
404 partout quand REFERRAL_ENABLED=false — surface invisible tant que la fonctionnalité est désactivée
Audit du blueprint IA : 19 règles, 11 agents spécialisés, 6 hooks, catalogue d'anti-patterns
v1.13Patch
21 avril 2026
Suite de la revue de codebase
Corrections critiques à mineures issues de la revue parallèle multi-agents, sur 4 niveaux de sévérité.
Drift de schéma corrigé — migrations index + RLS de janvier 2026 reportées dans schema.sql
Webhook Stripe extrait : passage de 1 632 à ~160 lignes + fonctions apply* réutilisables
CSRF Double-Submit + CSP basée sur nonce en production (plus de unsafe-inline)
Défense anti-DNS-rebinding sur les webhooks de jobs — résolution IP rejetant les plages privées
Latence du stream IA : ~60–100 ms gagnés par requête (lookup auth dupliqué supprimé)
Listing blog : jusqu'à ~1 Mo économisé au cold load en supprimant le JSONB content/seo_*
MarkdownRenderer passé en Server Component — ~100 Ko retirés du bundle changelog/CMS
Prompt caching Anthropic activé — les prompts système longs bénéficient de la remise read 0,1×
v1.12Patch
1er avril 2026
Corrections SEO & sitemap
Sitemap complété, données structurées corrigées et métadonnées peaufinées sur tous les types de pages.
Fuite de pages brouillon dans le sitemap corrigée (filtre published manquant après passage au service client)
/contact, /blog, /changelog et les billets de blog apparaissent enfin dans le sitemap
Les 10 types de pages émettent maintenant un hreflang x-default
Suppression du SearchAction cassé du schéma WebSite (route /search inexistante)
Pages CMS : type OG corrigé en website, noindex/nofollow respectés, canonical + og_image supportés
Fallback de description pour métadonnées SEO vides (excerpt → 160 premiers caractères du contenu)
Blog : URLs filtrées/paginées (?page=, ?category=, ?tag=) émettent désormais noindex,follow
v1.11Minor
29 mars 2026
Skill UI/UX Pro Max
Décisions UI/UX assistées par IA avec bases de données curées et règles de raisonnement.