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

baobox

v0.1.1-rc.4

Published

TypeScript-native schema library optimised for Bun — a lean, Bun-first reimagining of TypeBox

Readme

Baobox

CI License Bun TypeScript

Baobox is a Bun-first, TypeScript-first schema library that keeps the familiar TypeBox-style authoring surface while adding result-first runtime APIs, scoped runtime contexts, compile caching, portable validator artifacts, and schema-interop helpers.

ELI5

  • You describe what valid data looks like once.
  • Baobox can then answer different questions with the same schema:
    • Is this value valid: Check
    • Can you clean and coerce it safely: TryParse
    • What exactly failed: Explain or Errors
    • Can I reuse this on a hot path: CompileCached
    • Can another tool consume the same schema: StandardSchemaV1
  • If you do not want exception-driven control flow, use the Try* family.

Why Baobox

  • All object properties are required by default, matching TypeBox behavior. Use Optional() to mark individual properties as optional.
  • TypeBox-compatible root surface with baobox-only additions at the root entrypoint.
  • Result-first runtime helpers: TryParse, TryDecode, TryEncode, TryCreate, and TryRepair.
  • Scoped runtime contexts so locale catalogs, registries, settings, and compile caches do not have to be process-global.
  • Compiled validators with cache reuse and portable Validator.Artifact() output.
  • Standard Schema V1 adapters for typed baobox schemas and raw JSON-schema-style objects.
  • Built-in codecs for common interop-heavy values: Uint8Array, Date, URL, and bigint.
  • Localized validation errors with official bundles for every declared locale code through baobox/locale.

Install

bun add baobox

Requirements:

  • bun >= 1.3.11
  • typescript >= 6.0.0

Quick Start

import Type, {
  Check,
  CompileCached,
  DateCodec,
  StandardSchemaV1,
  TryDecode,
  TryParse,
} from 'baobox'

const User = Type.Object({
  id: Type.String(),
  email: Type.String({ format: 'email' }),
  age: Type.Number({ minimum: 0 }),
})

Check(User, { id: 'usr_1', email: '[email protected]', age: 37 })
// true

TryParse(Type.Object({ count: Type.Number() }), { count: '5' })
// { success: true, value: { count: 5 } }

const validator = CompileCached(User)
validator.Check({ id: 'usr_1', email: '[email protected]', age: 37 })
// true

TryDecode(DateCodec(), '2024-01-01T00:00:00.000Z')
// { success: true, value: new Date('2024-01-01T00:00:00.000Z') }

const StandardUser = StandardSchemaV1(User)
StandardUser['~standard'].validate({ id: 'usr_1', email: '[email protected]', age: '37' })
// { value: { id: 'usr_1', email: '[email protected]', age: 37 } }

Required and Optional Properties

All object properties are required by default. Use Optional() to mark individual properties as optional:

import Type, { Check } from 'baobox'

const User = Type.Object({
  name: Type.String(),                    // required
  email: Type.String({ format: 'email' }), // required
  bio: Type.Optional(Type.String()),       // optional
})

Check(User, { name: 'Ada', email: '[email protected]' })
// true — bio is optional

Check(User, { name: 'Ada' })
// false — email is required

Clean() strips extra properties not defined in the schema by default. Set additionalProperties: true to keep them:

import { Clean, Object, String } from 'baobox'

Clean(Object({ name: String() }), { name: 'Ada', extra: true })
// { name: 'Ada' } — extra is stripped

API Map

| Problem | API | Result | | --- | --- | --- | | Fast pass/fail validation | Check(schema, value) | boolean | | Normalize without exceptions | TryParse(schema, value) | ParseResult<T> | | Throwing parity path | Parse(schema, value) | normalized value or ParseError | | Codec decode without exceptions | TryDecode(schema, value) | ParseResult<T> | | Codec encode without exceptions | TryEncode(schema, value) | ParseResult<T> | | Default generation without exceptions | TryCreate(schema) | ParseResult<T> | | Repair without exceptions | TryRepair(schema, value) | ParseResult<T> | | Raw issue metadata plus localized messages | Explain(schema, value) | diagnostics array | | Reusable hot-path validator | Compile(schema) or CompileCached(schema) | Validator | | Reload a prebuilt validator body | CompileFromArtifact(schema, artifact) | Validator | | TypeBox-compatible error iterator | ErrorsIterator(schema, value) | IterableIterator<ValueError> | | First validation error or undefined | First(schema, value) | ValueError \| undefined | | Extract inferred TypeScript type | Static<T> | TypeScript type | | Extract decoded type from codec | StaticDecode<T> | TypeScript type | | Extract encoded type from codec | StaticEncode<T> | TypeScript type | | Adapt to Standard Schema V1 | StandardSchemaV1(schema) | standard-compatible wrapper |

