@sleekcms/json-zen
v1.6.0
Published
Easify verify and shape JSON objects using a simplified alternative JSON schema
Maintainers
Readme
JSON Zen
A compact, string-based schema syntax for validating and shaping JSON objects — with far less boilerplate than JSON Schema.
Try it live at json-zen.sleekcms.site.
Installation
npm install @sleekcms/json-zen// CommonJS
const { verify, check, shape, toSchema, extendTypes } = require('@sleekcms/json-zen');
// ESM / TypeScript
import { verify, check, shape, toSchema, extendTypes } from '@sleekcms/json-zen';Quick Start
import { verify, check, shape, toSchema } from '@sleekcms/json-zen';
const data = { name: 'Alice', age: 30, tags: ['admin', 'user'] };
// Validate (throws on failure)
verify(data, '{name:s, age:n, tags:[s]}'); // true
// using full type names is equivalent:
verify(data, '{name:string, age:number, tags:[string]}'); // true
// Safe boolean check
check(data, '{name:s, age:s}'); // false
// Generate a schema from an existing object
toSchema(data); // "{name:string,age:number,tags:[string]}"
// Fill in missing fields with typed defaults
shape({ name: 'Bob' }, '{name:s, age:n, active:b}');
// { name: 'Bob', age: 0, active: true }Schema Syntax
Alt schemas are plain strings. Whitespace is ignored.
Built-in Types
| Shorthand | Full name | Validates | Default (shape) |
|-----------|-----------|----------------------|-----------------|
| s | string | string | "" |
| n | number | number (incl. floats)| 0 |
| b | boolean | boolean | true |
| — | null | strictly null | null |
| ? | — | any value / nullable | null |
Shorthands and full names are interchangeable: {a:s} and {a:string} behave identically.
Objects
{key:type, key:type, ...}verify({ a: 'hello', b: 42 }, '{a:s, b:n}'); // true — shorthands
verify({ a: 'hello', b: 42 }, '{a:string, b:number}'); // true — full namesArrays
A single type applies to every element. Multiple comma-separated types cycle by index.
verify([1, 2, 3], '[n]'); // true — all numbers
verify([1, 'x', 2, 'y'], '[n,s]'); // true — alternating number/stringOptional fields (?)
For object properties, suffix the key with ? to make it optional (TypeScript-style). For array elements, prefix the type with ?. A missing or null value passes validation.
verify({ a: 1 }, '{a:n, b?:s}'); // true — b is missing but optional
verify({ a: 1, b: null }, '{a:n, b?:s}'); // true
verify([1, null, 3], '[?n]'); // true — array elements are optionalPresence-only validation
Omit the type to require only that a key exists and is not null/undefined.
verify({ a: 1, b: false }, '{a, b}'); // true
verify({ a: 1, b: null }, '{a, b}'); // throws — b is nullDefault values (type:default)
Attach a default after the type, used by shape to fill missing values.
shape({}, '{name:s:anonymous, count:n:0}');
// { name: 'anonymous', count: 0 }Use quotes for defaults containing spaces:
shape({}, '{title:s:"Hello World"}');
// { title: 'Hello World' }Wildcard key (*)
*:type matches any key not already listed in the schema.
verify({ a: 1, b: 2, c: 3 }, '{*:n}'); // true
verify({ a: 1, b: '2' }, '{a:n, *:s}'); // true — b matched by wildcard
shape({ x: 1, y: 2, z: 3 }, '{*:n}'); // { x: 1, y: 2, z: 3 }Nested objects and arrays
const schema = '{user:{name:s, scores:[n]}, active:b}';
verify({ user: { name: 'Alice', scores: [10, 20] }, active: true }, schema); // trueAPI
verify(json, schema, options?)
Validates json against schema. Returns true on success, throws an ZenError on failure.
verify({ a: 1 }, '{a:s}');
// throws: "json.a: validation failed"Access all failures at once via error.errors:
try {
verify({}, '{a:n, b:n}');
} catch (e) {
console.log(e.message); // "Validation failed: 2 errors found"
console.log(e.errors); // [['json.a', 'is required'], ['json.b', 'is required']]
}Options:
| Option | Type | Description |
|---------|----------|---------------------------------------------------|
| _path | string | Override the root label in error messages |
| (key) | validator | Inline custom type — see Custom Validators |
verify({ a: 'x' }, '{a:s}', { _path: 'payload' });
// throws: "payload.a: validation failed"check(json, schema, options?)
Same as verify but returns false instead of throwing. Schema errors still throw.
check({ a: 1, b: 'x' }, '{a:n, b:n}'); // false
check({ a: 1, b: 2 }, '{a:n, b:n}'); // trueshape(json, schema, options?)
Returns a new object shaped to the schema. Values from json are used when valid; otherwise, typed defaults fill in the gaps. Extra keys in json not in the schema are omitted.
shape({ a: 1, extra: 99 }, '{a:n, b:s, c:b}');
// { a: 1, b: '', c: true }Nested structures are shaped recursively:
shape({ a: {} }, '{a:{x:n, y:s}}');
// { a: { x: 0, y: '' } }Options:
| Option | Type | Description |
|-------------|-----------|--------------------------------------------------------------|
| fillNulls | boolean | When true, fill optional (?) fields with defaults instead of null |
| (key) | validator | Inline custom type |
shape({}, '{a?:n}'); // { a: null }
shape({}, '{a?:n}', { fillNulls: true }); // { a: 0 }toSchema(json, options?)
Generates a schema string from a JSON object. Useful for bootstrapping a schema from a known-good example.
toSchema({ a: 'foo', b: 1, c: [1, 2], d: { e: null } });
// "{a:string,b:number,c:[number],d:{e:?}}"Options:
| Option | Type | Default | Description |
|-----------|-----------|---------|-------------|
| default | string | — | A type name to treat as implicit. Object attributes whose inferred type matches this value will omit the :type suffix in the output. |
| compact | boolean | false | When true, omits the :type suffix for every scalar attribute in objects (equivalent to setting default to all types at once). |
toSchema({ name: 'Alice', age: 30, active: true });
// "{name:string,age:number,active:boolean}"
// Omit ":string" everywhere — handy when most fields are strings
toSchema({ name: 'Alice', age: 30, active: true }, { default: 'string' });
// "{name,age:number,active:boolean}"
// Strip all :type annotations
toSchema({ name: 'Alice', age: 30, active: true }, { compact: true });
// "{name,age,active}"extendTypes(validators)
Registers one or more custom types globally. Once registered, the type name can be used in any schema.
The validator function receives the value when checking, and undefined when shape needs a default — return the default value in that case.
import { extendTypes, shape, verify } from '@sleekcms/json-zen';
extendTypes({
url: (value) => {
if (value === undefined) return 'https://example.com'; // default for shape
return typeof value === 'string' && value.startsWith('http');
}
});
verify({ img: 'https://example.com/pic.jpg' }, '{img:url}'); // true
shape({}, '{img:url}'); // { img: 'https://example.com' }Custom Validators
Custom validators can be passed inline via options, registered globally via extendTypes, or provided as enum arrays or RegExp patterns.
Inline function
verify({ status: 'active' }, '{status:myType}', {
myType: (value) => value === 'active' || value === 'inactive'
});Enum (array)
verify({ role: 'admin' }, '{role:r}', { r: ['admin', 'user', 'guest'] }); // true
verify({ role: 'root' }, '{role:r}', { r: ['admin', 'user', 'guest'] }); // throwsRegex
verify({ slug: 'hello-world' }, '{slug:sl}', { sl: /^[a-z-]+$/ }); // trueContext-aware validator
Validators receive { path, json, parent } as a second argument, enabling cross-field validation:
extendTypes({
matchesType: (value, { parent }) => {
if (parent.type === 'number') return typeof value === 'number';
if (parent.type === 'string') return typeof value === 'string';
return false;
}
});
verify(
[{ type: 'number', value: 42 }, { type: 'string', value: 'hi' }],
'[{type:s, value:matchesType}]'
); // trueLicense
MIT © Yusuf Bhabhrawala, SleekSky LLC
