| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0; |
| const rules_1 = require("../rules"); |
| const applicability_1 = require("./applicability"); |
| const errors_1 = require("../errors"); |
| const codegen_1 = require("../codegen"); |
| const util_1 = require("../util"); |
| var DataType; |
| (function (DataType) { |
| DataType[DataType["Correct"] = 0] = "Correct"; |
| DataType[DataType["Wrong"] = 1] = "Wrong"; |
| })(DataType = exports.DataType || (exports.DataType = {})); |
| function getSchemaTypes(schema) { |
| const types = getJSONTypes(schema.type); |
| const hasNull = types.includes("null"); |
| if (hasNull) { |
| if (schema.nullable === false) |
| throw new Error("type: null contradicts nullable: false"); |
| } |
| else { |
| if (!types.length && schema.nullable !== undefined) { |
| throw new Error('"nullable" cannot be used without "type"'); |
| } |
| if (schema.nullable === true) |
| types.push("null"); |
| } |
| return types; |
| } |
| exports.getSchemaTypes = getSchemaTypes; |
| function getJSONTypes(ts) { |
| const types = Array.isArray(ts) ? ts : ts ? [ts] : []; |
| if (types.every(rules_1.isJSONType)) |
| return types; |
| throw new Error("type must be JSONType or JSONType[]: " + types.join(",")); |
| } |
| exports.getJSONTypes = getJSONTypes; |
| function coerceAndCheckDataType(it, types) { |
| const { gen, data, opts } = it; |
| const coerceTo = coerceToTypes(types, opts.coerceTypes); |
| const checkTypes = types.length > 0 && |
| !(coerceTo.length === 0 && types.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types[0])); |
| if (checkTypes) { |
| const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong); |
| gen.if(wrongType, () => { |
| if (coerceTo.length) |
| coerceData(it, types, coerceTo); |
| else |
| reportTypeError(it); |
| }); |
| } |
| return checkTypes; |
| } |
| exports.coerceAndCheckDataType = coerceAndCheckDataType; |
| const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]); |
| function coerceToTypes(types, coerceTypes) { |
| return coerceTypes |
| ? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array")) |
| : []; |
| } |
| function coerceData(it, types, coerceTo) { |
| const { gen, data, opts } = it; |
| const dataType = gen.let("dataType", (0, codegen_1._) `typeof ${data}`); |
| const coerced = gen.let("coerced", (0, codegen_1._) `undefined`); |
| if (opts.coerceTypes === "array") { |
| gen.if((0, codegen_1._) `${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen |
| .assign(data, (0, codegen_1._) `${data}[0]`) |
| .assign(dataType, (0, codegen_1._) `typeof ${data}`) |
| .if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data))); |
| } |
| gen.if((0, codegen_1._) `${coerced} !== undefined`); |
| for (const t of coerceTo) { |
| if (COERCIBLE.has(t) || (t === "array" && opts.coerceTypes === "array")) { |
| coerceSpecificType(t); |
| } |
| } |
| gen.else(); |
| reportTypeError(it); |
| gen.endIf(); |
| gen.if((0, codegen_1._) `${coerced} !== undefined`, () => { |
| gen.assign(data, coerced); |
| assignParentData(it, coerced); |
| }); |
| function coerceSpecificType(t) { |
| switch (t) { |
| case "string": |
| gen |
| .elseIf((0, codegen_1._) `${dataType} == "number" || ${dataType} == "boolean"`) |
| .assign(coerced, (0, codegen_1._) `"" + ${data}`) |
| .elseIf((0, codegen_1._) `${data} === null`) |
| .assign(coerced, (0, codegen_1._) `""`); |
| return; |
| case "number": |
| gen |
| .elseIf((0, codegen_1._) `${dataType} == "boolean" || ${data} === null |
| || (${dataType} == "string" && ${data} && ${data} == +${data})`) |
| .assign(coerced, (0, codegen_1._) `+${data}`); |
| return; |
| case "integer": |
| gen |
| .elseIf((0, codegen_1._) `${dataType} === "boolean" || ${data} === null |
| || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`) |
| .assign(coerced, (0, codegen_1._) `+${data}`); |
| return; |
| case "boolean": |
| gen |
| .elseIf((0, codegen_1._) `${data} === "false" || ${data} === 0 || ${data} === null`) |
| .assign(coerced, false) |
| .elseIf((0, codegen_1._) `${data} === "true" || ${data} === 1`) |
| .assign(coerced, true); |
| return; |
| case "null": |
| gen.elseIf((0, codegen_1._) `${data} === "" || ${data} === 0 || ${data} === false`); |
| gen.assign(coerced, null); |
| return; |
| case "array": |
| gen |
| .elseIf((0, codegen_1._) `${dataType} === "string" || ${dataType} === "number" |
| || ${dataType} === "boolean" || ${data} === null`) |
| .assign(coerced, (0, codegen_1._) `[${data}]`); |
| } |
| } |
| } |
| function assignParentData({ gen, parentData, parentDataProperty }, expr) { |
| // TODO use gen.property |
| gen.if((0, codegen_1._) `${parentData} !== undefined`, () => gen.assign((0, codegen_1._) `${parentData}[${parentDataProperty}]`, expr)); |
| } |
| function checkDataType(dataType, data, strictNums, correct = DataType.Correct) { |
| const EQ = correct === DataType.Correct ? codegen_1.operators.EQ : codegen_1.operators.NEQ; |
| let cond; |
| switch (dataType) { |
| case "null": |
| return (0, codegen_1._) `${data} ${EQ} null`; |
| case "array": |
| cond = (0, codegen_1._) `Array.isArray(${data})`; |
| break; |
| case "object": |
| cond = (0, codegen_1._) `${data} && typeof ${data} == "object" && !Array.isArray(${data})`; |
| break; |
| case "integer": |
| cond = numCond((0, codegen_1._) `!(${data} % 1) && !isNaN(${data})`); |
| break; |
| case "number": |
| cond = numCond(); |
| break; |
| default: |
| return (0, codegen_1._) `typeof ${data} ${EQ} ${dataType}`; |
| } |
| return correct === DataType.Correct ? cond : (0, codegen_1.not)(cond); |
| function numCond(_cond = codegen_1.nil) { |
| return (0, codegen_1.and)((0, codegen_1._) `typeof ${data} == "number"`, _cond, strictNums ? (0, codegen_1._) `isFinite(${data})` : codegen_1.nil); |
| } |
| } |
| exports.checkDataType = checkDataType; |
| function checkDataTypes(dataTypes, data, strictNums, correct) { |
| if (dataTypes.length === 1) { |
| return checkDataType(dataTypes[0], data, strictNums, correct); |
| } |
| let cond; |
| const types = (0, util_1.toHash)(dataTypes); |
| if (types.array && types.object) { |
| const notObj = (0, codegen_1._) `typeof ${data} != "object"`; |
| cond = types.null ? notObj : (0, codegen_1._) `!${data} || ${notObj}`; |
| delete types.null; |
| delete types.array; |
| delete types.object; |
| } |
| else { |
| cond = codegen_1.nil; |
| } |
| if (types.number) |
| delete types.integer; |
| for (const t in types) |
| cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct)); |
| return cond; |
| } |
| exports.checkDataTypes = checkDataTypes; |
| const typeError = { |
| message: ({ schema }) => `must be ${schema}`, |
| params: ({ schema, schemaValue }) => typeof schema == "string" ? (0, codegen_1._) `{type: ${schema}}` : (0, codegen_1._) `{type: ${schemaValue}}`, |
| }; |
| function reportTypeError(it) { |
| const cxt = getTypeErrorContext(it); |
| (0, errors_1.reportError)(cxt, typeError); |
| } |
| exports.reportTypeError = reportTypeError; |
| function getTypeErrorContext(it) { |
| const { gen, data, schema } = it; |
| const schemaCode = (0, util_1.schemaRefOrVal)(it, schema, "type"); |
| return { |
| gen, |
| keyword: "type", |
| data, |
| schema: schema.type, |
| schemaCode, |
| schemaValue: schemaCode, |
| parentSchema: schema, |
| params: {}, |
| it, |
| }; |
| } |
| //# sourceMappingURL=dataType.js.map |