mongoose 8.23.0 → 8.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/aggregate.js CHANGED
@@ -1027,7 +1027,7 @@ Aggregate.prototype.search = function(options) {
1027
1027
  *
1028
1028
  * MyModel.aggregate().match({ test: 1 }).pipeline(); // [{ $match: { test: 1 } }]
1029
1029
  *
1030
- * @return {Array} The current pipeline similar to the operation that will be executed
1030
+ * @return {PipelineStage[]} The current pipeline similar to the operation that will be executed
1031
1031
  * @api public
1032
1032
  */
1033
1033
 
@@ -1035,6 +1035,29 @@ Aggregate.prototype.pipeline = function() {
1035
1035
  return this._pipeline;
1036
1036
  };
1037
1037
 
1038
+ /**
1039
+ * Returns the current pipeline as a `$unionWith`-safe pipeline.
1040
+ * Throws if this pipeline contains `$out` or `$merge`.
1041
+ *
1042
+ * #### Example:
1043
+ *
1044
+ * const base = MyModel.aggregate().match({ test: 1 });
1045
+ * base.pipelineForUnionWith(); // [{ $match: { test: 1 } }]
1046
+ *
1047
+ * @return {PipelineStage[]} The current pipeline with `$unionWith` stage restrictions
1048
+ * @api public
1049
+ */
1050
+
1051
+ Aggregate.prototype.pipelineForUnionWith = function pipelineForUnionWith() {
1052
+ for (const stage of this._pipeline) {
1053
+ if (stage?.$out != null || stage?.$merge != null) {
1054
+ throw new MongooseError('Aggregate pipeline for $unionWith cannot include `$out` or `$merge` stages');
1055
+ }
1056
+ }
1057
+
1058
+ return this._pipeline;
1059
+ };
1060
+
1038
1061
  /**
1039
1062
  * Executes the aggregate pipeline on the currently bound Model.
1040
1063
  *
@@ -114,7 +114,20 @@ class ChangeStream extends EventEmitter {
114
114
  if (this.errored) {
115
115
  throw new MongooseError('Cannot call hasNext() on errored ChangeStream');
116
116
  }
117
- return this.driverChangeStream.hasNext(cb);
117
+
118
+ if (this.driverChangeStream != null) {
119
+ return this.driverChangeStream.hasNext(cb);
120
+ }
121
+
122
+ return this.$driverChangeStreamPromise.then(
123
+ () => this.driverChangeStream.hasNext(cb),
124
+ err => {
125
+ if (cb != null) {
126
+ return cb(err);
127
+ }
128
+ throw err;
129
+ }
130
+ );
118
131
  }
119
132
 
120
133
  next(cb) {
@@ -135,7 +148,20 @@ class ChangeStream extends EventEmitter {
135
148
  };
136
149
  }
137
150
 
138
- let maybePromise = this.driverChangeStream.next(cb);
151
+ let maybePromise;
152
+ if (this.driverChangeStream != null) {
153
+ maybePromise = this.driverChangeStream.next(cb);
154
+ } else {
155
+ maybePromise = this.$driverChangeStreamPromise.then(
156
+ () => this.driverChangeStream.next(cb),
157
+ err => {
158
+ if (cb != null) {
159
+ return cb(err);
160
+ }
161
+ throw err;
162
+ }
163
+ );
164
+ }
139
165
  if (maybePromise && typeof maybePromise.then === 'function') {
140
166
  maybePromise = maybePromise.then(data => {
141
167
  if (data.fullDocument != null) {
@@ -147,7 +173,19 @@ class ChangeStream extends EventEmitter {
147
173
  return maybePromise;
148
174
  }
149
175
 
150
- return this.driverChangeStream.next(cb);
176
+ if (this.driverChangeStream != null) {
177
+ return this.driverChangeStream.next(cb);
178
+ }
179
+
180
+ return this.$driverChangeStreamPromise.then(
181
+ () => this.driverChangeStream.next(cb),
182
+ err => {
183
+ if (cb != null) {
184
+ return cb(err);
185
+ }
186
+ throw err;
187
+ }
188
+ );
151
189
  }
152
190
 
153
191
  addListener(event, handler) {
@@ -25,7 +25,7 @@ module.exports = function(filter, schema, castedDoc, options) {
25
25
  }
26
26
 
27
27
  const keys = Object.keys(castedDoc || {});
28
- const updatedKeys = {};
28
+ const updatedKeys = Object.create(null);
29
29
  const updatedValues = {};
30
30
  const numKeys = keys.length;
31
31
 
@@ -58,6 +58,20 @@ module.exports = function(filter, schema, castedDoc, options) {
58
58
  }
59
59
  }
60
60
  updatedKeys[path] = true;
61
+ if (path.indexOf('.') === -1) {
62
+ continue;
63
+ }
64
+ // Also mark all parent prefixes so child-path lookups are O(1).
65
+ // e.g. 'extraProps.location' also marks 'extraProps'
66
+ const pieces = schema.paths[path] ?
67
+ // If the SchemaType already split for us, use that to avoid the extra overhead
68
+ schema.paths[path].splitPath() :
69
+ path.split('.');
70
+ let cur = pieces[0];
71
+ for (let j = 1; j < pieces.length; ++j) {
72
+ updatedKeys[cur] = true;
73
+ cur += '.' + pieces[j];
74
+ }
61
75
  }
62
76
 
63
77
  if (options && options.overwrite && !hasDollarUpdate) {
package/lib/model.js CHANGED
@@ -3490,7 +3490,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3490
3490
  sort((v1, v2) => v1.index - v2.index).
3491
3491
  map(v => v.error);
3492
3492
 
3493
- const validOps = validOpIndexes.sort().map(index => ops[index]);
3493
+ const validOps = validOpIndexes.sort((a, b) => a - b).map(index => ops[index]);
3494
3494
 
3495
3495
  if (validOps.length === 0) {
3496
3496
  if (options.throwOnValidationError && validationErrors.length) {
@@ -474,12 +474,14 @@ SchemaString.prototype.maxlength = function(value, message) {
474
474
 
475
475
  if (value !== null && value !== undefined) {
476
476
  let msg = message || MongooseError.messages.String.maxlength;
477
- msg = msg.replace(/{MAXLENGTH}/, value);
477
+ if (typeof msg !== 'function') {
478
+ msg = msg.replace(/{MAXLENGTH}/, value);
479
+ }
478
480
  this.validators.push({
479
481
  validator: this.maxlengthValidator = function(v) {
480
482
  return v === null || v.length <= value;
481
483
  },
482
- message: msg,
484
+ message: formatMaxLengthValidatorMessage(msg),
483
485
  type: 'maxlength',
484
486
  maxlength: value
485
487
  });
@@ -731,3 +733,18 @@ SchemaString.prototype.autoEncryptionType = function autoEncryptionType() {
731
733
  */
732
734
 
733
735
  module.exports = SchemaString;
736
+
737
+ function formatMaxLengthValidatorMessage(msg) {
738
+ return function(props, doc) {
739
+ if (typeof msg === 'function') {
740
+ return MongooseError.ValidatorError.prototype.formatMessage(msg, props, doc);
741
+ }
742
+
743
+ if (typeof msg === 'string' && typeof props.value === 'string' && props.value.length > 30) {
744
+ props = Object.assign({}, props, {
745
+ value: props.value.slice(0, 30) + '...'
746
+ });
747
+ }
748
+ return MongooseError.ValidatorError.prototype.formatMessage(msg, props, doc);
749
+ };
750
+ }
package/lib/schemaType.js CHANGED
@@ -1381,9 +1381,6 @@ SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1381
1381
  validatorProperties.value = value;
1382
1382
  if (typeof value === 'string') {
1383
1383
  validatorProperties.length = value.length;
1384
- if (validatorProperties.value.length > 30) {
1385
- validatorProperties.value = validatorProperties.value.slice(0, 30) + '...';
1386
- }
1387
1384
  }
1388
1385
 
1389
1386
  if (validator instanceof RegExp) {
@@ -1508,9 +1505,6 @@ SchemaType.prototype.doValidateSync = function(value, scope, options) {
1508
1505
  validatorProperties.value = value;
1509
1506
  if (typeof value === 'string') {
1510
1507
  validatorProperties.length = value.length;
1511
- if (validatorProperties.value.length > 30) {
1512
- validatorProperties.value = validatorProperties.value.slice(0, 30) + '...';
1513
- }
1514
1508
  }
1515
1509
  let ok = false;
1516
1510
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.23.0",
4
+ "version": "8.24.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -36,9 +36,9 @@
36
36
  "@typescript-eslint/eslint-plugin": "^8.19.1",
37
37
  "@typescript-eslint/parser": "^8.19.1",
38
38
  "acquit": "1.4.0",
39
- "acquit-ignore": "0.2.1",
39
+ "acquit-ignore": "0.2.2",
40
40
  "acquit-require": "0.1.1",
41
- "ajv": "8.17.1",
41
+ "ajv": "8.20.0",
42
42
  "assert-browserify": "2.0.0",
43
43
  "babel-loader": "8.2.5",
44
44
  "broken-link-checker": "^0.7.8",
@@ -70,7 +70,7 @@
70
70
  "tsd": "0.33.0",
71
71
  "typescript": "5.9.3",
72
72
  "uuid": "11.1.0",
73
- "webpack": "5.102.1"
73
+ "webpack": "5.106.2"
74
74
  },
75
75
  "directories": {
76
76
  "lib": "./lib/mongoose"
@@ -123,6 +123,9 @@ declare module 'mongoose' {
123
123
  /** Returns the current pipeline */
124
124
  pipeline(): PipelineStage[];
125
125
 
126
+ /** Returns the current pipeline typed for use in `$unionWith` */
127
+ pipelineForUnionWith(): PipelineStage.UnionWithPipelineStage[];
128
+
126
129
  /** Appends a new $project operator to this aggregate pipeline. */
127
130
  project(arg: PipelineStage.Project['$project'] | string): this;
128
131
 
@@ -72,7 +72,7 @@ declare module 'mongoose' {
72
72
  : // If the type key isn't callable, then this is an array of objects, in which case
73
73
  // we need to call InferRawDocType to correctly infer its type.
74
74
  Array<InferRawDocType<Item>>
75
- : IsSchemaTypeFromBuiltinClass<Item> extends true ? ResolveRawPathType<Item, { enum: Options['enum'] }, TypeKey>[]
75
+ : IsSchemaTypeFromBuiltinClass<Item> extends true ? ResolveRawPathType<Item, { enum: Exclude<Options['enum'], undefined> }, TypeKey>[]
76
76
  : IsItRecordAndNotAny<Item> extends true ?
77
77
  Item extends Record<string, never> ?
78
78
  ObtainRawDocumentPathType<Item, TypeKey>[]
@@ -287,7 +287,7 @@ type ResolvePathType<
287
287
  : // If the type key isn't callable, then this is an array of objects, in which case
288
288
  // we need to call ObtainDocumentType to correctly infer its type.
289
289
  Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>>
290
- : IsSchemaTypeFromBuiltinClass<Item> extends true ? ResolvePathType<Item, { enum: Options['enum'] }, TypeKey>[]
290
+ : IsSchemaTypeFromBuiltinClass<Item> extends true ? ResolvePathType<Item, { enum: Exclude<Options['enum'], undefined> }, TypeKey>[]
291
291
  : IsItRecordAndNotAny<Item> extends true ?
292
292
  Item extends Record<string, never> ?
293
293
  ObtainDocumentPathType<Item, TypeKey>[]
@@ -115,6 +115,7 @@ declare module 'mongoose' {
115
115
  }
116
116
 
117
117
  export type FacetPipelineStage = Exclude<PipelineStage, PipelineStage.CollStats | PipelineStage.Facet | PipelineStage.GeoNear | PipelineStage.IndexStats | PipelineStage.Out | PipelineStage.Merge | PipelineStage.PlanCacheStats>;
118
+ export type UnionWithPipelineStage = Exclude<PipelineStage, PipelineStage.Out | PipelineStage.Merge>;
118
119
 
119
120
  export interface GeoNear {
120
121
  /** [`$geoNear` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/) */
@@ -303,8 +304,8 @@ declare module 'mongoose' {
303
304
  /** [`$unionWith` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/) */
304
305
  $unionWith:
305
306
  | string
306
- | { coll: string; pipeline?: Exclude<PipelineStage, PipelineStage.Out | PipelineStage.Merge>[] }
307
- | { coll?: string; pipeline: Exclude<PipelineStage, PipelineStage.Out | PipelineStage.Merge>[] }
307
+ | { coll: string; pipeline?: UnionWithPipelineStage[] }
308
+ | { coll?: string; pipeline: UnionWithPipelineStage[] }
308
309
  }
309
310
 
310
311
  export interface Unset {
@@ -110,7 +110,7 @@ declare module 'mongoose' {
110
110
  * The default value for this path. If a function, Mongoose executes the function
111
111
  * and uses the return value as the default.
112
112
  */
113
- default?: DefaultType<T> | ((this: EnforcedDocType, doc: any) => DefaultType<T> | null | undefined) | null;
113
+ default?: DefaultType<T> | ((this: EnforcedDocType, doc: any) => DefaultType<T> | null | undefined) | null | undefined;
114
114
 
115
115
  /**
116
116
  * The model that `populate()` should use if populating this path.