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

@optique/valibot

v1.2.0

Published

Valibot value parsers for Optique

Readme

@optique/valibot

Valibot value parsers for Optique. This package provides seamless integration between Valibot schemas and @optique/core, enabling powerful validation and type-safe parsing of command-line arguments with minimal bundle size.

Installation

deno add --jsr @optique/valibot @optique/run @optique/core @valibot/valibot
npm  add       @optique/valibot @optique/run @optique/core valibot
pnpm add       @optique/valibot @optique/run @optique/core valibot
yarn add       @optique/valibot @optique/run @optique/core valibot
bun  add       @optique/valibot @optique/run @optique/core valibot

[!NOTE] When using Deno, import Valibot from @valibot/valibot instead of valibot:

import * as v from "@valibot/valibot";  // Deno
import * as v from "valibot";            // Node.js, Bun

This package supports Valibot versions 0.42.0 and above.

Quick example

The following example uses the valibot() value parser to validate an email address.

import { run } from "@optique/run";
import { object } from "@optique/core/constructs";
import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

const cli = run(
  object({
    email: option("--email",
      valibot(v.pipe(v.string(), v.email()), { placeholder: "" }),
    ),
  }),
);

console.log(`Welcome, ${cli.email}!`);

Run it:

$ node cli.js --email [email protected]
Welcome, [email protected]!

$ node cli.js --email invalid-email
Error: Invalid email

Common use cases

Email validation

import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

const email = option("--email",
  valibot(v.pipe(v.string(), v.email()), { placeholder: "" }),
);

URL validation

import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

const url = option("--url",
  valibot(v.pipe(v.string(), v.url()), { placeholder: "" }),
);

Port numbers with range validation

[!IMPORTANT] Always use explicit transformations with v.pipe() and v.transform() for non-string types, since CLI arguments are always strings.

import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

const port = option("-p", "--port",
  valibot(v.pipe(
    v.string(),
    v.transform(Number),
    v.number(),
    v.integer(),
    v.minValue(1024),
    v.maxValue(65535)
  ), { placeholder: 0 }),
);

Enum choices

import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

const logLevel = option("--log-level",
  valibot(v.picklist(["debug", "info", "warn", "error"]),
    { placeholder: "debug" }),
);

Date transformations

import { argument } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

const startDate = argument(
  valibot(v.pipe(v.string(), v.transform((s: string) => new Date(s))),
    { placeholder: new Date(0) }),
);

Async schemas

Use valibotAsync() when a schema depends on async validations, and run the containing parser with runAsync() or await run():

import { runAsync } from "@optique/run";
import { object } from "@optique/core/constructs";
import { option } from "@optique/core/primitives";
import { valibotAsync } from "@optique/valibot";
import * as v from "valibot";

async function checkProject(value: string): Promise<boolean> {
  return await Promise.resolve(value.startsWith("project-"));
}

const parser = object({
  project: option("--project",
    valibotAsync(
      v.pipeAsync(v.string(), v.checkAsync(checkProject, "Unknown project.")),
      { placeholder: "" },
    ),
  ),
});

const cli = await runAsync(parser);

The valibot() helper remains synchronous and rejects schemas that require Valibot's async parse path. valibotAsync() preserves metavar inference, choices, suggestions, formatting, and custom errors. Fallback values from bindEnv() or bindConfig() are validated by the same schema before they are accepted.

Async validation can run during fallback resolution and other repeated parser paths, including shell completion requests. Keep remote checks bounded and cached when possible.

Custom error messages

You can customize error messages using the errors option:

import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import { message } from "@optique/core/message";
import * as v from "valibot";

const email = option("--email", valibot(v.pipe(v.string(), v.email()), {
  placeholder: "",
  metavar: "EMAIL",
  errors: {
    valibotError: (issues, input) =>
      message`Please provide a valid email address, got ${input}.`
  }
}));

Important notes

Always use explicit transformations for non-string types

CLI arguments are always strings. If you want to parse numbers, booleans, or other types, you must use explicit v.transform():

import { option } from "@optique/core/primitives";
import { valibot } from "@optique/valibot";
import * as v from "valibot";

// ✅ Correct
const port = option("-p",
  valibot(v.pipe(v.string(), v.transform(Number)), { placeholder: 0 }),
);

// ❌ Won't work (CLI arguments are always strings)
// const port = option("-p", valibot(v.number()));

valibot() is synchronous

The valibot() helper returns a sync value parser, so async Valibot features like pipeAsync() require valibotAsync():

import { option } from "@optique/core/primitives";
import { valibot, valibotAsync } from "@optique/valibot";
import * as v from "valibot";

// ❌ Not supported by valibot()
const syncEmail = option("--email",
  valibot(v.pipeAsync(v.string(),
    v.checkAsync(async (val) => await checkDB(val))),
    { placeholder: "" }),
);

// ✅ Use valibotAsync() and runAsync()
const asyncEmail = option("--email",
  valibotAsync(v.pipeAsync(v.string(),
    v.checkAsync(async (val) => await checkDB(val))),
    { placeholder: "" }),
);

For more resources