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

shape

v11.4.1

Published

An object shape validation utility.

Readme

shape (TypeScript / JavaScript)

The canonical implementation of shape, a schema-by-example validator: your schema looks (almost) exactly like your data.

const { Shape } = require('shape')

const shape = Shape({
  port: 8080,        // optional, defaults to 8080, must be a number
  host: 'localhost', // optional, defaults to 'localhost', must be a string
  debug: Boolean,    // required, must be a boolean
})

shape({ debug: true })
// → { debug: true, port: 8080, host: 'localhost' }

shape({ debug: 'yes' })
// throws ShapeError: Validation failed for property "debug" with string "yes"
//   because the string is not of type boolean.

Literal values are optional with a default; the wrapper constructors (String, Number, Boolean, Object, Array, Function, Date) are required type markers. Objects and arrays fill out and validate to any depth. There are no dependencies.

This package defines the behaviour; the Go port and the Rust port match it exactly, held there by a shared conformance corpus and a differential harness. The full documentation is in ../docs; this file is the TypeScript surface in one place.

Install

npm install shape
const { Shape, Min, Optional } = require('shape')   // CommonJS
import { Shape, Min, Optional } from 'shape'         // ESM / TypeScript

Node 20+. Type declarations ship with the package. Bundlers pick up the CommonJS build, with Node's util swapped for a stub by the browser field; a minified standalone bundle, dist/shape.min.js, exposes a global Shape for a plain script tag. See the browser how-to.

Using a shape

const shape = Shape(spec, options?)

shape(value, ctx?)         // the produced value, with defaults injected; throws on failure
shape.match(value)         // boolean, no mutation
shape.valid(value)         // boolean (a type guard in TypeScript)
shape.error(value)         // the issues, [{ path, why, text, … }] (empty when valid)
shape.spec()               // a JSON-friendly description of the compiled shape
shape.node()               // the compiled root node
shape.stringify()          // the shape as DSL-ish text
shape.json()               // the shape as declarative JSON
Shape.build(shape.json())  // and back: the same shape again
shape.jsonSchema()         // a JSON Schema (draft 2020-12) for the values accepted
Shape(fromJsonSchema(doc)) // and back: a spec built from a JSON Schema
shape['~standard']         // a Standard Schema V1 validator

Shape mutates the input to inject defaults; pass a fresh object if you need the original kept. Pass { err: [] } as ctx to collect errors instead of throwing. See the Shape API and errors.

Builders

Every builder is a named export, a property of Shape, and—except One, Some, All, Exact and Discriminated—a chainable method on a node. G-prefixed aliases (GMin, GPick, …) avoid clashes with local names.

const { Shape, Required, Optional, Min, Max, Email, Exact, Coerce, One, Open } = require('shape')

Shape({
  name:  Min(1, String),                 // required, at least one character
  age:   Coerce(Min(0, Max(120, Number))),  // "42" is accepted as 42
  email: Email,                          // a required email address
  role:  Exact('admin', 'user'),         // one of these
  tags:  Optional([String]),             // an optional array of strings
  id:    One(Number, String),            // either kind
  addr:  Open({ city: String }),         // other keys allowed
})

Required(Number).Min(2)                  // the same builders, chained

| Group | Builders | | ----- | -------- | | Required / optional / defaults | Required Optional Default Skip Ignore Empty Nullable Fault | | Type / equality | Type Integer Date Exact Never Func Any | | Coercion | Coerce—a decimal string to a number, "true"/"1" to a boolean, a number or boolean to a string, an ISO 8601 string or millisecond count to a Date | | String formats | Email Url Uuid DateTime Ip Ipv4 Ipv6 | | Bounds | Min Max Above Below Len—value for numbers, length for strings, arrays and objects | | Custom checks | Check (a function or a RegExp) Before After | | Isolation | Catch(fallback, …) Transform(fn, …) Describe(text, …) | | Composition | One Some All Discriminated(tag, { … }) | | Objects / arrays | Open Closed Child Rest | | Object algebra | Pick Omit Partial Extend—each builds a new object shape out of another | | References | Define Refer Rename | | Misc | Key |

The builder reference has the semantics of each.

Key expressions and the string DSL

A property key of the form "name: <expression>" applies builders to the value, which is the example the expression works on:

Shape({
  'name: Min(1)':          String,
  'port: Optional(Number)': 8080,
  'user: Pick(["id"])':    { id: Number, name: String },
})

expr(source) compiles one expression into a node, and build(value) compiles a JSON structure whose string leaves are expressions, returning the shape:

const { Shape, expr, build } = require('shape')

Shape(expr('String.Min(2).Max(10)'))
build({ name: 'Min(1,String)', tags: ['String'] })   // a compiled shape

See key and value expressions and the string DSL.

TypeScript

Shape(spec) infers the produced type from the spec, through every builder: Min(1, String) is string, Exact('a', 'b') is 'a' | 'b', Skip(Number) is number | undefined, a discriminated union is a union of its branches, and a key expression 'port: Max(9)' is the property port. The exported types are Node, Context, Update, State, Validate, Builder, ShapeShape and StandardSchemaV1. See TypeScript types.

Development

npm install
npm run build      # tsc: src → dist, test → dist-test (both git-ignored), then the browser bundle
npm run build-web  # esbuild: src/shape.web.js → dist/shape.min.js (a global Shape)
npm test           # node --test over dist-test
node --test --experimental-test-coverage dist-test/*.test.js   # the coverage gate, Node 22+

src/shape.ts is the whole library. The suite is held at 100% line coverage of dist/shape.js; a genuinely non-exercisable branch may carry a /* node:coverage disable */ pragma with a one-line reason. npm test also runs the shared corpus in ../test/*.tsv, whose expected columns are generated from this build:

npm run build && node ../test/gen-compat.js    # regenerate the corpus
make -C .. test                                # all three languages must pass it
make -C .. diff                                # the differential harness

A behaviour change starts here and is then mirrored in Go and Rust—see ../AGENTS.md.

License

Copyright (c) 2021-2024 Richard Rodger and other contributors. Licensed under MIT.