PRNext 0.1.20 : moins de travail, plus de contrôle

PRNext 0.1.20-alpha est disponible. Cette version travaille sur deux coûts que l’on rencontre en faisant grandir une application : le travail effectué sans nécessité et la difficulté à comprendre ce qui se passe en production. Une page publique peut commencer par son shell natif, une modification de Server Action peut épargner les onglets sans rapport, et les caches de compilation disposent d’un budget partagé.

La version renforce aussi les conditions d’une mise en production : une origine HTTPS explicite, des contrôles de santé qui refusent les redirections, un rollback après bascule et des métriques protégées. Les nouveaux chemins expérimentaux restent désactivés par défaut. Chacun répond à un usage précis, avec ses conditions d’activation, ses cas de repli ou ses erreurs explicites.

Commencer une page PPR avant l’initialisation de Node

Une page PPR associe une partie préparée pendant le build et une continuation React qui produit le contenu dynamique. Avec experimental.nativePprShell, le serveur Rust peut envoyer le shell certifié avant de démarrer le worker Node. La continuation reprend ensuite depuis le même artefact vérifié, au lieu de reconstruire une première réponse indépendante.

Ce chemin convient aux requêtes document GET de pages App Router PPR concrètes, sans cookies, autorisation ni paramètres de recherche. Il exige cacheComponents. Les requêtes avec middleware, réécriture, basePath, nonce CSP ou en-têtes de navigation Flight conservent le rendu ordinaire. Les shells génériques de routes paramétrées, Edge et les caches externes restent également hors de ce périmètre.

prnext.config.mjs
export default {
  cacheComponents: true,
  experimental: {
    nativePprShell: true
  }
};

Le shell doit être intact et non expiré. Toute invalidation persistée désactive cette optimisation du snapshot de build, y compris après un redémarrage, sans désactiver PPR. Une requête déjà engagée termine avec son artefact initial. Les réponses restent privées et no-store : ce mécanisme ne transforme pas une page authentifiée en contenu public et ne remplace pas ses contrôles d’accès.

Actualiser les routes concernées par une Server Action

Modifier une action utilisée dans un écran de facturation ne doit pas nécessairement perturber un onglet ouvert sur une autre partie du produit. PRNext compare les graphes de dépendances avant et après la modification pour identifier les routes qui importent cette action, directement ou à travers ses dépendances.

Lorsque ce périmètre est établi, le rafraîchissement de développement peut cibler les routes concernées. Un onglet épargné applique la génération différée avant de naviguer vers une route modifiée. Les générations serveur restent publiées dans leur ensemble, et une mutation en attente n’est jamais soumise à nouveau pour accompagner le rafraîchissement.

Un graphe incomplet ou inconnu conserve une actualisation globale. Il en va de même pour les changements de configuration, de route, de runtime ou de liaison, ainsi que pour le middleware, les réécritures et certains routages internationalisés, parallèles ou interceptés. Le ciblage dépend d’une preuve de portée, pas d’une supposition sur le fichier que vous venez d’enregistrer.

Choisir Oxc pour React Compiler, sans l’imposer à l’installation

React Compiler dispose d’un backend Oxc expérimental en complément du backend Babel existant. Le navigateur et le rendu serveur des Client Components App Router utilisent des transformations distinctes. Les identités de cache intègrent le backend, sa version et le mode client ou SSR, afin de ne pas réutiliser un résultat destiné à un autre contexte.

Babel reste le choix par défaut lorsque reactCompiler est activé. Pour sélectionner Oxc, installez explicitement oxc-transform-react@0.157.0 dans le projet de l’application, avec ses dépendances natives optionnelles. Même une CLI globale résout ce compilateur depuis l’application. Un compilateur absent, incompatible ou privé de son binding natif produit une erreur explicite ; PRNext ne change pas de backend silencieusement.

prnext.config.mjs
export default {
  reactCompiler: {
    backend: 'oxc'
  }
};

Le périmètre reste celui des composants et hooks du projet éligibles. Les Server Components, les Server Actions, les paquets npm et le graphe serveur Pages restent hors de cette transformation. Les source maps et Fast Refresh sont conservés. Le rapport prnext compiler identifie le backend et ses diagnostics par fichier ; l’API Oxc ne fournit pas les événements nécessaires pour compter les fonctions optimisées comme Babel.

Les projets qui conservent Babel ou désactivent React Compiler n’installent plus Oxc pour les besoins internes de PRNext. Le choix d’un compilateur écrit en Rust ne constitue toutefois pas un gain mesuré pour votre application : comparez la compilation, l’hydratation et les interactions avec votre propre code.

Émettre les styles indépendants en parallèle

