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

@lexique/cli

v0.2.0

Published

Official CLI for pulling and pushing translations between developer codebases and Lexique.

Downloads

32

Readme

Lexique CLI

Official CLI for pulling and pushing translation keys and locale files between developer codebases and Lexique.

Status

This package is the first npm-distributable CLI for Lexique. It uses the machine-facing API implemented by the Lexique server:

  • GET /api/projects/{id}/translations/pull
  • POST /api/projects/{id}/translations/push

Authentication uses account-based CLI auth for humans and account-owned machine tokens for CI. Project-scoped API tokens are legacy compatibility only.

Installation

During development:

npm test
node src/bin/lexique.js --help

Published usage:

npx @lexique/cli --help

or, after global/project installation:

lexique pull --help

Command-specific help:

lexique login --help
lexique config --help
lexique projects --help
lexique tokens --help
lexique pull --help
lexique push --help
lexique check --help

Authentication

Sign in with your Lexique account:

lexique login

login opens Lexique in your browser, starts a localhost callback, and completes a PKCE authorization flow. The CLI never asks for your password and does not reuse web cookies. For another Lexique deployment:

lexique login --base-url http://127.0.0.1:8000

Account credentials are stored outside the repository. On macOS, the refresh token is stored in Keychain when available; otherwise the CLI falls back to:

~/.config/lexique/auth.json

The fallback file is written with 0600 permissions. Refresh tokens are rotated by the server. lexique logout revokes the saved CLI session and removes the local credential.

For CI and agents, create account-owned machine tokens:

lexique tokens create --name CI --project 1 --scope projects:read --scope translations:read --scope translations:write
export LEXIQUE_TOKEN="lxq_mch_<selector>_<secret>"

Machine tokens are scoped to explicit projects and permissions. They are intended for LEXIQUE_TOKEN; they are not written to lexique.config.json.

When a token is supplied through LEXIQUE_TOKEN, --token, or a legacy repository token, the CLI does not trust a repository-controlled baseUrl. It uses https://lexique.app or an explicit LEXIQUE_BASE_URL/--base-url. Self-hosted CI must therefore pin both values:

export LEXIQUE_BASE_URL="https://lexique.example"
export LEXIQUE_TOKEN="lxq_mch_<selector>_<secret>"

Legacy project-token login remains temporarily available during migration:

lexique login --project-id 1 --token 'lxq_pjt_<selector>_<secret>'

Authentication precedence is intentionally migration-safe:

  1. --token or LEXIQUE_TOKEN
  2. legacy token values already present in lexique.config.json
  3. saved account sessions from lexique login
  4. legacy saved project-token logins

Configuration

Every project command needs:

  • account auth from lexique login or a LEXIQUE_TOKEN
  • a numeric project ID from --project, LEXIQUE_PROJECT_ID, or lexique.config.json

baseUrl defaults to https://lexique.app. You only need --base-url for local development, self-hosting, staging, or another deployment. HTTPS is required except for loopback development URLs using localhost, 127.0.0.1, or ::1.

The server does not expose project slug discovery yet, so project IDs must be numeric.

Configuration priority:

  1. CLI flags
  2. environment variables
  3. repo config file
  4. saved account auth
  5. legacy saved project-token login config
  6. default base URL

Environment variables:

export LEXIQUE_BASE_URL="https://lexique.app"
export LEXIQUE_PROJECT_ID="1"
export LEXIQUE_TOKEN="lxq_mch_<selector>_<secret>"

Config file names searched from the current directory upward:

  • lexique.config.json
  • .lexiquerc.json

lexique.config.json stores project and sync configuration only: base URL, project ID, locales, adapter, and paths. Do not put auth secrets in it. Existing legacy token values are still read for migration compatibility before saved account sessions, but every use emits a security warning and lexique config removes them when it rewrites the file.

.lexique/state.json stores the local sync baseline used by changed-only push. Add it to .gitignore; it is per working copy.

Configured translation paths are resolved relative to the directory containing lexique.config.json. They must remain inside that directory tree and must not traverse symbolic links. Explicit --file and --output paths remain under the caller's control. Catalog files are limited to 10 MiB, and API responses are limited to 20 MiB with a 30-second request timeout.

Version 0.2 uses an adapter-aware version 2 baseline. If a working copy still has a 0.1 baseline, run lexique pull --all once before the next changed-only push.

Example:

