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

@lexeri/check-cli

v1.1.2

Published

Check documents for forbidden terminology with Lexeri — CI-friendly CLI

Readme

lexeri-check

CI-friendly CLI that checks documents for forbidden terminology with Lexeri.

It uploads the given file(s), runs a term check against the termbase linked to your API token, waits for the results, prints every matched term with its state, and exits with a CI-meaningful exit code.

Requires Node.js >= 20. No runtime dependencies.

Usage

lexeri-check [options] <file...>
export LEXERI_API_TOKEN=eyJhbGciOi...   # created in the Lexeri web UI
lexeri-check --locale en src/i18n/en.json Resources/Strings.resx

Example output:

src/i18n/en.json  (check a1b2c3, 214 sentences, 12.3s)
  STATE            TERM         PREFERRED    OCCURRENCES
  not_recommended  "whitelist"  "allowlist"  4
  preferred        "email"      -            2

  6 matches: 4 not_recommended, 2 preferred  →  FAIL

1 file checked: 0 passed, 1 failed — FAIL (failing states: not_recommended, outdated)

API token

Create an API token in the Lexeri web UI for the termbase you want to check against (see API tokens in the Lexeri help center). The token needs the check_text, read and show scopes. The termbase is derived from the token — no termbase flag is needed.

Pass it via the LEXERI_API_TOKEN environment variable (recommended for CI; use a masked variable) or --token.

Options

| Flag | Env var | Default | Description | |---|---|---|---| | -t, --token <jwt> | LEXERI_API_TOKEN | — | Lexeri API token (required) | | -l, --locale <code> | LEXERI_LOCALE | detected at upload | Source language of the documents | | --target-locales <csv> | — | — | Target locales for XLIFF (.xlf/.xliff) translation checks | | --topics <csv> | — | — | Restrict matching to these topic identifiers | | --tags <csv> | — | — | Restrict matching to terms carrying these tags | | --filter <key>=<value> | — | — | Filter by a custom termbase field (repeatable, see below) | | --fail-on <csv> | — | termbase's negative states | Term states that fail the check (see below) | | --terms-url <url> | LEXERI_TERMS_URL | https://terms.lexeri.com | Terms service base URL | | --files-url <url> | LEXERI_FILES_URL | https://files.lexeri.com | Files service base URL | | --timeout <seconds> | — | 300 | Max wait per file for check completion | | --keep-check | — | off | Keep the check and the uploaded document on the server after fetching results (deleted by default) | | --json | — | off | Machine-readable JSON report on stdout | | -v, --verbose | — | off | Log API requests to stderr (token never logged) |

Exit codes

| Code | Meaning | |---|---| | 0 | All files passed | | 1 | At least one match in a failing term state | | 2 | Usage or configuration error (bad flags, missing token/locale, file not found) | | 3 | Operational error (auth, network, API failure, timeout) |

With multiple files, the worst outcome wins: an operational error (3) beats failing terms (1) beats pass (0). A failure on one file does not stop the remaining files from being checked.

Failing states (--fail-on)

By default the CLI fetches your termbase and fails on its negative term states: not_recommended, outdated, plus any custom states flagged as replaceable. preferred and admitted matches are reported but never fail.

--fail-on replaces that set entirely, e.g.:

lexeri-check --fail-on not_recommended,deprecated,misspelled docs/manual.docx

misspelled is a pseudo-state: include it to fail on misspelled term matches.

Matches in states that are neither failing nor known-positive are shown as unknown and never fail the check — add them to --fail-on explicitly if they should.

Scoping the check (--topics, --tags)

By default a check matches against the whole termbase. To scope it — for example to check UI strings only against your UI terminology — restrict matching by topic and/or tag at check creation:

lexeri-check --topics 3fa5b2c1 --tags ui,frontend src/i18n/en.json

Topics are referenced by their identifier; unknown topic identifiers are ignored by the server. These are the same filters the topic/tag pickers in the Lexeri check form use.

Custom field filters (--filter)

