UNPKG

22.4 kBJavaScriptView Raw
1'use strict';
2
3/*!
4 * Module dependencies.
5 */
6
7const UUID = require('bson').UUID;
8const ms = require('ms');
9const mpath = require('mpath');
10const ObjectId = require('./types/objectid');
11const PopulateOptions = require('./options/populateOptions');
12const clone = require('./helpers/clone');
13const immediate = require('./helpers/immediate');
14const isObject = require('./helpers/isObject');
15const isMongooseArray = require('./types/array/isMongooseArray');
16const isMongooseDocumentArray = require('./types/documentArray/isMongooseDocumentArray');
17const isBsonType = require('./helpers/isBsonType');
18const isPOJO = require('./helpers/isPOJO');
19const getFunctionName = require('./helpers/getFunctionName');
20const isMongooseObject = require('./helpers/isMongooseObject');
21const promiseOrCallback = require('./helpers/promiseOrCallback');
22const schemaMerge = require('./helpers/schema/merge');
23const specialProperties = require('./helpers/specialProperties');
24const { trustedSymbol } = require('./helpers/query/trusted');
25
26let Document;
27
28exports.specialProperties = specialProperties;
29
30exports.isMongooseArray = isMongooseArray.isMongooseArray;
31exports.isMongooseDocumentArray = isMongooseDocumentArray.isMongooseDocumentArray;
32exports.registerMongooseArray = isMongooseArray.registerMongooseArray;
33exports.registerMongooseDocumentArray = isMongooseDocumentArray.registerMongooseDocumentArray;
34
35const oneSpaceRE = /\s/;
36const manySpaceRE = /\s+/;
37
38/**
39 * Produces a collection name from model `name`. By default, just returns
40 * the model name
41 *
42 * @param {String} name a model name
43 * @param {Function} pluralize function that pluralizes the collection name
44 * @return {String} a collection name
45 * @api private
46 */
47
48exports.toCollectionName = function(name, pluralize) {
49 if (name === 'system.profile') {
50 return name;
51 }
52 if (name === 'system.indexes') {
53 return name;
54 }
55 if (typeof pluralize === 'function') {
56 return pluralize(name);
57 }
58 return name;
59};
60
61/**
62 * Determines if `a` and `b` are deep equal.
63 *
64 * Modified from node/lib/assert.js
65 *
66 * @param {any} a a value to compare to `b`
67 * @param {any} b a value to compare to `a`
68 * @return {Boolean}
69 * @api private
70 */
71
72exports.deepEqual = function deepEqual(a, b) {
73 if (a === b) {
74 return true;
75 }
76
77 if (typeof a !== 'object' || typeof b !== 'object') {
78 return a === b;
79 }
80
81 if (a instanceof Date && b instanceof Date) {
82 return a.getTime() === b.getTime();
83 }
84
85 if ((isBsonType(a, 'ObjectId') && isBsonType(b, 'ObjectId')) ||
86 (isBsonType(a, 'Decimal128') && isBsonType(b, 'Decimal128'))) {
87 return a.toString() === b.toString();
88 }
89
90 if (a instanceof RegExp && b instanceof RegExp) {
91 return a.source === b.source &&
92 a.ignoreCase === b.ignoreCase &&
93 a.multiline === b.multiline &&
94 a.global === b.global &&
95 a.dotAll === b.dotAll &&
96 a.unicode === b.unicode &&
97 a.sticky === b.sticky &&
98 a.hasIndices === b.hasIndices;
99 }
100
101 if (a == null || b == null) {
102 return false;
103 }
104
105 if (a.prototype !== b.prototype) {
106 return false;
107 }
108
109 if (a instanceof Map || b instanceof Map) {
110 if (!(a instanceof Map) || !(b instanceof Map)) {
111 return false;
112 }
113 return deepEqual(Array.from(a.keys()), Array.from(b.keys())) &&
114 deepEqual(Array.from(a.values()), Array.from(b.values()));
115 }
116
117 // Handle MongooseNumbers
118 if (a instanceof Number && b instanceof Number) {
119 return a.valueOf() === b.valueOf();
120 }
121
122 if (Buffer.isBuffer(a)) {
123 return exports.buffer.areEqual(a, b);
124 }
125
126 if (Array.isArray(a) || Array.isArray(b)) {
127 if (!Array.isArray(a) || !Array.isArray(b)) {
128 return false;
129 }
130 const len = a.length;
131 if (len !== b.length) {
132 return false;
133 }
134 for (let i = 0; i < len; ++i) {
135 if (!deepEqual(a[i], b[i])) {
136 return false;
137 }
138 }
139 return true;
140 }
141
142 if (a.$__ != null) {
143 a = a._doc;
144 } else if (isMongooseObject(a)) {
145 a = a.toObject();
146 }
147
148 if (b.$__ != null) {
149 b = b._doc;
150 } else if (isMongooseObject(b)) {
151 b = b.toObject();
152 }
153
154 const ka = Object.keys(a);
155 const kb = Object.keys(b);
156 const kaLength = ka.length;
157
158 // having the same number of owned properties (keys incorporates
159 // hasOwnProperty)
160 if (kaLength !== kb.length) {
161 return false;
162 }
163
164 // ~~~cheap key test
165 for (let i = kaLength - 1; i >= 0; i--) {
166 if (ka[i] !== kb[i]) {
167 return false;
168 }
169 }
170
171 // equivalent values for every corresponding key, and
172 // ~~~possibly expensive deep test
173 for (const key of ka) {
174 if (!deepEqual(a[key], b[key])) {
175 return false;
176 }
177 }
178
179 return true;
180};
181
182/**
183 * Get the last element of an array
184 * @param {Array} arr
185 */
186
187exports.last = function(arr) {
188 if (arr.length > 0) {
189 return arr[arr.length - 1];
190 }
191 return void 0;
192};
193
194/*!
195 * ignore
196 */
197
198exports.promiseOrCallback = promiseOrCallback;
199
200/*!
201 * ignore
202 */
203
204exports.cloneArrays = function cloneArrays(arr) {
205 if (!Array.isArray(arr)) {
206 return arr;
207 }
208
209 return arr.map(el => exports.cloneArrays(el));
210};
211
212/*!
213 * ignore
214 */
215
216exports.omit = function omit(obj, keys) {
217 if (keys == null) {
218 return Object.assign({}, obj);
219 }
220 if (!Array.isArray(keys)) {
221 keys = [keys];
222 }
223
224 const ret = Object.assign({}, obj);
225 for (const key of keys) {
226 delete ret[key];
227 }
228 return ret;
229};
230
231/**
232 * Merges `from` into `to` without overwriting existing properties.
233 *
234 * @param {Object} to
235 * @param {Object} from
236 * @param {Object} [options]
237 * @param {String} [path]
238 * @api private
239 */
240
241exports.merge = function merge(to, from, options, path) {
242 options = options || {};
243
244 const keys = Object.keys(from);
245 let i = 0;
246 const len = keys.length;
247 let key;
248
249 if (from[trustedSymbol]) {
250 to[trustedSymbol] = from[trustedSymbol];
251 }
252
253 path = path || '';
254 const omitNested = options.omitNested || {};
255
256 while (i < len) {
257 key = keys[i++];
258 if (options.omit && options.omit[key]) {
259 continue;
260 }
261 if (omitNested[path]) {
262 continue;
263 }
264 if (specialProperties.has(key)) {
265 continue;
266 }
267 if (to[key] == null) {
268 if (isPOJO(from[key])) {
269 to[key] = { ...from[key] };
270 } else if (Array.isArray(from[key])) {
271 to[key] = [...from[key]];
272 } else {
273 to[key] = from[key];
274 }
275 } else if (exports.isObject(from[key])) {
276 if (!exports.isObject(to[key])) {
277 to[key] = {};
278 }
279 if (from[key] != null) {
280 // Skip merging schemas if we're creating a discriminator schema and
281 // base schema has a given path as a single nested but discriminator schema
282 // has the path as a document array, or vice versa (gh-9534)
283 if (options.isDiscriminatorSchemaMerge &&
284 (from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
285 (from[key].$isMongooseDocumentArray && to[key].$isSingleNested)) {
286 continue;
287 } else if (from[key].instanceOfSchema) {
288 if (to[key].instanceOfSchema) {
289 schemaMerge(to[key], from[key].clone(), options.isDiscriminatorSchemaMerge);
290 } else {
291 to[key] = from[key].clone();
292 }
293 continue;
294 } else if (isBsonType(from[key], 'ObjectId')) {
295 to[key] = new ObjectId(from[key]);
296 continue;
297 }
298 }
299 merge(to[key], from[key], options, path ? path + '.' + key : key);
300 } else if (options.overwrite) {
301 to[key] = from[key];
302 }
303 }
304};
305
306/**
307 * Applies toObject recursively.
308 *
309 * @param {Document|Array|Object} obj
310 * @return {Object}
311 * @api private
312 */
313
314exports.toObject = function toObject(obj) {
315 Document || (Document = require('./document'));
316 let ret;
317
318 if (obj == null) {
319 return obj;
320 }
321
322 if (obj instanceof Document) {
323 return obj.toObject();
324 }
325
326 if (Array.isArray(obj)) {
327 ret = [];
328
329 for (const doc of obj) {
330 ret.push(toObject(doc));
331 }
332
333 return ret;
334 }
335
336 if (exports.isPOJO(obj)) {
337 ret = {};
338
339 if (obj[trustedSymbol]) {
340 ret[trustedSymbol] = obj[trustedSymbol];
341 }
342
343 for (const k of Object.keys(obj)) {
344 if (specialProperties.has(k)) {
345 continue;
346 }
347 ret[k] = toObject(obj[k]);
348 }
349
350 return ret;
351 }
352
353 return obj;
354};
355
356exports.isObject = isObject;
357
358/**
359 * Determines if `arg` is a plain old JavaScript object (POJO). Specifically,
360 * `arg` must be an object but not an instance of any special class, like String,
361 * ObjectId, etc.
362 *
363 * `Object.getPrototypeOf()` is part of ES5: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
364 *
365 * @param {Object|Array|String|Function|RegExp|any} arg
366 * @api private
367 * @return {Boolean}
368 */
369
370exports.isPOJO = require('./helpers/isPOJO');
371
372/**
373 * Determines if `arg` is an object that isn't an instance of a built-in value
374 * class, like Array, Buffer, ObjectId, etc.
375 * @param {Any} val
376 */
377
378exports.isNonBuiltinObject = function isNonBuiltinObject(val) {
379 return typeof val === 'object' &&
380 !exports.isNativeObject(val) &&
381 !exports.isMongooseType(val) &&
382 !(val instanceof UUID) &&
383 val != null;
384};
385
386/**
387 * Determines if `obj` is a built-in object like an array, date, boolean,
388 * etc.
389 * @param {Any} arg
390 */
391
392exports.isNativeObject = function(arg) {
393 return Array.isArray(arg) ||
394 arg instanceof Date ||
395 arg instanceof Boolean ||
396 arg instanceof Number ||
397 arg instanceof String;
398};
399
400/**
401 * Determines if `val` is an object that has no own keys
402 * @param {Any} val
403 */
404
405exports.isEmptyObject = function(val) {
406 return val != null &&
407 typeof val === 'object' &&
408 Object.keys(val).length === 0;
409};
410
411/**
412 * Search if `obj` or any POJOs nested underneath `obj` has a property named
413 * `key`
414 * @param {Object} obj
415 * @param {String} key
416 */
417
418exports.hasKey = function hasKey(obj, key) {
419 const props = Object.keys(obj);
420 for (const prop of props) {
421 if (prop === key) {
422 return true;
423 }
424 if (exports.isPOJO(obj[prop]) && exports.hasKey(obj[prop], key)) {
425 return true;
426 }
427 }
428 return false;
429};
430
431/**
432 * process.nextTick helper.
433 *
434 * Wraps `callback` in a try/catch + nextTick.
435 *
436 * node-mongodb-native has a habit of state corruption when an error is immediately thrown from within a collection callback.
437 *
438 * @param {Function} callback
439 * @api private
440 */
441
442exports.tick = function tick(callback) {
443 if (typeof callback !== 'function') {
444 return;
445 }
446 return function() {
447 try {
448 callback.apply(this, arguments);
449 } catch (err) {
450 // only nextTick on err to get out of
451 // the event loop and avoid state corruption.
452 immediate(function() {
453 throw err;
454 });
455 }
456 };
457};
458
459/**
460 * Returns true if `v` is an object that can be serialized as a primitive in
461 * MongoDB
462 * @param {Any} v
463 */
464
465exports.isMongooseType = function(v) {
466 return isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128') || v instanceof Buffer;
467};
468
469exports.isMongooseObject = isMongooseObject;
470
471/**
472 * Converts `expires` options of index objects to `expiresAfterSeconds` options for MongoDB.
473 *
474 * @param {Object} object
475 * @api private
476 */
477
478exports.expires = function expires(object) {
479 if (!(object && object.constructor.name === 'Object')) {
480 return;
481 }
482 if (!('expires' in object)) {
483 return;
484 }
485
486 object.expireAfterSeconds = (typeof object.expires !== 'string')
487 ? object.expires
488 : Math.round(ms(object.expires) / 1000);
489 delete object.expires;
490};
491
492/**
493 * populate helper
494 * @param {String} path
495 * @param {String} select
496 * @param {Model} model
497 * @param {Object} match
498 * @param {Object} options
499 * @param {Any} subPopulate
500 * @param {Boolean} justOne
501 * @param {Boolean} count
502 */
503
504exports.populate = function populate(path, select, model, match, options, subPopulate, justOne, count) {
505 // might have passed an object specifying all arguments
506 let obj = null;
507 if (arguments.length === 1) {
508 if (path instanceof PopulateOptions) {
509 // If reusing old populate docs, avoid reusing `_docs` because that may
510 // lead to bugs and memory leaks. See gh-11641
511 path._docs = {};
512 path._childDocs = [];
513 return [path];
514 }
515
516 if (Array.isArray(path)) {
517 const singles = makeSingles(path);
518 return singles.map(o => exports.populate(o)[0]);
519 }
520
521 if (exports.isObject(path)) {
522 obj = Object.assign({}, path);
523 } else {
524 obj = { path: path };
525 }
526 } else if (typeof model === 'object') {
527 obj = {
528 path: path,
529 select: select,
530 match: model,
531 options: match
532 };
533 } else {
534 obj = {
535 path: path,
536 select: select,
537 model: model,
538 match: match,
539 options: options,
540 populate: subPopulate,
541 justOne: justOne,
542 count: count
543 };
544 }
545
546 if (typeof obj.path !== 'string') {
547 throw new TypeError('utils.populate: invalid path. Expected string. Got typeof `' + typeof path + '`');
548 }
549
550 return _populateObj(obj);
551
552 // The order of select/conditions args is opposite Model.find but
553 // necessary to keep backward compatibility (select could be
554 // an array, string, or object literal).
555 function makeSingles(arr) {
556 const ret = [];
557 arr.forEach(function(obj) {
558 if (oneSpaceRE.test(obj.path)) {
559 const paths = obj.path.split(manySpaceRE);
560 paths.forEach(function(p) {
561 const copy = Object.assign({}, obj);
562 copy.path = p;
563 ret.push(copy);
564 });
565 } else {
566 ret.push(obj);
567 }
568 });
569
570 return ret;
571 }
572};
573
574function _populateObj(obj) {
575 if (Array.isArray(obj.populate)) {
576 const ret = [];
577 obj.populate.forEach(function(obj) {
578 if (oneSpaceRE.test(obj.path)) {
579 const copy = Object.assign({}, obj);
580 const paths = copy.path.split(manySpaceRE);
581 paths.forEach(function(p) {
582 copy.path = p;
583 ret.push(exports.populate(copy)[0]);
584 });
585 } else {
586 ret.push(exports.populate(obj)[0]);
587 }
588 });
589 obj.populate = exports.populate(ret);
590 } else if (obj.populate != null && typeof obj.populate === 'object') {
591 obj.populate = exports.populate(obj.populate);
592 }
593
594 const ret = [];
595 const paths = oneSpaceRE.test(obj.path) ? obj.path.split(manySpaceRE) : [obj.path];
596 if (obj.options != null) {
597 obj.options = clone(obj.options);
598 }
599
600 for (const path of paths) {
601 ret.push(new PopulateOptions(Object.assign({}, obj, { path: path })));
602 }
603
604 return ret;
605}
606
607/**
608 * Return the value of `obj` at the given `path`.
609 *
610 * @param {String} path
611 * @param {Object} obj
612 * @param {Any} map
613 */
614
615exports.getValue = function(path, obj, map) {
616 return mpath.get(path, obj, getValueLookup, map);
617};
618
619/*!
620 * ignore
621 */
622
623const mapGetterOptions = Object.freeze({ getters: false });
624
625function getValueLookup(obj, part) {
626 let _from = obj?._doc || obj;
627 if (_from != null && _from.isMongooseArrayProxy) {
628 _from = _from.__array;
629 }
630 return _from instanceof Map ?
631 _from.get(part, mapGetterOptions) :
632 _from[part];
633}
634
635/**
636 * Sets the value of `obj` at the given `path`.
637 *
638 * @param {String} path
639 * @param {Anything} val
640 * @param {Object} obj
641 * @param {Any} map
642 * @param {Any} _copying
643 */
644
645exports.setValue = function(path, val, obj, map, _copying) {
646 mpath.set(path, val, obj, '_doc', map, _copying);
647};
648
649/**
650 * Returns an array of values from object `o`.
651 *
652 * @param {Object} o
653 * @return {Array}
654 * @api private
655 */
656
657exports.object = {};
658exports.object.vals = function vals(o) {
659 const keys = Object.keys(o);
660 let i = keys.length;
661 const ret = [];
662
663 while (i--) {
664 ret.push(o[keys[i]]);
665 }
666
667 return ret;
668};
669
670const hop = Object.prototype.hasOwnProperty;
671
672/**
673 * Safer helper for hasOwnProperty checks
674 *
675 * @param {Object} obj
676 * @param {String} prop
677 */
678
679exports.object.hasOwnProperty = function(obj, prop) {
680 return hop.call(obj, prop);
681};
682
683/**
684 * Determine if `val` is null or undefined
685 *
686 * @param {Any} val
687 * @return {Boolean}
688 */
689
690exports.isNullOrUndefined = function(val) {
691 return val === null || val === undefined;
692};
693
694/*!
695 * ignore
696 */
697
698exports.array = {};
699
700/**
701 * Flattens an array.
702 *
703 * [ 1, [ 2, 3, [4] ]] -> [1,2,3,4]
704 *
705 * @param {Array} arr
706 * @param {Function} [filter] If passed, will be invoked with each item in the array. If `filter` returns a falsy value, the item will not be included in the results.
707 * @param {Array} ret
708 * @return {Array}
709 * @api private
710 */
711
712exports.array.flatten = function flatten(arr, filter, ret) {
713 ret || (ret = []);
714
715 arr.forEach(function(item) {
716 if (Array.isArray(item)) {
717 flatten(item, filter, ret);
718 } else {
719 if (!filter || filter(item)) {
720 ret.push(item);
721 }
722 }
723 });
724
725 return ret;
726};
727
728/*!
729 * ignore
730 */
731
732const _hasOwnProperty = Object.prototype.hasOwnProperty;
733
734exports.hasUserDefinedProperty = function(obj, key) {
735 if (obj == null) {
736 return false;
737 }
738
739 if (Array.isArray(key)) {
740 for (const k of key) {
741 if (exports.hasUserDefinedProperty(obj, k)) {
742 return true;
743 }
744 }
745 return false;
746 }
747
748 if (_hasOwnProperty.call(obj, key)) {
749 return true;
750 }
751 if (typeof obj === 'object' && key in obj) {
752 const v = obj[key];
753 return v !== Object.prototype[key] && v !== Array.prototype[key];
754 }
755
756 return false;
757};
758
759/*!
760 * ignore
761 */
762
763const MAX_ARRAY_INDEX = Math.pow(2, 32) - 1;
764
765exports.isArrayIndex = function(val) {
766 if (typeof val === 'number') {
767 return val >= 0 && val <= MAX_ARRAY_INDEX;
768 }
769 if (typeof val === 'string') {
770 if (!/^\d+$/.test(val)) {
771 return false;
772 }
773 val = +val;
774 return val >= 0 && val <= MAX_ARRAY_INDEX;
775 }
776
777 return false;
778};
779
780/**
781 * Removes duplicate values from an array
782 *
783 * [1, 2, 3, 3, 5] => [1, 2, 3, 5]
784 * [ ObjectId("550988ba0c19d57f697dc45e"), ObjectId("550988ba0c19d57f697dc45e") ]
785 * => [ObjectId("550988ba0c19d57f697dc45e")]
786 *
787 * @param {Array} arr
788 * @return {Array}
789 * @api private
790 */
791
792exports.array.unique = function(arr) {
793 const primitives = new Set();
794 const ids = new Set();
795 const ret = [];
796
797 for (const item of arr) {
798 if (typeof item === 'number' || typeof item === 'string' || item == null) {
799 if (primitives.has(item)) {
800 continue;
801 }
802 ret.push(item);
803 primitives.add(item);
804 } else if (isBsonType(item, 'ObjectId')) {
805 if (ids.has(item.toString())) {
806 continue;
807 }
808 ret.push(item);
809 ids.add(item.toString());
810 } else {
811 ret.push(item);
812 }
813 }
814
815 return ret;
816};
817
818exports.buffer = {};
819
820/**
821 * Determines if two buffers are equal.
822 *
823 * @param {Buffer} a
824 * @param {Object} b
825 */
826
827exports.buffer.areEqual = function(a, b) {
828 if (!Buffer.isBuffer(a)) {
829 return false;
830 }
831 if (!Buffer.isBuffer(b)) {
832 return false;
833 }
834 if (a.length !== b.length) {
835 return false;
836 }
837 for (let i = 0, len = a.length; i < len; ++i) {
838 if (a[i] !== b[i]) {
839 return false;
840 }
841 }
842 return true;
843};
844
845exports.getFunctionName = getFunctionName;
846
847/**
848 * Decorate buffers
849 * @param {Object} destination
850 * @param {Object} source
851 */
852
853exports.decorate = function(destination, source) {
854 for (const key in source) {
855 if (specialProperties.has(key)) {
856 continue;
857 }
858 destination[key] = source[key];
859 }
860};
861
862/**
863 * merges to with a copy of from
864 *
865 * @param {Object} to
866 * @param {Object} fromObj
867 * @api private
868 */
869
870exports.mergeClone = function(to, fromObj) {
871 if (isMongooseObject(fromObj)) {
872 fromObj = fromObj.toObject({
873 transform: false,
874 virtuals: false,
875 depopulate: true,
876 getters: false,
877 flattenDecimals: false
878 });
879 }
880 const keys = Object.keys(fromObj);
881 const len = keys.length;
882 let i = 0;
883 let key;
884
885 while (i < len) {
886 key = keys[i++];
887 if (specialProperties.has(key)) {
888 continue;
889 }
890 if (typeof to[key] === 'undefined') {
891 to[key] = clone(fromObj[key], {
892 transform: false,
893 virtuals: false,
894 depopulate: true,
895 getters: false,
896 flattenDecimals: false
897 });
898 } else {
899 let val = fromObj[key];
900 if (val != null && val.valueOf && !(val instanceof Date)) {
901 val = val.valueOf();
902 }
903 if (exports.isObject(val)) {
904 let obj = val;
905 if (isMongooseObject(val) && !val.isMongooseBuffer) {
906 obj = obj.toObject({
907 transform: false,
908 virtuals: false,
909 depopulate: true,
910 getters: false,
911 flattenDecimals: false
912 });
913 }
914 if (val.isMongooseBuffer) {
915 obj = Buffer.from(obj);
916 }
917 exports.mergeClone(to[key], obj);
918 } else {
919 to[key] = clone(val, {
920 flattenDecimals: false
921 });
922 }
923 }
924 }
925};
926
927/**
928 * Executes a function on each element of an array (like _.each)
929 *
930 * @param {Array} arr
931 * @param {Function} fn
932 * @api private
933 */
934
935exports.each = function(arr, fn) {
936 for (const item of arr) {
937 fn(item);
938 }
939};
940
941/**
942 * Rename an object key, while preserving its position in the object
943 *
944 * @param {Object} oldObj
945 * @param {String|Number} oldKey
946 * @param {String|Number} newKey
947 * @api private
948 */
949exports.renameObjKey = function(oldObj, oldKey, newKey) {
950 const keys = Object.keys(oldObj);
951 return keys.reduce(
952 (acc, val) => {
953 if (val === oldKey) {
954 acc[newKey] = oldObj[oldKey];
955 } else {
956 acc[val] = oldObj[val];
957 }
958 return acc;
959 },
960 {}
961 );
962};
963
964/*!
965 * ignore
966 */
967
968exports.getOption = function(name) {
969 const sources = Array.prototype.slice.call(arguments, 1);
970
971 for (const source of sources) {
972 if (source == null) {
973 continue;
974 }
975 if (source[name] != null) {
976 return source[name];
977 }
978 }
979
980 return null;
981};
982
983/*!
984 * ignore
985 */
986
987exports.noop = function() {};
988
989exports.errorToPOJO = function errorToPOJO(error) {
990 const isError = error instanceof Error;
991 if (!isError) {
992 throw new Error('`error` must be `instanceof Error`.');
993 }
994
995 const ret = {};
996 for (const properyName of Object.getOwnPropertyNames(error)) {
997 ret[properyName] = error[properyName];
998 }
999 return ret;
1000};
1001
1002/*!
1003 * ignore
1004 */
1005
1006exports.warn = function warn(message) {
1007 return process.emitWarning(message, { code: 'MONGOOSE' });
1008};
1009
1010
1011exports.injectTimestampsOption = function injectTimestampsOption(writeOperation, timestampsOption) {
1012 if (timestampsOption == null) {
1013 return;
1014 }
1015 writeOperation.timestamps = timestampsOption;
1016};