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

@ascendance-hub/sapphire-json-schema

v1.0.0

Published

JSON Schema 2020-12 adapter for Sapphire.

Readme

@ascendance-hub/sapphire-json-schema

JSON Schema 2020-12 adapter for Sapphire. Converts a Sapphire IR (SapphireSchemaNode) into a JSON Schema object suitable for AJV, MCP tool input schemas, frontend form generators, and cross-language validators.

Install

npm install @ascendance-hub/sapphire-core @ascendance-hub/sapphire-json-schema

@ascendance-hub/sapphire-core is a peer dependency. There are no other peer requirements — pick whichever JSON Schema validator you like (AJV, Hyperjump, browser-based, etc.).

Register the adapter

The adapter is not auto-registered. Call registerAdapter once in your application entry point:

import { Sapphire, registerAdapter } from '@ascendance-hub/sapphire-core'
import { toJsonSchema } from '@ascendance-hub/sapphire-json-schema'

registerAdapter('json-schema', toJsonSchema)

export const a = new Sapphire({ defaultAdapter: 'json-schema' })

Quickstart

import Ajv2020 from 'ajv/dist/2020.js'
import { toJsonSchema } from '@ascendance-hub/sapphire-json-schema'
import { a } from './sapphire'

const CreateUser = a
  .object({
    name: a.string().min(1),
    email: a.string().email(),
    age: a.number().int().min(0).optional(),
  })
  .name('CreateUser')

const schema = toJsonSchema(CreateUser.toSchema(), {
  $id: 'https://example.com/schemas/create-user',
})

const ajv = new Ajv2020({ strict: false })
const validate = ajv.compile(schema)
validate({ name: 'Ada', email: '[email protected]' }) // → true

The output is a plain JSON Schema 2020-12 object. Use it as an MCP tool's inputSchema, feed it to react-jsonschema-form, or share it with a Python validator — same shape everywhere.

IR mapping table

| IR kind | JSON Schema output | Notes | | --------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | string | { type: 'string', ... } | format (email/uri/uuid); Sapphire's url is emitted as uri. startsWith/endsWith become escaped patterns; combined with regex they merge into allOf. | | number | { type: 'number' \| 'integer', ... } | int()'integer'. min/max/exclusiveMin/exclusiveMax/multipleOf map to JSON Schema keywords. finite/safe are no-ops (no JSON Schema equivalent). | | boolean | { type: 'boolean' } | — | | date | { type: 'string', format: 'date-time' } | min/max (Date) are no-ops; AJV requires ajv-formats to validate date-time. | | object | { type: 'object', properties, required, ... } | required derived from child.required === true. Named objects emit a $ref; the body lives in top-level $defs. | | array | { type: 'array', items, minItems, maxItems } | nonempty()minItems: 1. length(n)minItems: maxItems: n. | | tuple | { type: 'array', prefixItems, items: false, min/max } | 2020-12-only shape. Use Ajv2020 for compilation. | | union | { oneOf: [...] } | oneOf (exactly one) — semantically discriminated. | | literal | { const: value } | — | | enum | { type, enum: [...values] } | type is 'string' or 'number' based on the values. | | record | { type: 'object', additionalProperties, propertyNames? } | propertyNames is emitted only when the key field carries real constraints. | | ref | { $ref: '#/$defs/<target>' } | Cyclic refs are supported natively in 2020-12. |

Schema-level

  • ObjectField.timestamps() and ObjectField.index([...]) → no-op (database semantics, not validation).
  • ObjectField.adapter('json-schema', { ... }) → see escape hatch.

.adapter('json-schema', opts) escape hatch

Anything passed via .adapter('json-schema', { ... }) is read from meta['json-schema'] and merged onto the resulting schema (last-wins). Useful keys:

| Key | Effect | | ---------------------- | ----------------------------------------------------------------------------------- | | format: 'ipv4' etc. | Custom format strings (require ajv-formats or equivalent to validate at runtime). | | contentEncoding | Annotation for binary/base64 fields. | | additionalProperties | Per-object override; pair with the global default in JsonSchemaAdapterOptions. | | examples | Annotation array surfaced by docs/UI generators. |

Blacklist: type and $ref cannot be overridden. They are always Sapphire-controlled.

JsonSchemaAdapterOptions

| Option | Default | Effect | | ---------------------- | ------- | ----------------------------------------------------------------------------------- | | additionalProperties | omitted | Default value emitted on every object schema. JSON Schema's spec default is true. | | $id | omitted | Top-level $id. | | defs | {} | Extra named schemas to include in $defs beyond what the walker auto-collects. | | emitSchemaUri | true | Whether to emit $schema: 'https://json-schema.org/draft/2020-12/schema'. |

toJsonSchema(node, {
  additionalProperties: false,
  $id: 'https://example.com/schemas/user',
  defs: { ExtraType: someNode },
})

Limitations

  • transforms/coerce no-op. JSON Schema is descriptive, not an engine. Apply transforms via core (safeParse) before persisting.
  • unique/index/timestamps/composite indexes no-op. These are database concerns, not validation.
  • nullable rewrites type to a union (type: [<x>, 'null']) for plain primitives, or oneOf: [original, { type: 'null' }] for composite/$ref/enum/literal cases.
  • name characters are limited to [A-Za-z0-9_-]+. JSON Pointer escaping for / and ~ is not performed; names with those characters throw at build time.
  • prefixItems is 2020-12-only. Compile tuples with Ajv2020 (ajv/dist/2020.js); the default 8.x Ajv constructor uses draft-07 and will not understand them.
  • format is annotation-only in AJV out-of-the-box. Install ajv-formats if you want runtime checking of email/uri/uuid/date-time etc.
  • number.finite/number.safe no-op. No JSON Schema equivalent.
  • Auto-register removed. Call registerAdapter('json-schema', toJsonSchema) once in your entry point.

License

MIT