Result-First Runtime

Baobox keeps Parse(schema, value) as the upstream-style throwing path, but the default baobox direction is result-first:

  • TryParse runs Clone -> Default -> Convert -> Clean -> Check
  • TryDecode and TryEncode apply codec transforms without exception control flow
  • TryCreate and TryRepair make default-generation and corrective flows explicit
  • Explain preserves issue codes, params, locale, and final message

If you are writing request handling, config loading, env parsing, or service boundaries, the Try* APIs are usually the better fit.

Compiled Validators

Compile() builds a reusable validator object. CompileCached() adds per-runtime-context cache reuse. CompileFromArtifact() reloads previously emitted validator code.

import { CompileCached, CompileFromArtifact, Number, Object } from 'baobox'

const schema = Object({
  count: Number({ minimum: 1 }),
})

const validator = CompileCached(schema)
const artifact = validator.Artifact()
const loaded = CompileFromArtifact(schema, artifact)

loaded.TryParse({ count: '2' })
// { success: true, value: { count: 2 } }

Technical details:

  • compile caching is scoped to the active runtime context
  • artifacts let you ship emitted validator code instead of regenerating it at startup
  • Bun-specific byte-oriented fast paths are used only when the schema shape makes that safe

Standard Schema Interop

Baobox can expose the same validation logic through the Standard Schema V1 contract.

import { FromJsonSchema, StandardSchemaV1, Type, ToStandardSchema } from 'baobox'

const typed = StandardSchemaV1(Type.Object({
  name: Type.String(),
  age: Type.Number(),
}))

const raw = FromJsonSchema({
  type: 'object',
  properties: {
    name: { type: 'string' },
    age: { type: 'number' },
  },
  required: ['name', 'age'],
})

ToStandardSchema(raw)['~standard'].validate({ name: 'Ada', age: 37 })

Use:

  • StandardSchemaV1() when you want the canonical baobox adapter
  • ToStandardSchema() when the input may already be typed or raw
  • FromJsonSchema() when the source is explicitly a raw JSON-schema-style object

For schema-agnostic integrations, you can also import from baobox/standard.

Locales And Runtime Contexts

The default runtime context is preloaded with an official bundle for every declared locale code. Native translated catalogs currently ship for de_DE, en_US, the Spanish family, the French family, ja_JP, ko_KR, the Portuguese family, and both Chinese packs. Remaining official bundles currently alias the English catalog until native translations are added.

import LocalePacks from 'baobox/locale'
import { CreateRuntimeContext, Errors, LocaleCodes, String } from 'baobox'
import { System } from 'baobox/system'

System.Locale.Set(System.Locale.ko_KR)
Errors(String(), 42)
// [{ path: '/', code: 'INVALID_TYPE', message: 'string이어야 합니다. 현재 값 유형: number' }]

const context = CreateRuntimeContext({ localeCatalogs: [] })
context.Locale.Register(LocaleCodes.it_IT, LocalePacks.it_IT)
context.Locale.Set(LocaleCodes.it_IT)

Errors(String(), 42, context)
// [{ path: '/', code: 'INVALID_TYPE', message: 'Expected string, got number' }]

Use runtime contexts when you need:

  • per-test or per-worker isolation
  • tenant-specific registries or locale catalogs
  • explicit compile-cache boundaries
  • non-global settings and type-policy changes

Elysia Integration

Baobox ships a dedicated Elysia adapter at baobox/elysia. It re-exports all type builders through a t namespace that automatically stamps the [Kind] symbol property required by Elysia's TypeBox 0.x type guards, alongside the standard '~kind' string.

import { t } from 'baobox/elysia'

const UserBody = t.Object({
  name: t.String({ minLength: 1 }),
  email: t.Email(),
  role: t.Enum(['admin', 'user']),
})

// Use with Elysia routes:
// app.post('/user', handler, { body: UserBody })

The adapter also re-exports Value, Compile, and decorateSchema() for manual decoration of custom schemas. Eden Treaty type inference works because the t.* wrappers preserve full generic type parameters.

TypeBox Migration

The baobox migrate CLI rewrites TypeBox imports and API calls to their baobox equivalents.

# Preview changes without writing files
baobox migrate --dry-run --path ./src

# Apply changes
baobox migrate --write --path ./src

