Socket
Socket
Sign inDemoInstall

simpl-schema

Package Overview
Dependencies
Maintainers
1
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

simpl-schema - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

dist/clean/convertToProperType.js

353

dist/clean.js

@@ -15,4 +15,2 @@ 'use strict';

var _utility = require('./utility.js');
var _underscore = require('underscore');

@@ -22,4 +20,18 @@

var _utility = require('./utility.js');
var _SimpleSchema = require('./SimpleSchema');
var _convertToProperType = require('./clean/convertToProperType');
var _convertToProperType2 = _interopRequireDefault(_convertToProperType);
var _setAutoValues = require('./clean/setAutoValues');
var _setAutoValues2 = _interopRequireDefault(_setAutoValues);
var _clone = require('clone');
var _clone2 = _interopRequireDefault(_clone);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -31,2 +43,3 @@

* @param {Object} [options]
* @param {Boolean} [options.mutate=false] - Mutate doc. Set this to true to improve performance if you don't mind mutating the object you're cleaning.
* @param {Boolean} [options.filter=true] - Do filtering?

@@ -39,2 +52,3 @@ * @param {Boolean} [options.autoConvert=true] - Do automatic type converting?

* @param {Boolean} [options.isModifier=false] - Is doc a modifier object?
* @param {Boolean} [options.mongoObject] - If you already have the mongoObject instance, pass it to improve performance
* @param {Object} [options.extendAutoValueContext] - This object will be added to the `this` context of autoValue functions.

@@ -55,4 +69,7 @@ * @returns {Object} The modified doc.

var mDoc = new _mongoObject2.default(doc, ss._blackboxKeys);
// Clone so we do not mutate
var cleanDoc = options.mutate ? doc : (0, _clone2.default)(doc);
var mongoObject = options.mongoObject || new _mongoObject2.default(cleanDoc, ss.blackboxKeys());
// Clean loop

