UNPKG

@sinclair/typebox

Version:

Json Schema Type Builder with Static Type Resolution for TypeScript

254 lines (252 loc) 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Convert = Convert; const index_1 = require("../clone/index"); const index_2 = require("../check/index"); const index_3 = require("../deref/index"); const index_4 = require("../../type/symbols/index"); // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ const index_5 = require("../guard/index"); // ------------------------------------------------------------------ // Conversions // ------------------------------------------------------------------ function IsStringNumeric(value) { return (0, index_5.IsString)(value) && !isNaN(value) && !isNaN(parseFloat(value)); } function IsValueToString(value) { return (0, index_5.IsBigInt)(value) || (0, index_5.IsBoolean)(value) || (0, index_5.IsNumber)(value); } function IsValueTrue(value) { return value === true || ((0, index_5.IsNumber)(value) && value === 1) || ((0, index_5.IsBigInt)(value) && value === BigInt('1')) || ((0, index_5.IsString)(value) && (value.toLowerCase() === 'true' || value === '1')); } function IsValueFalse(value) { return value === false || ((0, index_5.IsNumber)(value) && (value === 0 || Object.is(value, -0))) || ((0, index_5.IsBigInt)(value) && value === BigInt('0')) || ((0, index_5.IsString)(value) && (value.toLowerCase() === 'false' || value === '0' || value === '-0')); } function IsTimeStringWithTimeZone(value) { return (0, index_5.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_5.IsString)(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value); } function IsDateTimeStringWithTimeZone(value) { return (0, index_5.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_5.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_5.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_5.IsString)(schema.const) ? TryConvertLiteralString(value, schema.const) : (0, index_5.IsNumber)(schema.const) ? TryConvertLiteralNumber(value, schema.const) : (0, index_5.IsBoolean)(schema.const) ? TryConvertLiteralBoolean(value, schema.const) : value); } function TryConvertBoolean(value) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value; } function TryConvertBigInt(value) { return IsStringNumeric(value) ? BigInt(parseInt(value)) : (0, index_5.IsNumber)(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value; } function TryConvertString(value) { return IsValueToString(value) ? value.toString() : (0, index_5.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_5.IsNumber)(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value; } function TryConvertNull(value) { return (0, index_5.IsString)(value) && value.toLowerCase() === 'null' ? null : value; } function TryConvertUndefined(value) { return (0, index_5.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_5.IsDate)(value) ? value : (0, index_5.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) { const elements = (0, index_5.IsArray)(value) ? value : [value]; return elements.map((element) => Visit(schema.items, references, element)); } 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); } function FromIntersect(schema, references, value) { return schema.allOf.reduce((value, schema) => Visit(schema, references, value), value); } 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) { if (!(0, index_5.IsObject)(value)) return value; for (const propertyKey of Object.getOwnPropertyNames(schema.properties)) { if (!(0, index_5.HasPropertyKey)(value, propertyKey)) continue; value[propertyKey] = Visit(schema.properties[propertyKey], references, value[propertyKey]); } return value; } function FromRecord(schema, references, value) { const isConvertable = (0, index_5.IsObject)(value); if (!isConvertable) return value; const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0]; const property = schema.patternProperties[propertyKey]; for (const [propKey, propValue] of Object.entries(value)) { value[propKey] = Visit(property, references, propValue); } return value; } 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_5.IsString)(value) || (0, index_5.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_5.IsArray)(value) && !(0, index_5.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, (0, index_1.Clone)(value)); if (!(0, index_2.Check)(subschema, references, converted)) continue; return converted; } return value; } function AddReference(references, schema) { references.push(schema); return references; } function Visit(schema, references, value) { const references_ = (0, index_5.IsString)(schema.$id) ? AddReference(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); } } /** `[Mutable]` 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]); }