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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@criteria/json-schema

v0.9.1

Published

TypeScript implementation of the JSON Schema specification.

Downloads

296

Readme

JSON Schema

TypeScript type definitions and functions for the JSON Schema specification.

Table of Contents

Overview

This package provides TypeScript type definitions and functions for working with JSON Schema documents. It is intended to be used by tooling authors, to build JSON Schema tools in TypeScript across various drafts of the specification easily.

This package also contains functions to dereference JSON Schema documents into plain JavaScript objects according to the reference resolution rules of the specification.

The following drafts are currently supported:

Getting Started

Install JSON Schema using npm:

npm install @criteria/json-schema

Let's define a simple JSON schema in code to get started:

import { JSONSchema } from '@criteria/json-schema/draft-2020-12'

const schema: JSONSchema = {
  type: 'object',
  title: 'person',
  properties: {
    name: {
      $ref: '#/$defs/requiredString'
    },
    email: {
      $ref: '#/$defs/requiredString'
    }
  },
  $defs: {
    requiredString: {
      title: 'requiredString',
      type: 'string',
      minLength: 1
    }
  }
}

Right now if we tried to inspect the type of the name or email properties we would get undefined:

console.log(schema.properties.name.type)
// undefined

We can use the dereferenceJSONSchema() function to transform our schema object with $ref values into plain JavaScript objects without any references:

import { JSONSchema, DereferencedJSONSchema, dereferenceJSONSchema } from '@criteria/json-schema/draft-2020-12'

const schema: JSONSchema = {
  type: 'object',
  title: 'person',
  properties: {
    name: {
      $ref: '#/definitions/requiredString'
    },
    email: {
      $ref: '#/definitions/requiredString'
    }
  },
  $defs: {
    requiredString: {
      title: 'requiredString',
      type: 'string',
      minLength: 1
    }
  }
}

const dereferencedSchema: DereferencedJSONSchema = dereferenceJSONSchema(schema)

console.log((dereferencedSchema.properties.name as DereferencedJSONSchemaObject).type)
// string

console.log((dereferencedSchema.properties.name as DereferencedJSONSchemaObject).minLength)
// 1

The return type of dereferenceJSONSchema(schema) is DereferencedJSONSchema instead of JSONSchema. This is a TypeScript type alias that replaces $ref keywords with their dereferenced values.

NOTE: In JSON Schema Draft 2020-12 true and false are valid JSON schemas, equivalent to {} and {"not": {}} respectively. This example uses type assertions to specify that the type of dereferencedSchema.properties.name is DereferencedJSONSchemaObject because we know the deferenced schemas are not boolean values. If we were loading this schema from an external source, you should check whether the schema or any subschema is a boolean value before trying to access any keywords.

Installation

npm install @criteria/json-schema

Usage

Importing type definitions

Type names are suffixed with the draft that they correspond to:

import { JSONSchemaDraft04, JSONSchemaDraft2020_12 } from '@criteria/json-schema'

const fooSchema: JSONSchemaDraft04 = {
  $schema: 'http://json-schema.org/draft-04/schema#'
}

const barSchema: JSONSchemaDraft2020_12 = {
  $schema: 'https://json-schema.org/draft/2020-12/schema'
}

If you only want to import types for a specific draft, you can import more concise types from the draft-specific module:

import { JSONSchema } from '@criteria/json-schema/draft-04'

const schema: JSONSchema = {
  $schema: 'http://json-schema.org/draft-04/schema#'
}
import { JSONSchema } from '@criteria/json-schema/draft-2020-12'

const schema: JSONSchema = {
  $schema: 'https://json-schema.org/draft/2020-12/schema'
}

The exclusiveMinimum and exclusiveMaximum keywords are an example of type differences between Draft 04 and Draft 2020-12. In Draft 04 they were boolean values, whereas in Draft 2020-12 they are now numbers to be consistent with the principle of keyword independence. Mixing these up is caught by the TypeScript compiler:

import { JSONSchemaDraft2020_12 } from '@criteria/json-schema'

const schema: JSONSchemaDraft2020_12 = {
  type: 'number',
  maximum: 100,
  exclusiveMaximum: true
}
// Type 'boolean' is not assignable to type 'number'.

Dereferencing schemas

Dereferencing refers to the process of transforming a JSON schema by replacing occurences of $ref with the actual subschema being referenced.

The dereferenceJSONSchema(schema) functions provided by this package observe the following behaviour:

  • The schema is treated as immutable. The return value is a copy of the input data.
  • Object identity is maintained. The dereferenced schema is the same JavaScript instance as the value that was referenced.
  • Circular references are preserved. Recursive or circular references in the input schema will be replicated in the dereferenced output.

