99 lines
3.5 KiB
JavaScript
99 lines
3.5 KiB
JavaScript
import { addContextToErr } from "./err.js";
|
|
// validateObjPropTypes takes an object as input, along with a list of checks
|
|
// that should performed on the properties of the object. If all of the
|
|
// properties are present in the object and adhere to the suggested types,
|
|
// `null` is returned. Otherwise a string is returned indicating the first
|
|
// property that failed a check.
|
|
//
|
|
// This function is intended to be used on objects that were decoded from JSON
|
|
// after being received by an untrusted source.
|
|
//
|
|
// validateObjProperties supports all of the basic types, as well as arrays for
|
|
// types boolean, number, bigint, and string. In the future, support for more
|
|
// types may be added as well.
|
|
//
|
|
// Below is an example object, followed by the call that you would make to
|
|
// checkObj to verify the object.
|
|
//
|
|
// const expectedObj = {
|
|
// aNum: 35,
|
|
// aStr: "hi",
|
|
// aBig: 10n,
|
|
// aArr: [1, 2, 3],
|
|
// };
|
|
//
|
|
// const err = validateObjPropTypes(expectedObj, [
|
|
// ["aNum", "number"],
|
|
// ["aStr", "string"],
|
|
// ["aBig", "bigint"],
|
|
// ["aArr", "numberArray"],
|
|
// ["aUint8Array", "Uint8Array"],
|
|
// ]);
|
|
function validateObjPropTypes(obj, checks) {
|
|
for (let i = 0; i < checks.length; i++) {
|
|
const [property, expectedType] = checks[i];
|
|
// Loop through the array cases.
|
|
const arrayCases = [
|
|
["booleanArray", "boolean"],
|
|
["numberArray", "number"],
|
|
["bigintArray", "bigint"],
|
|
["stringArray", "string"],
|
|
];
|
|
let checkPassed = false;
|
|
for (let j = 0; j < arrayCases.length; j++) {
|
|
// If this is not an array case, ignore it.
|
|
const [arrCaseType, arrType] = arrayCases[j];
|
|
if (expectedType !== arrCaseType) {
|
|
continue;
|
|
}
|
|
// Check every element in the array.
|
|
const err = validateArrayTypes(obj[property], arrType);
|
|
if (err !== null) {
|
|
return addContextToErr(err, `check failed for array property '${property}'`);
|
|
}
|
|
// We found the expected type for this check, we can stop checking the
|
|
// rest.
|
|
checkPassed = true;
|
|
break;
|
|
}
|
|
// If the type was an array type, we don't need to perform the next check.
|
|
if (checkPassed === true) {
|
|
continue;
|
|
}
|
|
// Uint8Array check.
|
|
if (expectedType === "Uint8Array") {
|
|
if (obj[property] instanceof Uint8Array) {
|
|
continue;
|
|
}
|
|
else {
|
|
return `check failed for property '${property};, expecting Uint8Array`;
|
|
}
|
|
}
|
|
// Generic typeof check.
|
|
const type = typeof obj[property];
|
|
if (type !== expectedType) {
|
|
return `check failed for property '${property}', expecting ${expectedType} got ${type}`;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
// validateArrayTypes takes an array as input and validates that every element
|
|
// in the array matches the provided type.
|
|
//
|
|
// This is a helper function for validateObjPropTypes, the property is provided
|
|
// as an input to produce a more coherent error message.
|
|
function validateArrayTypes(arr, expectedType) {
|
|
// Check that the provided input is actually an array.
|
|
if (!Array.isArray(arr)) {
|
|
return `not an array`;
|
|
}
|
|
for (let i = 0; i < arr.length; i++) {
|
|
const type = typeof arr[i];
|
|
if (type !== expectedType) {
|
|
return `element ${i} is expected to be ${expectedType}, got ${type}`;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
export { validateObjPropTypes };
|