npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lystech/sections

v0.6.0

Published

Contenu de site editable par le client — lecture legere, edition chargee a la demande.

Readme

@lystech/sections

Le contenu d'un site, modifiable par son propriétaire — sans reconstruction ni déploiement.

Pourquoi un paquet à part de @lystech/core

core exige React 18 et embarque antd, Mantine et React Query : 2,4 Mo de JavaScript et 1,2 Mo de CSS. Une clinique qui veut changer trois textes n'a aucune raison de charger un magasin, un panier et PayPal — et beaucoup de sites clients sont en React 19.

Ce paquet pèse 11 Ko en lecture (4,4 Ko compressé) et ne dépend que de React. core le réexporte, pour qu'une boutique qui l'a déjà n'ait rien de plus à installer.

La plateforme fixe l'alphabet, pas les mots

Le serveur ne connaît aucun type de bloc. « plat-du-jour », « praticien », « tarifs » appartiennent au site, qui les déclare. Ce que la plateforme connaît, ce sont les types de champs — text, richText, image, number, boolean, url, date, choice, list — vrais dans tous les métiers, et suffisants pour fabriquer un formulaire pour un bloc jamais vu.

Usage

import { SectionsProvider, useSection } from "@lystech/sections";

<SectionsProvider site="armoise" page="massage" langue={i18n.language}>
  <MaPage />
</SectionsProvider>;

// Dans un composant déjà écrit :
const { contenu: tarifs } = useSection("tarifs-massotherapie", {
  defaut: SITE.tarifs.massotherapie, // ce que le site affichait déjà
});

Le repli n'est pas une option

defaut porte les valeurs actuelles du site. Tant que rien n'est publié — ou si l'API ne répond pas — ce sont elles qui s'affichent. Brancher un site existant ne peut donc rien casser : au pire il continue d'afficher ce qu'il affichait hier.

Publié ≠ brouillon

Le site public ne lit QUE le contenu publié. Un brouillon enregistré ne change rien pour les visiteurs tant que personne n'a cliqué « Publier ».

Champs traduits

Un champ déclaré localized: true stocke { fr: …, en: … } ; le paquet rend la langue courante, avec repli sur une autre langue plutôt que sur du vide — une version anglaise oubliée vaut mieux affichée en français qu'absente.

L'éditeur en place

La personne qui édite ouvre SON site avec un jeton dans l'adresse. Le paquet le détecte, le retire aussitôt de la barre d'adresse, et charge l'interface d'édition — 24 Ko compressés qu'aucun visiteur ne télécharge jamais, parce que le import() qui y mène n'est atteint que s'il y a un jeton.

Elle voit alors son site, ses polices, sa mise en page, avec chaque bloc modifiable encadré. Ce qu'elle tape apparaît dedans à mesure qu'elle le tape.

Marquer les blocs dans la page

L'éditeur doit savoir quel rectangle de la page correspond à quel bloc. Aucun moyen fiable de le deviner : le site le dit.

const { contenu, ancrage } = useSection("tarifs", { defaut });
return <section {...ancrage}>…</section>;

Hors édition, c'est un attribut data- inerte : aucun poids, aucun effet.

Corriger un prix sur place : champ

Un texte traduit se retrouve tout seul dans la page. Un prix, non : « 85 $ » ne dit pas que ce 85 est le prix de la séance. Le site pose champ(…) sur l'élément qui ne contient QUE la valeur :

const { contenu, champ } = useSection("tarifs", { defaut });
return <p><span {...champ("seance")}>{contenu.seance}</span> $</p>;

En mode « modifier », un clic sur la valeur ouvre une saisie posée dessus : Entrée enregistre le brouillon, Échap annule, et la page montre aussitôt la nouvelle valeur. Un champ qui ne se saisit pas sur une ligne (image, liste, texte riche) ouvre plutôt le panneau de son bloc. Si le format vient d'une traduction (« {{amount}} $ »), découpez-le autour de la valeur au lieu de rendre la phrase d'un bloc : seul le nombre doit porter champ.

Trois états, jamais confondus

| | Qui le voit | |---|---| | Aperçu | la personne qui édite, dans son navigateur | | Brouillon | elle seule, enregistré sur le serveur | | Publié | les visiteurs |

Le jeton

Délivré par POST /site-sections/edit-token depuis la console Lystech. Il vaut quelques heures, pour UNE organisation, et il est plafonné à l'éditeur même si la personne est administratrice : un lien qui fuite ne peut pas ouvrir la console. Le droit est relu dans les rôles à chaque requête, donc retirer un accès ferme les jetons déjà émis sur-le-champ.

Des gestes propres au site : appelEdition

Un site peut offrir, à la personne connectée, des gestes que l'éditeur générique ne connaît pas — retirer un résultat, masquer un évènement. Le contexte fournit un appel qui porte le jeton, sans jamais en livrer la valeur :

const ctx = useSectionsContext();
if (ctx?.modeEdition) {
  const r = await ctx.appelEdition("/results/site/droits");
}

Seuls les chemins de l'API sont acceptés. Un 401 fait sortir du mode édition (jeton expiré) ; un 403 revient tel quel. C'est la ROUTE qui décide de ce que le jeton ouvre : plafonné à l'éditeur partout, il n'obtient plus qu'en de rares routes qui le disent explicitement (ex. /results/site, admins seulement).

Des boutons qui restent des boutons

En mode « modifier », cliquer un texte le corrige au lieu de l'actionner — mais seulement un texte qui a un bloc déclaré où s'enregistrer. Pour qu'une zone reste actionnable quoi qu'il arrive (une suppression et sa confirmation), posez CLASSE_HORS_EDITION dessus — et sur rootClassName des fenêtres surgissantes, qui se rendent hors de l'arbre :

<section className={CLASSE_HORS_EDITION}>…</section>
<Popconfirm rootClassName={CLASSE_HORS_EDITION} …>

Un site qui signe déjà

<SignatureLystech mention={null} /> ne garde que le bouton « Connexion » — pour un pied de page qui porte déjà sa propre mention de Lystech.

Sites prérendus

Un site généré au build affiche les replis du code dans son HTML, puis bascule sur le contenu publié une fois le JavaScript chargé — l'ancien prix clignote vers le nouveau, et les moteurs de recherche n'indexent que l'ancien.

Passez le contenu au premier rendu pour supprimer les deux problèmes :

<SectionsProvider site="armoise" page="massage" sectionsInitiales={dejaLu}>

dejaLu vient d'un appel, dans le script de prérendu, à GET /site-sections/public/<site>/<page>.