{
  "baseUrl": "https://lexique.app",
  "projectId": 1,
  "defaultLocale": "en",
  "supportedLocales": ["en", "fr"],
  "translationAdapter": "flat-json",
  "translationAdapterConfig": {
    "pathPattern": "locales/{locale}.json"
  },
  "sync": {
    "format": "json",
    "path": "locales/{locale}.json",
    "locales": ["en", "fr"]
  }
}

Do not commit tokens to repo config files.

Local Sync Baseline

Configured pull and push workflows use .lexique/state.json to avoid uploading every configured file on each push. The baseline is deliberately conservative: it is only advanced when the CLI can trust that local files match what Lexique returned or accepted.

The baseline is updated after:

  • a full configured pull, such as lexique pull or lexique pull --all
  • a configured push whose server summary confirms that no values, keys, entries, or locales were skipped

The baseline is not updated after:

  • filtered pulls using --locale, --key, --tag, --match, or --only-completed
  • pushes using --since, because they compare against Git instead of the local baseline
  • pushes using an explicit --match, because only part of the local changes may have been uploaded
  • pushes where local inputs were skipped, for example with --skip-invalid
  • pushes where the server summary is missing or reports skipped values, missing keys, invalid entries, empty values, or unsupported locales

When the baseline is not updated, the CLI prints a message explaining why. This keeps unresolved local changes visible to the next lexique push instead of marking them as synchronized too early.

License

Lexique CLI is proprietary software. You may install and use it to interact with Lexique services. See LICENSE for details.

lexique login

Authenticate the current machine with a Lexique account.

Usage:

lexique login
lexique login --base-url http://127.0.0.1:8000

Flags:

--base-url <url>
--name <device-name>

Deprecated migration form:

lexique login --project-id 1 --token 'lxq_pjt_<selector>_<secret>'

lexique projects

List account projects or save the current repository's project selection.

lexique projects
lexique projects use 1

projects use writes only project metadata to lexique.config.json. Run lexique config when you also want adapter/path sync settings.

lexique config

Configure a repository for repeatable pull and push workflows.

Usage:

lexique config --project-id <id> [options]
lexique config print

config reads the project adapter from GET /api/projects/{id}/meta and writes a matching local sync config when the adapter is supported. Supported adapter-aware config targets are:

  • flat-json
  • localized-json
  • symfony-yaml
  • i18next-json

XLIFF projects are detected from server metadata but rejected with a clear unsupported-capability error until the Lexique server can import and export XLIFF.

Interactive config uses existing lexique.config.json values as editable defaults when rerun. It asks for:

  • translation format and location

Authenticate first with lexique login, or set LEXIQUE_TOKEN to an account-owned machine token for CI.

Non-interactive examples:

lexique config --project-id 1 --translations-format json --path 'locales/{locale}.json'
lexique config --project-id 1 --translations-format yaml --path 'translations/{locale}.yaml'
lexique config --project-id 1 --translations-format localized-json --path translations.json
lexique config --base-url http://127.0.0.1:8000 --project-id 1 --translations-format json --path locales

Flags:

--base-url <url>
--project-id <id>
--project <id>
--token <token>   Deprecated project token override
--translations-format <json|yaml|localized-json>
--path <path>
--domain <domain>
--namespace <namespace>
--print

config writes lexique.config.json in the current repository. It does not store auth secrets there.

Use print or --print to display the effective project setup as JSON without changing files:

lexique config print

Example output:

{
  "baseUrl": "http://127.0.0.1:8000",
  "projectId": "11",
  "configPath": "/path/to/lexique.config.json",
  "defaultLocale": "en",
  "supportedLocales": ["en", "fr"],
  "translationAdapter": "symfony-yaml",
  "translationAdapterConfig": {
    "pathPattern": "translations/{domain}.{locale}.yaml",
    "defaultDomain": "messages"
  },
  "sync": {
    "format": "yaml",
    "path": "translations/messages.{locale}.yaml",
    "locales": ["en", "fr"],
    "domain": "messages"
  }
}

Supported path styles:

  • locales/{locale}.json: explicit per-locale file pattern
  • translations/{locale}.yaml: explicit per-locale YAML file pattern
  • translations/{domain}.{locale}.yaml: Symfony YAML, with {domain} resolved during config
  • translations: Symfony YAML directory mode, scans {domain}.{locale}.yml and skips locales outside sync.locales
  • locales/{locale}/{namespace}.json: i18next JSON, with {namespace} resolved during config
  • locales: directory mode, writes en.json, fr.json, etc.
  • translations.json: single multi-locale JSON file for localized-json

lexique setup remains as a deprecated alias for lexique config.

After config, basic sync commands are short:

