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

regex-wand

v0.4.3

Published

Typed regular expressions without giving up readable regex authoring.

Readme

regex-wand

Typed regular expressions without giving up readable regex authoring.

npm version CI Playground License: MIT

regex-wand lets you write patterns with the composable magic-regexp API and get ArkRegex-powered TypeScript inference on the final RegExp.

At runtime, the result is still a native RegExp. The extra value is the typed surface: .infer, .inferCaptures, .inferNamedCaptures, typed exec() groups, literal flags, and test() narrowing. ArkRegex is type-only in the built JavaScript.

Try It

Install

bun add regex-wand
npm install regex-wand

magic-regexp and arkregex are installed with regex-wand. They remain part of the public type surface because Magic Regex primitives are re-exported and ArkRegex powers the inferred RegExp types.

Quick Start

import { defineRegex, digit } from "regex-wand"

const route = defineRegex({
	match: "exact",
	inputs: ["/users/", digit.times.atLeast(1).as("userId")],
})

route.infer satisfies `/users/${number}`
route.inferNamedCaptures.userId satisfies `${number}`

const match = route.exec("/users/42")
match?.groups.userId satisfies string | undefined

declare const value: string

if (route.test(value)) {
	value satisfies `/users/${number}`
}

Common Patterns

Exact semver-style match:

import { defineRegex, digit } from "regex-wand"

const semver = defineRegex({
	match: "exact",
	inputs: [
		digit.times.any().grouped(),
		".",
		digit.times.any().grouped(),
		".",
		digit.times.any().grouped(),
	],
})

semver.infer satisfies `${number}.${number}.${number}`
semver.inferCaptures satisfies [`${number}`, `${number}`, `${number}`]

Contains-style text search:

import { defineRegex, digit } from "regex-wand"

const ticketId = defineRegex({
	inputs: ["id:", digit.times.atLeast(1).grouped()],
})

ticketId.infer satisfies `${string}id:${number}${string}`
ticketId.test("ticket id:8042 is ready")

Flags:

import {
	caseInsensitive,
	defineRegex,
	global,
} from "regex-wand"

const accepted = defineRegex({
	flags: [global, caseInsensitive],
	match: "exact",
	inputs: ["ok"],
})

accepted.flags satisfies "gi"
accepted.infer satisfies "ok" | "oK" | "Ok" | "OK"

Why Not Just Magic Regex Or ArkRegex?

regex-wand exists because Magic Regex and ArkRegex solve different parts of the problem.

Raw regex strings are compact but hard to safely compose:

const route = /^\/users\/(?<userId>\d{1,})$/

Raw Magic Regex gives you readable, composable authoring:

import { createRegExp, digit } from "magic-regexp"

const route = createRegExp("/users/", digit.times.atLeast(1).as("userId"))

route.test("/users/42")

Magic Regex also ships a build-time transform that can compile those createRegExp(...) calls to plain RegExp literals.

Raw ArkRegex gives you strong result types from a raw regex string:

import { regex } from "arkregex"

const route = regex("^/users/(?<userId>\\d{1,})$")

route.infer satisfies `/users/${number}`
route.inferNamedCaptures.userId satisfies `${number}`

regex-wand keeps the Magic Regex authoring model, adds readable object params, and returns the ArkRegex-type surface:

import { defineRegex, digit } from "regex-wand"

const route = defineRegex({
	match: "exact",
	inputs: ["/users/", digit.times.atLeast(1).as("userId")],
})

route.infer satisfies `/users/${number}`
route.inferNamedCaptures.userId satisfies `${number}`
route.test("/users/42")

Use raw magic-regexp when you only need composable regex construction. Use raw arkregex when you already have a regex string and want type inference for it. Use regex-wand when you want both authoring ergonomics and result inference.

For new code, prefer defineRegex({ inputs, match, flags }). It is more readable than ordered arguments once definitions need matching mode or flags. For migration or Magic Regex-style authoring, createRegExp(...inputs) mirrors Magic Regex's positional input API and returns the richer regex-wand result.

Build-Time Transform Note

Magic Regex's transform only recognizes imports from magic-regexp and magic-regexp/further-magic. If you do not enable regex-wand/transform, direct regex-wand builders are small runtime adapters:

import { defineRegex, digit } from "regex-wand"

const route = defineRegex({
	match: "exact",
	inputs: ["/users/", digit.times.atLeast(1).as("userId")],
})

If you want Magic Regex's build-time transform in an app, compose with raw Magic Regex and adapt at the boundary:

import { createRegExp, digit, exactly } from "magic-regexp"
import { fromMagic } from "regex-wand"

const magicRoute = createRegExp(
	exactly("/users/", digit.times.atLeast(1).as("userId"))
		.at.lineStart()
		.at.lineEnd(),
)

const route = fromMagic(magicRoute)

route.inferNamedCaptures.userId satisfies `${number}`

