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

@mostajs/piece-extract

v0.2.0

Published

Lire une PIÈCE commerciale photographiée (ticket de caisse, facture d'achat) et en rendre des données vérifiées.

Readme

@mostajs/piece-extract

Auteur : Dr Hamid MADANI [email protected] Licence : AGPL-3.0-or-later · Couche : N3 (composite) · Dépendances de production : aucune

Lit une pièce commerciale photographiée — ticket de caisse, bientôt facture d'achat — et rend une structure typée accompagnée d'un verdict.

Le module n'extrait pas : il valide. L'état de l'art plafonne autour de 84 à 93 % de F1 sur les référentiels publics : une pièce sur dix est fausse quelque part, chez tout le monde. Dire laquelle est la valeur ajoutée.

⚠️ Trois « ticket » dans l'écosystème : @mostajs/ticketing est une billetterie, @mostajs/ticket-print imprime des numéros de file d'attente, et ce module lit des reçus de caisse. Un renommage en @mostajs/piece-extract est proposé — voir docs/00-PROPOSITION-MODULE.md §4.3.


État

Jalon 1 livré — toutes les fonctions pures : lecture d'un montant, lecture des lignes, découpage en zones, gabarits, et le contrôle. Tout ce qui décide de la justesse d'une pièce est écrit et éprouvé, sans dépendre d'un moteur d'OCR.

| Jalon | Contenu | État | |---|---|---| | J1 | Types, montantDepuisTexte, lignesDepuisTexte, verifier(), 2 gabarits | ✅ 9 tests verts | | J2 | extrairePiece() — pipeline complet sur une image | à venir | | J3 | extrairePdf(), campagne sur les 791 pièces, rapport chiffré | à venir | | J4 | Corrections apprises (ocr-learn) | à venir | | J5 | Factures d'achat, plusieurs taux | à venir |


Le contrôle, en trente secondes

import { verifier, recalculerTotaux } from '@mostajs/piece-extract';

const piece = {
  lignes: [
    { quantite: 1, libelle: 'POULET KATSU', ttcLigne: 1050 },   // centimes, jamais 10.50
    { quantite: 1, libelle: 'Mixte roll…',   ttcLigne: 1190 },
    { quantite: 1, libelle: 'Riz Nature',    ttcLigne: 180 },
  ],
  totaux: { ttcLu: 2420, nbArticlesLu: 3 },
  dateHeure: '2026-03-31T21:42:04',
  canal: 'emporte',
};

verifier(piece, { taux: 100 });        // taux en POUR MILLE : 100 = 10 %
// → { fiable: true, controles: [...], aRelire: [] }

recalculerTotaux(piece, { taux: 100 });
// → { ttcCalcule: 2420, ht: 2201, tva: 219, ttcLu: 2420, nbArticlesLu: 3 }

fiable ne porte que sur l'arithmétique. aRelire nomme ce qu'un humain doit vérifier — 'dateHeure', 'canal', 'lignes[2]'. Les deux questions sont distinctes : les confondre reviendrait à jeter des pièces saines, ou à valider des pièces incomplètes.


Ajouter un restaurant : un gabarit, pas du code

C'est ce qui rend le module réutilisable d'une enseigne à l'autre. Un nouveau restaurant ne demande aucune ligne du module — seulement la description de son papier :

import { definirModele, registerModele, lignesDepuisTexte } from '@mostajs/piece-extract';

registerModele(definirModele('le-gourmet', {
  reperes: {
    reference: /Facture\s*n[°o]\s*(\d+)/i,   // cette caisse numérote autrement
    total:     /MONTANT\s*D[ÛU]/i,            // et n'écrit pas « TOTAL À PAYER »
  },
  ignorer: [/merci de votre visite/i],
}));

lignesDepuisTexte(texteOcr, 'le-gourmet');

definirModele() part du générique et n'exige que les différences ; les lignes à ignorer s'ajoutent à celles du générique. Deux gabarits sont livrés :

| Gabarit | Rôle | |---|---| | generique-fr | traite une enseigne inconnue au premier passage | | sushi-tori | écrit d'après le papier réel du corpus |

Les avoir tous les deux n'est pas un luxe : c'est ce qui prouve que l'abstraction tient. Un gabarit qui ne servirait qu'une fois ne serait pas un gabarit, seulement du code particulier déguisé. Un gabarit inconnu est refusé en listant ceux qui existent — jamais un repli silencieux, qui produirait une lecture dégradée sans le dire.


Référence API — jalon 1

| Fonction | Rend | Pure ? | |---|---|---| | montantDepuisTexte(txt) | entier de centimes, ou null | ✅ | | lignesDepuisTexte(texte, modele?) | LigneLue[] | ✅ | | decouperZones(texte, modele) | { entete, corps, pied } | ✅ | | verifier(piece, { taux?, tolerance? }) | Verdict | ✅ | | recalculerTotaux(piece, { taux? }) | Totaux | ✅ | | definirModele · registerModele · getModele · listModeles | gabarits | ✅ |

Ce que le lecteur de lignes sait faire, et pourquoi

Trois comportements viennent du texte OCR réel, et aucun n'aurait été imaginé depuis un bureau :

  • le libellé se coupe sur deux lignes — papier étroit : 1 Mixte roll saumon thon avocat puis oignons 11,90. Une ligne à quantité sans montant est mise en attente, la suivante la complète ;
  • du bruit suit le montant1 Riz Nature 1.80 2e. On repère le dernier montant et sa position, plutôt que d'espérer une fin de ligne propre ;
  • la quantité est bornée — sans quoi 60430 NOAILLES deviendrait 60 430 unités d'un article nommé « NOAILLES », et 8 PLACE DE L'HÔTEL DE VILLE un article de la commande.

Les règles qui ne se négocient pas

  1. La somme des lignes doit égaler le total. Sinon la pièce n'est pas fiable.
  2. Un seul contrôle ne suffit pas. Deux erreurs qui se compensent passent le premier — d'où le contrôle croisé sur le nombre d'articles imprimé.
  3. Le HT et la TVA ne se lisent jamais, ils se recalculent. Mesuré : l'OCR rend HT: 22001 là où le papier porte 22,01. Le HT lu ne sert que de contrôle.
  4. Absent ≠ zéro. Un montant illisible vaut null. Une date bruitée n'est pas rendue.
  5. Le canal ne se devine pas. 'inconnu' est une valeur de plein droit.

Tests

npm test    # typecheck + build + suite — instantanée, sur du TEXTE OCR GELÉ

La suite ne lance aucune reconnaissance : elle tourne sur le texte que tesseract a réellement rendu sur les tickets du corpus, à 80 dpi (bruité) et à 200 dpi. Le texte bruité y est un cas de test à part entière — c'est exactement ce que le module doit savoir écarter.


Documentation

docs/00-PROPOSITION-MODULE.md · 01-ETUDE-ETAT-ART · 02-AUDIT-CHAINE-OCR · 03-PLAN-DEV · 03bis-PROMPT-IMAGE-OBJECTIF (+ docs/img/) · 04-PLAN-TESTS · DEVTEST-PLAN.ticket-extract.json (jumeau qatrax) · llms.txt (fiche LLM).