experimental.parallelBuilds agit sur une étape précise du build App Router. Après la découverte ordonnée des dépendances, PRNext fige les plans de feuilles de style, déduplique les sorties et répartit les travaux indépendants. Le rattachement des résultats reste déterministe pour conserver l’ordre des styles.

La concurrence dépend du nombre de CPU, de la mémoire disponible et des plafonds configurés. Cet exemple fixe au plus deux workers et un budget d’admission estimé de 512 Mio. Ce budget ne constitue pas une limite RSS imposée au code tiers.

prnext.config.mjs
export default {
  experimental: {
    parallelBuilds: {
      maxWorkers: 2,
      maxMemoryMb: 512
    }
  }
};

Les hooks webpack, loaders et pipelines PostCSS personnalisés, ainsi que les callbacks Sass, conservent une exécution séquentielle. Si un travail échoue, PRNext arrête les nouvelles admissions, attend la fin des travaux déjà commencés et abandonne le build temporaire. Le dernier build publié reste disponible. Le manifeste enregistre le nombre réel de travaux CSS et la concurrence retenue, pour distinguer l’option activée du parallélisme effectivement obtenu.

Lire un upload avant la fin de son envoi

Avec experimental.streamingRequestBodies, un Route Handler App Router exécuté dans Node peut commencer à lire Request.body avant la réception complète de l’upload. Le transport natif négocie cette capacité avec le worker et n’envoie au plus qu’une trame de 64 Kio par crédit de lecture. Le consommateur détermine ainsi le rythme de livraison.

Les workers sans cette capacité utilisent le chemin bufferisé existant. Les Server Actions, API Pages et requêtes déjà bufferisées par le middleware conservent également leur comportement antérieur. La limite totale de requête reste de 32 Mio et les réservations d’admission existantes restent appliquées. Cette option ne promet ni une réduction générale du heap V8 ni davantage de requêtes admises en mémoire.

prnext.config.mjs
export default {
  experimental: {
    streamingRequestBodies: true
  }
};

Le handler peut s’exécuter avant qu’une troncature ou un dépassement de taille soit détecté. Validez donc l’entrée complète et l’autorisation avant de valider un effet métier. Une annulation ou un retour anticipé ferme le canal de cet upload sans rejouer la mutation et sans sacrifier le processus Node sain. Pour un import de données, vous pouvez analyser le flux progressivement puis confirmer la transaction lorsque toutes les conditions sont satisfaites.

Donner un budget aux caches de compilation

Les caches de compilation partagent une cible de rétention de 256 Mio par projet, configurable avec compilationCache.maxDiskSize. La politique s’applique après les builds, y compris en cas d’échec, et après un arrêt propre du développement. Elle borne la conservation des fichiers éligibles ; un worker actif, un fichier occupé ou un parcours incomplet peut différer leur récupération.

La commande prnext cache affiche les statistiques des fichiers reconnus. Son mode dry-run permet de prévisualiser une purge sans supprimer de données. L’exemple suivant inspecte les caches, puis simule une cible de 64 Mio depuis une application utilisant cette version de PRNext.

Terminal
npx --no-install prnext cache --json
npx --no-install prnext cache --prune --dry-run --max-bytes 67108864 --json

Les gros enregistrements JSON sont compressés avec contrôle de taille décodée, de clé et de somme de contrôle. Les anciens enregistrements bruts restent lisibles ; une entrée corrompue ou incompatible devient un cache manquant. La publication reste atomique et une panne de cache ne devient pas une dépendance obligatoire de la compilation.

Arrêtez le développement avant d’appliquer une purge. Les bases de données runtime, data.sqlite3, les caches de pages et d’images ainsi que l’historique d’invalidation sont exclus. Le budget est une cible de rétention, pas un quota disque strict. Une éviction peut ralentir un build suivant ou demander de télécharger à nouveau une police.

Conserver moins de dépendances et d’artefacts redondants

PRNext embarque l’entrée Node create() de Fontkit avec sa provenance et les licences des dépendances. Les formats TTF, OTF/CFF, WOFF, WOFF2, TTC, DFONT et les polices variables restent couverts, sans installer pour cet usage tout l’arbre externe de Fontkit. Cette intégration interne ne remplace pas un import Fontkit effectué par votre propre application.

Un build de production conserve un seul renderer OpenGraph lorsqu’il en a besoin. Une analyse bornée du graphe compilé peut l’omettre uniquement si elle établit qu’ImageResponse n’est pas utilisé. Edge, le développement, les modules externes inconnus et les chargements calculés gardent le chemin prudent. Les noms, licences et chargements WASM relatifs restent préservés.

La récupération d’espace runtime se fait hors du chemin des requêtes, par petites étapes SQLite et maintenance bornée des fichiers orphelins. Les lecteurs et publications actifs sont protégés. Les anciennes bases gardent leur format et aucun VACUUM complet ne s’exécute pendant une requête. Les dossiers temporaires de build et de développement possèdent aussi un journal de propriété : seuls les propriétaires locaux vérifiés comme abandonnés deviennent récupérables, tandis que les sauvegardes nécessaires à une reprise sont conservées.