The migration tool:

  • Rewrites @sinclair/typebox imports to baobox equivalents (including /value, /compiler, /system, /format, /guard)
  • Transforms TypeCompiler.Compile() to Compile() and TypeCompiler.Code() to Code()
  • Transforms all Value.* calls: Value.Check, Value.Clean, Value.Convert, Value.Create, Value.Default, Value.Decode, Value.Encode, Value.Parse, Value.Assert, Value.Diff, Value.Patch, Value.Hash, Value.Equal, Value.Clone, Value.Repair
  • Flags patterns that need manual review: [Kind] symbol usage, Value.Errors() iterator differences, TypeSystemPolicy, FormatRegistry.Set, TypeRegistry.Set
  • Use --include-js to also scan .js, .jsx, .mjs, and .cjs files

For incremental migration, the baobox/typebox subpath provides a TypeBox-compatible surface:

import { Value, Compile, Object, String } from 'baobox/typebox'

Built-In Codecs

Baobox ships codec helpers for common wire-format boundaries:

  • Uint8ArrayCodec() for base64 JSON payloads and runtime byte arrays
  • DateCodec() for ISO datetime strings to Date
  • URLCodec() for string to URL
  • BigIntCodec() for integer-string to bigint

These work with the same value and compile APIs as ordinary schemas.

Static Type Inference

Extract TypeScript types from schemas at compile time:

import Type, { type Static, type StaticDecode, type StaticEncode } from 'baobox'

const User = Type.Object({
  name: Type.String(),
  age: Type.Optional(Type.Integer()),
})

type User = Static<typeof User>
// { name: string; age?: number | undefined }

StaticDecode<T> and StaticEncode<T> extract the decoded/encoded types for schemas with codecs:

const Schema = Type.Codec(Type.String())
  .Decode((v: string) => parseInt(v, 10))
  .Encode((v: number) => String(v))

type Decoded = StaticDecode<typeof Schema> // number
type Encoded = StaticEncode<typeof Schema> // string

Supported types include TFunction, TConstructor, TPromise, TIterator, TAsyncIterator, TTemplateLiteral, and all standard schema kinds.

Immutable and Refine Wrappers

Immutable() marks a schema as deeply readonly at the type level. Refine() adds custom validation predicates:

import Type, { Check } from 'baobox'

const Config = Type.Immutable(Type.Object({
  port: Type.Integer({ minimum: 1, maximum: 65535 }),
  host: Type.String(),
}))

const PositiveNumber = Type.Refine(
  Type.Number(),
  (value) => (value as number) > 0,
  'Must be positive',
)

Check(PositiveNumber, 5)   // true
Check(PositiveNumber, -1)  // false

Both wrappers are supported across all value operations: Check, Clean, Convert, Create, Default, Decode, Encode, and Repair.

Codec Builder Pattern

Build type-safe encode/decode transforms with the chainable Codec builder:

import Type from 'baobox'
import { Decode, Encode } from 'baobox/value'

const DateString = Type.Codec(Type.String())
  .Decode((v: string) => new Date(v))
  .Encode((v: Date) => v.toISOString())

Decode(DateString, '2024-01-01T00:00:00Z')
// Date('2024-01-01T00:00:00Z')

Encode(DateString, new Date('2024-01-01'))
// '2024-01-01T00:00:00.000Z'

Public Entrypoints

| Entrypoint | Purpose | | --- | --- | | baobox | Root builders, value helpers, compile helpers, and baobox additions | | baobox/type | Type builders and static type exports | | baobox/value | Runtime value operations: Check, Parse, TryParse, Clean, Convert, Create, Default, Decode, Encode, Assert, Errors, ErrorsIterator, First, Repair, Diff, Patch, Clone, Equal, Hash, Mutate, Pointer, Pipeline, HasCodec | | baobox/schema | Raw schema runtime plus baobox schema-emitter helpers | | baobox/error | Structured validation error surface | | baobox/compile | Compile, Code, and Validator | | baobox/format | Format registry and format helpers | | baobox/guard | Guard namespaces aligned with the TypeBox-style guard surface | | baobox/system | Runtime settings, locale, hashing, memory, and environment helpers | | baobox/script | Script DSL helpers | | baobox/locale | Official locale bundles for the declared locale registry | | baobox/standard | Standard Schema V1 adapter helpers | | baobox/typebox | TypeBox-compatible re-export surface with Value and Compile | | baobox/elysia | Elysia adapter with [Kind] symbol decoration and t namespace |

Published consumers should only rely on package entrypoints. Direct src/* imports are internal to this repository and its tests.

Guides

Repository Scripts

bun run build
bun run typecheck
bun test
bun run bench

bun run bench compares validation and codec throughput against the installed typebox package so performance numbers stay tied to the current upstream implementation.

License

MIT