The following example demonstrates this behaviour:

import { dereferenceJSONSchema } from '@criteria/json-schema/draft-2020-12'

const inputSchema: JSONSchema = {
  type: 'object',
  title: 'person',
  properties: {
    name: {
      $ref: '#/definitions/requiredString'
    },
    children: {
      type: 'array',
      items: {
        $ref: '#'
      }
    }
  },
  $defs: {
    requiredString: {
      title: 'requiredString',
      type: 'string',
      minLength: 1
    }
  }
}

const dereferencedSchema = dereferenceJSONSchema(inputSchema)

console.log(dereferencedSchema === inputSchema)
// false, input data is not mutated

console.log(dereferencedSchema.properties.name === dereferencedSchema.$defs.requiredString)
// true, object identity is maintained

console.log(dereferencedSchema.properties.children.items === dereferencedSchema)
// true, circular references are supported

A $ref property may contain an absolute URI or a URI reference that is resolved against the containing schema's base URI. The way $ref values are resolved is unique to each draft.

In all drafts, objects that only contain a $ref property are wholly replaced with the referenced value.

Draft 2020-12 notes

In Draft 2020-12, $ref keywords may appear alongside other keywords. This changes the semantics of $ref from transclusion to delegation since the referencing and referenced schemas can contain the same keywords. In these situations dereferenceJSONSchema(schema) will not replace the $ref keyword, but it will replace the URI reference value with the actual dereferenced object:

For example, given the following schema:

{
  $defs: {
    alphanumericWithInitialLetter: {
      $ref: '#/$defs/alphanumeric',
      pattern: '^[a-zA-Z]'
    },
    alphanumeric: {
      type: 'string',
      pattern: '^[a-zA-Z0-9]*$'
    }
  }
}

Deferencing the schema would produce:

{
  $defs: {
    alphanumericWithInitialLetter: {
      $ref: {
        type: 'string',
        pattern: '^[a-zA-Z0-9]*$'
      },
      pattern: '^[a-zA-Z]'
    },
    alphanumeric: {
      type: 'string',
      pattern: '^[a-zA-Z0-9]*$'
    }
  }
}

Draft 04 notes

In Draft 04, having additional keywords alongside an occurance of $ref is not supported. Nonetheless, this package will attempt to dereference such situations according to best efforts and to maintain parity with other tools such as @apidevtools/json-schema-ref-parser.

For example, given the following schema:

{
  $defs: {
    alphanumericWithInitialLetter: {
      $ref: '#/$defs/alphanumeric',
      pattern: '^[a-zA-Z]'
    },
    alphanumeric: {
      type: 'string',
      pattern: '^[a-zA-Z0-9]*$'
    }
  }
}

Deferencing the schema would produce:

{
  $defs: {
    alphanumericWithInitialLetter: {
      type: 'string',
      pattern: '^[a-zA-Z]'
    },
    alphanumeric: {
      type: 'string',
      pattern: '^[a-zA-Z0-9]*$'
    }
  }
}

Notice how the pattern keyword from the referencing schema shadows the equivalent in the referenced schema.

Retrieving external documents

It's common for schema authors to organize complex schemas as multiple shared files. You can provide a custom retrieve function to dereferenceJSONSchema() in order to perform an filesystem or network operations.

The following example dereferences a JSON schema that references other files within a shared-schemas directory.

import { readFileSync } from 'fs'

const baseURI = resolve(__dirname, 'shared-schemas/root.json')

const retrieve = (uri: string) => {
  const data = readFileSync(uri, { encoding: 'utf8' })
  return JSON.parse(data)
}

const dereferencedSchema = dereferenceJSONSchema(rootSchema, { baseURI, retrieve })

The argument passed to your retrieve function is a URI that has been resolved according to the specification rules. Note that this URI is an identifier and not necessarily a network locator. In the case of a network-addressable URL, a schema need not be downloadable from its canonical URI.

Definining additional vocabularies

Note: This is only supported in Draft 2020-12.

Schema authors can require additional vocabularies not defined by the Core/Validation Dialect meta-schema in order to process the schema.

Tooling can extend the base JSONSchema type to provide type safety for additional vocabularies:

import { JSONSchema } from '@criteria/json-schema'

interface MyValidationKeywords {
  foo: boolean
}

type MyJSONSchema = JSONSchema<MyValidationKeywords>

const schema: MyJSONSchema = {}
// Property 'foo' is missing...

Passing the type to JSONSchema's generic argument ensures that the additional vocabulary is also applied to all subschemas.

Acknowledgments

Many test cases have been ported from @apidevtools/json-schema-ref-parser.

License

This project is MIT licensed.

About Criteria

Criteria is a collaborative platform for designing APIs.