@@ -63,56 +80,65 @@ if (options.filter || options.autoConvert || options.removeEmptyStrings || options.trimStrings) {

mDoc.forEachNode(function eachNode() {
mongoObject.forEachNode(function eachNode() {
// The value of a $unset is irrelevant, so no point in cleaning it.
// Also we do not care if fields not in the schema are unset.
if (this.operator === '$unset') return;
var gKey = this.genericKey;
if (!gKey) return;
var val = this.value;
if (val === undefined) return;
var p = void 0;
var def = void 0;
var val = void 0;
function trimAndRemoveStrings() {
// trim strings
if (options.trimStrings && typeof val === 'string' && (!def || def && def.trim !== false)) {
val = val.trim();
this.updateValue(val);
}
// remove empty strings
if (options.removeEmptyStrings && (!this.operator || this.operator === '$set') && typeof val === 'string' && !val.length) {
// For a document, we remove any fields that are being set to an empty string
// Filter out props if necessary
if (options.filter && !ss.allowsKey(gKey) || options.removeNullsFromArrays && this.isArrayItem) {
// XXX Special handling for $each; maybe this could be made nicer
if (this.position.slice(-7) === '[$each]') {
mongoObject.removeValueForPosition(this.position.slice(0, -7));
removedPositions.push(this.position.slice(0, -7));
} else {
this.remove();
// For a modifier, we $unset any fields that are being set to an empty string. But only if we're not already within an entire object that is being set.
if (this.operator === '$set' && this.position.match(/\[.+?\]/g).length < 2) {
p = this.position.replace('$set', '$unset');
mDoc.setValueForPosition(p, '');
}
removedPositions.push(this.position);
}
if (_SimpleSchema.SimpleSchema.debug) {
console.info('SimpleSchema.clean: filtered out value that would have affected key "' + gKey + '", which is not allowed by the schema');
}
return; // no reason to do more
}
if (gKey) {
def = ss._schema[gKey];
val = this.value;
// Filter out props if necessary; any property is OK for $unset because we want to
// allow conversions to remove props that have been removed from the schema.
if (options.filter && this.operator !== '$unset' && !ss.allowsKey(gKey) || options.removeNullsFromArrays && this.isArrayItem) {
// XXX Special handling for $each; maybe this could be made nicer
if (this.position.slice(-7) === '[$each]') {
mDoc.removeValueForPosition(this.position.slice(0, -7));
removedPositions.push(this.position.slice(0, -7));
} else {
this.remove();
removedPositions.push(this.position);
}
if (_SimpleSchema.SimpleSchema.debug) {
console.info('SimpleSchema.clean: filtered out value that would have affected key "' + gKey + '", which is not allowed by the schema');
}
return; // no reason to do more
var outerDef = ss._schema[gKey];
var def = outerDef && outerDef.type.definitions[0];
// Autoconvert values if requested and if possible
if (options.autoConvert && def) {
var newVal = (0, _convertToProperType2.default)(val, def.type);
if (newVal !== undefined && newVal !== val) {
_SimpleSchema.SimpleSchema.debug && console.info('SimpleSchema.clean: autoconverted value ' + val + ' from ' + (typeof val === 'undefined' ? 'undefined' : _typeof(val)) + ' to ' + (typeof newVal === 'undefined' ? 'undefined' : _typeof(newVal)) + ' for ' + gKey);
val = newVal;
this.updateValue(newVal);
}
if (val !== void 0) {
// Autoconvert values if requested and if possible
if (options.autoConvert && this.operator !== '$unset' && def) {
var newVal = typeconvert(val, def.type);
if (newVal !== void 0 && newVal !== val) {
_SimpleSchema.SimpleSchema.debug && console.info('SimpleSchema.clean: autoconverted value ' + val + ' from ' + (typeof val === 'undefined' ? 'undefined' : _typeof(val)) + ' to ' + (typeof newVal === 'undefined' ? 'undefined' : _typeof(newVal)) + ' for ' + gKey);
val = newVal;
this.updateValue(newVal);
}
}
trimAndRemoveStrings.call(this);
}
// Trim strings if
// 1. The trimStrings option is `true` AND
// 2. The field is not in the schema OR is in the schema with `trim` !== `false` AND
// 3. The value is a string.
if (options.trimStrings && (!def || def.trim !== false) && typeof val === 'string') {
val = val.trim();
this.updateValue(val);
}
// Remove empty strings if
// 1. The removeEmptyStrings option is `true` AND
// 2. The value is in a normal object or in the $set part of a modifier
// 3. The value is an empty string.
if (options.removeEmptyStrings && (!this.operator || this.operator === '$set') && typeof val === 'string' && !val.length) {
// For a document, we remove any fields that are being set to an empty string
this.remove();
// For a modifier, we $unset any fields that are being set to an empty string.
// But only if we're not already within an entire object that is being set.
if (this.operator === '$set' && this.position.match(/\[.+?\]/g).length < 2) {
p = this.position.replace('$set', '$unset');
mongoObject.setValueForPosition(p, '');
}

@@ -134,4 +160,4 @@ }

var removedPositionParent = removedPosition.slice(0, lastBrace);
var value = mDoc.getValueForPosition(removedPositionParent);
if (_underscore2.default.isEmpty(value)) mDoc.removeValueForPosition(removedPositionParent);
var value = mongoObject.getValueForPosition(removedPositionParent);
if (_underscore2.default.isEmpty(value)) mongoObject.removeValueForPosition(removedPositionParent);
}

@@ -153,3 +179,3 @@ } catch (err) {

mDoc.removeArrayItems();
mongoObject.removeArrayItems();
})();

@@ -159,3 +185,3 @@ }

// Set automatic values
options.getAutoValues && getAutoValues(ss, mDoc, options.isModifier, options.extendAutoValueContext);
options.getAutoValues && (0, _setAutoValues2.default)(ss.autoValueFunctions(), mongoObject, options.isModifier, options.extendAutoValueContext);

@@ -165,225 +191,10 @@ // Ensure we don't have any operators set to an empty object

if (options.isModifier) {
Object.keys(doc || {}).forEach(function (op) {
if (_underscore2.default.isEmpty(doc[op])) delete doc[op];
Object.keys(cleanDoc || {}).forEach(function (op) {
if (_underscore2.default.isEmpty(cleanDoc[op])) delete cleanDoc[op];
});
}
return doc;
return cleanDoc;
}
/*
* PRIVATE
*/
/**
* Converts value to proper type
*
* @param {Any} value Value to try to convert
* @param {Any} type A type
* @returns {Any} Value converted to type.
*/
function typeconvert(value, type) {
// Can't and shouldn't convert arrays or objects
if (Array.isArray(value) || value && (typeof value === 'function' || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') && !(value instanceof Date)) return value;
// Convert to String type
if (type === String) {
if (value === null || value === undefined) return value;
return value.toString();
}
// Convert to Number type
if (type === Number || type === _SimpleSchema.SimpleSchema.Integer) {
if (typeof value === 'string' && value.length > 0) {
// Try to convert numeric strings to numbers
var numberVal = Number(value);
if (!isNaN(numberVal)) return numberVal;
}
// Leave it; will fail validation
return value;
}
// If target type is a Date we can safely convert from either a
// number (Integer value representing the number of milliseconds
// since 1 January 1970 00:00:00 UTC) or a string that can be parsed
// by Date.
if (type === Date) {
if (typeof value === 'string') {
var parsedDate = Date.parse(value);
if (isNaN(parsedDate) === false) return new Date(parsedDate);
}
if (typeof value === 'number') return new Date(value);
}
// If an array is what you want, I'll give you an array
if (type === Array) return [value];
// Could not convert
return value;
}
/**
* @method getAutoValues
* @private
* @param {SimpleSchema} ss - A SimpleSchema instance
* @param {MongoObject} mDoc
* @param {Boolean} [isModifier=false] - Is it a modifier doc?
* @param {Object} [extendedAutoValueContext] - Object that will be added to the context when calling each autoValue function
* @returns {undefined}
*
* Updates doc with automatic values from autoValue functions or default
* values from defaultValue. Modifies the referenced object in place.
*/
function getAutoValues(ss, mDoc, isModifier, extendedAutoValueContext) {
var doneKeys = [];
function runAV(func) {
var affectedKey = this.key;
// If already called for this key, skip it
if (Array.includes(doneKeys, affectedKey)) return;
var lastDot = affectedKey.lastIndexOf('.');
var fieldParentName = lastDot === -1 ? '' : affectedKey.slice(0, lastDot + 1);
var doUnset = false;
var autoValue = func.call(Object.assign({
isSet: this.value !== void 0,
unset: function unset() {
doUnset = true;
},
value: this.value,
operator: this.operator,
field: function field(fName) {
var keyInfo = mDoc.getInfoForKey(fName) || {};
return {
isSet: keyInfo.value !== void 0,
value: keyInfo.value,
operator: keyInfo.operator || null
};
},
siblingField: function siblingField(fName) {
var keyInfo = mDoc.getInfoForKey(fieldParentName + fName) || {};
return {
isSet: keyInfo.value !== void 0,
value: keyInfo.value,
operator: keyInfo.operator || null
};
}
}, extendedAutoValueContext || {}), mDoc.getObject());
// Update tracking of which keys we've run autovalue for
doneKeys.push(affectedKey);
if (doUnset) mDoc.removeValueForPosition(this.position);
if (autoValue === void 0) return;
// If the user's auto value is of the pseudo-modifier format, parse it
// into operator and value.
if (isModifier) {
var op = void 0;
var newValue = void 0;
if (autoValue && (typeof autoValue === 'undefined' ? 'undefined' : _typeof(autoValue)) === 'object') {
var avOperator = Object.keys(autoValue).find(function (avProp) {
return avProp.substring(0, 1) === '$';
});
if (avOperator) {
op = avOperator;
newValue = autoValue[avOperator];
}
}
// Add $set for updates and upserts if necessary
if (!op && isModifier && this.position.slice(0, 1) !== '$') {
op = '$set';
newValue = autoValue;
}
if (op) {
// Update/change value
mDoc.removeValueForPosition(this.position);
mDoc.setValueForPosition(op + '[' + affectedKey + ']', newValue);
return;
}
}
// Update/change value
mDoc.setValueForPosition(this.position, autoValue);
}
_underscore2.default.each(ss._autoValues, function (func, fieldName) {
// AV should run for the exact key only, for each array item if under array
// should run whenever
// 1 it is set
// 2 it will be set by an ancestor field being set
// 3 it is not set and is not within an array
// 4 it is not set and is within an array, run for each array item that is set
// 5 if doing $set[a.$] or $set[a.$.b]
var test = fieldName;
var positions = [];
var lastDot = void 0;
var lastDollar = fieldName.lastIndexOf('$');
var isOrIsWithinArray = lastDollar !== -1;
// We always need to start by checking the array itself in case
// it has items that have objects that need AV run. If not, we
// will later check for the exact field name.
if (isOrIsWithinArray) test = fieldName.slice(0, lastDollar + 1);
while (positions.length === 0 && test.length > 0) {
positions = mDoc.getPositionsInfoForGenericKey(test);
if (positions.length > 0) {
if (fieldName !== test) {
if (fieldName.indexOf('.$.') > -1) {
(function () {
var lastPart = '';
if (fieldName.indexOf(test + '.') === 0) {
lastPart = fieldName.replace(test + '.', '');
}
positions = _underscore2.default.map(positions, function (position) {
position.key = position.key + '.' + lastPart;
position.position = position.position + '[' + lastPart + ']';
position.value = mDoc.getValueForPosition(position.position);
return position;
});
})();
} else {
positions = [];
break;
}
}
} else {
lastDot = test.lastIndexOf('.');
if (lastDot > -1) {
test = test.slice(0, lastDot);
} else {
test = '';
}
}
}
if (positions.length === 0) {
if (isOrIsWithinArray) {
positions = mDoc.getPositionsInfoForGenericKey(fieldName);
} else {
// Not set directly or indirectly
positions.push({
key: fieldName,
value: undefined,
operator: isModifier ? '$set' : null,
position: isModifier ? '$set[' + fieldName + ']' : _mongoObject2.default._keyToPosition(fieldName)
});
}
}
// Run the autoValue function once for each place in the object that
// has a value or that potentially should.
_underscore2.default.each(positions, function (position) {
runAV.call(position, func);
});
});
}
exports.default = clean;

@@ -15,4 +15,2 @@ 'use strict';

var _utility = require('./utility.js');
var _underscore = require('underscore');

@@ -24,12 +22,32 @@

var _utility = require('./utility.js');
var _typeValidator = require('./validation/typeValidator');
var _typeValidator2 = _interopRequireDefault(_typeValidator);
var _requiredValidator = require('./validation/requiredValidator');
var _requiredValidator2 = _interopRequireDefault(_requiredValidator);
var _allowedValuesValidator = require('./validation/allowedValuesValidator');
var _allowedValuesValidator2 = _interopRequireDefault(_allowedValuesValidator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function shouldCheck(key) {
if (key === '$pushAll') throw new Error('$pushAll is not supported; use $push + $each');
return ['$pull', '$pullAll', '$pop', '$slice'].indexOf(key) === -1;
}
function doValidation(_ref) {
var obj = _ref.obj;
var extendedCustomContext = _ref.extendedCustomContext;
var ignoreTypes = _ref.ignoreTypes;
var isModifier = _ref.isModifier;
var isUpsert = _ref.isUpsert;
var keysToValidate = _ref.keysToValidate;
var mongoObject = _ref.mongoObject;
var obj = _ref.obj;
var schema = _ref.schema;
var extendedCustomContext = _ref.extendedCustomContext;
var ignoreTypes = _ref.ignoreTypes;

@@ -46,6 +64,5 @@ // First do some basic checks of the object, and throw errors if necessary

var validationErrors = [];
var mDoc = void 0; // for caching the MongoObject if necessary
// Validation function called for each affected key
function validate(val, affectedKey, affectedKeyGeneric, def, op, skipRequiredCheck, isInArrayItemObject, isInSubObject) {
function validate(val, affectedKey, affectedKeyGeneric, def, op, isInArrayItemObject, isInSubObject) {
// Get the schema for this key, marking invalid if there isn't one.

@@ -61,23 +78,4 @@ if (!def) {

// Check for missing required values. The general logic is this:
// * If the operator is $unset or $rename, it's invalid.
// * If the value is null, it's invalid.
// * If the value is undefined and one of the following are true, it's invalid:
// * We're validating a key of a sub-object.
// * We're validating a key of an object that is an array item.
// * We're validating a document (as opposed to a modifier).
// * We're validating a key under the $set operator in a modifier, and it's an upsert.
if (!skipRequiredCheck && !def.optional) {
if (val === null || op === '$unset' || op === '$rename' || val === void 0 && (isInArrayItemObject || isInSubObject || !op || op === '$set')) {
validationErrors.push({
name: affectedKey,
type: _SimpleSchema.SimpleSchema.ErrorTypes.REQUIRED,
value: null
});
return;
}
}
// For $rename, make sure that the new name is allowed by the schema
if (op === '$rename' && typeof val === 'string' && !schema.allowsKey(val)) {
if (op === '$rename' && !schema.allowsKey(val)) {
validationErrors.push({

@@ -91,95 +89,126 @@ name: val,

// Value checks are not necessary for null or undefined values, except for null array items,
// or for $unset or $rename values
if (op !== '$unset' && op !== '$rename' && (val !== undefined && val !== null || affectedKeyGeneric.slice(-2) === '.$' && val === null)) {
// Check that value is of the correct type
var typeError = doTypeChecks(def, val, op);
if (typeError) {
validationErrors.push(_extends({
name: affectedKey,
value: val
}, typeError));
return;
}
// Prepare the context object for the validator functions
var fieldParentName = (0, _utility.getParentOfKey)(affectedKey, true);
// Check value against allowedValues array
if (def.allowedValues && !Array.includes(def.allowedValues, val)) {
validationErrors.push({
name: affectedKey,
type: _SimpleSchema.SimpleSchema.ErrorTypes.VALUE_NOT_ALLOWED,
value: val
});
return;
}
function getFieldInfo(key) {
// Create mongoObject if necessary, cache for speed
if (!mongoObject) mongoObject = new _mongoObject2.default(obj, schema.blackboxKeys());
var keyInfo = mongoObject.getInfoForKey(key) || {};
return {
isSet: keyInfo.value !== undefined,
value: keyInfo.value,
operator: keyInfo.operator || null
};
}
// Perform custom validation
var lastDot = affectedKey.lastIndexOf('.');
var fieldParentName = lastDot === -1 ? '' : affectedKey.slice(0, lastDot + 1);
var validators = def.custom ? [def.custom] : [];
validators = validators.concat(schema._validators).concat(_SimpleSchema.SimpleSchema._validators);
_underscore2.default.every(validators, function (validator) {
var result = validator.call(Object.assign({
key: affectedKey,
genericKey: affectedKeyGeneric,
definition: def,
isSet: val !== undefined,
value: val,
operator: op,
field: function field(fName) {
mDoc = mDoc || new _mongoObject2.default(obj, schema._blackboxKeys); // create if necessary, cache for speed
var keyInfo = mDoc.getInfoForKey(fName) || {};
return {
isSet: keyInfo.value !== undefined,
value: keyInfo.value,
operator: keyInfo.operator
};
},
siblingField: function siblingField(fName) {
mDoc = mDoc || new _mongoObject2.default(obj, schema._blackboxKeys); // create if necessary, cache for speed
var keyInfo = mDoc.getInfoForKey(fieldParentName + fName) || {};
return {
isSet: keyInfo.value !== undefined,
value: keyInfo.value,
operator: keyInfo.operator
};
},
addValidationErrors: function addValidationErrors(errors) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var fieldValidationErrors = [];
var validatorContext = _extends({
addValidationErrors: function addValidationErrors(errors) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = errors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var error = _step.value;
fieldValidationErrors.push(error);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
for (var _iterator = errors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var error = _step.value;
validationErrors.push(error);
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}, extendedCustomContext || {}));
if (typeof result === 'string') {
validationErrors.push({
name: affectedKey,
type: result,
value: val
});
return false;
},
field: function field(fName) {
return getFieldInfo(fName);
},
genericKey: affectedKeyGeneric,
isInArrayItemObject: isInArrayItemObject,
isInSubObject: isInSubObject,
isModifier: isModifier,
isSet: val !== undefined,
key: affectedKey,
obj: obj,
operator: op,
siblingField: function siblingField(fName) {
return getFieldInfo(fieldParentName + fName);
},
value: val,
// Value checks are not necessary for null or undefined values,
// except for null array items, or for $unset or $rename values
valueShouldBeChecked: op !== '$unset' && op !== '$rename' && (val !== undefined && val !== null || affectedKeyGeneric.slice(-2) === '.$' && val === null)
}, extendedCustomContext || {});
var builtInValidators = [_requiredValidator2.default, _typeValidator2.default, _allowedValuesValidator2.default];
var validators = builtInValidators.concat(schema._validators).concat(_SimpleSchema.SimpleSchema._validators);
// Loop through each of the definitions in the SimpleSchemaGroup.
// If any return true, we're valid.
var fieldIsValid = _underscore2.default.some(def.type, function (typeDef) {
var finalValidatorContext = _extends({}, validatorContext, {
// Take outer definition props like "optional" and "label"
// and add them to inner props like "type" and "min"
definition: _extends({}, _underscore2.default.omit(def, 'type'), typeDef)
});
// Add custom field validators to the list after the built-in
// validators but before the schema and global validators.
var fieldValidators = validators.slice(0);
if (typeof typeDef.custom === 'function') {
fieldValidators.splice(builtInValidators.length, 0, typeDef.custom);
}
if (result === false) return false;
return true;
// We use _.every just so that we don't continue running more validator
// functions after the first one returns false or an error string.
return _underscore2.default.every(fieldValidators, function (validator) {
var result = validator.call(finalValidatorContext);
// If the validator returns a string, assume it is the
// error type.
if (typeof result === 'string') {
fieldValidationErrors.push({
name: affectedKey,
type: result,
value: val
});
return false;
}
// If the validator returns an object, assume it is an
// error object.
if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) === 'object' && result !== null) {
fieldValidationErrors.push(_extends({
name: affectedKey,
value: val
}, result));
return false;
}
// If the validator returns false, assume they already
// called this.addValidationErrors within the function
if (result === false) return false;
// Any other return value we assume means it was valid
return true;
});
});
if (!fieldIsValid) {
validationErrors = validationErrors.concat(fieldValidationErrors);
}
}

@@ -192,3 +221,2 @@

var operator = _ref2.operator;
var setKeys = _ref2.setKeys;
var _ref2$isInArrayItemOb = _ref2.isInArrayItemObject;

@@ -212,3 +240,3 @@ var isInArrayItemObject = _ref2$isInArrayItemOb === undefined ? false : _ref2$isInArrayItemOb;

var shouldValidateKey = !keysToValidate || _underscore2.default.any(keysToValidate, function (keyToValidate) {
return keyToValidate === affectedKey || keyToValidate === affectedKeyGeneric || affectedKey.indexOf(keyToValidate + '.') === 0 || affectedKeyGeneric.indexOf(keyToValidate + '.') === 0;
return keyToValidate === affectedKey || keyToValidate === affectedKeyGeneric || affectedKey.startsWith(keyToValidate + '.') || affectedKeyGeneric.startsWith(keyToValidate + '.');
});

@@ -218,16 +246,14 @@

if (shouldValidateKey) {
// We can skip the required check for keys that are ancestors
// of those in $set or $setOnInsert because they will be created
// by MongoDB while setting.
var skipRequiredCheck = _underscore2.default.some(setKeys, function (sk) {
return sk.slice(0, affectedKey.length + 1) === affectedKey + '.';
});
validate(val, affectedKey, affectedKeyGeneric, def, operator, skipRequiredCheck, isInArrayItemObject, isInSubObject);
validate(val, affectedKey, affectedKeyGeneric, def, operator, isInArrayItemObject, isInSubObject);
}
}
// If affectedKeyGeneric is undefined due to this being the first run of this
// function, objectKeys will return the top-level keys.
var childKeys = schema.objectKeys(affectedKeyGeneric);
// Temporarily convert missing objects to empty objects
// so that the looping code will be called and required
// descendent keys can be validated.
if ((val === undefined || val === null) && (!def || def.type === Object && !def.optional)) {
if ((val === undefined || val === null) && (!def || !def.optional && childKeys && childKeys.length > 0)) {
val = {};

@@ -242,7 +268,6 @@ }

affectedKey: affectedKey + '.' + i,
operator: operator,
setKeys: setKeys
operator: operator
});
});
} else if (_mongoObject2.default.isBasicObject(val) && (!def || !def.blackbox)) {
} else if ((0, _utility.isObjectWeShouldTraverse)(val) && (!def || !def.blackbox)) {
// Loop through object keys

@@ -256,3 +281,3 @@

// any missing required keys, and to run any custom functions for other keys.
var keysToCheck = _underscore2.default.union(presentKeys, schema.objectKeys(affectedKeyGeneric));
var keysToCheck = _underscore2.default.union(presentKeys, childKeys);

@@ -269,3 +294,2 @@ // If this object is within an array, make sure we check for

operator: operator,
setKeys: setKeys,
isInArrayItemObject: isInArrayItemObject,

@@ -279,5 +303,2 @@ isInSubObject: true

function checkModifier(mod) {
// Get a list of all keys in $set and $setOnInsert combined, for use later
var setKeys = Object.keys(mod.$set || {}).concat(Object.keys(mod.$setOnInsert || {}));
// If this is an upsert, add all the $setOnInsert keys to $set;

@@ -300,3 +321,3 @@ // since we don't know whether it will be an insert or update, we'll

}
if ((0, _utility.shouldCheck)(op)) {
if (shouldCheck(op)) {
// For an upsert, missing props would not be set if an insert is performed,

@@ -307,3 +328,3 @@ // so we check them all with undefined value to force any 'required' checks to fail

var presentKeys = Object.keys(opObj);
_underscore2.default.each(schema.objectKeys(), function (schemaKey) {
schema.objectKeys().forEach(function (schemaKey) {
if (!Array.includes(presentKeys, schemaKey)) {

@@ -313,4 +334,3 @@ checkObj({

affectedKey: schemaKey,
operator: op,
setKeys: setKeys
operator: op
});

@@ -332,4 +352,3 @@ }

affectedKey: k,
operator: op,
setKeys: setKeys
operator: op
});

@@ -348,2 +367,10 @@ });

// Custom whole-doc validators
var docValidators = schema._docValidators.concat(_SimpleSchema.SimpleSchema._docValidators);
docValidators.forEach(function (func) {
var errors = func(obj);
if (!Array.isArray(errors)) throw new Error('Custom doc validator must return an array of error objects');
if (errors.length) validationErrors = validationErrors.concat(errors);
});
var addedFieldNames = [];

@@ -362,73 +389,2 @@ validationErrors = _underscore2.default.filter(validationErrors, function (errObj) {

function doTypeChecks(def, keyValue, op) {
var expectedType = def.type;
if (expectedType === String) {
// String checks
if (typeof keyValue !== 'string') {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'String' };
} else if (def.max !== null && def.max < keyValue.length) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MAX_STRING, max: def.max };
} else if (def.min !== null && def.min > keyValue.length) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MIN_STRING, min: def.min };
} else if (def.regEx instanceof RegExp && !def.regEx.test(keyValue)) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.FAILED_REGULAR_EXPRESSION, regExp: def.regEx.toString() };
} else if (Array.isArray(def.regEx)) {
var regExError = void 0;
_underscore2.default.every(def.regEx, function (re) {
if (!re.test(keyValue)) {
regExError = { type: _SimpleSchema.SimpleSchema.ErrorTypes.FAILED_REGULAR_EXPRESSION, regExp: re.toString() };
return false;
}
return true;
});
if (regExError) return regExError;
}
} else if (expectedType === Number || expectedType === _SimpleSchema.SimpleSchema.Integer) {
// Number checks
if (typeof keyValue !== 'number' || isNaN(keyValue)) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: expectedType === _SimpleSchema.SimpleSchema.Integer ? 'Integer' : 'Number' };
} else if (op !== '$inc' && def.max !== null && (!!def.exclusiveMax ? def.max <= keyValue : def.max < keyValue)) {
return { type: !!def.exclusiveMax ? _SimpleSchema.SimpleSchema.ErrorTypes.MAX_NUMBER_EXCLUSIVE : _SimpleSchema.SimpleSchema.ErrorTypes.MAX_NUMBER, max: def.max };
} else if (op !== '$inc' && def.min !== null && (!!def.exclusiveMin ? def.min >= keyValue : def.min > keyValue)) {
return { type: !!def.exclusiveMin ? _SimpleSchema.SimpleSchema.ErrorTypes.MIN_NUMBER_EXCLUSIVE : _SimpleSchema.SimpleSchema.ErrorTypes.MIN_NUMBER, min: def.min };
} else if (expectedType === _SimpleSchema.SimpleSchema.Integer && !Number.isInteger(keyValue)) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MUST_BE_INTEGER };
}
} else if (expectedType === Boolean) {
// Boolean checks
if (typeof keyValue !== 'boolean') {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'Boolean' };
}
} else if (expectedType === Object) {
// Object checks
if (!_mongoObject2.default.isBasicObject(keyValue)) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'Object' };
}
} else if (expectedType === Array) {
// Array checks
if (!Array.isArray(keyValue)) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'Array' };
} else if (def.minCount !== null && keyValue.length < def.minCount) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MIN_COUNT, minCount: def.minCount };
} else if (def.maxCount !== null && keyValue.length > def.maxCount) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MAX_COUNT, maxCount: def.maxCount };
}
} else if (expectedType instanceof Function || (0, _utility.safariBugFix)(expectedType)) {
// Generic constructor checks
if (!(keyValue instanceof expectedType)) return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: expectedType.name };
// Date checks
if (expectedType === Date) {
if (isNaN(keyValue.getTime())) return { type: _SimpleSchema.SimpleSchema.ErrorTypes.BAD_DATE };
if (_underscore2.default.isDate(def.min) && def.min.getTime() > keyValue.getTime()) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MIN_DATE, min: (0, _utility.dateToDateString)(def.min) };
} else if (_underscore2.default.isDate(def.max) && def.max.getTime() < keyValue.getTime()) {
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MAX_DATE, max: (0, _utility.dateToDateString)(def.max) };
}
}
}
}
exports.default = doValidation;

