@depup/mongoose 9.1.3-depup.0 → 9.1.5-depup.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.
@@ -599,6 +599,9 @@ function _nextDoc(ctx, doc, pop, callback) {
599
599
  if (err != null) {
600
600
  return callback(err);
601
601
  }
602
+ if (options.session != null) {
603
+ doc.$session(options.session);
604
+ }
602
605
  ctx.model.hooks.execPost('find', ctx.query, [[doc]]).then(() => callback(null, doc), err => callback(err));
603
606
  });
604
607
  }
package/lib/document.js CHANGED
@@ -2824,7 +2824,13 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
2824
2824
 
2825
2825
  // Optimization: if primitive path with no validators, or array of primitives
2826
2826
  // with no validators, skip validating this path entirely.
2827
- if (!_pathType.schema && !_pathType.embeddedSchemaType && _pathType.validators.length === 0 && !_pathType.$parentSchemaDocArray) {
2827
+ if (!_pathType.schema &&
2828
+ !_pathType.embeddedSchemaType &&
2829
+ _pathType.validators.length === 0 &&
2830
+ !_pathType.$parentSchemaDocArray &&
2831
+ // gh-15957: skip this optimization for SchemaMap as maps can contain subdocuments
2832
+ // that need validation even if the map itself has no validators
2833
+ !_pathType.$isSchemaMap) {
2828
2834
  paths.delete(path);
2829
2835
  } else if (_pathType.$isMongooseArray &&
2830
2836
  !_pathType.$isMongooseDocumentArray && // Skip document arrays...
@@ -51,11 +51,20 @@ function clone(obj, options, isArrayChild) {
51
51
  clonedDoc.__index = obj.__index;
52
52
  }
53
53
  if (obj.__parentArray != null) {
54
- clonedDoc.__parentArray = obj.__parentArray;
54
+ clonedDoc.__parentArray = options.parentArray ?? obj.__parentArray;
55
55
  }
56
56
  clonedDoc.$__setParent(options.parentDoc ?? obj.$__parent);
57
57
  return clonedDoc;
58
58
  }
59
+ if (options.retainDocuments && obj.$isMongooseMap) {
60
+ const clonedParent = options.parentDoc ?? obj.$__parent;
61
+ const MongooseMap = obj.constructor;
62
+ const ret = new MongooseMap({}, obj.$__path, clonedParent, obj.$__schemaType);
63
+ for (const [key, value] of obj) {
64
+ ret.$__set(key, clone(value, options));
65
+ }
66
+ return ret;
67
+ }
59
68
  }
60
69
 