Pour un déploiement standalone, déployez le répertoire standalone complet, sans recopier à côté la sortie ordinaire du build. Vous évitez ainsi de doubler vous-même les artefacts que le framework vient de regrouper.

Retirer des passages inutiles dans le build et le navigateur

Les imports des chunks App sont réécrits en mémoire avant leur première écriture. La découverte des dossiers parents est partagée pendant le build, et des résumés Oxc bornés permettent de réutiliser les informations de directives et d’exports sans conserver de gros arbres syntaxiques. Les transformations qui ont besoin de Babel gardent leur chemin existant.

Dans le navigateur, l’adoption d’un ensemble de feuilles de style construit un index temporaire des liens existants au lieu de reparcourir le document pour chaque feuille. Le routage Rust emprunte davantage les chemins et segments ordinaires. La capture Flight supprime une copie intermédiaire uniquement pour les buffers internes dont la propriété le permet, en conservant les copies défensives pour les producteurs externes.

Les sondes de compression Flight deviennent asynchrones et leur concurrence reste bornée. À saturation, la réponse utilise Flight ordinaire. Les préchargements PPR réutilisent le décodage base64 natif lorsqu’il existe, avec le repli navigateur. Ces changements réduisent des opérations identifiées ; ils ne constituent pas à eux seuls une mesure de latence HTTP ou de durée totale de build.

Conserver HTTPS et séparer les comptes dans l’état client

Derrière un reverse proxy, une URL reconstruite en HTTP peut casser une redirection, un cookie sécurisé ou un callback d’authentification. server.publicOrigin définit maintenant l’origine publique canonique utilisée pour les URL de requête et les redirections. Elle est prioritaire sur les en-têtes transférés.

server.trustedProxies limite les adresses IP ou réseaux CIDR des proxys qui se connectent directement à PRNext. Seul un pair TCP autorisé peut fournir le protocole et l’hôte transférés. Cette règle ne valide pas une chaîne complète d’adresses client. Remplacez le domaine et les adresses de cet exemple par ceux de votre déploiement, puis reconstruisez le build qui enregistre ces paramètres.

prnext.config.mjs
export default {
  server: {
    publicOrigin: 'https://example.com',
    trustedProxies: ['127.0.0.1', '::1']
  },
  navigationCache: {
    authCookies: [
      'session-cookie-name',
      '__Secure-session-cookie-name'
    ]
  }
};

Les noms de cookies doivent correspondre à votre bibliothèque d’authentification, y compris ses variantes sécurisées. Avec cette politique, une identité changée ou inconnue impose un nouveau document avant d’adopter les layouts ou pages conservés. Une identité fraîchement vérifiée comme identique peut garder son brouillon. Les prévisualisations PPR attendent cette vérification et les modèles entièrement statiques passent par une requête document.

Ce contrôle protège aussi le changement de compte et le retour dans l’historique. Vérifiez le parcours compte A, déconnexion, compte B dans le même onglet : les anciennes propriétés de layout et le brouillon privé de A ne doivent pas apparaître pour B. L’application reste responsable de l’autorisation de chaque lecture et mutation côté serveur.

Vérifier une instance après la bascule de trafic

Un code 302 vers une page de connexion ne prouve pas qu’une application est saine. Les contrôles exigent désormais une réponse HTTP 200 complète et bornée. L’endpoint natif réservé /_prnext/health confirme que le serveur peut accepter du trafic et devient indisponible dès le début de l’arrêt gracieux.

Ce contrôle natif ne certifie ni un worker Node démarré à la demande ni une base de données. Une route applicative dédiée, anonyme et en lecture seule, peut vérifier les dépendances nécessaires au service. Configurez-la comme health-path pour une application persistante. Les chemins de santé déjà enregistrés sont conservés : un ancien chemin / qui redirige doit être remplacé explicitement par cette route dédiée.

Après une bascule réussie, le gestionnaire conserve l’instance précédente pendant une fenêtre de surveillance, de 15 secondes par défaut. Trois échecs de santé consécutifs ou la sortie inattendue de la nouvelle instance peuvent déclencher un retour vers l’ancienne, à condition qu’elle soit encore saine. Les réponses déjà commencées se drainent, mais aucune requête ni mutation n’est rejouée. Un rollback de code n’annule pas une écriture SQL ni une migration de schéma.

Comprendre les pannes et les effets déjà confirmés

L’exemple SaaS montre une idempotence transactionnelle liée au compte. Si une écriture réussit puis que sa réponse est perdue, une nouvelle tentative explicite avec la même clé ne crée qu’une écriture. La contrainte et la transaction appartiennent à l’application ; le framework ne peut pas déduire qu’un nouvel envoi est sans danger.