@@ -46,2 +46,8 @@ 'use strict';

var _expandShorthand = require('./expandShorthand');
var _expandShorthand2 = _interopRequireDefault(_expandShorthand);
var _utility = require('./utility');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -52,11 +58,16 @@

// Exported for tests
var schemaDefinitionOptions = exports.schemaDefinitionOptions = ['type', 'label', 'optional', 'required', 'min', 'max', 'minCount', 'maxCount', 'allowedValues', 'exclusiveMin', 'exclusiveMax', 'regEx', 'custom', 'blackbox', 'autoValue', 'defaultValue', 'tracker', 'trim', 'humanizeAutoLabels'];
var schemaDefinitionOptions = exports.schemaDefinitionOptions = ['type', 'label', 'optional', 'required', 'autoValue', 'defaultValue'];
var optionsThatCanBeFunction = ['label', 'optional', 'min', 'max', 'minCount', 'maxCount', 'allowedValues'];
var oneOfProps = ['type', 'min', 'max', 'minCount', 'maxCount', 'allowedValues', 'exclusiveMin', 'exclusiveMax', 'regEx', 'custom', 'blackbox', 'trim'];
var propsThatCanBeFunction = ['label', 'optional'];
var oneOfPropsThatCanBeFunction = ['min', 'max', 'minCount', 'maxCount', 'allowedValues', 'exclusiveMin', 'exclusiveMax', 'regEx'];
var regExpMessages = [{ exp: _regExp2.default.Email, msg: 'must be a valid email address' }, { exp: _regExp2.default.WeakEmail, msg: 'must be a valid email address' }, { exp: _regExp2.default.Domain, msg: 'must be a valid domain' }, { exp: _regExp2.default.WeakDomain, msg: 'must be a valid domain' }, { exp: _regExp2.default.IP, msg: 'must be a valid IPv4 or IPv6 address' }, { exp: _regExp2.default.IPv4, msg: 'must be a valid IPv4 address' }, { exp: _regExp2.default.IPv6, msg: 'must be a valid IPv6 address' }, { exp: _regExp2.default.Url, msg: 'must be a valid URL' }, { exp: _regExp2.default.Id, msg: 'must be a valid alphanumeric ID' }];
var SimpleSchema = function () {
function SimpleSchema(schemas, options) {
var _this = this;
function SimpleSchema() {
var schema = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

@@ -68,78 +79,18 @@ _classCallCheck(this, SimpleSchema);

options = options || {};
schemas = schemas || {};
// Stash the options object
this._constructorOptions = _extends({}, options);
if (this._constructorOptions.humanizeAutoLabels !== false) this._constructorOptions.humanizeAutoLabels = true;
if (options.humanizeAutoLabels !== false) options.humanizeAutoLabels = true;
if (!Array.isArray(schemas)) schemas = [schemas];
// adjust and store a copy of the schema definitions
this._schema = mergeSchemas(schemas);
// store the list of defined keys for speedier checking
this._schemaKeys = [];
// store autoValue functions by key
this._autoValues = {};
// store the list of blackbox keys for passing to MongoObject constructor
this._blackboxKeys = [];
// store the list of first level keys
this._firstLevelSchemaKeys = [];
// a place to store custom validators for this instance
// Custom validators for this instance
this._validators = [];
this._docValidators = [];
this._depsLabels = {};
_underscore2.default.each(this._schema, function (definition, fieldName) {
if (definition instanceof _SimpleSchemaGroup2.default) {
definition.definitions = definition.definitions.map(function (def) {
return checkAndScrubDefinition(fieldName, def, options);
});
} else {
definition = checkAndScrubDefinition(fieldName, definition, options);
}
_this._schema[fieldName] = definition;
// Keep list of all keys
_this._schemaKeys.push(fieldName);
// Keep list of all top level keys
if (fieldName.indexOf('.') === -1) _this._firstLevelSchemaKeys.push(fieldName);
// Initialize label reactive dependency
if (options.tracker) {
_this._depsLabels[fieldName] = new options.tracker.Dependency();
}
// Keep list of all blackbox keys
if (definition.blackbox === true) _this._blackboxKeys.push(fieldName);
// Keep list of autoValue functions
if (definition.autoValue) _this._autoValues[fieldName] = definition.autoValue;
// Check array items
if (definition.type === Array) {
if (!_this._schema[fieldName + '.$']) throw new Error('Missing definition for key ' + fieldName + '.$');
// Set array item label to same as array label if array item label is missing
if (!_this._schema[fieldName + '.$'].hasOwnProperty('label')) {
_this._schema[fieldName + '.$'].label = _this._schema[fieldName].label;
if (options.tracker) {
_this._depsLabels[fieldName + '.$'] = new options.tracker.Dependency();
}
}
}
});
// Store a list of all object keys
this._objectKeys = getObjectKeys(this._schema, this._schemaKeys);
// We will store named validation contexts here
// Named validation contexts
this._validationContexts = {};
this._constructorOptions = options;
// Clone, expanding shorthand, and store the schema object in this._schema
this._schema = {};
this.extend(schema);
// Define default validation error messages
this.messageBox = new _messageBox2.default({

@@ -196,14 +147,50 @@ initialLanguage: 'en',

}, options.clean);
this.version = SimpleSchema.version;
}
/**
* @param {String} [key] One specific or generic key for which to get the schema
* @returns {Object} The entire schema object or just the definition for one key
*/
_createClass(SimpleSchema, [{
key: 'findFirstAncestorSimpleSchema',
value: function findFirstAncestorSimpleSchema(key, func) {
var _this = this;
var genericKey = _mongoObject2.default.makeKeyGeneric(key);
_createClass(SimpleSchema, [{
var foundSchema = false;
(0, _utility.forEachKeyAncestor)(genericKey, function (ancestor) {
if (foundSchema) return; // skip remaining once we've found it
var def = _this._schema[ancestor];
if (!def) return;
def.type.definitions.forEach(function (typeDef) {
if (typeDef.type instanceof SimpleSchema) {
func(typeDef.type, ancestor, genericKey.slice(ancestor.length + 1));
foundSchema = true;
}
});
});
return foundSchema;
}
/**
* @param {String} [key] One specific or generic key for which to get the schema
* @returns {Object} The entire schema object or just the definition for one key
*/
}, {
key: 'schema',
value: function schema(key) {
return key ? this._schema[_mongoObject2.default.makeKeyGeneric(key)] : this._schema;
if (!key) return this._schema;
var genericKey = _mongoObject2.default.makeKeyGeneric(key);
var keySchema = this._schema[genericKey];
// If not defined in this schema, see if it's defined in a subschema
if (!keySchema) {
this.findFirstAncestorSimpleSchema(key, function (simpleSchema, ancestor, subSchemaKey) {
keySchema = simpleSchema.schema(subSchemaKey);
});
}
return keySchema;
}

@@ -232,12 +219,87 @@

_underscore2.default.each(defs, function (val, prop) {
result[prop] = val;
if (optionsThatCanBeFunction.indexOf(prop) > -1 && typeof result[prop] === 'function') {
result[prop] = result[prop].call(functionContext || {});
if (propsThatCanBeFunction.indexOf(prop) > -1 && typeof val === 'function') {
result[prop] = val.call(functionContext || {});
// Inflect label if undefined
if (prop === 'label' && typeof result[prop] !== 'string') result[prop] = inflectedLabel(key, _this2._constructorOptions.humanizeAutoLabels);
} else {
result[prop] = val;
}
});
// Resolve all the types and convert to a normal array to make it easier
// to use.
if (defs.type) {
result.type = defs.type.definitions.map(function (typeDef) {
var newTypeDef = {};
_underscore2.default.each(typeDef, function (val, prop) {
if (oneOfPropsThatCanBeFunction.indexOf(prop) > -1 && typeof val === 'function') {
newTypeDef[prop] = val.call(functionContext || {});
} else {
newTypeDef[prop] = val;
}
});
return newTypeDef;
});
}
return result;
}
// Returns an array of all the autovalue functions, including those in subschemas all the
// way down the schema tree
}, {
key: 'autoValueFunctions',
value: function autoValueFunctions() {
var result = [];
function addFuncs(autoValues, closestSubschemaFieldName) {
_underscore2.default.each(autoValues, function (func, fieldName) {
result.push({
func: func,
fieldName: fieldName,
closestSubschemaFieldName: closestSubschemaFieldName
});
});
}
addFuncs(this._autoValues, '');
_underscore2.default.each(this._schema, function (keySchema, key) {
keySchema.type.definitions.forEach(function (typeDef) {
if (!(typeDef.type instanceof SimpleSchema)) return;
result = result.concat(typeDef.type.autoValueFunctions().map(function (_ref2) {
var func = _ref2.func;
var fieldName = _ref2.fieldName;
var closestSubschemaFieldName = _ref2.closestSubschemaFieldName;
return {
func: func,
fieldName: key + '.' + fieldName,
closestSubschemaFieldName: closestSubschemaFieldName.length ? key + '.' + closestSubschemaFieldName : key
};
}));
});
});
return result;
}
// Returns an array of all the blackbox keys, including those in subschemas
}, {
key: 'blackboxKeys',
value: function blackboxKeys() {
var blackboxKeys = this._blackboxKeys;
_underscore2.default.each(this._schema, function (keySchema, key) {
keySchema.type.definitions.forEach(function (typeDef) {
if (!(typeDef.type instanceof SimpleSchema)) return;
typeDef.type._blackboxKeys.forEach(function (blackboxKey) {
blackboxKeys.push(key + '.' + blackboxKey);
});
});
});
return _underscore2.default.uniq(blackboxKeys);
}
// Check if the key is a nested dot-syntax key inside of a blackbox object

@@ -248,15 +310,19 @@

value: function keyIsInBlackBox(key) {
var testKey = _mongoObject2.default.makeKeyGeneric(key);
var lastDot = void 0;
var _this3 = this;
// Iterate the dot-syntax hierarchy until we find a key in our schema
do {
lastDot = testKey.lastIndexOf('.');
if (lastDot !== -1) {
testKey = testKey.slice(0, lastDot); // Remove last path component
if (this._blackboxKeys.indexOf(testKey) > -1) return true;
var isInBlackBox = false;
(0, _utility.forEachKeyAncestor)(_mongoObject2.default.makeKeyGeneric(key), function (ancestor, remainder) {
if (_this3._blackboxKeys.indexOf(ancestor) > -1) {
isInBlackBox = true;
} else {
var testKeySchema = _this3.schema(ancestor);
if (testKeySchema) {
testKeySchema.type.definitions.forEach(function (typeDef) {
if (!(typeDef.type instanceof SimpleSchema)) return;
if (typeDef.type.keyIsInBlackBox(remainder)) isInBlackBox = true;
});
}
}
} while (lastDot !== -1);
return false;
});
return isInBlackBox;
}

@@ -271,25 +337,33 @@

value: function allowsKey(key) {
var _this3 = this;
var _this4 = this;
// Loop through all keys in the schema
return _underscore2.default.any(this._schemaKeys, function (schemaKey) {
return _underscore2.default.any(this._schemaKeys, function (loopKey) {
// If the schema key is the test key, it's allowed.
if (schemaKey === key) return true;
if (loopKey === key) return true;
var fieldSchema = _this4.schema(loopKey);
var compare1 = key.slice(0, loopKey.length + 2);
var compare2 = compare1.slice(0, -1);
// Blackbox and subschema checks are needed only if key starts with
// loopKey + a dot
if (compare2 !== loopKey + '.') return false;
// Black box handling
if (_this3.schema(schemaKey).blackbox === true) {
var kl = schemaKey.length;
var compare1 = key.slice(0, kl + 2);
var compare2 = compare1.slice(0, -1);
if (fieldSchema.blackbox === true) {
// If the test key is the black box key + ".$", then the test
// key is NOT allowed because black box keys are by definition
// only for objects, and not for arrays.
if (compare1 === schemaKey + '.$') return false;
// Otherwise
if (compare2 === schemaKey + '.') return true;
return compare1 !== loopKey + '.$';
}
return false;
// Subschemas
var allowed = false;
var subKey = key.slice(loopKey.length + 1);
fieldSchema.type.definitions.forEach(function (typeDef) {
if (!(typeDef.type instanceof SimpleSchema)) return;
if (typeDef.type.allowsKey(subKey)) allowed = true;
});
return allowed;
});

@@ -316,4 +390,4 @@ }

*
* @param {SimpleSchema} schema
* @returns {SimpleSchema} The new, extended schema.
* @param {SimpleSchema|Object} schema
* @returns undefined
*/

@@ -323,4 +397,59 @@

key: 'extend',
value: function extend(schema) {
return new SimpleSchema([this, schema]);
value: function extend() {
var _this5 = this;
var schema = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var schemaObj = void 0;
if (schema instanceof SimpleSchema) {
schemaObj = schema._schema;
// Merge the validators
this._validators = this._validators.concat(schema._validators);
this._docValidators = this._docValidators.concat(schema._docValidators);
} else {
schemaObj = (0, _expandShorthand2.default)(schema);
}
// Extend this._schema with additional fields and definitions from schema
this._schema = mergeSchemas([this._schema, schemaObj]);
checkSchemaOverlap(this._schema);
// Set/Reset all of these
this._schemaKeys = [];
this._autoValues = {};
this._blackboxKeys = [];
this._firstLevelSchemaKeys = [];
this._depsLabels = {};
this._objectKeys = {};
// Update all of the information cached on the instance
_underscore2.default.each(this._schema, function (definition, fieldName) {
_this5._schema[fieldName] = definition = checkAndScrubDefinition(fieldName, definition, _this5._constructorOptions, _this5._schema);
// Keep list of all keys for speedier checking
_this5._schemaKeys.push(fieldName);
// Keep list of all top level keys
if (fieldName.indexOf('.') === -1) _this5._firstLevelSchemaKeys.push(fieldName);
// Initialize label reactive dependency (Meteor only)
if (_this5._constructorOptions.tracker) {
_this5._depsLabels[fieldName] = new _this5._constructorOptions.tracker.Dependency();
}
// Keep list of all blackbox keys for passing to MongoObject constructor
if (definition.blackbox) _this5._blackboxKeys.push(fieldName);
// Keep list of autoValue functions by key
if (definition.autoValue) _this5._autoValues[fieldName] = definition.autoValue;
// Store child keys keyed by parent
if (fieldName.indexOf('.') > -1 && fieldName.slice(-2) !== '.$') {
var parentKey = fieldName.slice(0, fieldName.lastIndexOf('.'));
var parentKeyWithDot = parentKey + '.';
_this5._objectKeys[parentKeyWithDot] = _this5._objectKeys[parentKeyWithDot] || [];
_this5._objectKeys[parentKeyWithDot].push(fieldName.slice(fieldName.lastIndexOf('.') + 1));
}
});
}

@@ -347,4 +476,19 @@ }, {

}, {
key: 'addDocValidator',
value: function addDocValidator(func) {
this._docValidators.push(func);
}
/**
* @param obj {Object|Object[]} Object or array of objects to validate.
* @param [options] {Object} Same options object that ValidationContext#validate takes
*
* Throws an Error with name `ClientError` and `details` property containing the errors.
*/
}, {
key: 'validate',
value: function validate(obj, options) {
var _this6 = this;
// For Meteor apps, `check` option can be passed to silence audit-argument-checks

@@ -358,18 +502,31 @@ if (typeof this._constructorOptions.check === 'function') {

var validationContext = this.newContext();
var isValid = validationContext.validate(obj, options);
// obj can be an array, in which case we validate each object in it and
// throw as soon as one has an error
var objects = Array.isArray(obj) ? obj : [obj];
objects.forEach(function (oneObj) {
var validationContext = _this6.newContext();
var isValid = validationContext.validate(oneObj, options);
if (isValid) return;
if (isValid) return;
var errors = validationContext.validationErrors();
var errors = validationContext.validationErrors();
// In order for the message at the top of the stack trace to be useful,
// we set it to the first validation error message.
var message = this.messageForError(errors[0]);
// In order for the message at the top of the stack trace to be useful,
// we set it to the first validation error message.
var message = _this6.messageForError(errors[0]);
var error = new Error(message);
error.name = error.errorType = 'ClientError';
error.details = errors;
error.error = 'validation-error';
throw error;
var error = new Error(message);
error.name = error.errorType = 'ClientError';
error.details = errors;
error.error = 'validation-error';
// The primary use for the validationErrorTransform is for the Meteor package
// to convert the vanilla Error into a Meteor.Error until DDP is able to pass
// vanilla errors back to the client.
if (typeof SimpleSchema.validationErrorTransform === 'function') {
throw SimpleSchema.validationErrorTransform(error);
} else {
throw error;
}
});
}

@@ -379,3 +536,3 @@ }, {

value: function validator() {
var _this4 = this;
var _this7 = this;

@@ -385,4 +542,9 @@ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

return function (obj) {
if (options.clean === true) _this4.clean(obj, options);
_this4.validate(obj, options);
var optionsClone = _extends({}, options);
if (options.clean === true) {
// Do this here and pass into both functions for better performance
optionsClone.mongoObject = new _mongoObject2.default(obj, _this7.blackboxKeys());
_this7.clean(obj, optionsClone);
}
_this7.validate(obj, optionsClone);
};

@@ -410,10 +572,10 @@ }

value: function labels(_labels) {
var _this5 = this;
var _this8 = this;
_underscore2.default.each(_labels, function (label, key) {
if (typeof label !== 'string' && typeof label !== 'function') return;
if (!_this5._schema.hasOwnProperty(key)) return;
if (!_this8._schema.hasOwnProperty(key)) return;
_this5._schema[key].label = label;
_this5._depsLabels[key] && _this5._depsLabels[key].changed();
_this8._schema[key].label = label;
_this8._depsLabels[key] && _this8._depsLabels[key].changed();
});

@@ -433,3 +595,3 @@ }

value: function label(key) {
var _this6 = this;
var _this9 = this;

@@ -440,4 +602,4 @@ // Get all labels

var result = {};
_underscore2.default.each(_this6._schemaKeys, function (schemaKey) {
result[schemaKey] = _this6.label(schemaKey);
_underscore2.default.each(_this9._schemaKeys, function (schemaKey) {
result[schemaKey] = _this9.label(schemaKey);
});

@@ -506,2 +668,10 @@ return {

}, {
key: 'defineValidationErrorTransform',
value: function defineValidationErrorTransform(transform) {
if (typeof transform !== 'function') {
throw new Error('SimpleSchema.defineValidationErrorTransform must be passed a function that accepts an Error and returns an Error');
}
SimpleSchema.validationErrorTransform = transform;
}
}, {
key: 'validate',

@@ -518,4 +688,8 @@ value: function validate(obj, schema, options) {

key: 'oneOf',
value: function oneOf(definitions) {
return new _SimpleSchemaGroup2.default(definitions);
value: function oneOf() {
for (var _len2 = arguments.length, definitions = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
definitions[_key2] = arguments[_key2];
}
return new (Function.prototype.bind.apply(_SimpleSchemaGroup2.default, [null].concat(definitions)))();
}

@@ -530,2 +704,7 @@

}
}, {
key: 'addDocValidator',
value: function addDocValidator(func) {
SimpleSchema._docValidators.push(func);
}

@@ -543,4 +722,6 @@ // Backwards compatibility

SimpleSchema.version = 2;
SimpleSchema.RegEx = _regExp2.default;
SimpleSchema._validators = [];
SimpleSchema._docValidators = [];
SimpleSchema.ErrorTypes = {

@@ -569,15 +750,4 @@ REQUIRED: 'required',

function mergeSchemas(schemas) {
// Merge all provided schema definitions.
// This is effectively a shallow clone of each object, too,
// which is what we want since we are going to manipulate it.
var mergedSchema = {};
_underscore2.default.each(schemas, function (schema) {
// Create a temporary SS instance so that the internal object
// we use for merging/extending will be fully expanded
if (schema instanceof SimpleSchema) {
schema = schema._schema;
} else {
schema = expandSchema(schema);
}
// Loop through and extend each individual field

@@ -597,106 +767,21 @@ // definition. That way you can extend and overwrite

});
return mergedSchema;
}
// Returns an object relating the keys in the list
// to their parent object.
function getObjectKeys(schema, schemaKeyList) {
var result = {};
// Throws an error if any fields are `type` SimpleSchema but then also
// have subfields defined outside of that.
function checkSchemaOverlap(schema) {
_underscore2.default.each(schema, function (val, key) {
_underscore2.default.each(val.type.definitions, function (def) {
if (!(def.type instanceof SimpleSchema)) return;
console.log(key, def.type);
_underscore2.default.each(schema, function (definition, key) {
if (definition.type !== Object) return;
var keyPrefix = key + '.';
var childKeys = [];
_underscore2.default.each(schemaKeyList, function (otherKey) {
// Is it a descendant key?
if (!otherKey.startsWith(keyPrefix)) return;
var remainingText = otherKey.substring(keyPrefix.length);
// Is it a direct child?
if (remainingText.indexOf('.') === -1) childKeys.push(remainingText);
});
result[keyPrefix] = childKeys;
});
return result;
}
function expandSchema(schema) {
// BEGIN SHORTHAND
var addArrayFields = [];
_underscore2.default.each(schema, function (definition, key) {
if (!_mongoObject2.default.isBasicObject(definition) && !(definition instanceof _SimpleSchemaGroup2.default)) {
if (Array.isArray(definition)) {
if (Array.isArray(definition[0])) {
throw new Error('Array shorthand may only be used to one level of depth (' + key + ')');
_underscore2.default.each(def.type._schema, function (subVal, subKey) {
var newKey = key + '.' + subKey;
if (schema.hasOwnProperty(newKey)) {
throw new Error('The type for "' + key + '" is set to a SimpleSchema instance that defines "' + key + '.' + subKey + '", but the parent SimpleSchema instance also tries to define "' + key + '.' + subKey + '"');
}
var type = definition[0];
if (type instanceof RegExp) {
addArrayFields.push({ key: key, type: String, regEx: type });
} else {
addArrayFields.push({ key: key, type: type });
}
schema[key] = { type: Array };
} else {
if (definition instanceof RegExp) {
schema[key] = {
type: String,
regEx: definition
};
} else {
schema[key] = { type: definition };
}
}
}
});
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = addArrayFields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _step.value;
var key = _step$value.key;
var type = _step$value.type;
var regEx = _step$value.regEx;
var itemKey = key + '.$';
if (schema[itemKey]) {
throw new Error('Array shorthand used for ' + key + ' field but ' + key + '.$ key is already in the schema');
}
schema[itemKey] = { type: type };
if (regEx) schema[itemKey].regEx = regEx;
}
// END SHORTHAND
// Flatten schema by inserting nested definitions
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
_underscore2.default.each(schema, function (val, key) {
if (!(val.type instanceof SimpleSchema)) return;
// Add child schema definitions to parent schema
_underscore2.default.each(val.type._schema, function (subVal, subKey) {
var newKey = key + '.' + subKey;
if (!schema.hasOwnProperty(newKey)) schema[newKey] = subVal;
});
});
val.type = Object;
});
return schema;
}

@@ -728,22 +813,45 @@

function checkAndScrubDefinition(fieldName, definition, options) {
function checkAndScrubDefinition(fieldName, definition, options, fullSchemaObj) {
var internalDefinition = _extends({}, definition);
// Internally, all definition types are stored as groups for simplicity of access
if (!(internalDefinition.type instanceof _SimpleSchemaGroup2.default)) {
internalDefinition.type = new _SimpleSchemaGroup2.default(_underscore2.default.pick(internalDefinition, oneOfProps));
}
// Limit to only the non-oneOf props
internalDefinition = _underscore2.default.omit(internalDefinition, _underscore2.default.without(oneOfProps, 'type'));
// Validate the field definition
_underscore2.default.each(definition, function (val, key) {
_underscore2.default.each(internalDefinition, function (val, key) {
if (schemaDefinitionOptions.indexOf(key) === -1) {
throw new Error('Invalid definition for ' + fieldName + ' field: "' + key + '" is not a supported option');
throw new Error('Invalid definition for ' + fieldName + ' field: "' + key + '" is not a supported property');
}
});
// TYPE
// Since type can be anything, make sure it's defined
if (!definition.type) throw new Error('Invalid definition for ' + fieldName + ' field: "type" option is required');
// Make sure the `type`s are OK
internalDefinition.type.definitions.forEach(function (_ref3) {
var blackbox = _ref3.blackbox;
var type = _ref3.type;
if (Array.isArray(definition.type)) {
throw new Error('Invalid definition for ' + fieldName + ' field: "type" may not be an array. Change it to Array.');
}
if (!type) throw new Error('Invalid definition for ' + fieldName + ' field: "type" option is required');
var internalDefinition = _extends({}, definition);
if (Array.isArray(type)) {
throw new Error('Invalid definition for ' + fieldName + ' field: "type" may not be an array. Change it to Array.');
}
// AUTOVALUE
if (type instanceof SimpleSchema) {
_underscore2.default.each(type._schema, function (subVal, subKey) {
var newKey = fieldName + '.' + subKey;
if (fullSchemaObj.hasOwnProperty(newKey)) {
throw new Error('The type for "' + fieldName + '" is set to a SimpleSchema instance that defines "' + newKey + '", but the parent SimpleSchema instance also tries to define "' + newKey + '"');
}
});
}
// If any of the valid types are blackbox, mark blackbox on the overall definition
if (blackbox === true) internalDefinition.blackbox = true;
});
// defaultValue -> autoValue
// We support defaultValue shortcut by converting it immediately into an

@@ -771,4 +879,4 @@ // autoValue.

internalDefinition.optional = function optional() {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}

@@ -805,4 +913,4 @@

return function pickOrOmit() {
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
args[_key4] = arguments[_key4];
}

@@ -809,0 +917,0 @@

@@ -19,2 +19,21 @@ 'use strict';

function isObjectWeShouldTraverse(val) {
if (val !== Object(val)) return false;
// There are some object types that we know we shouldn't traverse because
// they will often result in overflows and it makes no sense to validate them.
if (val instanceof Date) return false;
if (val instanceof Int8Array) return false;
if (val instanceof Uint8Array) return false;
if (val instanceof Uint8ClampedArray) return false;
if (val instanceof Int16Array) return false;
if (val instanceof Uint16Array) return false;
if (val instanceof Int32Array) return false;
if (val instanceof Uint32Array) return false;
if (val instanceof Float32Array) return false;
if (val instanceof Float64Array) return false;
return true;
}
function looksLikeModifier(obj) {

@@ -26,11 +45,29 @@ return !!Object.keys(obj || {}).find(function (key) {

// The latest Safari returns false for Uint8Array, etc. instanceof Function
// unlike other browsers.
function safariBugFix(type) {
return typeof Uint8Array !== 'undefined' && type === Uint8Array || typeof Uint16Array !== 'undefined' && type === Uint16Array || typeof Uint32Array !== 'undefined' && type === Uint32Array || typeof Uint8ClampedArray !== 'undefined' && type === Uint8ClampedArray;
/**
* Run loopFunc for each ancestor key in a dot-delimited key. For example,
* if key is "a.b.c", loopFunc will be called first with ('a.b', 'c') and then with ('a', 'b.c')
*/
function forEachKeyAncestor(key, loopFunc) {
var lastDot = void 0;
// Iterate the dot-syntax hierarchy
var ancestor = key;
do {
lastDot = ancestor.lastIndexOf('.');
if (lastDot !== -1) {
ancestor = ancestor.slice(0, lastDot);
var remainder = key.slice(ancestor.length + 1);
loopFunc(ancestor, remainder); // Remove last path component
}
} while (lastDot !== -1);
}
function shouldCheck(key) {
if (key === '$pushAll') throw new Error('$pushAll is not supported; use $push + $each');
return ['$pull', '$pullAll', '$pop', '$slice'].indexOf(key) === -1;
/**
* Returns the parent of a key. For example, returns 'a.b' when passed 'a.b.c'.
* If no parent, returns an empty string. If withEndDot is true, the return
* value will have a dot appended when it isn't an empty string.
*/
function getParentOfKey(key, withEndDot) {
var lastDot = key.lastIndexOf('.');
return lastDot === -1 ? '' : key.slice(0, lastDot + Number(!!withEndDot));
}

@@ -40,4 +77,5 @@

exports.dateToDateString = dateToDateString;
exports.isObjectWeShouldTraverse = isObjectWeShouldTraverse;
exports.looksLikeModifier = looksLikeModifier;
exports.safariBugFix = safariBugFix;
exports.shouldCheck = shouldCheck;
exports.forEachKeyAncestor = forEachKeyAncestor;
exports.getParentOfKey = getParentOfKey;

@@ -206,24 +206,25 @@ 'use strict';

var _ref$modifier = _ref.modifier;
var modifier = _ref$modifier === undefined ? false : _ref$modifier;
var _ref$upsert = _ref.upsert;
var upsert = _ref$upsert === undefined ? false : _ref$upsert;
var _ref$extendedCustomCo = _ref.extendedCustomContext;
var extendedCustomContext = _ref$extendedCustomCo === undefined ? {} : _ref$extendedCustomCo;
var _ref$ignore = _ref.ignore;
var ignore = _ref$ignore === undefined ? [] : _ref$ignore;
var _ref$keys = _ref.keys;
var keys = _ref$keys === undefined ? null : _ref$keys;
var ignoreTypes = _ref$ignore === undefined ? [] : _ref$ignore;
var keysToValidate = _ref.keys;
var _ref$modifier = _ref.modifier;
var isModifier = _ref$modifier === undefined ? false : _ref$modifier;
var mongoObject = _ref.mongoObject;
var _ref$upsert = _ref.upsert;
var isUpsert = _ref$upsert === undefined ? false : _ref$upsert;
var validationErrors = (0, _doValidation2.default)({
extendedCustomContext: extendedCustomContext,
ignoreTypes: ignoreTypes,
isModifier: isModifier,
isUpsert: isUpsert,
keysToValidate: keysToValidate,
mongoObject: mongoObject,
obj: obj,
isModifier: modifier,
isUpsert: upsert,
keysToValidate: keys,
schema: this._simpleSchema,
extendedCustomContext: extendedCustomContext,
ignoreTypes: ignore
schema: this._simpleSchema
});
if (keys) {
if (keysToValidate) {
// We have only revalidated the listed keys, so if there

@@ -240,3 +241,3 @@ // are any other existing errors that are NOT in the keys list,

var wasValidated = _underscore2.default.any(keys, function (key) {
var wasValidated = _underscore2.default.any(keysToValidate, function (key) {
return key === error.name || error.name.startsWith(key + '.');

@@ -243,0 +244,0 @@ });

{
"name": "simpl-schema",
"version": "0.0.1",
"version": "0.0.2",
"description": "A schema validation package that supports direct validation of MongoDB update modifier objects.",

@@ -5,0 +5,0 @@ "author": "Eric Dobbertin <aldeed@gmail.com>",

@@ -16,2 +16,75 @@ # simple-schema

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [simple-schema](#simple-schema)
- [The History of SimpleSchema](#the-history-of-simpleschema)
- [Installation](#installation)
- [Lingo](#lingo)
- [Quick Start](#quick-start)
- [Validate an Object and Throw an Error](#validate-an-object-and-throw-an-error)
- [Validate an Array of Objects and Throw an Error](#validate-an-array-of-objects-and-throw-an-error)
- [Validate an Object and Get the Errors](#validate-an-object-and-get-the-errors)
- [Validate a MongoDB Modifier](#validate-a-mongodb-modifier)
- [Enable Meteor Tracker Reactivity](#enable-meteor-tracker-reactivity)
- [Automatically Clean the Object Before Validating It](#automatically-clean-the-object-before-validating-it)
- [Explicitly Clean an Object](#explicitly-clean-an-object)
- [Defining a Schema](#defining-a-schema)
- [Shorthand Definitions](#shorthand-definitions)
- [Longhand Definitions](#longhand-definitions)
- [Mixing Shorthand with Longhand](#mixing-shorthand-with-longhand)
- [More Shorthand](#more-shorthand)
- [Multiple Definitions For One Key](#multiple-definitions-for-one-key)
- [Extending Schemas](#extending-schemas)
- [Overriding When Extending](#overriding-when-extending)
- [Subschemas](#subschemas)
- [Extracting Schemas](#extracting-schemas)
- [Schema Keys](#schema-keys)
- [Schema Rules](#schema-rules)
- [type](#type)
- [label](#label)
- [optional](#optional)
- [min/max](#minmax)
- [exclusiveMin/exclusiveMax](#exclusiveminexclusivemax)
- [minCount/maxCount](#mincountmaxcount)
- [allowedValues](#allowedvalues)
- [regEx](#regex)
- [blackbox](#blackbox)
- [trim](#trim)
- [custom](#custom)
- [defaultValue](#defaultvalue)
- [autoValue](#autovalue)
- [Validating Data](#validating-data)
- [The Object to Validate](#the-object-to-validate)
- [Ways to Perform Validation](#ways-to-perform-validation)
- [Named Validation Contexts](#named-validation-contexts)
- [Unnamed Validation Contexts](#unnamed-validation-contexts)
- [Validating an Object](#validating-an-object)
- [Validating Only Some Keys in an Object](#validating-only-some-keys-in-an-object)
- [Validation Options](#validation-options)
- [Validating and Throwing ValidationErrors](#validating-and-throwing-validationerrors)
- [Customize the Error That is Thrown](#customize-the-error-that-is-thrown)
- [Custom Field Validation](#custom-field-validation)
- [Custom Whole-Document Validators](#custom-whole-document-validators)
- [Manually Adding a Validation Error](#manually-adding-a-validation-error)
- [Asynchronous Custom Validation on the Client](#asynchronous-custom-validation-on-the-client)
- [Getting a List of Invalid Keys and Validation Error Messages](#getting-a-list-of-invalid-keys-and-validation-error-messages)
- [Customizing Validation Messages](#customizing-validation-messages)
- [Other Validation Context Methods](#other-validation-context-methods)
- [Other SimpleSchema Methods](#other-simpleschema-methods)
- [Cleaning Objects](#cleaning-objects)
- [Dates](#dates)
- [Best Practice Code Examples](#best-practice-code-examples)
- [Make a field conditionally required](#make-a-field-conditionally-required)
- [Validate one key against another](#validate-one-key-against-another)
- [Debug Mode](#debug-mode)
- [Extending the Schema Options](#extending-the-schema-options)
- [Add On Packages](#add-on-packages)
- [License](#license)
- [Contributing](#contributing)
- [Thanks](#thanks)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## The History of SimpleSchema

@@ -18,0 +91,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc