metaforge
Version:
Library that allow you to describe data structures by subset of JavaScript syntax and validate them at runtime
67 lines (60 loc) • 2.16 kB
JavaScript
'use strict';
const create = type => ({ $kind: type });
module.exports = new Map(
Object.entries({
unknown: create('unknown'),
boolean: create('scalar'),
string: create('scalar'),
number: create('scalar'),
bigint: create('scalar'),
null: create('null'),
any: create('any'),
schema: Schema,
union: Union,
array: List,
tuple: List,
set: List,
record: Struct,
object: Struct,
map: Struct,
enum: Enum,
}),
);
const ENUM_WARN = 'Recieved incorrect enumerable';
function Enum(plan, { warn }) {
this.$kind = 'enum';
const filter = el => typeof el === 'string' || typeof el === 'number';
this.$enum = Array.isArray(plan.enum) ? [...new Set(plan.enum)].filter(filter) : [];
const isFiltered = this.$enum.length !== plan.enum?.length;
isFiltered && warn({ cause: ENUM_WARN, plan, sample: plan.enum });
}
const ITEMS_ERROR = 'Plan items are invalid or empty';
function List(plan, { warn, build }) {
this.$kind = 'struct';
const isArray = Array.isArray(plan.items);
this.$isTuple = this.$type === 'tuple' || isArray;
this.$items = (isArray ? plan.items : [plan.items]).map(build);
!this.$items.length && warn({ plan, cause: ITEMS_ERROR, sample: plan.items });
}
const PLANS_ERROR = 'Received plan without properties';
function Struct(plan, { build, warn }) {
[this.$kind, this.$properties, this.$patterns] = ['struct', new Map(), new Map()];
this.$isRecord = this.$type === 'record' || (plan.isRecord ?? false);
this.$requires = [];
!plan.properties && warn({ plan, sample: plan.properties, cause: PLANS_ERROR });
for (const [key, value] of Object.entries(plan.properties ?? {})) {
const builded = build(value);
builded.$required && this.$requires.push(key);
(value.isPattern ? this.$patterns : this.$properties).set(key, builded);
}
}
function Union(plan, { build }) {
this.$kind = 'union';
this.$condition = plan.condition ?? 'anyof';
this.$types = (Array.isArray(plan.types) ? plan.types : [plan]).map(build);
}
function Schema(plan) {
Object.assign(this, plan.schema);
if (plan.$id) this.$id = plan.$id;
this.$required = plan.$required;
}