@traversable/schema
    Preparing search index...

    Module @traversable/json-schema - v0.0.9


    แฏ“๐˜๐—ฟ๐—ฎ๐˜ƒ๐—ฒ๐—ฟ๐˜€๐—ฎ๐—ฏ๐—น๐—ฒ/๐—ท๐˜€๐—ผ๐—ป-๐˜€๐—ฐ๐—ต๐—ฒ๐—บ๐—ฎ


    @traversable/json-schema is a schema rewriter for JSON Schema specs.

    NPM Version   TypeScript   License   npm  
    Static Badge   Static Badge   Static Badge  


    Note

    Currently this package only supports JSON Schema Draft 2020-12

    $ pnpm add @traversable/json-schema
    

    Here's an example of importing the library:

    import { JsonSchema } from '@traversable/json-schema'

    // or, if you prefer, you can use named imports:
    import { deepClone, deepEqual } from '@traversable/json-schema'

    // see below for specific examples

    JsonSchema.check converts a JSON Schema into a super-performant type-guard.

    • Consistently better performance than Ajv
    • Works in any environment that supports defining functions using the Function constructor, including (as of May 2025) Cloudflare workers ๐ŸŽ‰

    Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ Average โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ Ajv โ”‚ 1.57x faster โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    import { JsonSchema } from '@traversable/json-schema'

    const check = JsonSchema.check({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    })

    check({ street1: '221B Baker St', city: 'London' }) // => true
    check({ street1: '221B Baker St' }) // => false

    JsonSchema.check converts a JSON Schema into a super-performant type-guard.

    Compared to JsonSchema.check, JsonSchema.check.writeable returns the check function in stringified ("writeable") form.

    • Useful when you're consuming a set of JSON Schemas schemas and writing them all to disc
    • Also useful for testing purposes or for troubleshooting, since it gives you a way to "see" exactly what the check functions check
    import { JsonSchema } from '@traversable/json-schema'

    const check = JsonSchema.check.writeable({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    }, { typeName: 'Address' })

    console.log(check)
    // =>
    // type Address = { street1: string; street2?: string; city: string }
    // function check(value: Address) {
    // return (
    // !!value &&
    // typeof value === "object" &&
    // typeof value.street1 === "string" &&
    // (!Object.hasOwn(value, "street2") || typeof value.street2 === "string") &&
    // typeof value.city === "string"
    // )
    // }

    JsonSchema.deepClone lets users derive a specialized "deep copy" function that works with values that have been already validated.

    Because the values have already been validated, clone times are significantly faster than alternatives like window.structuredClone and Lodash.cloneDeep.

    Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ Average โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ Lodash.cloneDeep โ”‚ 13.99x faster โ”‚
    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ window.structuredClone โ”‚ 17.23x faster โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

    This article goes into more detail about what makes JsonSchema.deepClone so fast.

    Click to see the detailed benchmark summary
    Lodash                       868.72 ns/iter   1.00 ยตs                 โ–‡โ–ˆ
    (269.22 ns โ€ฆ 1.20 ยตs) 1.14 ยตs โ–ˆโ–ˆ
    ( 8.05 b โ€ฆ 963.18 b) 307.93 b โ–ƒโ–‚โ–โ–โ–โ–โ–†โ–…โ–‡โ–ƒโ–‚โ–โ–โ–‚โ–ƒโ–ˆโ–ˆโ–ˆโ–…โ–ƒโ–
    3.64 ipc ( 1.47% stalls) 98.24% L1 data cache
    2.41k cycles 8.77k instructions 38.66% retired LD/ST ( 3.39k)

    structuredClone 1.07 ยตs/iter 1.08 ยตs โ–„โ–ˆโ–„
    (1.02 ยตs โ€ฆ 1.24 ยตs) 1.22 ยตs โ–ƒโ–ˆโ–ˆโ–ˆโ–„โ–‚
    ( 13.91 b โ€ฆ 369.62 b) 38.79 b โ–โ–…โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–„โ–‚โ–„โ–ƒโ–โ–‚โ–‚โ–‚โ–‚โ–‚โ–โ–โ–
    4.35 ipc ( 1.33% stalls) 98.23% L1 data cache
    3.10k cycles 13.50k instructions 34.90% retired LD/ST ( 4.71k)

    JSON.stringify + JSON.parse 527.05 ns/iter 575.48 ns โ–ˆ โ–ƒ
    (367.58 ns โ€ฆ 2.30 ยตs) 732.21 ns โ–ˆโ–ˆ โ–ˆโ–‡
    ( 3.97 b โ€ฆ 383.93 b) 75.70 b โ–ƒโ–ƒโ–โ–‚โ–†โ–ˆโ–ˆโ–ˆโ–ƒโ–…โ–ˆโ–ˆโ–ˆโ–†โ–„โ–‚โ–‚โ–‚โ–โ–โ–
    4.41 ipc ( 1.07% stalls) 98.42% L1 data cache
    1.53k cycles 6.73k instructions 36.86% retired LD/ST ( 2.48k)

    JsonSchema.deepClone 62.08 ns/iter 65.56 ns โ–ˆโ–…
    (8.95 ns โ€ฆ 255.66 ns) 208.93 ns โ–ˆโ–ˆโ–„
    ( 1.92 b โ€ฆ 214.18 b) 47.77 b โ–„โ–โ–โ–‡โ–ˆโ–ˆโ–ˆโ–…โ–‚โ–โ–โ–โ–โ–โ–โ–โ–โ–‚โ–‚โ–‚โ–
    2.94 ipc ( 1.29% stalls) 98.86% L1 data cache
    164.89 cycles 485.47 instructions 44.83% retired LD/ST ( 217.63)

    Lodash โ”คโ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ–  868.72 ns
    structuredClone โ”คโ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ–  1.07 ยตs
    JSON.stringify + JSON.parse โ”คโ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ–  527.05 ns
    JsonSchema.deepClone โ”ค 62.08 ns
    โ”” โ”˜

    โ”Œ โ”
    โ•ท โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ” โ•ท
    Lodash โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”œโ”€โ”€โ”€โ”€โ”ค
    โ•ต โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”˜ โ•ต
    โ•ทโ”ฌโ” โ•ท
    structuredClone โ”œโ”‚โ”œโ”€โ”€โ”€โ”€โ”ค
    โ•ตโ”ดโ”˜ โ•ต
    โ•ท โ”Œโ”€โ”ฌโ”€โ” โ•ท
    JSON.stringify + JSON.parse โ”œโ”€โ”€โ”€โ”ค โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”ค
    โ•ต โ””โ”€โ”ดโ”€โ”˜ โ•ต
    โ•ทโ”Œโ”ฌ โ•ท
    JsonSchema.deepClone โ”œโ”คโ”‚โ”€โ”€โ”€โ”€โ”ค
    โ•ตโ””โ”ด โ•ต
    โ”” โ”˜
    8.95 ns 613.29 ns 1.22 ยตs

    summary
    JsonSchema.deepClone
    8.49x faster than JSON.stringify + JSON.parse
    13.99x faster than Lodash
    17.23x faster than structuredClone

    For a more detailed breakdown, see all the benchmark results.

    import { JsonSchema } from '@traversable/json-schema'

    const Address = {
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    } as const

    const deepClone = JsonSchema.deepClone(Address)
    const deepEqual = JsonSchema.deepEqual(Address)

    const sherlock = { street1: '221 Baker St', street2: '#B', city: 'London' }
    const harry = { street1: '4 Privet Dr', city: 'Little Whinging' }

    const sherlockCloned = deepClone(sherlock)
    const harryCloned = deepClone(harry)

    deepEqual(sherlockCloned, sherlock) // => true
    sherlock === sherlockCloned // => false

    deepEqual(harryCloned, harry) // => true
    harry === harryCloned // => false

    JsonSchema.deepClone.writeable lets users derive a specialized "deep clone" function that works with values that have been already validated.

    Compared to JsonSchema.deepClone, JsonSchema.deepClone.writeable returns the clone function in stringified ("writeable") form.

    import { JsonSchema } from '@traversable/json-schema'

    const deepClone = JsonSchema.deepClone.writeable({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    }, { typeName: 'Address' })

    console.log(deepClone)
    // =>
    // type Address = { street1: string; street2?: string; city: string; }
    // function deepClone(prev: Address): Address {
    // return {
    // street1: prev.street1,
    // ...prev.street2 !== undefined && { street2: prev.street2 },
    // city: prev.city
    // }
    // }

    JsonSchema.deepEqual lets users derive a specialized "deep equal" function that works with values that have been already validated.

    Because the values have already been validated, comparison times are significantly faster than alternatives like NodeJS.isDeepStrictEqual and Lodash.isEqual.

    Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚ Array (avg) โ”‚ Object (avg) โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ NodeJS.isDeepStrictEqual โ”‚ 40.3x faster โ”‚ 56.5x faster โ”‚
    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ Lodash.isEqual โ”‚ 53.7x faster โ”‚ 60.1x faster โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

    This article goes into more detail about what makes JsonSchema.deepEqual so fast.

    • Best performance
    • Works in any environment that supports defining functions using the Function constructor, including (as of May 2025) Cloudflare workers ๐ŸŽ‰
    import { JsonSchema } from '@traversable/json-schema'

    const deepEqual = JsonSchema.deepEqual({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    })

    deepEqual(
    { street1: '221 Baker St', street2: '#B', city: 'London' },
    { street1: '221 Baker St', street2: '#B', city: 'London' }
    ) // => true

    deepEqual(
    { street1: '221 Baker St', street2: '#B', city: 'London' },
    { street1: '4 Privet Dr', city: 'Little Whinging' }
    ) // => false

    JsonSchema.deepEqual.writeable lets users derive a specialized "deep equal" function that works with values that have been already validated.

    Compared to JsonSchema.deepEqual, JsonSchema.deepEqual.writeable returns the deep equal function in stringified ("writeable") form.

    • Useful when you're consuming a set of JSON Schemas and writing all them to disc somewhere
    • Also useful for testing purposes or for troubleshooting, since it gives you a way to "see" exactly what the deepEqual functions are doing
    import { JsonSchema } from '@traversable/json-schema'

    const deepEqual = JsonSchema.deepEqual.writeable({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    }, { typeName: 'Address' })

    console.log(deepEqual)
    // =>
    // type Address = { street1: string; street2?: string; city: string; }
    // function deepEqual(x: Address, y: Address) {
    // if (x === y) return true;
    // if (x.street1 !== y.street1) return false;
    // if (x.street2 !== y.street2) return false;
    // if (x.city !== y.city) return false;
    // return true;
    // }
    Note

    JsonSchema.fold is an advanced API.

    Use JsonSchema.fold to define a recursive traversal of a JSON Schema. Useful when building a schema rewriter.

    Writing an arbitrary traversal with JsonSchema.fold is:

    1. non-recursive
    2. 100% type-safe

    The way it works is pretty simple: if you imagine all the places in the JSON Schema specification that are recursive, those "holes" will be the type that you provide via type parameter.

    As an example, let's write a function called check that takes a JSON Schema, and returns a function that validates its input against the schema.

    Here's how you could use JsonSchema.fold to implement it:

    import { JsonSchema } from '@traversable/json-schema'

    const isObject = (u: unknown): u is { [x: string]: unknown } =>
    !!u && typeof u === 'object' && !Array.isArray(u)

    const check = JsonSchema.fold<(data: unknown) => boolean>(
    (schema) => { // ๐™˜_______________________๐™˜
    // this type will fill the "holes" in our schema
    switch (true) {
    case JsonSchema.isNull(schema):
    return (data) => data === null
    case JsonSchema.isBoolean(schema):
    return (data) => typeof data === 'boolean'
    case JsonSchema.isInteger(schema):
    return (data) => Number.isSafeInteger(data)
    case JsonSchema.isNumber(schema):
    return (data) => Number.isFinite(data)
    case JsonSchema.isArray(schema):
    return (data) => Array.isArray(data)
    && schema.every(schema.items)
    // ๐™˜___๐™˜
    // items: (data: unknown) => boolean
    case JsonSchema.isObject(schema):
    return (data) => isObject(data)
    && Object.entries(schema.properties).every(
    ([key, property]) => schema.required.includes(key)
    // ๐™˜______๐™˜
    // property: (data: unknown) => boolean
    ? (Object.hasOwn(data, key) && property(data[key]))
    : (!Object.hasOwn(data, key) || property(data[key]))
    )
    default: return () => false
    }
    }
    )

    // Let's use `check` to create a predicate:
    const isBooleanArray = check({
    type: 'array',
    items: { type: 'boolean' }
    })

    // Using the predicate looks like this:
    isBooleanArray([false]) // true
    isBooleanArray([true, 42]) // false

    That's it!

    If you'd like to see a more complex example, here's how JsonSchema.check is actually implemented.

    JsonSchema.fold is similar to, but more powerful than, the visitor pattern.

    If you're curious about the theory behind it, its implementation was based on a 1991 paper called Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire.

    Note

    JsonSchema.Functor is an advanced API.

    JsonSchema.Functor is the primary abstraction that powers @traversable/json-schema.

    JsonSchema.Functor is a powertool. Most of @traversable/json-schema uses JsonSchema.Functor under the hood.

    Compared to the rest of the library, it's fairly "low-level", so unless you're doing something pretty advanced you probably won't need to use it directly.

    Namespaces

    Bounds
    check
    deepClone
    deepEqual
    F
    JsonSchema
    toType

    Interfaces

    Array
    Boolean
    Const
    Enum
    Free
    Integer
    Intersection
    Null
    Number
    Object
    Record
    String
    Tuple
    Union
    Unknown

    Type Aliases

    DeepClonePrimitive
    DeepEqualPrimitive
    Discriminated
    Fixpoint
    JsonSchema
    Never
    Nullary
    PathSpec
    Scalar
    Tagged
    toType
    TypeName
    Unary
    VERSION

    Variables

    checkJson
    defaultNextSpec
    defaultPrevSpec
    Invariant
    TypeName
    TypeNames
    VERSION

    Functions

    areAllObjects
    check
    deepClone
    deepCloneInlinePrimitiveCheck
    deepCloneIsPrimitive
    deepCloneSchemaOrdering
    deepEqual
    deepEqualInlinePrimitiveCheck
    deepEqualIsPrimitive
    deepEqualSchemaOrdering
    flattenUnion
    getTags
    isArray
    isBoolean
    isConst
    isEnum
    isInteger
    isIntersection
    isNever
    isNull
    isNullary
    isNumber
    isNumeric
    isObject
    isRecord
    isScalar
    isSpecialCase
    isString
    isTuple
    isTypelevelNullary
    isUnary
    isUnion
    isUnknown
    toType

    References

    Algebraย โ†’ย F.Algebra
    CompilerFunctorย โ†’ย F.CompilerFunctor
    CompilerIndexย โ†’ย F.CompilerIndex
    defaultCompilerIndexย โ†’ย F.defaultCompilerIndex
    defaultIndexย โ†’ย F.defaultIndex
    foldย โ†’ย F.fold
    Functorย โ†’ย F.Functor
    Indexย โ†’ย F.Index