Termbases can define custom fields on term entries and terms (picklist, number, boolean, date). Filter by them with repeatable --filter <key>=<value> arguments, where the key is the backend parameter name: term_entry_field_<field-identifier> or term_field_<field-identifier> (date fields take _start/_end suffixes):

lexeri-check \
  --filter term_field_a1b2c3=released \
  --filter term_entry_field_d4e5f6=ui \
  --filter term_entry_field_d4e5f6=web \
  src/i18n/en.json

Repeating the same key sends the values as an array, which is how multiple-selection picklist fields are queried. Values are validated server-side against the field's definition (e.g. picklist options); invalid queries are silently dropped by the server, not errored.

File formats

Files are parsed server-side, so the CLI does not filter by extension — the server decides what it can parse.

The formats most relevant for UI and software development:

  • String resources: .resx, .json, .properties, .strings, .po, .yaml/.yml, .ini, .csv, .xml, .twine
  • Translation interchange: .xlf/.xliff, .sdlxliff, .mqxliff, .tmx — XLIFF files additionally support translation checks against target languages via --target-locales
  • Docs in the repo: .md, .html, .txt, .srt, .dita/.ditamap

Office and DTP documents are supported as well: Word, Excel and PowerPoint including legacy and macro-enabled variants (.docx/.doc/.docm, .xlsx/.xls/.xlsm, .pptx/.ppt/.pptm), OpenDocument (.odt, .ods, .odp), Apple iWork (.pages, .key), plus .pdf, .rtf, .epub, .vsdx, .idml and .mif.

For string formats, matches are reported with the string keys they occur under, so violations can be traced back to the exact resource entry.

GitLab CI

Set LEXERI_API_TOKEN as a masked CI/CD variable, then:

terminology-check:
  image: node:22-alpine
  script:
    - npx --yes @lexeri/check-cli --locale en src/i18n/en.json
  rules:
    - changes:
        - "src/i18n/**/*"

Checking all string resources of a given type, e.g. every .resx file (glob expansion is done by the shell — on minimal images, use find):

  script:
    - find . -name '*.resx' -exec npx --yes @lexeri/check-cli --locale en {} +

Checking translated XLIFF files against a target language:

  script:
    - npx --yes @lexeri/check-cli --locale en --target-locales de translations/de.xlf

To keep a machine-readable report as an artifact:

terminology-check:
  image: node:22-alpine
  script:
    - npx --yes @lexeri/check-cli --json --locale en src/i18n/en.json > term-report.json
  artifacts:
    when: always
    paths: [term-report.json]

With --json, the report goes to stdout and everything else (progress, warnings) to stderr, so redirection stays clean.

JSON report shape

{
  "version": 1,
  "verdict": "fail",
  "exitCode": 1,
  "failOn": ["not_recommended", "outdated"],
  "termbase": { "identifier": "…", "name": "Marketing" },
  "files": [
    {
      "file": "docs/manual.docx",
      "status": "fail",
      "checkIdentifier": "…",
      "localeCode": "en",
      "sentenceCount": 214,
      "durationMs": 12300,
      "totalMatches": 6,
      "countsByState": { "not_recommended": 4, "preferred": 2 },
      "misspelledCount": 0,
      "matches": [
        { "term": "whitelist", "state": "not_recommended", "classification": "fail", "preferred": "allowlist", "occurrences": 4, "misspelled": false }
      ],
      "error": null
    }
  ]
}

How it works

  1. GET /termbases/current — resolves the termbase from the API token and derives the failing state set.
  2. POST /v2/documents (files service) — multipart upload, marked temporary; returns a short-lived document token.
  3. POST /checks — creates the check for the document, locale and termbase.
  4. GET /checks/:id — polled every 2.5s until progress reaches 100 (or --timeout).
  5. GET /term_checks/:id/matches — fetches the term matches, classifies each by its term state, prints the report.
  6. DELETE /checks/:id — removes the check and its uploaded document from the server (skipped with --keep-check; a failure here only warns and never changes the verdict).