UNPKG

@baqhub/sdk

Version:

The official JavaScript SDK for the BAQ federated app platform.

400 lines (399 loc) 13.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.utf8Bytes = exports.base64Bytes = exports.isoDate = void 0; exports.object = object; exports.partialObject = partialObject; exports.dualObject = dualObject; exports.union = union; exports.exclusiveUnion = exclusiveUnion; exports.defaultValue = defaultValue; exports.arrayIgnore = arrayIgnore; exports.enumRecord = enumRecord; exports.listEnumValues = listEnumValues; exports.enumeration = enumeration; exports.weakEnumeration = weakEnumeration; exports.enumerationWithValues = enumerationWithValues; exports.weakEnumerationWithValues = weakEnumerationWithValues; exports.optional = optional; exports.clean = clean; exports.validate = validate; exports.tryDecode = tryDecode; exports.decode = decode; exports.encode = encode; const tslib_1 = require("tslib"); const Either_js_1 = require("fp-ts/lib/Either.js"); const function_js_1 = require("fp-ts/lib/function.js"); const t = tslib_1.__importStar(require("io-ts")); const io_ts_reporters_1 = tslib_1.__importDefault(require("io-ts-reporters")); const camelCase_js_1 = tslib_1.__importDefault(require("lodash/camelCase.js")); const isArray_js_1 = tslib_1.__importDefault(require("lodash/isArray.js")); const isNumber_js_1 = tslib_1.__importDefault(require("lodash/isNumber.js")); const isString_js_1 = tslib_1.__importDefault(require("lodash/isString.js")); const map_js_1 = tslib_1.__importDefault(require("lodash/map.js")); const snakeCase_js_1 = tslib_1.__importDefault(require("lodash/snakeCase.js")); const fixImport_js_1 = require("./fixImport.js"); const type_js_1 = require("./type.js"); tslib_1.__exportStar(require("io-ts"), exports); const reporter = (0, fixImport_js_1.fixImport)(io_ts_reporters_1.default); exports.isoDate = new t.Type("DateFromISOString", (u) => u instanceof Date, (u, c) => (0, function_js_1.pipe)(t.string.validate(u, c), (0, Either_js_1.chain)(s => { const d = new Date(s); return isNaN(d.getTime()) ? t.failure(u, c) : t.success(d); })), a => a.toISOString()); // // Readonly + Exact + ChangeCase combinator. // function getProps(codec) { switch (codec._tag) { case "RefinementType": case "ReadonlyType": return getProps(codec.type); case "InterfaceType": case "StrictType": case "PartialType": return codec.props; case "IntersectionType": return codec.types.reduce((props, type) => Object.assign(props, getProps(type)), {}); } } function getExactWithCaseTypeName(codec) { return `ExactWithCase<${codec.name}>`; } function prefixAwareCase(transform) { return (value) => { if (value[0] === "$") { return "$" + transform(value.substring(1)); } return transform(value); }; } function stripKeysAndChangeCase(o, props, transformCheckKey, transformResultKey) { const keys = Object.getOwnPropertyNames(o); let shouldStrip = false; const r = {}; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const checkKey = transformCheckKey(key); const resultKey = transformResultKey(key); const isPropKey = Object.prototype.hasOwnProperty.call(props, checkKey); if (!isPropKey || key !== resultKey) { shouldStrip = true; } if (isPropKey) { r[resultKey] = o[key]; } } return shouldStrip ? r : o; } function exactWithCase(codec, name = getExactWithCaseTypeName(codec)) { const props = getProps(codec); return new t.ExactType(name, codec.is, (u, c) => { const unknownResult = t.UnknownRecord.validate(u, c); if ((0, Either_js_1.isLeft)(unknownResult)) { return unknownResult; } const strippedObject = stripKeysAndChangeCase(unknownResult.right, props, prefixAwareCase(camelCase_js_1.default), prefixAwareCase(camelCase_js_1.default)); return codec.validate(strippedObject, c); }, a => { const encoded = codec.encode(a); return stripKeysAndChangeCase(encoded, props, t.identity, prefixAwareCase(snakeCase_js_1.default)); }, codec); } function object(props, name) { return t.readonly(exactWithCase(t.type(props, name))); } function partialObject(props, name) { return t.readonly(exactWithCase(t.partial(props, name))); } function dualObject(props, partialProps) { return t.intersection([object(props), partialObject(partialProps)]); } // // Standard union. // function pushAll(xs, ys) { const l = ys.length; for (let i = 0; i < l; i++) { xs.push(ys[i]); } } function union(codecs) { const name = `Union(${codecs.map(type => type.name).join(" | ")})`; return new t.UnionType(name, (u) => codecs.some(type => type.is(u)), (u, c) => { const errors = []; let result; for (let i = 0; i < codecs.length; i++) { const codec = codecs[i]; const r = codec.validate(u, t.appendContext(c, String(i), codec, u)); if ((0, Either_js_1.isLeft)(r)) { pushAll(errors, r.left); } else if (t.UnknownRecord.is(u)) { result = result ? { ...result, ...r.right, } : r.right; } else { return t.success(r.right); } } if ((0, type_js_1.isDefined)(result)) { return t.success(result); } return t.failures(errors); }, codecs.every(c => c.encode === t.identity) ? t.identity : a => { let result; for (const codec of codecs) { if (!codec.is(a)) { continue; } const value = codec.encode(a); if (t.UnknownRecord.is(value)) { const value = codec.encode(a); result = result ? { ...result, ...value, } : value; } else { return value; } } if ((0, type_js_1.isDefined)(result)) { return result; } // https://github.com/gcanti/io-ts/pull/305 throw new Error(`no codec found to encode value in union type ${name}`); }, codecs); } // // Todo: Optimized union for records. // // // Exclusive union. // function exclusiveUnion(codecs) { const name = `ExclusiveUnion(${codecs.map(type => type.name).join(" | ")})`; return new t.UnionType(name, (u) => codecs.some(type => type.is(u)), (u, c) => { const errors = []; const successes = []; for (let i = 0; i < codecs.length; i++) { const codec = codecs[i]; const r = codec.validate(u, t.appendContext(c, String(i), codec, u)); if ((0, Either_js_1.isLeft)(r)) { errors.push(...r.left); } else { successes.push(r.right); } } if (successes.length === 1) { return t.success(successes[0]); } else if (successes.length > 1) { return t.failure(u, c, "Multiple matching codecs."); } else { return t.failures(errors); } }, codecs.every(c => c.encode === t.identity) ? t.identity : a => { for (const codec of codecs) { if (codec.is(a)) { return codec.encode(a); } } // https://github.com/gcanti/io-ts/pull/305 throw new Error(`no codec found to encode value in union type ${name}`); }, codecs); } // // Default value. // function defaultValue(type, defaultValue) { return new t.Type("DefaultOf" + type.name, type.is, value => { if (!(0, type_js_1.isDefined)(value)) { return t.success(defaultValue); } return type.decode(value); }, value => { if (value === defaultValue) { return undefined; } return type.encode(value); }); } // // Array that ignores invalid elements. // function arrayIgnore(itemsType) { return new t.Type("ArrayIgnoreOf" + itemsType.name, (value) => { if (!(0, isArray_js_1.default)(value)) { return false; } return value.every(value => itemsType.is(value)); }, (value, context) => { if (!(0, isArray_js_1.default)(value)) { return t.failure(value, context); } const array = value .map(item => itemsType.decode(item)) .filter(Either_js_1.isRight) .map(item => item.right); return t.success(array); }, value => { return value.map(item => itemsType.encode(item)); }); } // // Partial record combinator for enums. // function enumRecord(domain, codomain) { return t.record(domain, codomain); } function listEnumValues(sourceEnum) { function isStringKey(key) { const numberKey = Number(key); return isNaN(numberKey) || sourceEnum[sourceEnum[key] || ""] !== numberKey; } function keyToValue(key) { return sourceEnum[key]; } return Object.keys(sourceEnum).filter(isStringKey).map(keyToValue); } function enumerationBase(name, sourceEnum) { const enumValues = new Set(listEnumValues(sourceEnum)); function isEnumValue(value) { return ((0, isString_js_1.default)(value) || (0, isNumber_js_1.default)(value)) && enumValues.has(value); } return new t.Type(name, isEnumValue, (value, context) => { if (isEnumValue(value)) { return t.success(value); } return t.failure(value, context); }, value => value); } function enumeration(sourceEnum) { return enumerationBase("Enum", sourceEnum); } function weakEnumeration(sourceEnum) { return enumerationBase("WeakEnum", sourceEnum); } function toLowerCase(source) { return source.toLowerCase(); } function enumerationWithValuesBase(name, sourceEnum, values, { isCaseSensitive } = { isCaseSensitive: true }) { // Case sensitivity. const valueTransform = isCaseSensitive ? t.identity : toLowerCase; // Mapper. const invertedValues = (0, map_js_1.default)(values, (value, key) => [valueTransform(value), key]); const valueMap = new Map(invertedValues); // Type guard. const enumValues = new Set(listEnumValues(sourceEnum)); function isEnumValue(value) { return ((0, isString_js_1.default)(value) || (0, isNumber_js_1.default)(value)) && enumValues.has(value); } return new t.Type(name, isEnumValue, (value, context) => { const enumValue = (0, isString_js_1.default)(value) && valueMap.get(valueTransform(value)); if (!enumValue) { return t.failure(value, context); } return t.success(enumValue); }, value => values[value]); } function enumerationWithValues(sourceEnum, values, options) { return enumerationWithValuesBase("EnumWithValues", sourceEnum, values, options); } function weakEnumerationWithValues(sourceEnum, values, options) { return enumerationWithValuesBase("WeakEnumWithValues", sourceEnum, values, options); } // // Bytes type. // class Base64Bytes extends t.Type { constructor() { function is(value) { return value instanceof Uint8Array; } function validate(value, context) { if (is(value)) { return t.success(value); } try { const valueBytes = atob(String(value)); return t.success(Uint8Array.from(valueBytes, c => c.charCodeAt(0))); } catch (_error) { return t.failure(value, context); } } function encode(value) { const bytes = String.fromCharCode.apply(null, value); return btoa(bytes); } super("Base64Bytes", is, validate, encode); } } exports.base64Bytes = new Base64Bytes(); class Utf8Bytes extends t.Type { constructor() { const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); function is(value) { return value instanceof Uint8Array; } function validate(value, context) { if (is(value)) { return t.success(value); } try { return t.success(textEncoder.encode(String(value))); } catch (_error) { return t.failure(value, context); } } function encode(value) { return textDecoder.decode(value); } super("Utf8Bytes", is, validate, encode); } } exports.utf8Bytes = new Utf8Bytes(); function optional(model) { return union([t.undefined, model]); } function clean(model) { return model; } function validate(model, value) { if (!model.is(value)) { throw new Error("Error while validating."); } return value; } function tryDecode(model, value) { const result = model.decode(value); if ((0, Either_js_1.isLeft)(result)) { return undefined; } return result.right; } function decode(model, value) { const result = model.decode(value); if ((0, Either_js_1.isLeft)(result)) { console.log(...reporter.report(result)); throw new Error("Error while decoding."); } return result.right; } function encode(model, value) { return model.encode(value); }