Les scénarios de qualification exercent l’indisponibilité puis le retour de PostgreSQL, les revendications concurrentes d’une même clé, la perte de réponse après COMMIT, le verrouillage et SQLITE_FULL sur une base jetable, ainsi qu’un vrai arrêt et redémarrage de Redis. Ils complètent les parcours nominalement réussis par des situations où le résultat métier doit rester compréhensible.

Les métriques HTTP sont optionnelles et protégées par un bearer token via PRNEXT_METRICS_TOKEN. Elles exposent les durées, requêtes actives, résultats, admissions saturées et événements des workers avec des labels bornés. Gardez leur collecte privée. Les pertes d’export OTLP, débordements de file et rejets partiels disposent de compteurs ; les diagnostics Node passent aussi par le canal prnext.telemetry pour rester observables si le collecteur de traces est indisponible.

La surveillance des dépendances examine quotidiennement npm, Cargo et les composants embarqués du renderer et des polices, en complément des mises à jour hebdomadaires et de leur provenance. Un contrôle effectué pendant une release reste une photographie : il ne remplace pas la veille des nouveaux avis de sécurité.

Ce que les mesures permettent de dire

L’étude de stockage antérieure au candidat final utilise Windows x64, Node 24.22.1, des peers React 19.3.1 identiques et le même exécutable natif public. L’installation fraîche y passe de 159,05 à 141,98 Mio, avec 13 paquets de moins. Ces tailles ne mesurent pas les archives finales de la 0.1.20-alpha et ne se transposent pas automatiquement à une autre plateforme.

Sur 28 builds de deux petits corpus SSR synthétiques, les médianes appariées à chaud réduisent les octets logiques du build de 67,79 % sans OpenGraph et de 26,61 % avec OpenGraph. Les caches de compilation diminuent d’environ 64,4 %. Le temps observé de l’API de build augmente de 13,94 et 15,85 ms. Il s’agit de fichiers logiques, pas de blocs réellement alloués sur disque, et d’un compromis de stockage plutôt que d’un gain général de vitesse.

La campagne SaaS référencée dans les notes techniques exécute deux heures sur la source de qualification 2323dc55, sous limites observées de deux CPU et 5 Gio. Elle réalise 9 238 écritures SQL et 11 rotations de workers, avec 235 observations de processus et de descripteurs vérifiées. Onze sondes de lecture échouent pendant les rotations induites et restent enregistrées ; aucune mutation n’est rejouée. Ces observations gardent leur source d’exécution et ne sont pas présentées comme un test des archives finales.

Ces résultats documentent des mécanismes et des charges contrôlées. Ils n’établissent ni gain global de latence HTTP, ni réduction générale de mémoire, ni absence de fuite, ni SLA de production. Les notes techniques liées à cet article conservent les conditions et les limites des campagnes.

Préparer un déploiement avec son application réelle

Avec les packages correspondants installés, prnext check --production --json examine les réglages de déploiement, les écarts de l’artefact, la conservation de l’état authentifié et les options expérimentales. Le contrôle donne des diagnostics avant de basculer le trafic ; il ne remplace pas les routes, formulaires et callbacks que seul votre produit connaît.

Terminal
npm install @thomas.f/prnext@0.1.20-alpha react@19.3.1 react-dom@19.3.1 react-server-dom-webpack@19.3.1
npx --no-install prnext check --production --json
npx --no-install prnext typecheck
npx --no-install prnext build
npx --no-install prnext output --json

PRNext 0.1.20-alpha exige Node ^22.23.4, ^24.22.1 ou >=26.12.1, avec React, React DOM et react-server-dom-webpack alignés sur 19.3.1. Gardez les dépendances natives optionnelles activées et les versions du framework et du package natif identiques. Le contrôle TypeScript explicite nécessite TypeScript et le tsconfig de l’application. Ces prérequis ne dispensent pas d’appliquer les nouveaux correctifs de sécurité de chaque dépendance.

Après un changement de version, reconstruisez et redémarrez l’application. Pour pstart, arrêtez d’abord le superviseur avec pdown et le même PRNEXT_PM_HOME, puis mettez à jour et reconstruisez toutes les applications activées qui partagent ce superviseur avant de le relancer. prestart seul ne recharge pas son code. Gardez le déploiement précédent et des migrations de base compatibles pour permettre un retour.

PRNext reste une alpha à compatibilité Next.js partielle. Activez les chemins expérimentaux séparément et confrontez-les à un parcours représentatif : chargement froid, styles, navigation, changement de compte, upload interrompu et panne d’une dépendance. Le guide de migration associé détaille ces vérifications et les limites de chaque option.

Sources et lectures complémentaires

Retour au blog