1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0;
|
4 | const rules_1 = require("../rules");
|
5 | const applicability_1 = require("./applicability");
|
6 | const errors_1 = require("../errors");
|
7 | const codegen_1 = require("../codegen");
|
8 | const util_1 = require("../util");
|
9 | var DataType;
|
10 | (function (DataType) {
|
11 | DataType[DataType["Correct"] = 0] = "Correct";
|
12 | DataType[DataType["Wrong"] = 1] = "Wrong";
|
13 | })(DataType = exports.DataType || (exports.DataType = {}));
|
14 | function getSchemaTypes(schema) {
|
15 | const types = getJSONTypes(schema.type);
|
16 | const hasNull = types.includes("null");
|
17 | if (hasNull) {
|
18 | if (schema.nullable === false)
|
19 | throw new Error("type: null contradicts nullable: false");
|
20 | }
|
21 | else {
|
22 | if (!types.length && schema.nullable !== undefined) {
|
23 | throw new Error('"nullable" cannot be used without "type"');
|
24 | }
|
25 | if (schema.nullable === true)
|
26 | types.push("null");
|
27 | }
|
28 | return types;
|
29 | }
|
30 | exports.getSchemaTypes = getSchemaTypes;
|
31 | function getJSONTypes(ts) {
|
32 | const types = Array.isArray(ts) ? ts : ts ? [ts] : [];
|
33 | if (types.every(rules_1.isJSONType))
|
34 | return types;
|
35 | throw new Error("type must be JSONType or JSONType[]: " + types.join(","));
|
36 | }
|
37 | exports.getJSONTypes = getJSONTypes;
|
38 | function coerceAndCheckDataType(it, types) {
|
39 | const { gen, data, opts } = it;
|
40 | const coerceTo = coerceToTypes(types, opts.coerceTypes);
|
41 | const checkTypes = types.length > 0 &&
|
42 | !(coerceTo.length === 0 && types.length === 1 && applicability_1.schemaHasRulesForType(it, types[0]));
|
43 | if (checkTypes) {
|
44 | const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong);
|
45 | gen.if(wrongType, () => {
|
46 | if (coerceTo.length)
|
47 | coerceData(it, types, coerceTo);
|
48 | else
|
49 | reportTypeError(it);
|
50 | });
|
51 | }
|
52 | return checkTypes;
|
53 | }
|
54 | exports.coerceAndCheckDataType = coerceAndCheckDataType;
|
55 | const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
|
56 | function coerceToTypes(types, coerceTypes) {
|
57 | return coerceTypes
|
58 | ? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array"))
|
59 | : [];
|
60 | }
|
61 | function coerceData(it, types, coerceTo) {
|
62 | const { gen, data, opts } = it;
|
63 | const dataType = gen.let("dataType", codegen_1._ `typeof ${data}`);
|
64 | const coerced = gen.let("coerced", codegen_1._ `undefined`);
|
65 | if (opts.coerceTypes === "array") {
|
66 | gen.if(codegen_1._ `${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen
|
67 | .assign(data, codegen_1._ `${data}[0]`)
|
68 | .assign(dataType, codegen_1._ `typeof ${data}`)
|
69 | .if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data)));
|
70 | }
|
71 | gen.if(codegen_1._ `${coerced} !== undefined`);
|
72 | for (const t of coerceTo) {
|
73 | if (COERCIBLE.has(t) || (t === "array" && opts.coerceTypes === "array")) {
|
74 | coerceSpecificType(t);
|
75 | }
|
76 | }
|
77 | gen.else();
|
78 | reportTypeError(it);
|
79 | gen.endIf();
|
80 | gen.if(codegen_1._ `${coerced} !== undefined`, () => {
|
81 | gen.assign(data, coerced);
|
82 | assignParentData(it, coerced);
|
83 | });
|
84 | function coerceSpecificType(t) {
|
85 | switch (t) {
|
86 | case "string":
|
87 | gen
|
88 | .elseIf(codegen_1._ `${dataType} == "number" || ${dataType} == "boolean"`)
|
89 | .assign(coerced, codegen_1._ `"" + ${data}`)
|
90 | .elseIf(codegen_1._ `${data} === null`)
|
91 | .assign(coerced, codegen_1._ `""`);
|
92 | return;
|
93 | case "number":
|
94 | gen
|
95 | .elseIf(codegen_1._ `${dataType} == "boolean" || ${data} === null
|
96 | || (${dataType} == "string" && ${data} && ${data} == +${data})`)
|
97 | .assign(coerced, codegen_1._ `+${data}`);
|
98 | return;
|
99 | case "integer":
|
100 | gen
|
101 | .elseIf(codegen_1._ `${dataType} === "boolean" || ${data} === null
|
102 | || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`)
|
103 | .assign(coerced, codegen_1._ `+${data}`);
|
104 | return;
|
105 | case "boolean":
|
106 | gen
|
107 | .elseIf(codegen_1._ `${data} === "false" || ${data} === 0 || ${data} === null`)
|
108 | .assign(coerced, false)
|
109 | .elseIf(codegen_1._ `${data} === "true" || ${data} === 1`)
|
110 | .assign(coerced, true);
|
111 | return;
|
112 | case "null":
|
113 | gen.elseIf(codegen_1._ `${data} === "" || ${data} === 0 || ${data} === false`);
|
114 | gen.assign(coerced, null);
|
115 | return;
|
116 | case "array":
|
117 | gen
|
118 | .elseIf(codegen_1._ `${dataType} === "string" || ${dataType} === "number"
|
119 | || ${dataType} === "boolean" || ${data} === null`)
|
120 | .assign(coerced, codegen_1._ `[${data}]`);
|
121 | }
|
122 | }
|
123 | }
|
124 | function assignParentData({ gen, parentData, parentDataProperty }, expr) {
|
125 |
|
126 | gen.if(codegen_1._ `${parentData} !== undefined`, () => gen.assign(codegen_1._ `${parentData}[${parentDataProperty}]`, expr));
|
127 | }
|
128 | function checkDataType(dataType, data, strictNums, correct = DataType.Correct) {
|
129 | const EQ = correct === DataType.Correct ? codegen_1.operators.EQ : codegen_1.operators.NEQ;
|
130 | let cond;
|
131 | switch (dataType) {
|
132 | case "null":
|
133 | return codegen_1._ `${data} ${EQ} null`;
|
134 | case "array":
|
135 | cond = codegen_1._ `Array.isArray(${data})`;
|
136 | break;
|
137 | case "object":
|
138 | cond = codegen_1._ `${data} && typeof ${data} == "object" && !Array.isArray(${data})`;
|
139 | break;
|
140 | case "integer":
|
141 | cond = numCond(codegen_1._ `!(${data} % 1) && !isNaN(${data})`);
|
142 | break;
|
143 | case "number":
|
144 | cond = numCond();
|
145 | break;
|
146 | default:
|
147 | return codegen_1._ `typeof ${data} ${EQ} ${dataType}`;
|
148 | }
|
149 | return correct === DataType.Correct ? cond : codegen_1.not(cond);
|
150 | function numCond(_cond = codegen_1.nil) {
|
151 | return codegen_1.and(codegen_1._ `typeof ${data} == "number"`, _cond, strictNums ? codegen_1._ `isFinite(${data})` : codegen_1.nil);
|
152 | }
|
153 | }
|
154 | exports.checkDataType = checkDataType;
|
155 | function checkDataTypes(dataTypes, data, strictNums, correct) {
|
156 | if (dataTypes.length === 1) {
|
157 | return checkDataType(dataTypes[0], data, strictNums, correct);
|
158 | }
|
159 | let cond;
|
160 | const types = util_1.toHash(dataTypes);
|
161 | if (types.array && types.object) {
|
162 | const notObj = codegen_1._ `typeof ${data} != "object"`;
|
163 | cond = types.null ? notObj : codegen_1._ `!${data} || ${notObj}`;
|
164 | delete types.null;
|
165 | delete types.array;
|
166 | delete types.object;
|
167 | }
|
168 | else {
|
169 | cond = codegen_1.nil;
|
170 | }
|
171 | if (types.number)
|
172 | delete types.integer;
|
173 | for (const t in types)
|
174 | cond = codegen_1.and(cond, checkDataType(t, data, strictNums, correct));
|
175 | return cond;
|
176 | }
|
177 | exports.checkDataTypes = checkDataTypes;
|
178 | const typeError = {
|
179 | message: ({ schema }) => `must be ${schema}`,
|
180 | params: ({ schema, schemaValue }) => typeof schema == "string" ? codegen_1._ `{type: ${schema}}` : codegen_1._ `{type: ${schemaValue}}`,
|
181 | };
|
182 | function reportTypeError(it) {
|
183 | const cxt = getTypeErrorContext(it);
|
184 | errors_1.reportError(cxt, typeError);
|
185 | }
|
186 | exports.reportTypeError = reportTypeError;
|
187 | function getTypeErrorContext(it) {
|
188 | const { gen, data, schema } = it;
|
189 | const schemaCode = util_1.schemaRefOrVal(it, schema, "type");
|
190 | return {
|
191 | gen,
|
192 | keyword: "type",
|
193 | data,
|
194 | schema: schema.type,
|
195 | schemaCode,
|
196 | schemaValue: schemaCode,
|
197 | parentSchema: schema,
|
198 | params: {},
|
199 | it,
|
200 | };
|
201 | }
|
202 |
|
\ | No newline at end of file |