lexique pull
lexique push

lexique pull

Download translations from Lexique.

Usage:

lexique pull [--project <id>] [options]
lexique pull --project-id 1 --locale fr --key checkout.pay

After lexique login and lexique config, no connection flags are needed:

lexique pull

Useful examples:

lexique pull --project-id 1 --locale en --locale fr --match '^checkout\.'
lexique pull --project-id 1 --locale fr --format yaml --output locales/fr.yaml
lexique pull --project-id 1 --locale en --locale fr --output locales

Flags:

--base-url <url>
--project-id <id>
--project <id>
--token <token>         Machine token or deprecated project token
--locale <locale>       Repeatable
--key <key>             Repeatable
--tag <tag>             Repeatable
--match <regex>
--format <json|yaml>
--output <path>
--only-completed

Pull behavior:

  • after lexique config, running lexique pull with no flags writes configured translation files
  • full configured pulls update .lexique/state.json, which becomes the baseline for changed-only push
  • filtered configured pulls, such as --locale, --key, --tag, --match, or --only-completed, do not update the baseline
  • first pull has no local baseline, so it is a full configured pull
  • --all is accepted as an explicit full refresh flag
  • repeated --key values are OR filters
  • repeated --tag values are OR filters
  • --match filters translation keys
  • response envelopes are JSON from the server
  • file output uses files[*].content from the server response
  • localized-json config writes a single JSON file from the response entries

If one file is returned and no --output is passed, the file content is printed to stdout. If multiple files are returned and no --output is passed, the full JSON envelope is printed.

When multiple files are returned with --output, the output path is treated as a directory and server-provided filenames are used. When one file is returned and --output has an extension, it is treated as the exact target file.

lexique push

Upload translations to Lexique.

Usage:

lexique push [options]
lexique push --project <id> --locale <locale> --file <path> [options]
lexique push --project <id> --locale <locale> --key <key> --value <value> [options]

Configured mode:

lexique push

File mode:

lexique push --project-id 1 --locale fr --file locales/fr.json

Single-key mode:

lexique push --project-id 1 --locale fr --key checkout.pay --value "Payer"

Useful examples:

lexique push
lexique push --all --dry-run
lexique push --since main --dry-run
lexique push --project-id 1 --locale fr --file locales/fr.json --tag billing
lexique push --project-id 1 --locale fr --file locales/fr.yaml --match '^checkout\.'
lexique push --project-id 1 --locale fr --file locales/fr.json --overwrite
lexique push --project-id 1 --locale fr --file locales/fr.json --skip-existing
lexique push --project-id 1 --locale fr --file locales/fr.json --no-create-missing
lexique push --since main
lexique push --all
lexique push --skip-invalid
lexique push --skip-invalid --batch-size 50
lexique push --skip-invalid --batch-max-bytes 4mb
lexique push --project-id 1 --locale fr --key checkout.pay --value "Payer"

Flags:

--base-url <url>
--project-id <id>
--project <id>
--token <token>         Machine token or deprecated project token
--locale <locale>
--key <key>
--value <value>
--file <path>
--tag <tag>             Repeatable
--match <regex>
--overwrite
--skip-existing
--create-missing
--no-create-missing
--all
--since <ref>
--dry-run
--skip-invalid
--batch-size <count>
--batch-max-bytes <bytes>

Push behavior:

  • after lexique config, running lexique push with no --file or --key sends configured translation keys changed since .lexique/state.json
  • if .lexique/state.json does not exist, default push fails safely and asks for lexique pull --all or lexique push --all --dry-run
  • --since <ref> keeps the legacy Git comparison mode for explicit one-off comparisons
  • use --all to send every configured translation file
  • successful configured pushes update .lexique/state.json only when the server summary confirms that no values or keys were skipped
  • pushes using --since or an explicit --match do not update .lexique/state.json
  • file mode sends raw file content to the server using the API inputs payload
  • supported input extensions are .json, .yaml, and .yml
  • parsing, validation, regex filtering, and merge behavior remain server-owned
  • default write behavior is conservative: overwrite=false, createMissing=true
  • --overwrite and --skip-existing are mutually exclusive
  • --create-missing and --no-create-missing are mutually exclusive
  • --file cannot be combined with --key or --value
  • --dry-run builds and prints the exact payload inputs without calling Lexique
  • changed-key filtering uses adapter-normalized keys, including nested i18next JSON and nested Symfony YAML
  • --skip-invalid omits locally invalid JSON/YAML files before uploading and reports them in --dry-run
  • --batch-size splits configured file uploads into multiple API requests and aggregates the final summary
  • --batch-max-bytes queues files until adding another file would exceed the serialized request size, then sends the batch
  • --batch-max-bytes accepts raw bytes or kb, kib, mb, and mib suffixes, such as 4000000, 512kb, or 4mb
  • batched pushes print per-batch progress and the final summary aggregates all batch responses

