UNPKG

@quenk/preconditions

Version:
184 lines 6.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = exports.defaultBuiltins = exports.DEFAULT_PIPELINE_KEY = void 0; const except_1 = require("@quenk/noni/lib/control/except"); const array_1 = require("@quenk/noni/lib/data/array"); const either_1 = require("@quenk/noni/lib/data/either"); const record_1 = require("@quenk/noni/lib/data/record"); const type_1 = require("@quenk/noni/lib/data/type"); exports.DEFAULT_PIPELINE_KEY = 'preconditions'; /** * defaultBuiltins available. */ exports.defaultBuiltins = { object: ['base.default', 'base.const', 'base.type'], array: [ 'base.default', 'base.const', 'base.type', 'array.nonEmpty', 'array.minItems', 'array.maxItems' ], string: [ 'base.default', 'base.cast', 'base.const', 'base.type', 'base.enum', 'string.nonEmpty', 'string.minLength', 'string.maxLength', 'string.pattern', 'string.trim', 'string.lowerCase', 'string.upperCase', 'string.split' ], number: [ 'base.default', 'base.cast', 'base.const', 'base.type', 'base.enum', 'number.min', 'number.max' ], boolean: [ 'base.default', 'base.cast', 'base.const', 'base.type', 'base.enum' ] }; const booleanExtractors = ['trim', 'lowerCase', 'upperCase', 'cast']; const typeTakers = ['cast']; /** * parse a Schema into a representation. * * This function works by converting a schema into some generic representation * T. It walks the properties of an object type schema or the items property * of an array schema in a stack safe manner. * * The provided ParseContext is given the chance to transform each encountered * schema into a precondition via visit(). * * @param ctx - The parse context. * @param schema - The root schema to parse. */ const parse = (ctx, schema) => { let result = []; let initItem = [schema, 0, result]; let initFrame = [[initItem]]; let pending = [initFrame]; PENDING: while (!(0, array_1.empty)(pending)) { let [stack, owner] = pending.pop(); while (!(0, array_1.empty)(stack)) { let [currentSchema, currentPath, currentTarget] = (stack.pop()); let builtins = currentSchema.readOnly ? [] : takeBuiltins((0, record_1.merge)(exports.defaultBuiltins, ctx.builtinsAvailable || {}), currentSchema); let preconditions = ctx.getPipeline(currentSchema); if (currentSchema.readOnly && (0, array_1.empty)(preconditions)) continue; if (!isComplex(currentSchema)) { let eprecs = toPrecondition(ctx, [ ...builtins, ...preconditions ]); if (eprecs.isLeft()) return (0, except_1.raise)(eprecs.takeLeft()); currentTarget[currentPath] = ctx.visit([ currentSchema.type, eprecs.takeRight(), Boolean(currentSchema.optional) ]); } else { pending.push([stack, owner]); // Save current state for later. let ebuiltinPrecs = toPrecondition(ctx, builtins); if (ebuiltinPrecs.isLeft()) return (0, except_1.raise)(ebuiltinPrecs.takeLeft()); let eprecs = toPrecondition(ctx, preconditions); if (eprecs.isLeft()) return (0, except_1.raise)(eprecs.takeLeft()); let builtinPrecs = ebuiltinPrecs.takeRight(); let precs = eprecs.takeRight(); if (currentSchema.type === 'object') { let schema = currentSchema; let newStack = []; let object = [ 'object', [builtinPrecs, {}, undefined, precs], Boolean(schema.optional) ]; let [, args] = object; for (let [key, prop] of Object.entries(schema.properties || {})) newStack.push([prop, key, args[1]]); if ((0, type_1.isObject)(schema.additionalProperties)) newStack.push([ schema.additionalProperties, 2, args ]); pending.push([ newStack, [object, currentPath, currentTarget] ]); } else if (currentSchema.type === 'array') { let schema = currentSchema; let array = [ 'array', [builtinPrecs, null, precs], Boolean(schema.optional) ]; let newStack = []; if (schema.items) newStack.push([schema.items, 1, array[1]]); pending.push([ newStack, [array, currentPath, currentTarget] ]); } continue PENDING; } } if (owner) { let [val, loc, target] = owner; target[loc] = ctx.visit(val); } } return (0, either_1.right)(result.pop()); }; exports.parse = parse; const takeBuiltins = (available, schema) => (available[schema.type] || []).reduce((result, path) => { let [, name] = path.split('.'); if (Object.prototype.hasOwnProperty.call(schema, name)) { if (booleanExtractors.includes(name) && schema[name] !== false) { result.push([ path, typeTakers.includes(name) ? [schema.type] : [] ]); } else { result.push([path, [schema[name]]]); } } return result; }, []); const toPrecondition = (ctx, list) => { let result = []; for (let spec of list) { let mprec = ctx.get(spec); if (mprec.isNothing()) return (0, except_1.raise)(`Could not resolve the following value to a precondition: ${spec} !`); result.push(mprec.get()); } return (0, either_1.right)(result); }; const complex = ['object', 'array']; const isComplex = (schema) => complex.includes(schema.type); //# sourceMappingURL=parse.js.map