UNPKG

@sinclair/typebox

Version:

Json Schema Type Builder with Static Type Resolution for TypeScript

259 lines (257 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Convert = void 0; const index_1 = require("../clone/index"); const index_2 = require("../check/index"); const index_3 = require("../deref/index"); const type_1 = require("../../type/guard/type"); const index_4 = require("../../type/symbols/index"); const index_5 = require("../../type/composite/index"); // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ const index_6 = require("../guard/index"); // ------------------------------------------------------------------ // Conversions // ------------------------------------------------------------------ function IsStringNumeric(value) { return (0, index_6.IsString)(value) && !isNaN(value) && !isNaN(parseFloat(value)); } function IsValueToString(value) { return (0, index_6.IsBigInt)(value) || (0, index_6.IsBoolean)(value) || (0, index_6.IsNumber)(value); } function IsValueTrue(value) { return value === true || ((0, index_6.IsNumber)(value) && value === 1) || ((0, index_6.IsBigInt)(value) && value === BigInt('1')) || ((0, index_6.IsString)(value) && (value.toLowerCase() === 'true' || value === '1')); } function IsValueFalse(value) { return value === false || ((0, index_6.IsNumber)(value) && (value === 0 || Object.is(value, -0))) || ((0, index_6.IsBigInt)(value) && value === BigInt('0')) || ((0, index_6.IsString)(value) && (value.toLowerCase() === 'false' || value === '0' || value === '-0')); } function IsTimeStringWithTimeZone(value) { return (0, index_6.IsString)(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value); } function IsTimeStringWithoutTimeZone(value) { return (0, index_6.IsString)(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value); } function IsDateTimeStringWithTimeZone(value) { return (0, index_6.IsString)(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value); } function IsDateTimeStringWithoutTimeZone(value) { return (0, index_6.IsString)(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value); } function IsDateString(value) { return (0, index_6.IsString)(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value); } // ------------------------------------------------------------------ // Convert // ------------------------------------------------------------------ function TryConvertLiteralString(value, target) { const conversion = TryConvertString(value); return conversion === target ? conversion : value; } function TryConvertLiteralNumber(value, target) { const conversion = TryConvertNumber(value); return conversion === target ? conversion : value; } function TryConvertLiteralBoolean(value, target) { const conversion = TryConvertBoolean(value); return conversion === target ? conversion : value; } // prettier-ignore function TryConvertLiteral(schema, value) { return ((0, index_6.IsString)(schema.const) ? TryConvertLiteralString(value, schema.const) : (0, index_6.IsNumber)(schema.const) ? TryConvertLiteralNumber(value, schema.const) : (0, index_6.IsBoolean)(schema.const) ? TryConvertLiteralBoolean(value, schema.const) : (0, index_1.Clone)(value)); } function TryConvertBoolean(value) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value; } function TryConvertBigInt(value) { return IsStringNumeric(value) ? BigInt(parseInt(value)) : (0, index_6.IsNumber)(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value; } function TryConvertString(value) { return IsValueToString(value) ? value.toString() : (0, index_6.IsSymbol)(value) && value.description !== undefined ? value.description.toString() : value; } function TryConvertNumber(value) { return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value; } function TryConvertInteger(value) { return IsStringNumeric(value) ? parseInt(value) : (0, index_6.IsNumber)(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value; } function TryConvertNull(value) { return (0, index_6.IsString)(value) && value.toLowerCase() === 'null' ? null : value; } function TryConvertUndefined(value) { return (0, index_6.IsString)(value) && value === 'undefined' ? undefined : value; } // ------------------------------------------------------------------ // note: this function may return an invalid dates for the regex // tests above. Invalid dates will however be checked during the // casting function and will return a epoch date if invalid. // Consider better string parsing for the iso dates in future // revisions. // ------------------------------------------------------------------ // prettier-ignore function TryConvertDate(value) { return ((0, index_6.IsDate)(value) ? value : (0, index_6.IsNumber)(value) ? new Date(value) : IsValueTrue(value) ? new Date(1) : IsValueFalse(value) ? new Date(0) : IsStringNumeric(value) ? new Date(parseInt(value)) : IsTimeStringWithoutTimeZone(value) ? new Date(`1970-01-01T${value}.000Z`) : IsTimeStringWithTimeZone(value) ? new Date(`1970-01-01T${value}`) : IsDateTimeStringWithoutTimeZone(value) ? new Date(`${value}.000Z`) : IsDateTimeStringWithTimeZone(value) ? new Date(value) : IsDateString(value) ? new Date(`${value}T00:00:00.000Z`) : value); } // ------------------------------------------------------------------ // Default // ------------------------------------------------------------------ function Default(value) { return value; } // ------------------------------------------------------------------ // Convert // ------------------------------------------------------------------ function FromArray(schema, references, value) { if ((0, index_6.IsArray)(value)) { return value.map((value) => Visit(schema.items, references, value)); } return value; } function FromBigInt(schema, references, value) { return TryConvertBigInt(value); } function FromBoolean(schema, references, value) { return TryConvertBoolean(value); } function FromDate(schema, references, value) { return TryConvertDate(value); } function FromInteger(schema, references, value) { return TryConvertInteger(value); } // prettier-ignore function FromIntersect(schema, references, value) { const allObjects = schema.allOf.every(schema => (0, type_1.IsObject)(schema)); if (allObjects) return Visit((0, index_5.Composite)(schema.allOf), references, value); return Visit(schema.allOf[0], references, value); // todo: fix this } function FromLiteral(schema, references, value) { return TryConvertLiteral(schema, value); } function FromNull(schema, references, value) { return TryConvertNull(value); } function FromNumber(schema, references, value) { return TryConvertNumber(value); } // prettier-ignore function FromObject(schema, references, value) { const isConvertable = (0, index_6.IsObject)(value); if (!isConvertable) return value; return Object.getOwnPropertyNames(schema.properties).reduce((value, key) => { return !(0, index_6.IsUndefined)(value[key]) ? ({ ...value, [key]: Visit(schema.properties[key], references, value[key]) }) : ({ ...value }); }, value); } function FromRecord(schema, references, value) { const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0]; const property = schema.patternProperties[propertyKey]; const result = {}; for (const [propKey, propValue] of Object.entries(value)) { result[propKey] = Visit(property, references, propValue); } return result; } function FromRef(schema, references, value) { return Visit((0, index_3.Deref)(schema, references), references, value); } function FromString(schema, references, value) { return TryConvertString(value); } function FromSymbol(schema, references, value) { return (0, index_6.IsString)(value) || (0, index_6.IsNumber)(value) ? Symbol(value) : value; } function FromThis(schema, references, value) { return Visit((0, index_3.Deref)(schema, references), references, value); } // prettier-ignore function FromTuple(schema, references, value) { const isConvertable = (0, index_6.IsArray)(value) && !(0, index_6.IsUndefined)(schema.items); if (!isConvertable) return value; return value.map((value, index) => { return (index < schema.items.length) ? Visit(schema.items[index], references, value) : value; }); } function FromUndefined(schema, references, value) { return TryConvertUndefined(value); } function FromUnion(schema, references, value) { for (const subschema of schema.anyOf) { const converted = Visit(subschema, references, value); if ((0, index_2.Check)(subschema, references, converted)) { return converted; } } return value; } function Visit(schema, references, value) { const references_ = (0, index_6.IsString)(schema.$id) ? [...references, schema] : references; const schema_ = schema; switch (schema[index_4.Kind]) { case 'Array': return FromArray(schema_, references_, value); case 'BigInt': return FromBigInt(schema_, references_, value); case 'Boolean': return FromBoolean(schema_, references_, value); case 'Date': return FromDate(schema_, references_, value); case 'Integer': return FromInteger(schema_, references_, value); case 'Intersect': return FromIntersect(schema_, references_, value); case 'Literal': return FromLiteral(schema_, references_, value); case 'Null': return FromNull(schema_, references_, value); case 'Number': return FromNumber(schema_, references_, value); case 'Object': return FromObject(schema_, references_, value); case 'Record': return FromRecord(schema_, references_, value); case 'Ref': return FromRef(schema_, references_, value); case 'String': return FromString(schema_, references_, value); case 'Symbol': return FromSymbol(schema_, references_, value); case 'This': return FromThis(schema_, references_, value); case 'Tuple': return FromTuple(schema_, references_, value); case 'Undefined': return FromUndefined(schema_, references_, value); case 'Union': return FromUnion(schema_, references_, value); default: return Default(value); } } /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ // prettier-ignore function Convert(...args) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]); } exports.Convert = Convert;