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

@concordance-wiki/nlp

v1.0.0

Published

Normalisation, Aho-Corasick, n-grams, C-value, MinHash and the language packs.

Readme


Why

Concordance gives every word of your business a page: the note someone wrote, if any, and every passage, in every file, that mentions it. You install @concordance-wiki/concordance for that. This package is the part of it that recognises the words: the language packs, the comparison form that makes "Occurrence Scans" and "occurrence scan" the same term, the dictionary of every title and alias of the wiki, the one-pass scan that finds them in every note, and the keyword discovery that scores the expressions no note defines yet. Install it alone to build on the engine, or to add a language from a plugin.

Quick start

npm install @concordance-wiki/nlp

The dictionary of a two-note wiki and the scan of one paragraph, the way concordance build recognises the words of a note:

import { buildDictionary, comparisonForm, languagePack, scanDocument } from "@concordance-wiki/nlp";

const pack = languagePack("en");
comparisonForm("Occurrence Scans", pack); // "occurrence scan"
const entities = [
  { id: "glossary/occurrence-scan", source: "glossary", type: "term", title: "Occurrence scan", aliases: ["scan"], locale: "en" },
  { id: "glossary/finding", source: "glossary", type: "term", title: "Finding", aliases: [], locale: "en" },
];
const dictionary = buildDictionary({ entities, locale: "en", glossarySources: new Set(["glossary"]), stopwords: pack.stopwords, shortTerms: new Set() });
const document = { path: "checks/link-broken.md", paragraphs: [{ line: 5, text: "The occurrence scan reports a finding per broken link." }] };
const scale = { base: 0.6, per_occurrence: 0.05, cap: 0.8, homonym_factor: 0.5, type_prefix_bonus: 0.1 };
scanDocument({ document, source: "specs", dictionary, pack, typePrefixes: {}, scale }).map((o) => o.target.id);
// ["glossary/occurrence-scan", "glossary/finding"]

What you get

  • Two languages, extensible: packs for en and fr built from stopwords and suffix files, and registerLanguagePack for the one a plugin adds.
  • One comparison form: lower-cased, accents stripped, singularised by the pack's suffix rules, so that plurals and capitals never split a term in two.
  • A dictionary of the wiki: every title and alias keyed by comparison form, glossary sources first, homonyms flagged with I-TERM-HOMONYM.
  • One pass per text: a word-level Aho-Corasick scan; each occurrence comes with its file, line, position, section, context and confidence, so that every page can cite the source line.
  • Words nobody defined: one- to four-word n-grams scored by C-value and IDF, each with a confidence of being a term of the subject rather than a word of the language, and the W-TERM-UNDEFINED findings.
  • Keyword pages: the expressions that pass the publication threshold become term entities marked keyword: true; the rest stay in the search index without a page.
  • Search tokens: the tokeniser the search index uses, with the same language packs.

Documentation

Part of Concordance, MIT licence.

