Skip to content

aurospire/jsondef

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jsondef

A TypeScript-like JSON Schema definition library that simplifies the creation, validation, and manipulation of JSON schemas. jsondef provides a concise and expressive syntax for defining complex data structures, making schema definitions more readable and maintainable.

Table of Contents

Features

  • TypeScript-like Syntax: Familiar syntax for defining JSON schemas.
  • Expressive Constraints: Easily define bounds and size constraints.
  • Scopes and Namespaces: Use root, this, and named references for complex schemas.
  • Extensible Types: Support for primitive, complex, and custom types.
  • Validation and Parsing: Validate data against schemas and parse schema definitions.
  • Type Inference: Infer TypeScript types directly from schemas.

Installation

Install jsondef using npm (or yarn):

npm install jsondef

Syntax Overview

jsondef provides a powerful and expressive syntax for defining JSON schemas. The syntax is inspired by TypeScript and allows for defining complex data structures with ease.

Simple Schemas

Any

Represents any valid JSON value.

any

Null

Represents a null value.

null

Boolean

Represents a true or false value.

boolean

Numeric

Represents an integer or number value with optional bounds, defined using relational operators:

  • Operators:

    • >: Greater than
    • >=: Greater than or equal to
    • <: Less than
    • <=: Less than or equal to
  • Examples:

    integer                   // Unbounded Integer    
    number                    // Unbounded Number
    integer(>0)               // Integers greater than 0
    number(>=-12.2, <=100e+2) // Numbers between -12.2 and 100e+2 inclusive

Literals

Represents a literal boolean, integer, number, or string.

  • Boolean Literal:

    true
    false
  • String Literal: Enclosed in single quotes.

    'active'
    'with\tescapes\n'
  • Integer Literal:

    42
    -12
  • Number Literal

    3.14
    -231.2
    2.3e+12

String

Represents a string with optional formats, regex patterns, and size constraints.

  • Formats:

    • string: Basic string
    • date, time, datetime: Date and time formats
    • uuid, email, base64: Specific string formats
    • Regex Patterns: /pattern/[flags]
  • Size Constraints:

    Use bounds to specify string length:

    • Operators:

      • >, >=, <, <=, =
    • Examples:

      string(<=100)             // String with max length 100
      uuid                      // String in UUID format
      email                     // String in email format
      /^(?:\+?\d{1,3})?[ -]?\d{7,14}$/ // Custom regex pattern

Complex Types

Arrays

Represnts arrays of elements conforming to a schema, with optional size bounds.

  • Syntax:

    <SCHEMA>[<BOUNDS>]
  • Examples:

    integer[>=1, <=10] // Array of integers with length between 1 and 10
    string[]           // Array of strings of any length

Records

Represents objects with dynamic keys and consistent value types, with optional size bounds

  • Syntax:

    record<SCHEMA>(<BOUNDS>)
    record<KEY_SCHEMA, VALUE_SCHEMA>(<BOUNDS>)
  • Examples:

    record<string>                 // Object with string values
    record<string, integer>        // Object with string keys and integer values
    record<uuid, { name: string }> // Object with UUID keys and objects as values
    record<boolean>(=10)           // Object with boolean values of exactly 10

Tuples

Represents fixed-size arrays with specific types for each element.

  • Syntax:

    [SCHEMA0, SCHEMA1, ...REST]
  • Examples:

    [string, integer, boolean]          // Tuple with a string, an integer, and a boolean
    [string, ...integer[]]              // Tuple starting with a string followed by any number of integers

Unions

Define a type that can be one of several specified schemas.

  • Syntax:

    SCHEMA0 | SCHEMA1 | ... | SCHEMAN
  • Examples:

    integer | 'active' | 'inactive' // Can be an integer or one of the specified string literals
    boolean | null                  // Can be a boolean or null

Objects

