Commit fada480b authored by Stefan Cameron's avatar Stefan Cameron

The build from previous commit

parent d399830e
......@@ -1007,31 +1007,49 @@ var slicedToArray = function () {
/**
* Pretty-print a value.
* @function rtv.util.print
* @param {*} value Value to print.
* @param {*} printValue Value to print.
* @returns {string} Pretty-printed value. It's not perfect and may not catch
* all types, but attempts to be good enough.
*/
var print = function print(value) {
var replacer = function replacer(key, val) {
if (typeof val === 'function') {
var print = function print(printValue) {
// NOTE: key will be undefined when the replacer is called outside of the
// JSON.stringify() call, as well as for the first stringify() call
var replacer = function replacer(stringifying, key, value) {
if (value === undefined || value === null) {
return stringifying ? value : value + '';
}
if (typeof value === 'string') {
return stringifying ? value : '"' + value + '"';
}
if (typeof value === 'number') {
// also catches NaN
return stringifying ? value : '' + value;
}
if (typeof value === 'boolean') {
return stringifying ? value : '' + value;
}
if (typeof value === 'function') {
return '<function>';
} else if ((typeof val === 'undefined' ? 'undefined' : _typeof(val)) === 'symbol') {
return '<<' + val.toString() + '>>';
}
return val + '';
if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'symbol') {
return value.toString();
}
return value; // keep stringifying since we're returning an object
};
// do an initial pass to see if we have a string
var result = replacer(undefined, value);
var result = replacer(false, undefined, printValue);
// if it's just a string, return it
if (typeof result === 'string') {
return result;
}
// otherwise, stringify it
return JSON.stringify(value, replacer);
return JSON.stringify(result, replacer.bind(null, true)); // recursive
};
////// Enumeration
......@@ -1114,7 +1132,7 @@ var Enumeration = function () {
Object.defineProperty(this, '$name', {
enumerable: false,
configurable: true,
value: name && print(name) || ''
value: name || ''
});
/**
......@@ -1176,7 +1194,7 @@ var Enumeration = function () {
var result = this.check(value);
if (result === undefined && !silent) {
throw new Error('Invalid value for ' + (this.$name ? this.$name + ' ' : '') + 'enum[' + this.$values.map(print).join(', ') + ']: ' + print(value));
throw new Error('Invalid value for ' + (this.$name ? print(this.$name) + ' ' : '') + 'enum[' + this.$values.map(print).join(', ') + ']: ' + print(value));
}
return result;
......@@ -1196,9 +1214,7 @@ var Enumeration = function () {
var pairs = Object.keys(this).map(function (k) {
return [k, _this2[k]];
});
return '{rtvref.Enumeration $name="' + this.$name + '" pairs=[' + pairs.map(function (p) {
return '[' + print(p) + ']';
}).join(', ') + ']}';
return '{rtvref.Enumeration $name=' + print(this.$name) + ' pairs=[' + pairs.map(print).join(', ') + ']}';
}
}]);
return Enumeration;
......@@ -1390,9 +1406,9 @@ var Enumeration = function () {
* required (see _Typeset Qualifiers_ below).
* - An Array is necessary if a type needs or requires
* {@link rtvref.types.type_arguments arguments}.
* - If the __first__ element (or second, if a {@link rtvref.types.qualifiers qualifier}
* - If the __first__ (or second, if a {@link rtvref.types.qualifiers qualifier}
* is provided, and this, in a typeset that is _not_
* {@link rtvref.types.fully_qualified_typeset fully-qualified}), is an `Object`,
* {@link rtvref.types.fully_qualified_typeset fully-qualified}), element is an `Object`,
* it's treated as a nested {@link rtvref.shape_descriptor shape descriptor}
* describing an object of the default {@link rtvref.types.OBJECT OBJECT} type.
* To include a shape descriptor at any other position within the array, it
......@@ -1825,10 +1841,12 @@ var defs = {
* be used).
*
* When describing arrays, either _shorthand_ or _full_ notation may be used.
* In the shorthand notation, the `ARRAY` type isn't necessary, but
* {@link rtvref.types.ARRAY_args arguments} can't be specified. In the full
* notation, the `ARRAY` type is required, but arguments can optionally be
* specified (which will include the typeset for each element).
* In the shorthand notation, the `ARRAY` type isn't necessary, but it's only
* possible to specify the Array typeset to use to validate each array element,
* and {@link rtvref.types.ARRAY_args arguments} can't be specified. In the
* {@link rtvref.types.fully_qualified_typeset fully-qualified} notation, the
* `ARRAY` type is required, but the Array typeset must be moved into the
* `typeset` argument (along with any other argument necessary).
*
* __NOTE__: It's important to realize that arrays (as in the JavaScript Array
* type) are essentially nested {@link rtvref.types.typeset Array typesets}.
......@@ -1863,30 +1881,39 @@ var defs = {
* <h4>Example: Shorthand, mixed types</h4>
*
* The `value` property must be either a boolean; or an array (possibly empty) of
* finite numbers of any value, or non-empty strings.
* finite numbers of any value, or non-empty strings, or a mix of both.
*
* <pre><code>{
* value: [BOOLEAN, [FINITE, STRING]]
* }
* </code></pre>
*
* <h4>Example: Full notation, no typeset</h4>
* <h4>Example: Fully-qualified notation, no typeset</h4>
*
* The `value` property must be a non-empty array of any type of value.
*
* <pre><code>{
* value: [ARRAY, {min: 1}]
* value: [REQUIRED, ARRAY, {min: 1}]
* }
* </code></pre>
*
* <h4>Example: Full notation, with typeset</h4>
* <h4>Example: Fully-qualified notation</h4>
*
* The `value` property must be an array (possibly empty) of finite numbers of
* any value, or an array of non-empty strings, and `value` must also not be
* empty.
* any value (nested typeset is not fully-qualified).
*
* <pre><code>{
* value: [ARRAY, {min: 1, typeset: [FINITE, STRING]}]
* value: [REQUIRED, ARRAY, {typeset: [FINITE]}]
* }
*
* <h4>Example: Fully-qualified, mixed types</h4>
*
* The `value` property must be either a boolean; or an array (possibly empty) of
* finite numbers of any value, or non-empty strings, or a mix of both
* (nested typeset is not fully-qualified).
*
* <pre><code>{
* value: [REQUIRED, BOOLEAN, ARRAY, {typeset: [FINITE, STRING]}]
* }
* </code></pre>
*
......@@ -2277,7 +2304,7 @@ var types = new Enumeration(function () {
* Type: {@link rtvref.types.ARRAY ARRAY}
* @const {string} rtvref.validation.isArray.type
*/
var type$1 = types.ARRAY;
var type = types.ARRAY;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2535,7 +2562,7 @@ var isMap_1 = isMap;
* Type: {@link rtvref.types.MAP MAP}
* @const {string} rtvref.validation.isMap.type
*/
var type$2 = types.MAP;
var type$1 = types.MAP;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2580,7 +2607,7 @@ var isWeakMap_1 = isWeakMap;
* Type: {@link rtvref.types.WEAK_MAP WEAK_MAP}
* @const {string} rtvref.validation.isWeakMap.type
*/
var type$3 = types.WEAK_MAP;
var type$2 = types.WEAK_MAP;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2639,7 +2666,7 @@ var isSet_1 = isSet;
* Type: {@link rtvref.types.SET SET}
* @const {string} rtvref.validation.isSet.type
*/
var type$4 = types.SET;
var type$3 = types.SET;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2684,7 +2711,7 @@ var isWeakSet_1 = isWeakSet;
* Type: {@link rtvref.types.WEAK_SET WEAK_SET}
* @const {string} rtvref.validation.isWeakSet.type
*/
var type$5 = types.WEAK_SET;
var type$4 = types.WEAK_SET;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2743,7 +2770,7 @@ var isRegExp_1 = isRegExp;
* Type: {@link rtvref.types.REGEXP REGEXP}
* @const {string} rtvref.validation.isRegExp.type
*/
var type$6 = types.REGEXP;
var type$5 = types.REGEXP;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2762,7 +2789,7 @@ function isRegExp$1(v) {
* Type: {@link rtvref.types.OBJECT OBJECT}
* @const {string} rtvref.validation.isObject.type
*/
var type$7 = types.OBJECT;
var type$6 = types.OBJECT;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2790,7 +2817,7 @@ function isObject$1(v) {
* Type: {@link rtvref.types.STRING STRING}
* @const {string} rtvref.validation.isString.type
*/
var type$8 = types.STRING;
var type$7 = types.STRING;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2815,7 +2842,7 @@ function isString(v) {
* Type: {@link rtvref.types.FUNCTION FUNCTION}
* @const {string} rtvref.validation.isFunction.type
*/
var type$9 = types.FUNCTION;
var type$8 = types.FUNCTION;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2834,7 +2861,7 @@ function isFunction$1(v) {
* Type: {@link rtvref.types.BOOLEAN BOOLEAN}
* @const {string} rtvref.validation.isBoolean.type
*/
var type$10 = types.BOOLEAN;
var type$9 = types.BOOLEAN;
/**
* {@link rtvref.validation.method Validation} for the
......@@ -2852,6 +2879,20 @@ function isBoolean(v) {
return v === true || v === false;
}
////// isValidator validation module
/**
* Determines if a value is a {@link rtvref.types.custom_validator custom validator}.
* @function rtvref.validation.isValidator.default
* @param {*} v Value to validate.
* @returns {boolean} `true` if it is; `false` otherwise.
*/
function isValidator(v) {
// TODO[plugins]: should this module be renamed to isCustomValidator since it's
// perhaps overloaded with 'validator' concept for plugins @see rtvref.validator?
return isFunction$1(v);
}
////// Qualifier Definitions
/**
......@@ -2923,7 +2964,7 @@ var DEFAULT_QUALIFIER = REQUIRED;
* @name rtvref.qualifiers.qualifiers
* @type {rtvref.Enumeration}
*/
var qualifiers$1 = new Enumeration({
var qualifiers = new Enumeration({
REQUIRED: REQUIRED,
EXPECTED: EXPECTED,
OPTIONAL: OPTIONAL
......@@ -2939,22 +2980,29 @@ var qualifiers$1 = new Enumeration({
* @param {boolean} [options.deep=false] If truthy, deeply-validates any nested typesets. Note
* that typesets in nested shapes are also deeply-validated.
* @param {boolean} [options.fullyQualified=false] If truthy, the typeset must be fully-qualified.
* @param {(string|undefined)} [options.failure] If an options object is specified, this
* property will be added and set to a failure message IIF the validation fails.
* @returns {boolean} `true` if it is; `false` otherwise.
* @see {@link rtvref.types.typeset}
*/
function isTypeset(v) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$deep = _ref.deep,
deep = _ref$deep === undefined ? false : _ref$deep,
_ref$fullyQualified = _ref.fullyQualified,
fullyQualified = _ref$fullyQualified === undefined ? false : _ref$fullyQualified;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { deep: false, fullyQualified: false };
var deep = !!options.deep;
var fullyQualified = !!options.fullyQualified;
// FIRST: make sure it's an acceptable type for a typeset: object (shape),
// string (just a plain type name), function (validator), or array (non-empty)
var valid = !!(v && (isObject$1(v) || isString(v) && types.check(v) || isFunction$1(v) || isArray$1(v) && v.length > 0));
var valid = !!(v && (isObject$1(v) || isString(v) && types.check(v) || isValidator(v) || isArray$1(v) && v.length > 0));
if (!valid) {
options.failure = 'Value v=' + print(v) + ' is not a valid type for a typeset: Expecting object (shape), string (single type), function (custom validator), or array (typeset, non-empty)';
}
// THEN: check if needs to be fully-qualified, and check deep within if requested
if (valid && fullyQualified) {
var failurePrefix = 'Fully-qualified ' + (deep ? 'deep ' : 'shallow') + ' typeset=' + print(v);
// must now be an array with at least 2 elements: [qualifier, type]
if (isArray$1(v) && v.length >= 2) {
var usedTypes = {}; // @type {Object.<string,boolean>} map of simple type to `true`
......@@ -2964,14 +3012,16 @@ function isTypeset(v) {
// Updates the current in-scope type (curType) and marks it as used in usedTypes.
// If the type has already been used, it sets valid to false.
// @param {string} type New in-scope type.
var updateCurType = function updateCurType(type) {
var updateCurType = function updateCurType(type$$1) {
// set the rule as the current in-scope type
curType = type;
curType = type$$1;
if (usedTypes[curType]) {
// a type cannot appear more than once in a typeset (but nested is OK)
valid = false;
options.failure = failurePrefix + ': Type "' + curType + '" may not be included more than once in the typeset (but may appear again in a nested typeset)';
}
usedTypes[curType] = true;
};
......@@ -2983,16 +3033,21 @@ function isTypeset(v) {
// more efficient to check for a string first than to always iterate
// all qualifiers (or all types, below) when it isn't since we know
// they're always strings
valid = isString(rule) && !!qualifiers$1.check(rule);
valid = isString(rule) && !!qualifiers.check(rule);
if (!valid) {
options.failure = failurePrefix + ': Expected a qualifier at index=' + i + ', found ' + print(rule);
}
} else if (isString(rule)) {
// additional qualifier, or simple type
if (qualifiers$1.check(rule)) {
if (qualifiers.check(rule)) {
// cannot have more than one qualifier and qualifier must be in first position
// (and this is not the first position because that's handled specially, above)
valid = false;
options.failure = failurePrefix + ': Cannot have more than one qualifier, and qualifier must be in first position, index=' + i;
} else if (!types.check(rule)) {
// if not a qualifier, it must be a valid type (since it's a string)
valid = false;
options.failure = failurePrefix + ': Expected a valid type in ' + types + ' at index=' + i + ', found ' + print(rule);
} else {
// set the rule as the current in-scope type
updateCurType(rule);
......@@ -3003,20 +3058,25 @@ function isTypeset(v) {
// have to ensure that args were given when we change the type)
argType = argTypes.check(rule);
}
} else if (isFunction$1(rule)) {
} else if (isValidator(rule)) {
// must be a validator, but there can't be more than 1, it must be
// in the last position (which enforces the 1 count), always after the
// qualifier, and since the typeset must be FQ'd, we must have an
// in-scope type
valid = !!(curType && i + 1 === v.length);
if (!valid) {
options.failure = failurePrefix + ': Unexpected custom validator at index=' + i + ': Must be in the last position, must not be more than 1 in the typeset, must be after the qualifier, and must be preceded by a type';
}
} else if (isObject$1(rule)) {
// could be a shape, or type args (either way, it's a single object)
// since the typeset must be fully-qualified, argType must already be
// a type that takes arguments, since arguments are always provided
// via objects (NOTE: for object types, the args are the shapes themselves,
// except for CLASS_OBJECT where the shape is specified within the args;
// via objects
// NOTE: for object types, the args are the shapes themselves, except
// for CLASS_OBJECT where the shape is specified within the args;
// still, there is always only ever at most one object per type that
// accepts args, never more)
// accepts args, never more
// NOTE: for the ARRAY type, the typeset is specified within the args
if (argType) {
// consume the object as the in-scope arg type's arguments
argType = undefined;
......@@ -3024,10 +3084,11 @@ function isTypeset(v) {
// since the typeset must be fully-qualified and we don't already
// have an in-scope arg type, the typeset is invalid
valid = false;
options.failure = failurePrefix + ': Expecting a type that takes arguments at index=' + (i - 1);
}
// only go deep if the object is a shape, which means the current in-scope
// type must be an object type
// only go deep if the object is a shape (which means the current in-scope
// type must be an object type) or ARRAY args with `typeset` specified
if (valid && deep && objTypes.check(curType)) {
// if it's a class object, the shape is an optional sub-property of the object;
// if it's a map object, there is no shape; otherwise, it's the object itself
......@@ -3035,38 +3096,46 @@ function isTypeset(v) {
// validate all of the shape's typesets (each own-prop should be a typeset)
shape && forEach_1(shape, function (ts, prop) {
valid = isTypeset(ts, { deep: deep, fullyQualified: fullyQualified }); // recursive
var opts = { deep: deep, fullyQualified: fullyQualified };
valid = isTypeset(ts, opts); // recursive
options.failure = opts.failure && failurePrefix + ' [index=' + i + ', prop="' + prop + '"]: ' + opts.failure;
return valid; // break on first invalid
});
}
} else if (isArray$1(rule)) {
// DEBUG TODO: somewhere, both FQ and non-FQ must account for change in ARRAY args where array is now included INSIDE args
// nested typeset for an array type: in-scope type must be ARRAY
if (curType === types.ARRAY) {
// go deep if requested; otherwise, assume it's valid
valid = !deep || isTypeset(rule, { deep: deep, fullyQualified: fullyQualified });
} else {
valid = false;
} else if (valid && deep && curType === types.ARRAY && rule.typeset) {
// ARRAY type with args.typeset specified, and we're deep-validating
var opts = { deep: deep, fullyQualified: fullyQualified };
valid = isTypeset(rule.typeset, opts);
options.failure = opts.failure && failurePrefix + ' [index=' + i + ']: ' + opts.failure;
}
} else {
// any other type in an array typeset is not supported
// any other type in a fully-qualified array typeset is not supported
// NOTE: the ARRAY shorthand notation is not supported in fully-qualified
// typesets, therefore a rule whose JavaScript type is an Array is not valid
valid = false;
options.failure = failurePrefix + ': Unexpected value at index=' + i + ': Expecting object (shape), string (single type), or function (custom validator)';
}
return valid; // break if no longer valid
});
// make sure at least one type was specified
valid = valid && !!curType;
if (valid) {
valid = !!curType;
if (!valid) {
options.failure = failurePrefix + ': Failed to find a type in the typeset';
}
}
} else {
// automatically invalid if not an array because a typeset must be in the
// array form in order to be FQ'd
valid = false;
options.failure = failurePrefix + ': Typeset cannot be fully-qualified unless it is an Array of minimum length=2';
}
// NEXT: if it's an array, valid, and does not need to be FQ'd, check its
// definition, and deep (if requested)
} else if (valid && !fullyQualified && isArray$1(v)) {
var _failurePrefix = 'Non-qualified ' + (deep ? 'deep ' : 'shallow') + ' typeset=' + print(v);
var _usedTypes = {}; // @type {Object.<string,boolean>} map of simple type to `true`
var _curType = void 0; // @type {string} current in-scope type
var _argType = void 0; // @type {(string|undefined)} current in-scope type IIF it accepts args
......@@ -3075,14 +3144,16 @@ function isTypeset(v) {
// Updates the current in-scope type (curType) and marks it as used in usedTypes.
// If the type has already been used, it sets valid to false.
// @param {string} type New in-scope type.
var _updateCurType = function _updateCurType(type) {
var _updateCurType = function _updateCurType(type$$1) {
// set the rule as the current in-scope type
_curType = type;
_curType = type$$1;
if (_usedTypes[_curType]) {
// a type cannot appear more than once in a typeset (but nested is OK)
valid = false;
options.failure = _failurePrefix + ': Type "' + _curType + '" may not be included more than once in the typeset (but may appear again in a nested typeset)';
}
_usedTypes[_curType] = true;
};
......@@ -3090,9 +3161,12 @@ function isTypeset(v) {
// rules/properties of a typeset are specified
forEach_1(v, function (rule, i) {
if (isString(rule)) {
if (qualifiers$1.check(rule)) {
if (qualifiers.check(rule)) {
hasQualifier = true;
valid = i === 0; // must be in the first position
if (!valid) {
options.failure = _failurePrefix + ': Unexpected qualifier at index=' + i + ': There must be at most one qualifier and it may only be in the first position';
}
} else if (types.check(rule)) {
// set the rule as the current in-scope type
_updateCurType(rule);
......@@ -3104,8 +3178,9 @@ function isTypeset(v) {
} else {
// some unknown/invalid qualifier or type
valid = false;
options.failure = _failurePrefix + ': Unknown/invalid qualifier/type=' + print(rule) + ' at index=' + i;
}
} else if (isFunction$1(rule)) {
} else if (isValidator(rule)) {
// must be a validator, but there can't be more than 1, and it must be
// in the last position (which enforces the 1 count), and always after
// the qualifier (if any)
......@@ -3113,6 +3188,8 @@ function isTypeset(v) {
if (valid && !_curType) {
// if we have a validator but no in-scope type, ANY is implied
_updateCurType(types.ANY);
} else if (!valid) {
options.failure = _failurePrefix + ': Unexpected custom validator at index=' + i + ': Must be at the last position';
}
} else if (isObject$1(rule)) {
// could be a shape, or type args (either way, it's just one object)
......@@ -3120,14 +3197,20 @@ function isTypeset(v) {
// for CLASS_OBJECT where the shape is specified within the args; still,
// there is always only ever at most one object per type that accepts
// args, never more
// NOTE: for the ARRAY type, the typeset is specified within the args
if (!_argType) {
// since there's no in-scope arg type, the object must be a shape using
// the default OBJECT type, but it must be in the first position (or
// second if the first element was a qualifier)
_updateCurType(DEFAULT_OBJECT_TYPE);
valid = valid && (i === 0 || hasQualifier && i === 1);
// NOTE: do not set argType because the shape is the default object type's
// args, so they should be consumed by the in-scope arg type
if (valid) {
valid = i === 0 || hasQualifier && i === 1;
// NOTE: do not set argType because the shape is the default object type's
// args, so they should be consumed by the in-scope arg type
if (!valid) {
options.failure = _failurePrefix + ': Shape at index=' + i + ' is missing an object type in ' + objTypes + ': Only in the first position (or second if a qualifier is specified) does a shape assume the default object type of "' + DEFAULT_OBJECT_TYPE + '"';
}
}
} else {
// consume the object as the in-scope arg type's arguments
// NOTE: currently, there are no types that _require_ args, only ones
......@@ -3137,7 +3220,7 @@ function isTypeset(v) {
}
// only go deep if the object is a shape, which means the current in-scope
// type must be an object type
// type must be an object type or ARRAY args with `typeset` specified
if (valid && deep && objTypes.check(_curType)) {
// if it's a class object, the shape is an optional sub-property of the object;
// if it's a map object, there is no shape; otherwise, it's the object itself
......@@ -3145,52 +3228,69 @@ function isTypeset(v) {
// validate all of the shape's typesets (each own-prop should be a typeset)
shape && forEach_1(shape, function (ts, prop) {
valid = isTypeset(ts, { deep: deep, fullyQualified: fullyQualified }); // recursive
var opts = { deep: deep, fullyQualified: fullyQualified };
valid = isTypeset(ts, opts); // recursive
options.failure = opts.failure && _failurePrefix + ' [index=' + i + ', prop="' + prop + '"]: ' + opts.failure;
return valid; // break on first invalid
});
} else if (valid && deep && _curType === types.ARRAY && rule.typeset) {
// ARRAY type with args.typeset specified, and we're deep-validating
var opts = { deep: deep, fullyQualified: fullyQualified };
valid = isTypeset(rule.typeset, opts);
options.failure = opts.failure && _failurePrefix + ' [index=' + i + ']: ' + opts.failure;
}
} else if (isArray$1(rule)) {
// if the current in-scope type is not ARRAY, set it since a nested array
// implies the ARRAY type
if (_curType !== types.ARRAY) {
_updateCurType(types.ARRAY);
// in this case, the in-scope arg type should be updated to ARRAY since
// arrays accept optional args, but since the current in-scope type
// was not set, this must be a short-hand ARRAY notation, which means
// args cannot be specified, therefore we update argType to undefined
// to clear it from the previous type (if it was set) and clear it
// from this type as well
_argType = undefined;
}
// a nested array implies the ARRAY type in shorthand notation
_updateCurType(types.ARRAY);
// in this case, the in-scope arg type should be updated to ARRAY since
// arrays accept optional args, but since this rule is a short-hand ARRAY
// notation, which means args cannot be specified, we update argType to
// undefined to clear it from the previous type (if it was set) and clear
// it from this type as well
_argType = undefined;
if (valid && deep) {
valid = isTypeset(rule, { deep: deep, fullyQualified: fullyQualified }); // recursive
var _opts = { deep: deep, fullyQualified: fullyQualified };
valid = isTypeset(rule, _opts); // recursive
options.failure = _opts.failure && _failurePrefix + ' [index=' + i + ']: ' + _opts.failure;
}
} else {
// any other type in an array typeset is not supported
// any other type in a non-qualified array typeset is not supported
// NOTE: ARRAY shorthand notation is permitted in non-qualified typesets,
// therefore a rule whose JavaScript type is an Array is valid
valid = false;
options.failure = _failurePrefix + ': Unexpected value at index=' + i + ': Expecting object (shape), string (single type), function (custom validator), or array (typeset)';
}
return valid; // break if no longer valid
});
// make sure at least one type was specified
valid = valid && !!_curType;
if (valid) {
valid = !!_curType;
if (!valid) {
options.failure = _failurePrefix + ': Failed to find a type in the typeset';
}
}
// NEXT: if it's a shape descriptor, check if deep is requested as long as it's
// valid and does not need to be FQ'd (otherwise, 'v' must be an array and
// would be invalid as a FQ'd typeset)
} else if (valid && deep && !fullyQualified && isObject$1(v)) {
var _failurePrefix2 = 'Non-qualified deep shape=' + print(v);
// we need to deep-validate a shape descriptor, which means each one of its
// own-properties must be a valid typeset
var props = Object.keys(v);
forEach_1(props, function (prop) {
valid = isTypeset(v[prop], { deep: deep, fullyQualified: fullyQualified }); // recursive
forEach_1(v, function (ts, prop) {
var opts = { deep: deep, fullyQualified: fullyQualified };
valid = isTypeset(ts, opts); // recursive
options.failure = opts.failure && _failurePrefix2 + ' [prop="' + prop + '"]: ' + opts.failure;
return valid; // break if no longer valid
});
}
// ELSE: must valid (but non-array/object and doesn't need to be FQ'd), or invalid
// ELSE: must be valid (but non-array/shape and doesn't need to be FQ'd), or invalid
return valid;
}
......@@ -3220,20 +3320,6 @@ function isTypeArgs(v) {
return isObject$1(v);
}
////// isValidator validation module
/**
* Determines if a value is a {@link rtvref.types.custom_validator custom validator}.
* @function rtvref.validation.isValidator.default
* @param {*} v Value to validate.
* @returns {boolean} `true` if it is; `false` otherwise.
*/
function isValidator(v) {
// TODO[plugins]: should this module be renamed to isCustomValidator since it's
// perhaps overloaded with 'validator' concept for plugins @see rtvref.validator?
return isFunction$1(v);
}
////// RtvSuccess Class
/**
......@@ -3293,7 +3379,9 @@ var extendsFrom = Error;
var renderPath = function renderPath(path) {
// returns '/' if the path is empty
return path.reduce(function (strPath, elem) {
return '' + strPath + print(elem);
// cast `elem` to string rather than print() to avoid quotes (should be a
// string anyway)
return '' + strPath + (strPath === '/' ? '' : '/') + (elem + '');
}, '/');
};
......@@ -3316,7 +3404,9 @@ var renderPath = function renderPath(path) {
* @param {Array.<string>} path The path deep into `value` where the failure occurred.
* An empty array signifies the _root_ (top-level) value that was checked.
* @param {rtvref.types.fully_qualified_typeset} cause The fully qualified typeset
* that caused the failure.
* that caused the failure. This is normally the fully-qualified version of `typeset`,
* but could be a sub-type if `typeset` is an Array typeset or a
* {@link rtvref.shape_descriptor shape descriptor}.
* @throws {Error} If `typeset`, `path`, or `cause` is invalid.
*/
var RtvError = function RtvError(value, typeset, path, cause) {
......@@ -3329,7 +3419,6 @@ var RtvError = function RtvError(value, typeset, path, cause) {
// by checking the prototype chain, which isn't properly constructed.
if (!isTypeset(typeset)) {
// DEBUG TODO: use unit tests everywhere an RtvError is provided to certify this property is a typeset so that isTypeset() isn't needed in this module
throw new Error('Invalid typeset: ' + print(typeset));
}
......@@ -3338,7 +3427,6 @@ var RtvError = function RtvError(value, typeset, path, cause) {
}
if (!isTypeset(cause, { fullyQualified: true })) {
// DEBUG TODO: use unit tests everywhere an RtvError is provided to certify this property is a typeset so that isTypeset() isn't needed in this module
throw new Error('Invalid cause (expecting a fully-qualified typeset): ' + print(cause));
}
......@@ -3347,7 +3435,7 @@ var RtvError = function RtvError(value, typeset, path, cause) {
// or there's something strange about the built-in Error type, so we just
// call the super's constructor as a formality.
extendsFrom.call(this);
this.message = 'Verification failed: value=' + print(value) + ', path="' + renderPath(path) + '"';
this.message = 'Verification failed: value=' + print(value) + ', path="' + renderPath(path) + '", cause=' + print(cause);
this.name = 'RtvError';
Object.defineProperties(this, {
......@@ -3407,7 +3495,6 @@ var RtvError = function RtvError(value, typeset, path, cause) {
}
},
// DEBUG TODO make sure the example is what it ends-up being...:
/**
* Fully qualified typeset that caused the failure. This will be a subset
* of `typeset`, and possibly of a nested typeset within `typeset`
......@@ -3441,7 +3528,7 @@ RtvError.prototype.constructor = RtvError;
* @returns {string} String representation.
*/
RtvError.prototype.toString = function () {
return '{rtvref.RtvError value=' + print(this.value) + ', path="' + renderPath(this.path) + '"}';
return '{rtvref.RtvError value=' + print(this.value) + ', path="' + renderPath(this.path) + '", cause=' + print(this.cause) + '}';
};
////// Main Implementation Module
......@@ -3474,7 +3561,7 @@ var _validatorMap = {};
var getQualifier = function getQualifier(typeset) {
if (!isTypeset(typeset)) {
// start by validating so we can be confident later
throw new Error('Invalid typeset="' + print(typeset) + '"');
throw new Error('Invalid typeset=' + print(typeset));
}
var qualifier = DEFAULT_QUALIFIER;
......@@ -3482,7 +3569,7 @@ var getQualifier = function getQualifier(typeset) {
if (isArray$1(typeset)) {
// if there's a qualifier, it must be the first element, and since it's a
// valid typeset, it cannot be an empty array
if (isString(typeset[0]) && qualifiers$1.check(typeset[0])) {
if (isString(typeset[0]) && qualifiers.check(typeset[0])) {
qualifier = typeset[0];
}
}
......@@ -3492,6 +3579,85 @@ var getQualifier = function getQualifier(typeset) {
return qualifier;
};