"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 = 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; // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents 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