"use strict"; const errorChain = require("error-chain"); let ValidationError = errorChain("ValidationError"); function isPresent(value) { if (value == null) { throw new ValidationError("Required value is missing", { errorType: "isPresent" }); } } function isString(value) { if (value != null && typeof value !== "string") { throw new ValidationError("Value is not a string", { errorType: "isString" }); } } function isBoolean(value) { if (value != null && typeof value !== "boolean") { throw new ValidationError("Value is not a boolean", { errorType: "isBoolean" }); } } function isOneOf(...acceptableValues) { let valueSet = new Set(acceptableValues); return function (value) { if (value != null && !valueSet.has(value)) { throw new ValidationError("Value is not one of the specified acceptable values", { errorType: "isOneOf", acceptableValues: acceptableValues }); } }; } /* NOTE: Extremely sketchy way of tracking validation state via a module-global object, but we can get away with this because validation is completely synchronous */ let context; function assertProperties(object, properties) { for (let [property, assertions] of Object.entries(properties)) { context.path.push(property); let value = object[property]; try { for (let assertion of assertions) { assertion(value); } } catch (error) { if (error.input == null) { error.input = value; } throw error; } context.path.pop(); } } /* FIXME: Abstract out the path and value assignment for errors into a `withPath(...)` abstraction */ function arrayOf(assertions) { return function (value) { if (value != null) { if (!Array.isArray(value)) { throw new ValidationError("Value is not an array", { errorType: "arrayOf" }); } else { for (let [i, item] of value.entries()) { context.path.push(i); try { for (let assertion of assertions) { assertion(item); } } catch (error) { if (error.input == null) { error.input = value; } throw error; } context.path.pop(); } } } }; } function validate(validator, value) { context = { path: [] }; try { validator(value); } catch (error) { if (error.path == null) { error.path = context.path; } throw error; } context = undefined; } module.exports = { validate, assertProperties, ValidationError, isPresent, isString, isOneOf, arrayOf, isBoolean }; // try { // validate(validateLoginPayload, { // type: 'm.login.password', // password: 'test', // identifier: { type: 'm.id.user', user: 'joepie91' }, // initial_device_display_name: 'http://localhost:3001/index.html via Firefox on Linux', // user: 'joepie91', // arrayStuff: [ 42 ] // }); // } catch (error) { // console.log(require("util").inspect(error, {colors: true, depth: null})); // }