Represents objects with specific properties.

  • Creates a local scope, that can be recursively referenced with a this schema.

  • If the object is top level, also sets the root scope, whic hcan be recursively referenced with a root schema.

  • Similar to typescript, use ?: instead of : to denote optional properties.

  • Syntax:

    { KEY0: SCHEMA0, KEY1: SCHEMA1, }
  • Examples:

    {
      name: string,
      age: integer(>=0),
      email?: email       // Optional property
    }

Models

Similar to objects, but sets the root scope wherever it is.

  • Syntax:

    model {
      KEY0: SCHEMA0,
      KEY1: SCHEMA1
    }
  • Examples:

    model {
      name: string,
      parent?: root // Recursive reference to the model itself
    }

Groups and Namespaces

Represents a group of schemas under a single namespace. You can select a specific schema from the group.

  • Syntax:

    group {
      KEY0: SCHEMA0,
      KEY1: SCHEMA1
    }
  • Select a Schema from a Group:

    select <KEY> of group {
      KEY0: SCHEMA0,
      KEY1: SCHEMA1
    }
  • Examples:

    // Simple group, no references or selections
    group {
      User: model { id: uuid, name: string },
      Admin: model { id: uuid, name: string, role: 'admin' }
    }
    
    // Selection from a group
    select User of group {
      User: model { id: uuid, name: string },
      Admin: model { id: uuid, name: string, role: 'admin' }
    }
    
    // Selection and Reference
    select User of group {
      User: model { id: uuid, name: string, role: Role },
      Role: 'user' | 'admin'
    }

References and Scopes

root and this

  • root: References the root scope of the current model, useful for recursive definitions that need to refer back to the root.

  • this: References the local scope of the current model, allowing for recursion within the same level.

  • Example:

    model {
      name: string(),
      age: integer(>=0),
      role: {
        title: string,
        manager: root,  // rescursively references the entire model
        subrole?: this  // recursively references the current object (role)
      }
    }

Identifiers (<IDENTIFIER>)

References a named schema within an ancestor group's namespace.

  • Example:

    group {
      Node: model {
        value: integer,
        next: Node | null
      }
    }

    Here, Node is used as an identifier to reference the Node schema within the same group.

API Reference

The d namespace provides a set of functions to build, parse, validate, and infer schemas programmatically.

Schema Builders

Schema Builders use a Fluid style to allow easy creation of schemas.

  • All Schema Builders have .optional() and .required() methods used for optional properties with models and objects.

d.null()

Creates a NullSchemaBuilder instance representing a null value.

Usage:

const nullSchema = d.null();

d.any()

Creates an AnySchemaBuilder instance representing any value.

Usage:

const anySchema = d.any();

d.boolean()

Creates a BooleanSchemaBuilder instance representing a boolean value.

Usage:

const booleanSchema = d.boolean();

d.root()

Creates a RootSchemaBuilder instance referencing the root scope.

Usage:

const rootSchema = d.root();

d.this()

Creates a ThisSchemaBuilder instance referencing the local scope.

Usage:

const thisSchema = d.this();

d.integer(bounds?: BoundedAttributes)

Creates an IntegerSchemaBuilder instance with optional bounds.

Usage:

const positiveInteger = d.integer({ min: 1 });

d.number(bounds?: BoundedAttributes)

Creates a NumberSchemaBuilder instance with optional bounds.

Usage:

const percentage = d.number({ min: 0, max: 100 });

d.string(size?: SizedAttributes)

Creates a StringSchemaBuilder instance with optional size constraints.

Usage:

const shortString = d.string({ max: 50 });

d.date(), d.time(), d.datetime(), d.uuid(), d.email(), d.base64()

Creates a StringSchemaBuilder instance for specific formats.

Usage:

const emailSchema = d.email();
const uuidSchema = d.uuid();

d.regex(pattern: RegExp | RegexString, size?: SizedAttributes)

Creates a StringSchemaBuilder instance with a custom regex pattern.

  • Parameters:
    • pattern: A RegExp object or regex string.
    • size (optional): Size constraints.

Usage:

const customString = d.regex(/^[a-z]+$/i);

d.literal(value: boolean | string | number)