lexique tokens

Manage account-owned machine tokens for CI and agents.

lexique tokens create --name CI --project 1 --scope projects:read --scope translations:read --scope translations:write
lexique tokens list
lexique tokens revoke 4

Usage:

lexique tokens list
lexique tokens create --name <name> --project <id>[,<id>] [options]
lexique tokens revoke <token-id>

Flags:

--base-url <url>
--name <name>
--project <ids>         Comma-separated numeric project IDs
--scope <scope>         Repeatable; defaults to projects:read and translations:read
--expires-in-days <n>   Optional; must be 30, 90, 180, or 365

Machine tokens are returned once on creation. Use them through LEXIQUE_TOKEN. Invalid project IDs or unsupported expiration windows are rejected before the CLI calls Lexique.

lexique status and lexique diff

lexique status
lexique diff

status prints the current project, auth source, config path, sync settings, and local baseline status. diff renders the same changed-input view as push --dry-run without uploading.

lexique check --staged

Validate exactly the translation catalogs staged in Git:

lexique check --staged

The check is offline and read-only. It reads lexique.config.json and catalog content from the Git index, ignores unstaged edits, and never contacts or updates Lexique. It reports added keys and updated locale values, requires a non-empty default-locale value for every new key, and warns when secondary locales are missing.

Exit codes:

  • 0: no changes, or staged changes are valid
  • 1: invalid staged catalog, missing default-locale value/file, or Git conflict
  • 2: invalid usage or project configuration

Install @lexique/cli as a project dependency so hooks can run without downloading packages during a commit. A native Git hook can call:

#!/bin/sh
npx --no-install @lexique/cli check --staged

The same command can be placed in .husky/pre-commit. For the pre-commit framework:

repos:
  - repo: local
    hooks:
      - id: lexique-staged-translations
        name: Lexique staged translations
        entry: npx --no-install @lexique/cli check --staged
        language: system
        pass_filenames: false

Recommended human workflow:

lexique pull
# edit translation catalogs
git add lexique.config.json locales translations
lexique check --staged
git commit
lexique push

Pushing remains explicit. Pre-commit checks do not require account credentials; CI jobs that pull or push should continue using a project-scoped account machine token through LEXIQUE_TOKEN.

Development

Run checks:

npm test
npm run check

Run the opt-in read-only contract test against a real Lexique deployment and a disposable project:

LEXIQUE_CONTRACT_BASE_URL=https://lexique.example \
LEXIQUE_CONTRACT_TOKEN="$LEXIQUE_TOKEN" \
LEXIQUE_CONTRACT_PROJECT_ID=42 \
npm run test:contract

The live push contract remains skipped unless LEXIQUE_CONTRACT_ALLOW_WRITE=true and LEXIQUE_CONTRACT_PUSH_LOCALE, LEXIQUE_CONTRACT_PUSH_KEY, and LEXIQUE_CONTRACT_PUSH_VALUE are all explicitly provided. Only enable it for a disposable test project.

Create a local npm package tarball:

npm pack

Distribution Plan

Primary target:

  • npm package: @lexique/cli
  • executable: lexique
  • one-off usage: npx @lexique/cli ...

Release

Releases are published by GitHub Actions after changes land on main.

Required npm setup:

  • @lexique/cli must exist on npm. Publish the first version manually with 2FA.
  • npm Trusted Publishing must trust GitHub Actions for Lyro1/lexique-cli, workflow file npm-release.yml, and environment npm.
  • Create a protected GitHub environment named npm and require maintainer approval for deployments.
  • In npm package settings, require 2FA and disallow traditional token publishing after Trusted Publishing is verified.

Pull requests targeting main run:

npm ci --ignore-scripts
npm run release:check

Pushes to main run the same checks, then publish package.json's version to npm if that exact version is not already published. If the version already exists, the publish job exits successfully without publishing again.

Prepare a release by bumping the package version in a pull request:

npm version patch

Use minor, major, or an explicit version when appropriate. After the pull request is merged into main, GitHub Actions publishes the new version through npm Trusted Publishing.

Follow-ups:

  • Homebrew formula or tap
  • shell completions
  • server-side XLIFF import/export and sync support