validata-mongo
Version:
MongoDB update `$set` operations
118 lines • 5.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isObjectSet = void 0;
const validata_1 = require("validata");
const dev_1 = require("validata/dev");
const DEEP = Symbol('deep');
const tidyDeepPath = (path) => {
return path
.reduce((acc, item) => {
if (acc.length) {
const lastItem = acc[acc.length - 1];
if (acc.length > 1) {
const prevItem = acc[acc.length - 2];
if (lastItem === DEEP && typeof prevItem === 'string' && typeof item === 'string') {
return [...acc.slice(0, -2), `${prevItem}.${item}`];
}
}
if (lastItem === DEEP) {
return [...acc.slice(0, -1), item];
}
}
return [...acc, item];
}, [])
.filter((item) => item !== DEEP);
};
const tidyDeepIssuePaths = (issues) => issues.map((issue) => validata_1.Issue.forPath(tidyDeepPath(issue.path), issue.value, issue.reason, issue.info));
class Generic {
constructor() {
this.check = (value) => {
return typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date);
};
this.convert = (value) => {
if (typeof value === 'string' && value[0] === '{' && value[value.length - 1] === '}') {
try {
return JSON.parse(value);
}
catch (_a) {
return undefined;
}
}
return undefined;
};
this.process = (contract, target, path) => {
const issues = [];
const output = {};
Object.keys(target).forEach((key) => {
const parts = key.split('.');
const firstPart = parts[0];
if (!(firstPart in contract)) {
issues.push(validata_1.Issue.forPath([...path, key], target[key], 'unexpected-property'));
return;
}
const childKey = parts.slice(1).join('.');
const check = contract[firstPart];
const value = target[key];
if (value && this.check(value)) {
Object.keys(value).forEach((nestedKey) => {
if (!nestedKey.includes('.'))
return;
issues.push(validata_1.Issue.forPath([...path, key, nestedKey], value[nestedKey], 'unexpected-property'));
});
}
const directProperty = parts.length === 1;
const childPath = directProperty ? [...path, key] : [...path, firstPart, DEEP];
const childResult = check.process(directProperty ? value : { [childKey]: value }, childPath);
if ((0, validata_1.isIssue)(childResult)) {
issues.push(...tidyDeepIssuePaths(childResult.issues));
return;
}
if (childResult.value === undefined && !(key in target))
return;
output[key] = parts.length === 1 ? childResult.value : childResult.value[childKey];
});
if (path.length && path[path.length - 1] !== DEEP) {
const targetKeys = new Set(Object.keys(target).map((key) => key.split('.')[0]));
const missingKeys = Object.keys(contract).filter((key) => !targetKeys.has(key));
missingKeys.forEach((key) => {
const childResult = contract[key].process(undefined, [...path, key]);
if ((0, validata_1.isIssue)(childResult)) {
issues.push(...tidyDeepIssuePaths(childResult.issues));
}
});
}
return issues.length ? { issues } : { value: output };
};
this.coerce = (options) => (next) => (value, path) => {
if (!options)
return next(value, path);
const coerced = Object.assign({}, value);
if (!options.contract)
return next(coerced, path);
if (options.stripExtraProperties) {
const allowedProperties = new Set(Object.keys(options.contract));
Object.keys(coerced).forEach((key) => {
if (allowedProperties.has(key))
return;
delete coerced[key];
});
}
const result = this.process(options.contract, coerced, path);
if ((0, validata_1.isIssue)(result))
return result;
if (result) {
return next(result.value, path);
}
else {
return next(coerced, path);
}
};
this.validate = (value, path, options) => (0, dev_1.basicValidation)(value, path, options);
}
}
const isObjectSet = (contract, options) => {
const generic = new Generic();
return (0, dev_1.createIsCheck)('object', generic.check, generic.coerce, generic.validate)(Object.assign(Object.assign({}, options), { contract }));
};
exports.isObjectSet = isObjectSet;
//# sourceMappingURL=object-set.js.map