@quenk/preconditions
Version:
Make data satisfy constraints before using.
168 lines • 6.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.schemaProperties = exports.exclude = exports.mergeRight = exports.merge = exports.map = exports.union = exports.intersect = exports.disjoint = exports.restrict = exports.isRecord = void 0;
const record_1 = require("@quenk/noni/lib/data/record");
const record_2 = require("./result/failure/record");
const array_1 = require("./array");
const result_1 = require("./result");
const _1 = require("./");
/**
* isRecord tests if the value is an js object (and not an Array).
*/
const isRecord = (value) => (0, record_1.isRecord)(value)
? (0, result_1.succeed)(value)
: (0, result_1.fail)('isRecord', value);
exports.isRecord = isRecord;
/**
* restrict applies a record of preconditions to an input object keeping
* only those properties that have a matching precondition.
*
* If any of the preconditions fail, the whole object is considered a failure.
*/
const restrict = (tests) => (value) => {
let add2Reports = (r, p, k) => p(value[k]).fold(onFailure(k, r), onSuccess(k, r));
let result = ((0, record_1.reduce)(tests, reports(), add2Reports));
return review(result, value);
};
exports.restrict = restrict;
/**
* disjoint applies a record of preconditions to a javascript object
* producing a new object with the final value of each precondition
* and the values of any additional properties in the input object.
*
* If any of the preconditions fail, the whole object is considered a failure.
*/
const disjoint = (tests) => (value) => {
let add2Reports = (r, v, k) => Object.hasOwnProperty.call(tests, k)
? tests[k](v).fold(onFailure(k, r), onSuccess(k, r))
: onSuccess(k, r)(v);
let result = (0, record_1.reduce)(value, reports(), add2Reports);
return review(result, value);
};
exports.disjoint = disjoint;
/**
* intersect applies only the properties in a record of preconditions
* that exist in the target input object. The resulting value is an
* object with properties that exist in the input object that have had a
* matching precondition applied.
*
* If any of the preconditions fail, the whole object is considered a failure.
*/
const intersect = (tests) => (value) => {
let add2Reports = (r, v, k) => Object.hasOwnProperty.call(tests, k)
? tests[k](v).fold(onFailure(k, r), onSuccess(k, r))
: onSuccess(k, r)(null);
let result = (0, record_1.reduce)(value, reports(), add2Reports);
return review(result, value);
};
exports.intersect = intersect;
/**
* union applies a record of preconditions to an input object.
*
* Union results in an object that has both the results of applied preconditions for
* found properties and any additional properties on the input object.
*
* If any of the preconditions fail, the whole object is considered a failure.
*/
const union = (tests) => (value) => {
let ks = (0, record_1.keys)(tests).concat((0, record_1.keys)(value));
let add2Reports = (r, k) => Object.hasOwnProperty.call(tests, k)
? tests[k](value[k]).fold(onFailure(k, r), onSuccess(k, r))
: onSuccess(k, r)(value[k]);
let results = ks.reduce(add2Reports, reports());
return review(results, value);
};
exports.union = union;
/**
* map applies the same Precondition to each property of an object.
*
* If any of the preconditions fail, the whole object is considered a failure.
*/
const map = (prec) => (0, _1.and)((0, _1.typeOf)('object'), (value) => {
let fcount = 0;
let failures = {};
let success = {};
for (let [key, val] of Object.entries(value)) {
let result = prec(val);
if (result.isLeft()) {
fcount++;
failures[key] = result.takeLeft();
}
else {
success[key] = result.takeRight();
}
}
return fcount > 0
? (0, record_2.fail)(failures, value, { value })
: (0, result_1.succeed)(success);
});
exports.map = map;
const reports = () => ({
failures: {},
values: {}
});
const review = (reports, value) => !(0, record_1.empty)(reports.failures)
? (0, record_2.fail)(reports.failures, value, { value })
: (0, result_1.succeed)(reports.values);
const onFailure = (key, { failures, values }) => (f) => ({
values,
failures: (0, record_1.merge)(failures, { [key]: f })
});
const onSuccess = (key, { failures, values }) => (v) => v == null
? { failures, values }
: {
failures,
values: (0, record_1.merge)(values, { [key]: v })
};
/**
* merge the properties of the value into the provided object.
*
* Any conflicting properties resolve to the value's property.
*/
const merge = (target) => (value) => {
console.error('TARGET', target);
console.error('VALUE ', value);
return (0, result_1.succeed)((0, record_1.merge)(target, value));
};
exports.merge = merge;
/**
* mergeRight is like merge except conflicts are resolved with the target's
* property.
*/
const mergeRight = (target) => (value) => (0, result_1.succeed)((0, record_1.merge)(value, target));
exports.mergeRight = mergeRight;
/**
* exclude the specified keys from a record value.
*/
const exclude = (keys) => (value) => (0, result_1.succeed)((0, record_1.exclude)(value, keys));
exports.exclude = exclude;
/**
* schemaProperties is a special precondition used internally for precondition
* generation from object type schema.
*
* @param props - The preconditions parsed from the property section of
* the object schema. If there are no properties, the
* precondition generated will drop any property values
* unless the "additionalProperties" section is specified.
*
* @param propsPrec - A precondition to wrap the props record in. This
* should be one of "restrict", "intersect", "union" etc.
*
* @param addPropsPrec - A precondition parsed from the "additionalProperties"
* section. This is used to intercept any not explicitly
* declared properties. Properties declared in the
* properties section are not passed to this precondition.
*/
const schemaProperties = (propsWrap, props, addPropsPrec) => {
let finalPropPrec = (0, record_1.empty)(props)
? _1.identity
: propsWrap(props);
if (!addPropsPrec)
return finalPropPrec;
return (0, _1.and)((0, _1.tee)([
finalPropPrec,
(0, _1.and)((0, exports.exclude)((0, record_1.keys)(props)), (0, exports.map)(addPropsPrec))
]), (0, array_1.reduce)(() => ({}), exports.merge));
};
exports.schemaProperties = schemaProperties;
//# sourceMappingURL=record.js.map