61
70
  if (isPOJO(obj) && obj.$__ != null && obj._doc != null) {
@@ -187,6 +196,11 @@ function cloneArray(arr, options) {
187
196
  }
188
197
 
189
198
  arr = isMongooseArray(arr) ? arr.__array : arr;
199
+ if (ret.isMongooseDocumentArray) {
200
+ // Create new options object to avoid mutating the shared options.
201
+ // Subdocs need parentArray to point to their own cloned array.
202
+ options = { ...options, parentArray: ret };
203
+ }
190
204
  for (i = 0; i < len; ++i) {
191
205
  ret[i] = clone(arr[i], options, true);
192
206
  }
@@ -46,6 +46,11 @@ module.exports = function mergeDiscriminatorSchema(to, from, path, seen) {
46
46
  if (specialProperties.has(key)) {
47
47
  continue;
48
48
  }
49
+ if (key === 'parentSchema') {
50
+ // Skip parentSchema to avoid merging parent schema multiple times
51
+ // through child paths' back-references (gh-15966)
52
+ continue;
53
+ }
49
54
  if (to[key] == null) {
50
55
  to[key] = from[key];
51
56
  } else if (isObject(from[key])) {
@@ -76,6 +76,9 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
76
76
  enumerable: true,
77
77
  configurable: true,
78
78
  get: function() {
79
+ if (!this.$__) {
80
+ return undefined;
81
+ }
79
82
  const _this = this;
80
83
  if (!this.$__.getters) {
81
84
  this.$__.getters = {};
@@ -398,6 +398,9 @@ SchemaArray.prototype.cast = function(value, doc, init, prev, options) {
398
398
  if (options.hydratedPopulatedDocs) {
399
399
  opts.hydratedPopulatedDocs = options.hydratedPopulatedDocs;
400
400
  }
401
+ if (options.virtuals) {
402
+ opts.virtuals = options.virtuals;
403
+ }
401
404
  rawValue[i] = caster.applySetters(rawValue[i], doc, init, void 0, opts);
402
405
  }
403
406
  } catch (e) {
package/lib/schema/map.js CHANGED
@@ -58,14 +58,10 @@ class SchemaMap extends SchemaType {
58
58
  return val;
59
59
  }
60
60
 
61
- const path = this.path;
61
+ const path = options?.path ?? this.path;
62
62
 
63
63
  if (init) {
64
- const map = new MongooseMap({}, path, doc, this.$__schemaType, options);
65
-
66
- // Use the map's path for passing to nested casts.
67
- // If map's parent is a subdocument, use the relative path so nested casts get relative paths.
68
- const mapPath = map.$__pathRelativeToParent != null ? map.$__pathRelativeToParent : map.$__path;
64
+ const map = new MongooseMap({}, path, doc, this.$__schemaType);
69
65
 
70
66
  if (val instanceof global.Map) {
71
67
  for (const key of val.keys()) {
@@ -73,7 +69,7 @@ class SchemaMap extends SchemaType {
73
69
  if (_val == null) {
74
70
  _val = map.$__schemaType._castNullish(_val);
75
71
  } else {
76
- _val = map.$__schemaType.cast(_val, doc, true, null, { ...options, path: mapPath + '.' + key });
72
+ _val = map.$__schemaType.cast(_val, doc, true, null, { ...options, path: path + '.' + key });
77
73
  }
78
74
  map.$init(key, _val);
79
75
  }
@@ -83,7 +79,7 @@ class SchemaMap extends SchemaType {
83
79
  if (_val == null) {
84
80
  _val = map.$__schemaType._castNullish(_val);
85
81
  } else {
86
- _val = map.$__schemaType.cast(_val, doc, true, null, { ...options, path: mapPath + '.' + key });
82
+ _val = map.$__schemaType.cast(_val, doc, true, null, { ...options, path: path + '.' + key });
87
83
  }
88
84
  map.$init(key, _val);
89
85
  }
@@ -92,7 +88,7 @@ class SchemaMap extends SchemaType {
92
88
  return map;
93
89
  }
94
90
 
95
- return new MongooseMap(val, path, doc, this.$__schemaType, options);
91
+ return new MongooseMap(val, path, doc, this.$__schemaType);
96
92
  }
97
93
 
98
94
  /**
package/lib/types/map.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const Mixed = require('../schema/mixed');
3
4
  const MongooseError = require('../error/mongooseError');
4
5
  const clone = require('../helpers/clone');
5
6
  const deepEqual = require('../utils').deepEqual;
@@ -17,30 +18,14 @@ const populateModelSymbol = require('../helpers/symbols').populateModelSymbol;
17
18
  */
18
19
 
19
20
  class MongooseMap extends Map {
20
- constructor(v, path, doc, schemaType, options) {
21
+ constructor(v, path, doc, schemaType) {
21
22
  if (getConstructorName(v) === 'Object') {
22
23
  v = Object.keys(v).reduce((arr, key) => arr.concat([[key, v[key]]]), []);
23
24
  }
24
25
  super(v);
25
26
  this.$__parent = doc?.$__ != null ? doc : null;
26
-
27
- // Calculate the full path from the root document
28
- // Priority: parent.$basePath (from subdoc) > options.path (from parent map/structure) > path (schema path)
29
- // Subdocuments have the most up-to-date path info, so prefer that over options.path
30
- if (this.$__parent?.$isSingleNested && this.$__parent.$basePath) {
31
- this.$__path = this.$__parent.$basePath + '.' + path;
32
- // Performance optimization: store path relative to parent subdocument
33
- // to avoid string operations in set() hot path
34
- this.$__pathRelativeToParent = path;
35
- } else if (options?.path) {
36
- this.$__path = options.path;
37
- this.$__pathRelativeToParent = null;
38
- } else {
39
- this.$__path = path;
40
- this.$__pathRelativeToParent = null;
41
- }
42
-
43
- this.$__schemaType = schemaType;
27
+ this.$__path = path;
28
+ this.$__schemaType = schemaType == null ? new Mixed(path) : schemaType;
44
29
 
45
30
  this.$__runDeferred();
46
31
  }
@@ -52,14 +37,6 @@ class MongooseMap extends Map {
52
37
 
53
38
  if (value?.$isSingleNested) {
54
39
  value.$basePath = this.$__path + '.' + key;
55
- // Store the path relative to parent subdoc for efficient markModified()
56
- if (this.$__pathRelativeToParent != null) {
57
- // Map's parent is a subdocument, store path relative to that subdoc
58
- value.$pathRelativeToParent = this.$__pathRelativeToParent + '.' + key;
59
- } else {
60
- // Map's parent is root document, store the full path
61
- value.$pathRelativeToParent = this.$__path + '.' + key;
62
- }
63
40
  }
64
41
  }
65
42
 
@@ -159,16 +136,9 @@ class MongooseMap extends Map {
159
136
  }
160
137
  } else {
161
138
  try {
162
- let options = null;
163
- if (this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested || this.$__schemaType.$isMongooseArray || this.$__schemaType.$isSchemaMap) {
164
- options = { path: fullPath.call(this) };
165
- // For subdocuments, also pass the relative path to avoid string operations
166
- if (this.$__schemaType.$isSingleNested) {
167
- options.pathRelativeToParent = this.$__pathRelativeToParent != null ?
168
- this.$__pathRelativeToParent + '.' + key :
169
- this.$__path + '.' + key;
170
- }
171
- }
139
+ const options = this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested || this.$__schemaType.$isMongooseArray || this.$__schemaType.$isSchemaMap ?
140
+ { path: fullPath.call(this) } :
141
+ null;
172
142
  value = this.$__schemaType.applySetters(
173
143
  value,
174
144
  this.$__parent,
@@ -187,34 +157,13 @@ class MongooseMap extends Map {
187
157
 
188
158
  super.set(key, value);
189
159
 
190
- // Set relative path on subdocuments to avoid string operations in markModified()
191
- // The path should be relative to the parent subdocument (if any), not just the key
192
- if (value?.$isSingleNested) {
193
- if (this.$__pathRelativeToParent != null) {
194
- // Map's parent is a subdocument, store path relative to that subdoc (e.g., 'items.i2')
195
- value.$pathRelativeToParent = this.$__pathRelativeToParent + '.' + key;
196
- } else {
197
- // Map's parent is root document, store just the full path
198
- value.$pathRelativeToParent = this.$__path + '.' + key;
199
- }
200
- }
201
-
202
160
  if (parent?.$__ != null && !deepEqual(value, priorVal)) {
203
- // Optimization: if parent is a subdocument, use precalculated relative path
204
- // to avoid building a full path just to strip the parent's prefix
205
- let pathToMark;
206
- if (this.$__pathRelativeToParent != null) {
207
- // Parent is a subdocument - use precalculated relative path (e.g., 'items.i1')
208
- pathToMark = this.$__pathRelativeToParent + '.' + key;
209
- } else {
210
- // Parent is root document or map - use full path
211
- pathToMark = fullPath.call(this);
212
- }
213
- parent.markModified(pathToMark);
161
+ const path = fullPath.call(this);
162
+ parent.markModified(path);
214
163
  // If overwriting the full document array or subdoc, make sure to clean up any paths that were modified
215
164
  // before re: #15108
216
165
  if (this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested) {
217
- cleanModifiedSubpaths(parent, pathToMark);
166
+ cleanModifiedSubpaths(parent, path);
218
167
  }
219
168
  }
220
169
 
@@ -26,21 +26,7 @@ function Subdocument(value, fields, parent, options) {
26
26
  if (options?.path != null) {
27
27
  this.$basePath = options.path;
28
28
  }
29
- if (options?.pathRelativeToParent != null) {
30
- this.$pathRelativeToParent = options.pathRelativeToParent;
31
- }
32
-
33
- // Don't pass `path` to Document constructor: path is used for storing the
34
- // absolute path to this schematype relative to the top-level document, but
35
- // subdocuments use relative paths (relative to the parent document) to track changes.
36
- // This avoids the subdocument's fields receiving the subdocument's path as options.path.
37
- let documentOptions = options;
38
- if (options?.path != null) {
39
- documentOptions = Object.assign({}, options);
40
- delete documentOptions.path;
41
- }
42
-
43
- Document.call(this, value, fields, documentOptions);
29
+ Document.call(this, value, fields, options);
44
30
 
45
31
  delete this.$__.priorDoc;
46
32
  }
@@ -125,12 +111,6 @@ Subdocument.prototype.$__fullPath = function(path) {
125
111
  */
126
112
 
127
113
  Subdocument.prototype.$__pathRelativeToParent = function(p) {
128
- // If this subdocument has a stored relative path (set by map when subdoc is created),
129
- // use it directly to avoid string operations
130
- if (this.$pathRelativeToParent != null) {
131
- return p == null ? this.$pathRelativeToParent : this.$pathRelativeToParent + '.' + p;
132
- }
133
-
134
114
  if (p == null) {
135
115
  return this.$basePath;
136
116
  }
@@ -183,13 +163,9 @@ Subdocument.prototype.$isValid = function(path) {
183
163
  Subdocument.prototype.markModified = function(path) {
184
164
  Document.prototype.markModified.call(this, path);
185
165
  const parent = this.$parent();
166
+ const fullPath = this.$__pathRelativeToParent(path);
186
167
 
187
- if (parent == null) {
188
- return;
189
- }
190
-
191
- const pathToMark = this.$__pathRelativeToParent(path);
192
- if (pathToMark == null) {
168
+ if (parent == null || fullPath == null) {
193
169
  return;
194
170
  }
195
171
 
@@ -197,8 +173,7 @@ Subdocument.prototype.markModified = function(path) {
197
173
  if (parent.isDirectModified(myPath) || this.isNew) {
198
174
  return;
199
175
  }
200
-
201
- this.$__parent.markModified(pathToMark, this);
176
+ this.$__parent.markModified(fullPath, this);
202
177
  };
203
178
 
204
179
  /*!
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@depup/mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "9.1.3-depup.0",
4
+ "version": "9.1.5-depup.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "@ark/attest": "0.56.0",
32
- "@mongodb-js/mongodb-downloader": "^1.1.4",
32
+ "@mongodb-js/mongodb-downloader": "^1.1.6",
33
33
  "acquit": "1.4.0",
34
34
  "acquit-ignore": "^0.2.2",
35
35
  "acquit-require": "0.1.1",
@@ -51,7 +51,7 @@
51
51
  "mocha": "11.7.5",
52
52
  "moment": "2.30.1",
53
53
  "mongodb-memory-server": "11.0.1",
54
- "mongodb-runner": "^6.5.2",
54
+ "mongodb-runner": "^6.5.4",
55
55
  "mongodb-client-encryption": "~7.0",
56
56
  "ncp": "^2.0.0",
57
57
  "nyc": "^17.1.0",
@@ -59,7 +59,7 @@
59
59
  "sinon": "21.0.1",
60
60
  "tsd": "0.33.0",
61
61
  "typescript": "5.9.3",
62
- "typescript-eslint": "^8.53.0",
62
+ "typescript-eslint": "^8.53.1",
63
63
  "uuid": "^13.0.0"
64
64
  },
65
65
  "directories": {
@@ -29,7 +29,9 @@ declare module 'mongoose' {
29
29
  SchemaDefinition,
30
30
  TSchemaOptions extends Record<any, any> = DefaultSchemaOptions,
31
31
  TTransformOptions = { bufferToBinary: false }
32
- > = Require_id<InferRawDocTypeWithout_id<SchemaDefinition, TSchemaOptions, TTransformOptions>>;
32
+ > = TSchemaOptions extends { _id: false }
33
+ ? InferRawDocTypeWithout_id<SchemaDefinition, TSchemaOptions, TTransformOptions>
34
+ : Require_id<InferRawDocTypeWithout_id<SchemaDefinition, TSchemaOptions, TTransformOptions>>;
33
35
 
34
36
  /**
35
37
  * @summary Allows users to optionally choose their own type for a schema field for stronger typing.
@@ -88,12 +90,25 @@ declare module 'mongoose' {
88
90
  > =
89
91
  IsNotNever<TypeHint> extends true ? TypeHint
90
92
  : [PathValueType] extends [neverOrAny] ? PathValueType
91
- : PathValueType extends Schema<infer RawDocType, any, any, any, any, any, any, any, any, infer TSchemaDefinition> ? IsItRecordAndNotAny<RawDocType> extends true ? RawDocType : InferRawDocType<TSchemaDefinition, DefaultSchemaOptions, TTransformOptions>
93
+ : PathValueType extends Schema<infer RawDocType, any, any, any, any, any, infer TSchemaOptions, infer DocType, any, infer TSchemaDefinition> ?
94
+ IsItRecordAndNotAny<RawDocType> extends true ?
95
+ RawDocType :
96
+ string extends keyof TSchemaDefinition ?
97
+ TSchemaOptions extends { _id: false } ?
98
+ FlattenMaps<SubdocsToPOJOs<DocType>> :
99
+ Require_id<FlattenMaps<SubdocsToPOJOs<DocType>>> :
100
+ InferRawDocType<TSchemaDefinition, TSchemaOptions & Record<any, any>, TTransformOptions>
92
101
  : PathValueType extends ReadonlyArray<infer Item> ?
93
102
  IfEquals<Item, never> extends true ? any[]
94
- : Item extends Schema<infer RawDocType, any, any, any, any, any, any, any, any, infer TSchemaDefinition> ?
103
+ : Item extends Schema<infer RawDocType, any, any, any, any, any, infer TSchemaOptions, infer DocType, any, infer TSchemaDefinition> ?
95
104
  // If Item is a schema, infer its type.
96
- Array<IsItRecordAndNotAny<RawDocType> extends true ? RawDocType : InferRawDocType<TSchemaDefinition, DefaultSchemaOptions, TTransformOptions>>
105
+ Array<IsItRecordAndNotAny<RawDocType> extends true ?
106
+ RawDocType :
107
+ string extends keyof TSchemaDefinition ?
108
+ TSchemaOptions extends { _id: false } ?
109
+ FlattenMaps<SubdocsToPOJOs<DocType>> :
110
+ Require_id<FlattenMaps<SubdocsToPOJOs<DocType>>> :
111
+ InferRawDocType<TSchemaDefinition, TSchemaOptions & Record<any, any>, TTransformOptions>>
97
112
  : TypeKey extends keyof Item ?
98
113
  Item[TypeKey] extends Function | String ?
99
114
  // If Item has a type key that's a string or a callable, it must be a scalar,