𧬠Pattern DSL β expand shell-safe %a/%w classes, ranges, literals and optionals into every name combination
β‘ Parallel checks β query the npm registry with configurable concurrency (default 100)
ποΈ Sorted output β taken, free and error names written to separate files
π§© Predefined sets β vowel, consonant, alpha, alnum, digit, hex built in
π Library + CLI β run the npmgrep binary or import the generator/checker functions
π― Dry-run generate β print candidate names without touching the network
π¦ Typed ESM build β ships ESM + .d.ts to lib/ via tsdown
π§ͺ Tested generator β the DSL parser is covered by bun:test
Install the CLI globally:
npm install -g npmgrep
# or
bun add -g npmgrep
# or
pnpm add -g npmgrep
# or
yarn global add npmgrepThen run npmgrep ... anywhere. The published binary is built to ESM and runs on Node.js or Bun.
The project uses Bun for development:
bun install
bun run start "<pattern>" # run the CLI from source (src/cli.ts)
bun run build # build to lib/ (ESM + .d.ts)The CLI exposes two commands. check is the default, so a bare pattern runs a check.
npmgrep <command> [options]
Commands:
npmgrep check <pattern> Generate names and check availability on the npm registry [default]
npmgrep generate <pattern> Generate names and print them (no network requests)
npmgrep %a%a%a # check all 3-letter names (no quotes needed)
npmgrep check example%w # explicit command form
npmgrep react-%a%a --name react2 # custom output-file name
npmgrep %w%w --out results --quiet # write to results/, no per-name logs
npmgrep %a%a%a -c 50 # lower the request concurrencyThe
%-syntax is shell-safe: it has no[ ] { } ? *, so zsh/bash won't try to glob-expand it and you never need quotes. (npm[alnum]fails in zsh withno matches foundβ the shell eats the brackets beforenpmgrepever runs.)
| Option | Alias | Default | Description |
|---|---|---|---|
--name |
-n |
derived from pattern | Base name for the output files |
--concurrency |
-c |
100 |
Number of parallel requests to the npm registry |
--out |
-o |
. |
Directory where result files are written |
--quiet |
-q |
false |
Suppress per-name logging |
--retries |
-r |
3 |
Retries for transient network errors (e.g. ENOTFOUND, ECONNRESET, 429, 5xx) |
--retry-delay |
500 |
Base delay in ms between retries (grows exponentially) |
At the end it prints a summary, e.g.:
βΉ Done "example-w": 36 checked β 30 free, 6 taken, 0 errors β ./
npmgrep generate %v%c # print all combinations
npmgrep generate %a%a --limit 20 # print only the first 20| Option | Alias | Description |
|---|---|---|
--limit |
-l |
Limit the number of printed names |
Run npmgrep --help (or npmgrep <command> --help) for the full reference.
<name> comes from --name, otherwise it is derived from the pattern (every
non-alphanumeric char becomes -). For example, the pattern example%w yields
<name> = example-w, i.e.:
exists-example-w.txt
free-example-w.txt
errors-example-w.txt
Files are appended to (appendFile), not overwritten β re-running adds lines to the end.
Classes start with %, literals are just plain text. No [ ] { } ? *, so it works
in zsh/bash without quotes.
| Construct | Meaning | Example |
|---|---|---|
| plain text | literal | react- β react- |
%a |
one letter a-z |
%a β a, b, β¦ z |
%d |
one digit 0-9 |
%d β 0, β¦ 9 |
%w |
letter or digit (alnum / word) | npm%w β npma, β¦ npm9 |
%v |
vowel aeiouy |
%v β a, e, i, β¦ |
%c |
consonant | %c β b, c, d, β¦ |
%h |
hex char 0-9a-f |
%h β 0, β¦ f |
%:set: |
one char from a custom set / range | %:a-c: β a, b, c |
%A %D β¦ |
UPPERCASE code = optional (0 or 1) | %a%D β a, a0, β¦ a9 |
%% |
a literal % |
a%%b β a%b |
Example patterns (paste as-is, no quoting):
%a%a%a # all 3-letter combinations
%w%w # 2 chars: letters and digits
react-%a%a # react-aa, react-ab, β¦
%a-kit # a-kit, b-kit, β¦
@scope/%a-ui # @scope/a-ui, @scope/b-ui, β¦
%v%c%v # vowel-consonant-vowel
%ash # ash, bsh, β¦, zsh (%a + literal "sh")
%:a-z:sh # same, via an explicit range
β οΈ The number of combinations grows multiplicatively. For example,%a%a%ais 26Β³ β 17,576 names, and each one is a separate HTTP request to npm.
Still supported, but the brackets are special in zsh/bash, so you must quote these:
| Construct | Meaning | Example |
|---|---|---|
{text} |
literal text | {react-} β react- |
[abc] |
one character from the set | [abc] β a, b, c |
[a-z] |
character range | [a-c] β a, b, c |
[0-9] |
digit range | [0-2] β 0, 1, 2 |
? |
makes the previous token optional | [ab][cd]? β a, ac, adβ¦ |
Predefined sets work in both syntaxes: [vowel], [consonant], [alpha] (26
letters), [digit], [alnum] (36), [hex].
pattern (positional arg)
β
βΌ
tools/generator.ts βββΊ list of names βββΊ utils/checker.ts βββΊ npm registry
β
ββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
βΌ βΌ βΌ
exists-<name>.txt free-<name>.txt errors-<name>.txt
(taken + version) (available, 404) (other errors)
Each name becomes a request to https://registry.npmjs.org/<name>:
- 200 β package exists; the name is written to
exists-<name>.txtalong withdist-tags.latest; - 404 β name is available; written to
free-<name>.txt; - any other status / network error β written to
errors-<name>.txt.
Checking runs with a concurrency of 100 requests by default (an internal worker-pool), tunable with --concurrency.
The generator and checker can be used as a library:
import {
generatePackageNames,
generateWithName,
generateNLetters,
generateWithSuffix,
generateWithPrefix,
checkPackages,
type CheckOptions,
type CheckResult,
} from "npmgrep";
generatePackageNames("[ab][cd]"); // ["ac", "ad", "bc", "bd"]
generateWithName("[ab]", "my-set"); // { name: "my-set", packages: ["a", "b"] }
generateNLetters(2); // all 2-letter combinations
generateWithSuffix("sh", 1); // ash, bsh, β¦, zsh
generateWithPrefix("react-", 2); // react-aa, β¦, react-zz
// generate + check against the registry; returns a CheckResult summary
await checkPackages("{react-}[alpha]", { outDir: "results", quiet: true });| Function | Description |
|---|---|
generatePackageNames(pattern) |
expands a pattern into an array of unique names |
generateWithName(pattern, name?) |
same, plus a name for naming the output files |
generateNLetters(n) |
all combinations of n letters |
generateWithSuffix(suffix, letters) |
letters letters + suffix |
generateWithPrefix(prefix, letters) |
prefix + letters letters |
checkPackages(pattern, options?) |
generate, query the registry, write the result files |
checkPackages accepts CheckOptions (name, concurrency, outDir, quiet, retries, retryDelay) and resolves to a CheckResult (name, outDir, total, free, exists, errors). Transient network errors (DNS failures like ENOTFOUND, dropped connections, timeouts, plus HTTP 429/5xx) are retried with exponential backoff; 404 (name is free) is never retried.
Invalid patterns throw: an unclosed [ / { / %:, a dangling %, an unknown %-code, or a ? with no preceding token.
Generator tests are written with bun:test:
bun run test:unit # bun test (fast)
bun run test # full check: lint + types + unit + sizesrc/
βββ index.ts # library entry β re-exports generator + checker
βββ cli.ts # CLI entry (yargs) β built to lib/cli.js, the `npmgrep` bin
βββ tools/
β βββ generator.ts # pure DSL parser + name-combination generator (no I/O)
βββ utils/
βββ checker.ts # checkPackages(): npm registry queries + result files
βββ concurrent.ts # mapWithConcurrency(): worker-pool with a concurrency cap
tests/
βββ generator.test.ts # generator tests (bun:test)
The build emits to lib/ (gitignored); bin, main and exports point at the built lib/*.js. Run output (exists-*/free-*/errors-*.txt) and scratch dirs (results/, packages/) are gitignored.
- TypeScript (strict) +
tsdownβ ESM build tolib/with.d.ts - Bun +
bun:testβ runtime and test runner Biomeβ lint + formatyargsβ CLI argument parsingaxiosβ HTTP requests to the npm registry@lsk4/logβ logging@lskjs/algosβuniqhelper
Licensed under the MIT license β see LICENSE.