With Magic Regex's transform enabled, the raw createRegExp(...) call can compile away, leaving only the regex-wand boundary adapter. ArkRegex remains type-only in regex-wand's built JavaScript.

If you want direct regex-wand builder calls to compile, use regex-wand/transform:

// vite.config.ts
import { defineConfig } from "vite"
import { RegexWandTransformPlugin } from "regex-wand/transform"

export default defineConfig({
	plugins: [RegexWandTransformPlugin.vite()],
})

The transform recognizes static defineRegex, createRegExp, createExactRegExp, createRegExpWithFlags, and createExactRegExpWithFlags calls imported from regex-wand. Dynamic expressions are left unchanged. The emitted code preserves the adapter shape, so .magic, .ark, and .toRegExp() keep working.

For expressions that are valid at runtime but too complex or dynamic for ArkRegex to infer, use fromMagicAs to provide the result type manually:

import { createRegExp, digit } from "magic-regexp"
import { fromMagicAs } from "regex-wand"

const magicRoute = createRegExp("/users/", digit.times.atLeast(1).as("userId"))

const route = fromMagicAs<
	`${string}/users/${number}${string}`,
	{ names: { userId: `${number}` } }
>(magicRoute)

API

  • defineRegex({ inputs, match, flags }) is the recommended object-shaped API. inputs is a Magic Regex-compatible tuple, match is "contains" by default or "exact" for start/end anchoring, and flags accepts helper values, arrays, strings, or Sets. pattern is also accepted as an alias for teams that prefer domain wording.
  • createRegExp(...inputs) is the Magic Regex-compatible positional builder for contains-style regexes.
  • createExactRegExp(...inputs) is a positional helper for start/end anchored regexes.
  • createRegExpWithFlags(inputs, ...flags) is a positional helper for Magic Regex flag helpers.
  • createRegExpWithFlags(inputs, flags) also accepts flag arrays, flag strings, and flag Sets.
  • createExactRegExpWithFlags(inputs, ...flags) combines anchoring and flags.
  • fromMagic(magic) adapts an existing MagicRegExp.
  • fromMagicAs<Pattern, Context>(magic) adapts an existing MagicRegExp with manually supplied ArkRegex result types.
  • RegexWandTransformPlugin from regex-wand/transform compiles static regex-wand builder calls at build time.
  • WandCompatibilityError marks strict type-level conversion failures.

All Magic Regex primitives are re-exported from the package.

Runtime Contract

The returned value is a native RegExp with extra typed properties attached:

  • magic is the original Magic Regex RegExp.
  • ark is an alias to the adapted value for explicit interop.
  • toRegExp() returns a new plain RegExp with the same source and flags.

Plain string inputs are escaped by Magic Regex. Magic Regex fragments keep their composition behavior. Native RegExp rules still apply at runtime, including duplicate flag errors and lastIndex behavior for global or sticky regexes.

Type Safety Contract

regex-wand is strict by default. If the adapter cannot preserve the ArkRegex type benefit for a Magic Regex value, the return type carries a compatibility error instead of silently degrading to a plain RegExp.

See docs/type-safety.md for the longer version. See docs/arktype-interop.md for when to use ArkType schema regexes directly. See docs/support.md for the explicit support matrix and known technical gaps. See docs/roadmap.md for the upstream compatibility audit and future support plan.

Verification

The package test suite covers both runtime behavior and compile-time inference:

  • Vitest runtime tests for builders, exact/contains matching, escaped strings, flag helpers/arrays/strings/Sets, indices, named groups, optional captures, lookarounds, backreferences, manual typing, string RegExp protocols, and lastIndex.
  • Vitest transform tests for direct builder calls, namespaced builder calls, and dynamic expressions that must be left untouched.
  • tsd tests for inferred strings, captures, named groups, flags, narrowing, manual typing, RegexParts, and compatibility errors.
  • A runtime import guard to ensure built browser code does not import ArkRegex.
  • A packed-consumer test that installs the generated tarball into a temporary project and verifies TypeScript, runtime behavior, and the regex-wand/transform Vite subpath.
  • TanStack Intent skill validation.

See docs/testing.md for the test matrix and what each layer proves.

Run everything from the monorepo root:

bun run release:check

Agent Skill

This package ships a TanStack Intent skill under skills/core/SKILL.md so coding agents can load version-aligned usage guidance from the installed npm package.

Development And Publishing

bun install
bun run check
bun run test:coverage

Useful monorepo commands:

bun run release:check
bun run publish:dry-run
bun run publish:regex-wand
bun run registry:check

Automated publishing lives in the monorepo release workflow: .github/workflows/release.yml.

The workflow publishes public GitHub releases to npm with provenance through npm trusted publishing. Local publishing remains a fallback for emergencies.

publish:regex-wand runs bun publish --access public in the package workspace and should only be used as a local fallback. Bump packages/regex-wand/package.json before publishing because npm does not allow republishing the same version.