Language packs for en and fr built from data files (locales/<locale>/pack.yaml, stopwords.txt and the optional suffixes.txt of inflected-form endings; the default stopwords are the words that name nothing on their own in any corpus, from the articles to every form of the auxiliaries and light verbs, the adverbs of prose, quantifiers, numerals, time words and the nouns that only count or place, never a word that could be a business term or end a technical compound, which a project lists under inference.stopwords), BCP 47 locale resolution with fallback to the language, Unicode word segmentation and a collation computed from the options of the pack without the collation data of the runtime (collation of the core package, accents and case set aside, digits compared by value), a registry for packs shipped by plugins (loadLanguagePack, languagePack, registerLanguagePack, resolveLocale, canonicalLocale, loadStopwords), and text normalisation: the comparison form of a text (lower-cased, accents stripped, apostrophes unified, each word singularised by the pack's suffix rules; comparisonForm, comparisonWords, singularize) and word boundaries from Unicode segmentation (wordBoundaries, isOnWordBoundaries); and the recognition dictionary: buildDictionary keys every title and alias of the entities of a locale by comparison form, with priority to glossary sources (glossarySources), leaves out stopwords (dictionaryStopwords merges the pack's defaults with the files of inference.stopwords) and terms shorter than three characters unless inference.short_terms allows them, and flags the forms shared by several entities as homonyms with an I-TERM-HOMONYM finding; and the occurrence scan: tokenize cuts a text into words in comparison form with their spans, buildAutomaton, scan and longestMatches run a word-level Aho-Corasick over them (one pass per text; an expression contained in a longer recognised one is dropped, while expressions that only partly overlap are all kept), and scanDocument turns the matches of a dictionary in the paragraphs of a document into occurrences with file, line, position, the matched text as written, section, an 80-character context, the type announced by a recognised prefix (type_prefixes) and a confidence from the glossary_occurrence scale (occurrenceConfidence gives the per-occurrence increments up to the cap; compareOccurrences is their canonical order). The scan reads the units that scannableText of the ingestion package produces, so code blocks, inline code, URLs, frontmatter and link targets never yield an occurrence; the visible text of a link does. And the keyword discovery: extractNgrams reads the same units and yields every n-gram of one to four words (keywordOptions resolves inference.ngrams, inference.candidate_score and the lock's rejected_terms), leaving out those starting or ending with a stopword, made only of digits or shorter than three characters, each with its surface form, position and a 160-character context; scoreCandidates groups them by comparison form, drops the dictionary entries and the rejected terms (which still penalise the n-grams they contain), scores each candidate by C-value × IDF and keeps those with three occurrences in two distinct files, best score first with its display form and its mentions (compareMentions), each with its confidence in [0, 1] of being a term of the subject rather than a word of the language, the product of five signals rounded to four decimals (confidenceOf, confidenceFactors, confidencePenalties, the measures in signals and the lowering signals in penalties): spread df / N, 1 up to maxSpread (0.5) then linear down to 0 for a word in every file, read only in a corpus of SPREAD_MIN_FILES (10); burst occurrences / df, 1 from BURST_PER_FILE (1.5) else 0.5 + (burst − 1), read only from BURST_MIN_FILES (5); position POSITION_BASE (0.8) plus 0.2 times the share of appearances in the prominent texts given as prominent n-grams (headings, written links, frontmatter), capped at one; neighbourhood 1 when the expression is, or is a component of, a frequent n-gram that also holds a defined term, NEIGHBOURHOOD_BASE (0.9) otherwise; morphology MORPHOLOGY_PENALTY (0.8) when a word ends with an inflected-form suffix of the pack (suffixes.txt, loadSuffixes; a pack without the file penalises no form), 1 otherwise; undefinedTermFindings turns the candidates at or above the score threshold into W-TERM-UNDEFINED findings; similarExpressions picks, among keyed expressions, those of a similar form to a key (similarForm: one a contiguous part of the other, or half the distinct words of the longer one shared), the closest first, five at most (SIMILAR_EXPRESSIONS_LIMIT), for the leads of a keyword page; definedExpressions lists the n-grams the dictionary already defines that reach the publication threshold, the expressions whose keyword page a note took over. And the publication threshold: publishKeywords splits the candidates into the pages to generate (keywords/<slug of the key>, sorted by identifier; two keys with the same slug take numeric suffixes in score order), the expressions discarded by the counts of inference.keyword_pages (keywordPublicationOptions: three occurrences in two distinct files by default) and those withheld by its min_confidence (one half by default), both staying in the output for the search index without a page; keywordEntities turns each page into a term entity marked keyword: true, located on the first mention of its expression, with its occurrences, files, score and confidence as attributes. The counts of the three lists go into the build summary (keywords.published, keywords.discarded, keywords.withheld in core's summarize), printed by the build as keyword pages, expressions under the threshold and expressions set aside by confidence once the build runs the discovery.