UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

265 lines (264 loc) 9.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeBuilder = void 0; const tslib_1 = require("tslib"); const schema = tslib_1.__importStar(require("../schema")); const classes = tslib_1.__importStar(require("./classes")); const { s } = schema; class TypeBuilder { constructor(system) { this.system = system; // --------------------------------------------------------------- shorthands this.undefined = () => this.undef; this.null = () => this.nil; this.boolean = () => this.bool; this.number = () => this.num; this.string = () => this.str; this.binary = () => this.bin; this.con = (value, options) => this.Const(value, options); this.literal = this.con; this.array = (type, options) => this.Array((type ?? this.any), options); this.tuple = (...types) => this.Tuple(...types); /** * Creates an object type with the specified properties. This is a shorthand for * `t.Object(t.prop(key, value), ...)`. * * Importantly, this method does not allow to specify object field order, * so the order of properties in the resulting type is not guaranteed. * * Example: * * ```ts * t.object({ * id: t.str, * name: t.string(), * age: t.num, * verified: t.bool, * }); * ``` * * @param record A mapping of property names to types. * @returns An object type. */ this.object = (record) => { const fields = []; for (const [key, value] of Object.entries(record)) fields.push(this.prop(key, value)); const obj = new classes.ObjType(fields); obj.system = this.system; return obj; }; /** * Creates a type that represents a value that may be present or absent. The * value is `undefined` if absent. This is a shorthand for `t.Or(type, t.undef)`. */ this.maybe = (type) => this.Or(type, this.undef); /** * Creates a union type from a list of values. This is a shorthand for * `t.Or(t.Const(value1), t.Const(value2), ...)`. For example, the below * are equivalent: * * ```ts * t.enum('red', 'green', 'blue'); * t.Or(t.Const('red'), t.Const('green'), t.Const('blue')); * ``` * * @param values The values to include in the union. * @returns A union type representing the values. */ this.enum = (...values) => this.Or(...values.map((type) => this.Const(type))); } // -------------------------------------------------------------- empty types get any() { return this.Any(); } get undef() { return this.Const(undefined); } get nil() { return this.Const(null); } get bool() { return this.Boolean(); } get num() { return this.Number(); } get str() { return this.String(); } get bin() { return this.Binary(this.any); } get arr() { return this.Array(this.any); } get obj() { return this.Object(); } get map() { return this.Map(this.any); } get fn() { return this.Function(this.undef, this.undef); } get fn$() { return this.Function$(this.undef, this.undef); } // --------------------------------------------------- base node constructors Any(options) { const type = new classes.AnyType(s.Any(options)); type.system = this.system; return type; } Const(value, options) { const type = new classes.ConType(schema.s.Const(value, options)); type.system = this.system; return type; } Boolean(options) { const type = new classes.BoolType(s.Boolean(options)); type.system = this.system; return type; } Number(options) { const type = new classes.NumType(s.Number(options)); type.system = this.system; return type; } String(options) { const type = new classes.StrType(s.String(options)); type.system = this.system; return type; } Binary(type, options = {}) { const bin = new classes.BinType(type, options); bin.system = this.system; return bin; } Array(type, options) { const arr = new classes.ArrType(type, options); arr.system = this.system; return arr; } Tuple(...types) { const tup = new classes.TupType(types); tup.system = this.system; return tup; } Object(...fields) { const obj = new classes.ObjType(fields); obj.system = this.system; return obj; } prop(key, value) { const field = new classes.ObjectFieldType(key, value); field.system = this.system; return field; } propOpt(key, value) { const field = new classes.ObjectOptionalFieldType(key, value); field.system = this.system; return field; } Map(val, key, options) { const map = new classes.MapType(val, key, options); map.system = this.system; return map; } Or(...types) { const or = new classes.OrType(types); or.system = this.system; return or; } Ref(ref) { const type = new classes.RefType(ref); type.system = this.system; return type; } Function(req, res, options) { const fn = new classes.FnType(req, res, options); fn.system = this.system; return fn; } Function$(req, res, options) { const fn = new classes.FnRxType(req, res, options); fn.system = this.system; return fn; } import(node) { switch (node.kind) { case 'any': return this.Any(node); case 'bool': return this.Boolean(node); case 'num': return this.Number(node); case 'str': return this.String(node); case 'bin': return this.Binary(this.import(node.type), node); case 'arr': return this.Array(this.import(node.type), node); case 'tup': return this.Tuple(...node.types.map((t) => this.import(t))).options(node); case 'obj': { return this.Object(...node.fields.map((f) => f.optional ? this.propOpt(f.key, this.import(f.value)).options(f) : this.prop(f.key, this.import(f.value)).options(f))).options(node); } case 'map': return this.Map(this.import(node.value), node.key ? this.import(node.key) : undefined, node); case 'con': return this.Const(node.value).options(node); case 'or': return this.Or(...node.types.map((t) => this.import(t))).options(node); case 'ref': return this.Ref(node.ref).options(node); case 'fn': return this.Function(this.import(node.req), this.import(node.res)).options(node); case 'fn$': return this.Function$(this.import(node.req), this.import(node.res)).options(node); } throw new Error(`UNKNOWN_NODE [${node.kind}]`); } from(value) { switch (typeof value) { case 'undefined': return this.undef; case 'boolean': return this.bool; case 'number': return this.num; case 'string': return this.str; case 'object': if (value === null) return this.nil; if (Array.isArray(value)) { if (value.length === 0) return this.arr; const getType = (v) => { switch (typeof v) { case 'object': if (v === null) return 'nil'; if (Array.isArray(v)) return 'arr'; return 'obj'; default: return typeof v; } }; const allElementsOfTheSameType = value.every((v) => getType(v) === getType(value[0])); return allElementsOfTheSameType ? this.Array(this.from(value[0])) : this.Tuple(...value.map((v) => this.from(v))); } return this.Object(...Object.entries(value).map(([key, value]) => this.prop(key, this.from(value)))); default: return this.any; } } } exports.TypeBuilder = TypeBuilder;