decode-it
Version:
a simple zero-dependency type safe json decoder for typescript
94 lines • 3.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDecoder = exports.getFailedDecodes = void 0;
const constructErrorPath_1 = require("./constructErrorPath");
const errorFormatter_1 = require("./errorFormatter");
const typeCheckers_1 = require("./typeCheckers");
const utils_1 = require("./utils");
require("./validators/ValidationResult");
require("./validators/Validator");
class DecodeError extends Error {
constructor(message) {
super(message);
this.name = '\nDecode Error';
}
}
const isValidationResult = (arg) => {
if (!typeCheckers_1.isObject(arg))
return false;
if (arg.state !== 'failed' && arg.state !== 'passed')
return false;
return true;
};
const concatNestedErrors = (errorsSources, currentKey) => errorsSources.map(errorSource => ({
...errorSource,
path: `${currentKey}.${errorSource.path}`,
}));
const getFailedDecodes = (schema, json) => {
if (typeCheckers_1.isFunction(schema)) {
const validate = schema;
const result = validate(json);
if (result.state === 'failed')
return [
{
actual: result.value,
expectedType: result.type,
expectedValue: result.literal,
wrapper: result.wrapper,
path: result.path,
},
];
}
return Object.entries(schema).reduce((errors, [key, validate]) => {
const field = json[key];
if (typeCheckers_1.isObject(validate)) {
if (!typeCheckers_1.isObject(field))
return errors.concat({
actual: field,
expectedType: 'object',
path: key,
});
const errorsSource = exports.getFailedDecodes(validate, field);
const error = concatNestedErrors(errorsSource, key);
return errors.concat(error);
}
const result = validate(field);
if (!isValidationResult(result))
return errors.concat({
actual: result,
expectedType: 'validator',
path: key,
});
if (result.state === 'failed')
return errors.concat({
actual: result.value,
expectedType: result.type,
expectedValue: result.literal,
wrapper: result.wrapper,
path: constructErrorPath_1.constructNestedErrorPath(key, result.path),
});
return errors;
}, []);
};
exports.getFailedDecodes = getFailedDecodes;
const createDecoder = (schema) => {
if (!typeCheckers_1.isObject(schema) && !typeCheckers_1.isFunction(schema))
throw new DecodeError(`Expected schema to be an object or an array validator but got ${errorFormatter_1.formatToJson(schema)}`);
if (typeCheckers_1.isObject(schema)) {
const nonFunctionField = utils_1.flatObject(schema).find(([_k, v]) => !typeCheckers_1.isFunction(v));
if (!typeCheckers_1.isUndefined(nonFunctionField))
throw new DecodeError(`Expected schema fields to be an validator or another schema but got ${nonFunctionField[1]} at ${nonFunctionField[0]}`);
}
return (json) => {
if (!typeCheckers_1.isObject(json) && !typeCheckers_1.isArray(json))
throw new DecodeError(`Expected json to be an object but got ${json}`);
const failedDecodes = exports.getFailedDecodes(schema, json);
const formattedErrors = failedDecodes.map(err => errorFormatter_1.formatFailedDecode(err));
formattedErrors.forEach(msg => {
throw new DecodeError(msg);
});
return json;
};
};
exports.createDecoder = createDecoder;
//# sourceMappingURL=decode.js.map