UNPKG

pragmatic-fp-ts

Version:

Opinionated functional programming library with easy use in mind

131 lines (130 loc) 5.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.from = void 0; const all_1 = require("./all"); const Either_1 = require("./Either"); const getValueOr_1 = require("./getValueOr"); const isDataObject_1 = require("./isDataObject"); const isFunction_1 = require("./isFunction"); const isNil_1 = require("./isNil"); const isNumber_1 = require("./isNumber"); const isString_1 = require("./isString"); const map_1 = require("./map"); const values_1 = require("./values"); const nameProp = "__name"; const typeName = (x) => Array.isArray(x) ? "array" : x === null ? "null object" : typeof x; const typeError = (validator, x) => { const expectedType = (0, isString_1.isString)(validator) ? validator : getName(validator); const receivedType = typeName(x); return new Error(`expected ${expectedType}, got ${receivedType} ${JSON.stringify(x)}`); }; const parseData = (x, decode) => { const parse = (0, isFunction_1.isFunction)(decode) ? decode : decode === true ? JSON.parse : undefined; return (0, isString_1.isString)(x) && parse ? parse(x) : x; }; const Validator = (validate, { decode } = { decode: false }) => assignName(getName(validate), (x) => { const xx = decode ? parseData(x, decode) : x; return validate(xx) ? (0, Either_1.right)(xx) : (0, Either_1.left)(typeError(validate, x)); }); const assignName = (value, x) => { Object.defineProperty(x, nameProp, { value }); return x; }; const getName = (x) => x[nameProp]; const joinArrayReasons = (coll) => { const badValues = coll .reduce((msgs, m, idx) => { var _a; if (m.isLeft()) msgs.push(`${idx}: ${(_a = m.getReason()) === null || _a === void 0 ? void 0 : _a.message}`); return msgs; }, []) .join(", "); return `[${badValues}]`; }; const liftArray = (coll) => (0, all_1.all)(Either_1.isRight, coll) ? (0, Either_1.right)(coll.map(liftValue)) : (0, Either_1.left)(new Error(joinArrayReasons(coll))); const liftValue = (v) => Array.isArray(v) ? liftArray(v) : (0, isDataObject_1.isDataObject)(v) ? liftRecord(v) : (0, getValueOr_1.getValueOr)(undefined, v); const joinRecordReasons = (coll) => { const badFields = coll .reduce((msgs, [key, value]) => { var _a; if ((0, Either_1.isLeft)(value)) msgs.push(`${key}: ${(_a = value.getReason()) === null || _a === void 0 ? void 0 : _a.message}`); return msgs; }, []) .join(", "); return `{${badFields}}`; }; const liftRecord = (data) => (0, all_1.all)(Either_1.isRight, (0, values_1.values)(data)) ? (0, Either_1.right)((0, map_1.map)(liftValue, data)) : (0, Either_1.left)(new Error(joinRecordReasons(Object.entries(data)))); const array = (validate) => { const contentType = `Array<${getName(validate)}>`; return assignName(contentType, (x, { decode } = { decode: false }) => { const xx = decode ? parseData(x, decode) : x; return Array.isArray(xx) ? liftValue(xx.map(validate)) : (0, Either_1.left)(typeError(contentType, x)); }); }; const record = (schema) => { const recordDataType = Object.entries(schema) .map(([k, v]) => `${k}: ${getName(v)}`) .join(", "); const recordType = `Record<{${recordDataType}}>`; return assignName(recordType, (x, { decode } = { decode: false }) => { const validateRecord = (y) => Object.fromEntries(Object.entries(schema).map(([k, decode]) => [k, decode(y[k])])); const xx = decode ? parseData(x, decode) : x; return (0, isDataObject_1.isDataObject)(xx) ? liftValue(validateRecord(xx)) : (0, Either_1.left)(typeError(recordType, x)); }); }; const dictionary = (validate) => { const dictType = `Dictionary<${getName(validate)}>`; return assignName(dictType, (x, { decode } = { decode: false }) => { const decodeDict = (o) => Object.fromEntries(Object.entries(o).map(([k, v]) => [k, validate(v)])); const xx = decode ? parseData(x, decode) : x; return (0, isDataObject_1.isDataObject)(xx) ? liftValue(decodeDict(xx)) : (0, Either_1.left)(typeError(dictType, x)); }); }; const from = (name, validate) => Validator(assignName(name, validate)); exports.from = from; const any = (0, exports.from)("any", (_) => true); const unknown = (0, exports.from)("unknown", (_) => true); const string = (0, exports.from)("string", isString_1.isString); const number = (0, exports.from)("number", isNumber_1.isNumber); const null_ = (0, exports.from)("null", (x) => x === null); const undefined_ = (0, exports.from)("undefined", (x) => x === undefined); const bool = (0, exports.from)("boolean", (x) => x === true || x === false); const nil = (0, exports.from)("nil", isNil_1.isNil); const dateString = (0, exports.from)("ISO date string", (x) => (0, isString_1.isString)(x) && String(new Date(x)) !== "Invalid Date"); const enum_ = (allowed, name = `one of [${allowed.join(",")}]`) => { const lookup = new Set(allowed); return (0, exports.from)(name !== null && name !== void 0 ? name : `one of [${allowed.join(",")}]`, (x) => lookup.has(x)); }; const oneOf = (...args) => { const typeNames = args.map(getName).join(" | "); return (0, exports.from)(`[${typeNames}]`, (x) => args.some((test) => test(x).isRight())); }; const optional = (type_) => oneOf(type_, undefined_); const nullable = (type_) => oneOf(type_, null_); exports.default = { from: exports.from, any, array, boolean: bool, dateString: dateString, dictionary, enum: enum_, nil, number, null: null_, nullable, oneOf, optional, record, string, undefined: undefined_, unknown, };