Creates a LiteralSchemaBuilder instance for a specific literal value.

Usage:

const activeStatus = d.literal('active');

d.enum(values: (boolean | string | number)[])

Creates a UnionSchemaBuilder instance representing an enum of literals.

Usage:

const statusEnum = d.enum(['active', 'inactive', 'pending']);

d.array(of: Schema, size?: SizedAttributes)

Creates an ArraySchemaBuilder instance for arrays of a specific schema.

Usage:

const stringArray = d.array(d.string(), { min: 1 });

d.tuple(of: Schema, rest?: ArraySchema)

Creates a TupleSchemaBuilder instance for tuples.

  • Parameters:
    • of: An array of schemas for the tuple elements.
    • rest (optional): A schema for additional elements.

Usage:

const mixedTuple = d.tuple([d.string(), d.integer()]);

d.record(of: , size?)

Creates a RecordSchemaBuilder instance for objects with uniform value types.

Special Methods:

// Constrains the key that the record can be indexed by
builder.by(key: StringSchema): RecordSchemaBuilder

Usage:

const numberRecord = d.record(d.number());

const uuidNumberRecord = numberRecord.by(d.uuid());

d.object(of: SchemaObject)

Creates an ObjectSchemaBuilder instance for an object with specified properties.

Usage:

const userObject = d.object({
  username: d.string(),
  password: d.string()
});

d.model(of: SchemaObject)

Creates a ModelSchemaBuilder instance, setting a new root scope.

Usage:

const userModel = d.model({
  username: d.string(),
  profile: d.object({
    age: d.integer(),
    bio: d.string().optional()
  })
});

d.union(of: Schema[])

Creates a UnionSchemaBuilder instance for a union of schemas.

Usage:

const statusUnion = d.union([d.literal('active'), d.literal('inactive')]);

d.ref(of: string)

Creates a RefSchemaBuilder instance referencing another schema in ancestral namespace by name.

Usage:

const employeeSchema = d.model({
  manager: d.ref('Employee')
});

d.group(of: SchemaObject)

Creates a GroupSchemaBuilder instance containing multiple schemas.

Special Methods:

// Sets which subschema is selected from the group
builder.select(key: keyof Of): GroupSchemaBuilder

Usage:

const schemas = d.group({
  Person: personSchema,
  Employee: employeeSchema
});

const Person = schemas.select('Person')

Parsing and Stringifying

d.parse(jsondef)

Parses a jsondef string into a schema object.

Usage:

const schema = d.parse(`
  model {
    name: string,
    age: integer(>=0)
  }
`);

d.tryParse(jsondef)

Attempts to parse a jsondef string, returning a Result object.

Usage:

const result = d.tryParse('invalid schema');
if (result.success) {
  // Use the schema
} else {
  // Handle errors
}

d.stringify(schema, format?, condensed?)

Converts a schema object back into a jsondef string.

  • Parameters:
    • schema: The schema object to stringify.
    • format (optional): Formatting options.
    • condensed (optional): Boolean indicating whether to use condensed format as base of optional supplied format.

Usage:

const schemaString = d.stringify(schema);

Validation

d.validate(value, schema)

Validate a value against a schema, returning the properly types value.

Usage:

const result = d.validate({ name: 'John', age: 30 }, personSchema);

d.tryValidate(value, schema)

Attemptes to validate a value against a schema, returning a Result object

Usage:

const result = d.validate({ name: 'John', age: 30 }, personSchema);
if (result.success) {
  // Value is valid
} else {
  // Handle validation errors
}

Type Inference

d.infer<Schema>(schema)

Infers the TypeScript type from a schema.

Usage:

type Person = d.infer<typeof personSchema>;

Other

d.summary(): string

Provides a summary of how tsdoc works, possibly useful for LLMs.

License

jsondef is licensed under the MIT License. You are free to use, modify, and distribute this software in compliance with the license.

About

Simple JSON Schema and Validation

Resources

License

Stars

Watchers

Forks

Packages

No packages published