@ne1410s/codl
Version:
Indulge your apps with codl: a component model library
808 lines (778 loc) • 33.1 kB
JavaScript
;
require('reflect-metadata');
/**
* Whether a value is considered as having been provided.
* Certain 'falsy' values pass, including 0, 0n, NaN.
*/
var isProvided = function (v) { return v !== null && v !== undefined && v !== ''; };
var Interception = /** @class */ (function () {
function Interception() {
}
/**
* Calls a custom function each with new instance created.
* @param fn A custom function.
*/
Interception.init = function (fn) {
return function (c) {
return Interception.expose(c, fn);
};
};
/**
* Provides an initial / default value.
* @param val The initial value.
*/
Interception.initial = function (val) {
return function (target, key) {
target[key] = val;
};
};
/**
* Intercepts the input of a function, preventing its execution.
* @param fn A custom function.
* @returns The result of the custom function.
*/
Interception.input = function (fn) {
return function (trg, key, desc) {
desc.value = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return fn(args, trg);
};
};
};
/**
* Intercepts the output of a function, after it is executed.
* @param fn A custom function.
* @returns The result of the custom function.
*/
Interception.output = function (fn) {
return function (trg, key, desc) {
var origFn = desc.value;
desc.value = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var origVal = origFn.apply(trg, args);
return fn(origVal, args, trg);
};
};
};
Interception.expose = function (ctor, fn) {
var retVal = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var maker = function () {
return ctor.apply(this, args);
};
maker.prototype = ctor.prototype;
var instance = new maker();
fn(instance);
return instance;
};
retVal.prototype = ctor.prototype;
return retVal;
};
return Interception;
}());
/** Metadata keys. */
var MetadataKey;
(function (MetadataKey) {
MetadataKey["DISPLAY_NAME"] = "ne-codl:metadata:display-name";
MetadataKey["DESCRIPTION"] = "ne-codl:metadata:description";
MetadataKey["FORMAT"] = "ne-codl:metadata:format";
MetadataKey["MODEL"] = "ne-codl:metadata:model";
})(MetadataKey || (MetadataKey = {}));
/** Validation keys. */
var ValidationKey;
(function (ValidationKey) {
ValidationKey["REQUIRED"] = "ne-codl:validation:required";
ValidationKey["FORBIDDEN"] = "ne-codl:validation:forbidden";
ValidationKey["MIN_LENGTH"] = "ne-codl:validation:min-length";
ValidationKey["MAX_LENGTH"] = "ne-codl:validation:max-length";
ValidationKey["MIN"] = "ne-codl:validation:min";
ValidationKey["MAX"] = "ne-codl:validation:max";
ValidationKey["REGEX"] = "ne-codl:validation:regex";
ValidationKey["TYPE"] = "ne-codl:validation:type";
ValidationKey["OPTIONS"] = "ne-codl:validation:options";
ValidationKey["CUSTOM"] = "ne-codl:validation:custom";
})(ValidationKey || (ValidationKey = {}));
/** Decorators for metadata purposes. */
var Metadata = /** @class */ (function () {
function Metadata() {
}
/**
* Associates a display name with the member to which it is applied.
* @param val The name.
*/
Metadata.displayName = function (val) {
return Reflect.metadata(MetadataKey.DISPLAY_NAME, val);
};
/**
* Associates a description with the member to which it is applied.
* @param val The description.
*/
Metadata.description = function (val) {
return Reflect.metadata(MetadataKey.DESCRIPTION, val);
};
/**
* Associates a format function with the member to which it is applied.
* @param fn The format function.
*/
Metadata.format = function (fn) {
return Reflect.metadata(MetadataKey.FORMAT, fn);
};
/** Associates a child's prototype data on the parent in which it appears. */
Metadata.model = function (ctor) {
return function (trg, key) {
Reflect.defineMetadata("".concat(MetadataKey.MODEL, ":").concat(key.toString()), ctor, trg);
};
};
return Metadata;
}());
/** Decorators for type purposes. */
var Type = /** @class */ (function () {
function Type() {
}
Type.boolean = function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, 'boolean', trg, key);
};
Type.date = function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, 'date', trg, key);
};
Type.integer = function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, 'integer', trg, key);
};
Type.number = function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, 'number', trg, key);
};
return Type;
}());
/** Decorators for validation purposes. */
var Validation = /** @class */ (function () {
function Validation() {
}
/**
* Makes a property required. 'Unprovided' values (null, undefined, '') are
* invalid, as is NaN. Anything else is valid (including: 0, 0n, false).
*/
Validation.required = function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.REQUIRED, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.REQUIRED, true, trg, key);
};
/**
* Makes a property forbidden. 'Unprovided' values (null, undefined, '') are
* valid. Anything else is considered invalid (including: 0, 0n, false).
*/
Validation.forbidden = function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.FORBIDDEN, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.FORBIDDEN, true, trg, key);
};
/**
* Associates a string or array with a minimum length. 'Unprovided' values
* (null, undefined, '') are not tested hence valid. Otherwise the string
* length must not be less than the bound supplied.
* @param lBound The minimum length.
*/
Validation.minLength = function (lBound) {
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.MIN_LENGTH, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.MIN_LENGTH, lBound, trg, key);
};
};
/**
* Associates a string or array with a maximum length. 'Unprovided' values
* (null, undefined, '') are not tested hence valid. Otherwise the string
* length must not exceed the upper bound supplied.
* @param uBound The maximum length.
*/
Validation.maxLength = function (uBound) {
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.MAX_LENGTH, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.MAX_LENGTH, uBound, trg, key);
};
};
/**
* Associates a property with a minimum value. 'Unprovided' values (null,
* undefined, '') are not tested hence valid. Otherwise the value must not be
* less than the lower bound supplied to be valid.
* @param lBound The minimum value.
*/
Validation.min = function (lBound) {
var typekey;
if (lBound instanceof Date)
typekey = 'date';
else if (typeof lBound === 'number')
typekey = 'number';
else
throw new TypeError('Unable to infer type key from: ' + lBound);
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata("".concat(ValidationKey.MIN, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, typekey, trg, key);
Reflect.defineMetadata(ValidationKey.MIN, lBound, trg, key);
};
};
/**
* Associates a number or date property with a maximum value. 'Unprovided'
* values (null, undefined, '') are not tested hence valid. Otherwise the
* value must not exceed the upper bound supplied to be valid.
* @param uBound The maximum value.
*/
Validation.max = function (uBound) {
var typekey;
if (uBound instanceof Date)
typekey = 'date';
else if (typeof uBound === 'number')
typekey = 'number';
else
throw new TypeError('Unable to infer type key from: ' + uBound);
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata("".concat(ValidationKey.MAX, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, typekey, trg, key);
Reflect.defineMetadata(ValidationKey.MAX, uBound, trg, key);
};
};
/**
* Associates a property with minimum and maximum values. 'Unprovided' values
* (null, undefined, '') are not tested hence valid. Otherwise the value must
* be (inclusively) between the lower and upper bounds.
* @param lBound The minimum value.
*/
Validation.range = function (lBound, uBound) {
var typekey;
if (lBound instanceof Date)
typekey = 'date';
else if (typeof lBound === 'number')
typekey = 'number';
else
throw new TypeError('Unable to infer type key from: ' + lBound);
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.TYPE, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata("".concat(ValidationKey.MIN, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata("".concat(ValidationKey.MAX, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.TYPE, typekey, trg, key);
Reflect.defineMetadata(ValidationKey.MIN, lBound, trg, key);
Reflect.defineMetadata(ValidationKey.MAX, uBound, trg, key);
};
};
/**
* Associates a property with a pattern. 'Unprovided' values (null, undefined,
* '') are not tested hence valid. Otherwise the result of .toString() must
* match the regex supplied to be valid.
* @param regex The validation pattern.
*/
Validation.regex = function (regex) {
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.REGEX, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.REGEX, regex, trg, key);
};
};
/**
* Associates a property with a predefined set of allowed values. Primitive
* types, enums and arrays thereof are all supported.
*/
Validation.options = function () {
var opts = [];
for (var _i = 0; _i < arguments.length; _i++) {
opts[_i] = arguments[_i];
}
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.OPTIONS, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.OPTIONS, opts, trg, key);
};
};
/**
* Associates a property with a custom validator. Unlike most other validation
* decorators, ALL values will be tested (rather than being skipped if it was
* deemed 'unprovided') - the decision is not taken on the caller's behalf. If
* the function returns false, the value is deemed invalid and a generic error
* is used. Else if it returns a string, this string is used as the message,
* with null or empty strings taken to indicate that the value is valid).
*/
Validation.custom = function (fn) {
return function (trg, key) {
Reflect.defineMetadata("".concat(ValidationKey.CUSTOM, ":").concat(key.toString()), key, trg);
Reflect.defineMetadata(ValidationKey.CUSTOM, fn, trg, key);
};
};
return Validation;
}());
var ReflectMetadata = /** @class */ (function () {
function ReflectMetadata() {
}
/**
* Retrieves the display name if provided (else the property key).
* @param target The parent object.
* @param key The property key.
*/
ReflectMetadata.getDisplayName = function (target, key) {
return Reflect.getMetadata(MetadataKey.DISPLAY_NAME, target, key) || key;
};
/**
* Retrieves the description.
* @param target The parent object.
* @param key The property key.
*/
ReflectMetadata.getDescription = function (target, key) {
return Reflect.getMetadata(MetadataKey.DESCRIPTION, target, key);
};
/**
* Retrieves the formatted value if format provided (else the raw value).
* @param target The parent object.
* @param key The property key.
* @param other If supplied, the formatter is applied to this value, rather
* than that of the property.
*/
ReflectMetadata.getFormatted = function (target, key, other) {
var fn = Reflect.getMetadata(MetadataKey.FORMAT, target, key);
var value = other != null ? other : target[key];
return fn ? fn(value) : value;
};
return ReflectMetadata;
}());
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/** Validates required items. 0, 0n and false are allowed */
var RequiredValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (!isProvided(value) || Number.isNaN(value)) {
var required = Reflect.getMetadata(ValidationKey.REQUIRED, proto, key) === true;
if (required) {
var name = ReflectMetadata.getDisplayName(proto, key);
retVal.message = "".concat(name, " is required");
}
}
retVal.valid = !retVal.message;
return retVal;
};
/** Validates regex using string representation of the member. */
var RegexValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (isProvided(value)) {
var regex_1 = new RegExp(Reflect.getMetadata(ValidationKey.REGEX, proto, key));
var isArray = Array.isArray(value);
var tests = isArray ? value : [value];
var allOk = tests.every(function (test) { return regex_1.test("".concat(test)); });
if (!allOk) {
var name = ReflectMetadata.getDisplayName(proto, key);
retVal.message = isArray ? "".concat(name, " contains an invalid item") : "".concat(name, " is invalid");
}
}
retVal.valid = !retVal.message;
return retVal;
};
var BooleanParser = function (obj) {
var str = "".concat(obj).toLowerCase();
if (['1', 'true'].indexOf(str) !== -1)
return true;
if (['0', 'false'].indexOf(str) !== -1)
return false;
};
var DateParser = function (obj) {
if (obj instanceof Date)
return obj;
if (typeof obj === 'string') {
var test = new Date(obj);
if (!isNaN(test.getTime()))
return test;
}
};
var IntegerParser = function (obj) {
var asInt = parseInt("".concat(obj), 10);
if (!isNaN(asInt) && asInt.toFixed(8) === parseFloat("".concat(obj)).toFixed(8))
return asInt;
};
var NumberParser = function (obj) {
var test = parseFloat("".concat(obj));
if (!isNaN(test))
return test;
};
var ReflectType = /** @class */ (function () {
function ReflectType() {
}
/**
* Parses provided according to its meta type. If no type found, the original
* value is returned, otherwise a value is only returned in parseable cases.
* @param target The object.
* @param key The property key.
* @param value Optional value override to parse instead.
*/
ReflectType.parse = function (target, key, value) {
value = value == undefined ? target[key] : value;
if (isProvided(value)) {
var type = this.getType(target, key);
return type ? this.getParser(type)(value) : value;
}
};
/** Gets the meta type of that identified by the supplied parameters. */
ReflectType.getType = function (target, key) {
return Reflect.getMetadata(ValidationKey.TYPE, target, key);
};
/** Gets parser for supported meta types. */
ReflectType.getParser = function (type) {
switch (type) {
case 'boolean':
return BooleanParser;
case 'date':
return DateParser;
case 'integer':
return IntegerParser;
case 'number':
return NumberParser;
default:
throw new RangeError("No parser implemented for ".concat(type));
}
};
return ReflectType;
}());
/** Validates values are within a range. */
var RangeValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (isProvided(value)) {
var min_1 = Reflect.getMetadata(ValidationKey.MIN, proto, key);
var max_1 = Reflect.getMetadata(ValidationKey.MAX, proto, key);
var hasMin_1 = isProvided(min_1);
var hasMax_1 = isProvided(max_1);
var isArray = Array.isArray(value);
var tests = isArray ? value : [value];
var allOk = tests.every(function (test) { return (!hasMin_1 || test >= min_1) && (!hasMax_1 || test <= max_1); });
if (!allOk) {
var name = ReflectMetadata.getDisplayName(proto, key);
var fmtMin = hasMin_1 ? ReflectMetadata.getFormatted(proto, key, min_1) : min_1;
var fmtMax = hasMax_1 ? ReflectMetadata.getFormatted(proto, key, max_1) : max_1;
var isDate = ReflectType.getType(proto, key) === 'date';
var compareLT = isDate ? 'before' : 'less than';
var compareGT = isDate ? 'after' : 'greater than';
var rangeImperative = isArray ? 'have all values' : 'be';
var boundImperative = isArray ? 'have a value' : 'be';
retVal.message =
hasMin_1 && hasMax_1
? "".concat(name, " must ").concat(rangeImperative, " between ").concat(fmtMin, " and ").concat(fmtMax)
: hasMin_1
? "".concat(name, " cannot ").concat(boundImperative, " ").concat(compareLT, " ").concat(fmtMin)
: "".concat(name, " cannot ").concat(boundImperative, " ").concat(compareGT, " ").concat(fmtMax);
}
retVal.valid = !retVal.message;
}
return retVal;
};
/** Validates string / array length are within specified range. */
var LengthRangeValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (isProvided(value)) {
var minLength = Reflect.getMetadata(ValidationKey.MIN_LENGTH, proto, key);
var maxLength = Reflect.getMetadata(ValidationKey.MAX_LENGTH, proto, key);
var hasMinLen = isProvided(minLength);
var hasMaxLen = isProvided(maxLength);
var minOk = !hasMinLen || value.length >= minLength;
var maxOk = !hasMaxLen || value.length <= maxLength;
if (!minOk || !maxOk) {
var name = ReflectMetadata.getDisplayName(proto, key);
var noun = typeof value === 'string' ? 'character' : 'item';
var pluraliser = value.length === 1 ? '' : 's';
retVal.message =
hasMinLen && hasMaxLen
? "".concat(name, " must contain between ").concat(minLength, " and ").concat(maxLength, " ").concat(noun, "s")
: hasMinLen
? "".concat(name, " cannot contain fewer than ").concat(minLength, " ").concat(noun).concat(pluraliser)
: "".concat(name, " cannot contain more than ").concat(maxLength, " ").concat(noun).concat(pluraliser);
}
retVal.valid = !retVal.message;
}
return retVal;
};
/** Validates type. */
var TypeValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (isProvided(value)) {
var type = ReflectType.getType(proto, key);
var parser_1 = ReflectType.getParser(type);
var isArray = Array.isArray(value);
var tests = isArray ? value : [value];
var allOk = tests.every(function (test) { return parser_1(test) != null; });
if (!allOk) {
var name = ReflectMetadata.getDisplayName(proto, key);
retVal.message = isArray
? "".concat(name, " contains an invalid ").concat(type)
: "".concat(name, " is not a valid ").concat(type);
}
}
retVal.valid = !retVal.message;
return retVal;
};
/** Validates with a custom function. */
var CustValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
var fn = Reflect.getMetadata(ValidationKey.CUSTOM, proto, key);
var result = fn(value, trg, proto);
var hasCustomErrorMessage = typeof result === 'string' && result.length != 0;
if (hasCustomErrorMessage)
retVal.message = result;
else if (result === false) {
var name = ReflectMetadata.getDisplayName(proto, key);
retVal.message = "".concat(name, " is invalid");
}
retVal.valid = !retVal.message;
return retVal;
};
/** Validates forbidden items. Only null, undefined and empty strings are ok. */
var ForbiddenValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (isProvided(value)) {
var forbidden = Reflect.getMetadata(ValidationKey.FORBIDDEN, proto, key) === true;
if (forbidden) {
var name = ReflectMetadata.getDisplayName(proto, key);
retVal.message = "".concat(name, " is forbidden");
}
}
retVal.valid = !retVal.message;
return retVal;
};
/** Validates the member value is contained within predefined options. */
var OptionsValidator = function (trg, key, proto) {
var value = trg[key];
var retVal = { key: key, value: value, valid: true };
if (isProvided(value)) {
var options_1 = Reflect.getMetadata(ValidationKey.OPTIONS, proto, key);
var isArray = Array.isArray(value);
var tests = isArray ? value : [value];
var allOk = tests.every(function (test) { return options_1.indexOf(test) !== -1; });
if (!allOk) {
var name = ReflectMetadata.getDisplayName(proto, key);
retVal.message = isArray
? "".concat(name, " contains an invalid option")
: "".concat(name, " is an invalid option");
}
}
retVal.valid = !retVal.message;
return retVal;
};
/** Reflects validation decoration. */
var ReflectValidation = /** @class */ (function () {
function ReflectValidation() {
}
/**
* Performs validation - for objects with decorated members.
* @param target The object to validate.
* @param type The strong type (to supply prototype data).
*/
ReflectValidation.validate = function (target, type) {
var proto = (type === null || type === void 0 ? void 0 : type.prototype) || Object.getPrototypeOf(target || {});
if (!proto)
throw new TypeError('No type data could be found');
var checks = this.getValidators();
var rawTests = this.getTestInstructions(checks, proto, target);
var testPlan = this.prepareTestPlan(rawTests);
var results = this.executeTestPlan(testPlan);
var retVal = this.summarise(results);
return retVal;
};
/**
* Gets all available tests and their associated validator. There is expected
* duplication where one validator is used to perform multiple tests.
*/
ReflectValidation.getValidators = function () {
return Object.keys(ValidationKey).map(function (test) {
var meta = ValidationKey[test];
var fn = ReflectValidation.getValidator(meta);
return { fn: fn, test: test, meta: meta };
});
};
/** Gets the validator function for a given key. */
ReflectValidation.getValidator = function (key) {
switch (key) {
case ValidationKey.FORBIDDEN:
return ForbiddenValidator;
case ValidationKey.MIN_LENGTH:
case ValidationKey.MAX_LENGTH:
return LengthRangeValidator;
case ValidationKey.OPTIONS:
return OptionsValidator;
case ValidationKey.MIN:
case ValidationKey.MAX:
return RangeValidator;
case ValidationKey.REQUIRED:
return RequiredValidator;
case ValidationKey.REGEX:
return RegexValidator;
case ValidationKey.TYPE:
return TypeValidator;
case ValidationKey.CUSTOM:
return CustValidator;
default:
throw new RangeError("No validator implemented for ".concat(key));
}
};
/** Prepares a sequence of validation instructions. */
ReflectValidation.getTestInstructions = function (allChecks, proto, trg, pfx) {
if (pfx === void 0) { pfx = ''; }
var fnScorer = function (ins) {
return ins.fn === RequiredValidator
? 3
: ins.fn === ForbiddenValidator
? 2
: ins.fn === TypeValidator
? 1
: 0;
};
return Reflect.getMetadataKeys(proto)
.map(function (k) { return "".concat(k); })
.reduce(function (acc, cur) {
var valDef = allChecks.filter(function (dg) { return cur.indexOf(dg.meta + ':') === 0; })[0];
if (valDef) {
var key = cur.replace(valDef.meta + ':', '');
var navkey_1 = pfx ? "".concat(pfx, ".").concat(key) : key;
var prior = acc.filter(function (a) { return a.navkey === navkey_1 && a.fn === valDef.fn; })[0];
if (prior) {
prior.tests.push(valDef.test);
}
else {
acc.push({
navkey: navkey_1,
key: key,
trg: trg,
proto: proto,
fn: valDef.fn,
tests: [valDef.test],
});
}
}
else if (cur.indexOf("".concat(MetadataKey.MODEL, ":")) === 0) {
var subkey_1 = cur.replace("".concat(MetadataKey.MODEL, ":"), '');
var subproto_1 = Reflect.getMetadata(cur, proto).prototype;
var subobj = (trg || {})[subkey_1];
if (Array.isArray(subobj)) {
subobj.forEach(function (subitem, i) {
var subpfx = pfx ? "".concat(pfx, ".").concat(subkey_1, "[").concat(i, "]") : "".concat(subkey_1, "[").concat(i, "]");
acc.push.apply(acc, ReflectValidation.getTestInstructions(allChecks, subproto_1, subitem || {}, subpfx));
});
}
else if (subobj) {
var subpfx = pfx ? "".concat(pfx, ".").concat(subkey_1) : subkey_1;
acc.push.apply(acc, ReflectValidation.getTestInstructions(allChecks, subproto_1, subobj, subpfx));
}
}
return acc;
}, [])
.sort(function (a, b) {
// sort by: depth > objname > keyname > validator
var aNesting = a.navkey.split('.').length;
var bNesting = b.navkey.split('.').length;
if (aNesting > bNesting)
return 1;
else if (aNesting < bNesting)
return -1;
else {
if (a.navkey > b.navkey)
return 1;
else if (a.navkey < b.navkey)
return -1;
return fnScorer(b) - fnScorer(a);
}
});
};
/** Reduces a full set of instructions into a condensed test plan. */
ReflectValidation.prepareTestPlan = function (instr) {
return instr.reduce(function (acc, cur) {
var lastDot = cur.navkey.lastIndexOf('.');
var objKey = lastDot === -1 ? '' : cur.navkey.substring(0, lastDot);
var priorObj = acc.filter(function (a) { return a.navkey === objKey; })[0];
var workObj = priorObj || {
navkey: objKey,
proto: cur.proto,
trg: cur.trg,
props: [],
};
var priorProp = workObj.props.filter(function (p) { return p.key === cur.key; })[0];
var workProp = priorProp || { key: cur.key, navkey: cur.navkey, fns: [] };
workProp.fns.push(cur.fn);
if (!priorProp)
workObj.props.push(workProp);
if (!priorObj)
acc.push(workObj);
return acc;
}, []);
};
/** Executes test plan, mapping to a result. */
ReflectValidation.executeTestPlan = function (tests) {
return tests.reduce(function (acc, cur) {
cur.props.forEach(function (p) {
for (var i = 0; i < p.fns.length; i++) {
var output = p.fns[i](cur.trg, p.key, cur.proto);
acc.push(__assign(__assign({}, output), { navkey: p.navkey }));
if (!output.valid &&
(p.fns[i] === RequiredValidator ||
p.fns[i] === ForbiddenValidator ||
p.fns[i] === TypeValidator)) {
break; // stop if we've failed a terminating test
}
}
});
return acc;
}, []);
};
/** Summarises a sequence of results. */
ReflectValidation.summarise = function (results) {
var retVal = { valid: results.every(function (r) { return r.valid; }) };
if (!retVal.valid) {
retVal.errors = results
.filter(function (r) { return !r.valid; })
.reduce(function (acc, cur) {
acc[cur.navkey] = acc[cur.navkey] || [];
acc[cur.navkey].push(cur.message);
return acc;
}, {});
}
return retVal;
};
return ReflectValidation;
}());
exports.Interception = Interception;
exports.Metadata = Metadata;
exports.ReflectMetadata = ReflectMetadata;
exports.ReflectValidation = ReflectValidation;
exports.Type = Type;
exports.Validation = Validation;
exports.isProvided = isProvided;