mongodb 6.15.0 → 6.16.0-dev.20250423.sha.28857b79

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.
Files changed (36) hide show
  1. package/README.md +2 -0
  2. package/lib/beta.d.ts +48 -12
  3. package/lib/bulk/common.js +1 -1
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/change_stream.js.map +1 -1
  6. package/lib/client-side-encryption/auto_encrypter.js +1 -5
  7. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  8. package/lib/cmap/connection.js +24 -11
  9. package/lib/cmap/connection.js.map +1 -1
  10. package/lib/cmap/wire_protocol/responses.js +7 -9
  11. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  12. package/lib/collection.js.map +1 -1
  13. package/lib/error.js +2 -1
  14. package/lib/error.js.map +1 -1
  15. package/lib/operations/client_bulk_write/command_builder.js +7 -0
  16. package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
  17. package/lib/operations/distinct.js +3 -0
  18. package/lib/operations/distinct.js.map +1 -1
  19. package/lib/operations/update.js +4 -0
  20. package/lib/operations/update.js.map +1 -1
  21. package/lib/sort.js +13 -6
  22. package/lib/sort.js.map +1 -1
  23. package/mongodb.d.ts +48 -12
  24. package/package.json +19 -19
  25. package/src/bulk/common.ts +8 -3
  26. package/src/change_stream.ts +5 -1
  27. package/src/client-side-encryption/auto_encrypter.ts +4 -8
  28. package/src/cmap/connection.ts +31 -11
  29. package/src/cmap/wire_protocol/responses.ts +4 -5
  30. package/src/collection.ts +2 -1
  31. package/src/error.ts +2 -1
  32. package/src/operations/client_bulk_write/command_builder.ts +9 -0
  33. package/src/operations/client_bulk_write/common.ts +5 -0
  34. package/src/operations/distinct.ts +18 -1
  35. package/src/operations/update.ts +10 -1
  36. package/src/sort.ts +32 -23
@@ -521,8 +521,12 @@ export interface UpdateDescription<TSchema extends Document = Document> {
521
521
  * }
522
522
  * ```
523
523
  *
524
- * This field is only present when there are ambiguous paths that are updated as a part of the update event and `showExpandedEvents`
524
+ * This field is only present when there are ambiguous paths that are updated as a part of the update event.
525
+ *
526
+ * On \<8.2.0 servers, this field is only present when `showExpandedEvents` is set to true.
525
527
  * is enabled for the change stream.
528
+ *
529
+ * On 8.2.0+ servers, this field is present for update events regardless of whether `showExpandedEvents` is enabled.
526
530
  * @sinceServerVersion 6.1.0
527
531
  */
528
532
  disambiguatedPaths?: Document;
@@ -365,14 +365,10 @@ export class AutoEncrypter {
365
365
  const client = await this._mongocryptdClient.connect();
366
366
  return client;
367
367
  } catch (error) {
368
- const { message } = error;
369
- if (message && (message.match(/timed out after/) || message.match(/ENOTFOUND/))) {
370
- throw new MongoRuntimeError(
371
- 'Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn',
372
- { cause: error }
373
- );
374
- }
375
- throw error;
368
+ throw new MongoRuntimeError(
369
+ 'Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn',
370
+ { cause: error }
371
+ );
376
372
  }
377
373
  }
378
374
 
@@ -27,6 +27,7 @@ import {
27
27
  MongoNetworkTimeoutError,
28
28
  MongoOperationTimeoutError,
29
29
  MongoParseError,
30
+ MongoRuntimeError,
30
31
  MongoServerError,
31
32
  MongoUnexpectedServerResponseError
32
33
  } from '../error';
@@ -791,22 +792,41 @@ export class SizedMessageTransform extends Transform {
791
792
  }
792
793
 
793
794
  this.bufferPool.append(chunk);
794
- const sizeOfMessage = this.bufferPool.getInt32();
795
795
 
796
- if (sizeOfMessage == null) {
797
- return callback();
798
- }
796
+ while (this.bufferPool.length) {
797
+ // While there are any bytes in the buffer
799
798
 
800
- if (sizeOfMessage < 0) {
801
- return callback(new MongoParseError(`Invalid message size: ${sizeOfMessage}, too small`));
802
- }
799
+ // Try to fetch a size from the top 4 bytes
800
+ const sizeOfMessage = this.bufferPool.getInt32();
801
+
802
+ if (sizeOfMessage == null) {
803
+ // Not even an int32 worth of data. Stop the loop, we need more chunks.
804
+ break;
805
+ }
806
+
807
+ if (sizeOfMessage < 0) {
808
+ // The size in the message has a negative value, this is probably corruption, throw:
809
+ return callback(new MongoParseError(`Message size cannot be negative: ${sizeOfMessage}`));
810
+ }
803
811
 
804
- if (sizeOfMessage > this.bufferPool.length) {
805
- return callback();
812
+ if (sizeOfMessage > this.bufferPool.length) {
813
+ // We do not have enough bytes to make a sizeOfMessage chunk
814
+ break;
815
+ }
816
+
817
+ // Add a message to the stream
818
+ const message = this.bufferPool.read(sizeOfMessage);
819
+
820
+ if (!this.push(message)) {
821
+ // We only subscribe to data events so we should never get backpressure
822
+ // if we do, we do not have the handling for it.
823
+ return callback(
824
+ new MongoRuntimeError(`SizedMessageTransform does not support backpressure`)
825
+ );
826
+ }
806
827
  }
807
828
 
808
- const message = this.bufferPool.read(sizeOfMessage);
809
- return callback(null, message);
829
+ callback();
810
830
  }
811
831
  }
812
832
 
@@ -8,6 +8,7 @@ import {
8
8
  parseToElementsToArray,
9
9
  parseUtf8ValidationOption,
10
10
  pluckBSONSerializeOptions,
11
+ serialize,
11
12
  type Timestamp
12
13
  } from '../../bson';
13
14
  import { MONGODB_ERROR_CODES, MongoUnexpectedServerResponseError } from '../../error';
@@ -230,11 +231,9 @@ export class CursorResponse extends MongoDBResponse {
230
231
  * This supports a feature of the FindCursor.
231
232
  * It is an optimization to avoid an extra getMore when the limit has been reached
232
233
  */
233
- static emptyGetMore: CursorResponse = {
234
- id: new Long(0),
235
- length: 0,
236
- shift: () => null
237
- } as unknown as CursorResponse;
234
+ static get emptyGetMore(): CursorResponse {
235
+ return new CursorResponse(serialize({ ok: 1, cursor: { id: 0n, nextBatch: [] } }));
236
+ }
238
237
 
239
238
  static override is(value: unknown): value is CursorResponse {
240
239
  return value instanceof CursorResponse || value === CursorResponse.emptyGetMore;
package/src/collection.ts CHANGED
@@ -87,6 +87,7 @@ import {
87
87
  } from './operations/update';
88
88
  import { ReadConcern, type ReadConcernLike } from './read_concern';
89
89
  import { ReadPreference, type ReadPreferenceLike } from './read_preference';
90
+ import { type Sort } from './sort';
90
91
  import {
91
92
  DEFAULT_PK_FACTORY,
92
93
  MongoDBCollectionNamespace,
@@ -365,7 +366,7 @@ export class Collection<TSchema extends Document = Document> {
365
366
  async updateOne(
366
367
  filter: Filter<TSchema>,
367
368
  update: UpdateFilter<TSchema> | Document[],
368
- options?: UpdateOptions
369
+ options?: UpdateOptions & { sort?: Sort }
369
370
  ): Promise<UpdateResult<TSchema>> {
370
371
  return await executeOperation(
371
372
  this.client,
package/src/error.ts CHANGED
@@ -56,7 +56,8 @@ export const MONGODB_ERROR_CODES = Object.freeze({
56
56
  FailedToSatisfyReadPreference: 133,
57
57
  CursorNotFound: 43,
58
58
  LegacyNotPrimary: 10058,
59
- WriteConcernFailed: 64,
59
+ // WriteConcernTimeout is WriteConcernFailed on pre-8.1 servers
60
+ WriteConcernTimeout: 64,
60
61
  NamespaceNotFound: 26,
61
62
  IllegalOperation: 20,
62
63
  MaxTimeMSExpired: 50,
@@ -3,6 +3,7 @@ import { DocumentSequence } from '../../cmap/commands';
3
3
  import { MongoAPIError, MongoInvalidArgumentError } from '../../error';
4
4
  import { type PkFactory } from '../../mongo_client';
5
5
  import type { Filter, OptionalId, UpdateFilter, WithoutId } from '../../mongo_types';
6
+ import { formatSort, type SortForCmd } from '../../sort';
6
7
  import { DEFAULT_PK_FACTORY, hasAtomicOperators } from '../../utils';
7
8
  import { type CollationOptions } from '../command';
8
9
  import { type Hint } from '../operation';
@@ -327,6 +328,7 @@ export interface ClientUpdateOperation {
327
328
  upsert?: boolean;
328
329
  arrayFilters?: Document[];
329
330
  collation?: CollationOptions;
331
+ sort?: SortForCmd;
330
332
  }
331
333
 
332
334
  /**
@@ -398,6 +400,9 @@ function createUpdateOperation(
398
400
  if (model.collation) {
399
401
  document.collation = model.collation;
400
402
  }
403
+ if (!multi && 'sort' in model && model.sort != null) {
404
+ document.sort = formatSort(model.sort);
405
+ }
401
406
  return document;
402
407
  }
403
408
 
@@ -410,6 +415,7 @@ export interface ClientReplaceOneOperation {
410
415
  hint?: Hint;
411
416
  upsert?: boolean;
412
417
  collation?: CollationOptions;
418
+ sort?: SortForCmd;
413
419
  }
414
420
 
415
421
  /**
@@ -443,6 +449,9 @@ export const buildReplaceOneOperation = (
443
449
  if (model.collation) {
444
450
  document.collation = model.collation;
445
451
  }
452
+ if (model.sort != null) {
453
+ document.sort = formatSort(model.sort);
454
+ }
446
455
  return document;
447
456
  };
448
457
 
@@ -2,6 +2,7 @@ import { type Document } from '../../bson';
2
2
  import type { Filter, OptionalId, UpdateFilter, WithoutId } from '../../mongo_types';
3
3
  import type { CollationOptions, CommandOperationOptions } from '../../operations/command';
4
4
  import type { Hint } from '../../operations/operation';
5
+ import { type Sort } from '../../sort';
5
6
 
6
7
  /** @public */
7
8
  export interface ClientBulkWriteOptions extends CommandOperationOptions {
@@ -89,6 +90,8 @@ export interface ClientReplaceOneModel<TSchema> extends ClientWriteModel {
89
90
  hint?: Hint;
90
91
  /** When true, creates a new document if no document matches the query. */
91
92
  upsert?: boolean;
93
+ /** Specifies the sort order for the documents matched by the filter. */
94
+ sort?: Sort;
92
95
  }
93
96
 
94
97
  /** @public */
@@ -113,6 +116,8 @@ export interface ClientUpdateOneModel<TSchema> extends ClientWriteModel {
113
116
  hint?: Hint;
114
117
  /** When true, creates a new document if no document matches the query. */
115
118
  upsert?: boolean;
119
+ /** Specifies the sort order for the documents matched by the filter. */
120
+ sort?: Sort;
116
121
  }
117
122
 
118
123
  /** @public */
@@ -8,7 +8,20 @@ import { CommandOperation, type CommandOperationOptions } from './command';
8
8
  import { Aspect, defineAspects } from './operation';
9
9
 
10
10
  /** @public */
11
- export type DistinctOptions = CommandOperationOptions;
11
+ export type DistinctOptions = CommandOperationOptions & {
12
+ /**
13
+ * @sinceServerVersion 7.1
14
+ *
15
+ * The index to use. Specify either the index name as a string or the index key pattern.
16
+ * If specified, then the query system will only consider plans using the hinted index.
17
+ *
18
+ * If provided as a string, `hint` must be index name for an index on the collection.
19
+ * If provided as an object, `hint` must be an index description for an index defined on the collection.
20
+ *
21
+ * See https://www.mongodb.com/docs/manual/reference/command/distinct/#command-fields.
22
+ */
23
+ hint?: Document | string;
24
+ };
12
25
 
13
26
  /**
14
27
  * Return a list of distinct values for the given key across a collection.
@@ -71,6 +84,10 @@ export class DistinctOperation extends CommandOperation<any[]> {
71
84
  cmd.comment = options.comment;
72
85
  }
73
86
 
87
+ if (options.hint != null) {
88
+ cmd.hint = options.hint;
89
+ }
90
+
74
91
  // Do we have a readConcern specified
75
92
  decorateWithReadConcern(cmd, coll, options);
76
93
 
@@ -4,6 +4,7 @@ import { MongoCompatibilityError, MongoInvalidArgumentError, MongoServerError }
4
4
  import type { InferIdType, TODO_NODE_3286 } from '../mongo_types';
5
5
  import type { Server } from '../sdam/server';
6
6
  import type { ClientSession } from '../sessions';
7
+ import { formatSort, type Sort, type SortForCmd } from '../sort';
7
8
  import { type TimeoutContext } from '../timeout';
8
9
  import { hasAtomicOperators, type MongoDBNamespace } from '../utils';
9
10
  import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
@@ -58,6 +59,8 @@ export interface UpdateStatement {
58
59
  arrayFilters?: Document[];
59
60
  /** A document or string that specifies the index to use to support the query predicate. */
60
61
  hint?: Hint;
62
+ /** Specifies the sort order for the documents matched by the filter. */
63
+ sort?: SortForCmd;
61
64
  }
62
65
 
63
66
  /**
@@ -214,6 +217,8 @@ export interface ReplaceOptions extends CommandOperationOptions {
214
217
  upsert?: boolean;
215
218
  /** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
216
219
  let?: Document;
220
+ /** Specifies the sort order for the documents matched by the filter. */
221
+ sort?: Sort;
217
222
  }
218
223
 
219
224
  /** @internal */
@@ -259,7 +264,7 @@ export class ReplaceOneOperation extends UpdateOperation {
259
264
  export function makeUpdateStatement(
260
265
  filter: Document,
261
266
  update: Document | Document[],
262
- options: UpdateOptions & { multi?: boolean }
267
+ options: UpdateOptions & { multi?: boolean } & { sort?: Sort }
263
268
  ): UpdateStatement {
264
269
  if (filter == null || typeof filter !== 'object') {
265
270
  throw new MongoInvalidArgumentError('Selector must be a valid JavaScript object');
@@ -290,6 +295,10 @@ export function makeUpdateStatement(
290
295
  op.collation = options.collation;
291
296
  }
292
297
 
298
+ if (!options.multi && options.sort != null) {
299
+ op.sort = formatSort(options.sort);
300
+ }
301
+
293
302
  return op;
294
303
  }
295
304
 
package/src/sort.ts CHANGED
@@ -8,24 +8,24 @@ export type SortDirection =
8
8
  | 'desc'
9
9
  | 'ascending'
10
10
  | 'descending'
11
- | { $meta: string };
11
+ | { readonly $meta: string };
12
12
 
13
13
  /** @public */
14
14
  export type Sort =
15
15
  | string
16
- | Exclude<SortDirection, { $meta: string }>
17
- | string[]
18
- | { [key: string]: SortDirection }
19
- | Map<string, SortDirection>
20
- | [string, SortDirection][]
21
- | [string, SortDirection];
16
+ | Exclude<SortDirection, { readonly $meta: string }>
17
+ | ReadonlyArray<string>
18
+ | { readonly [key: string]: SortDirection }
19
+ | ReadonlyMap<string, SortDirection>
20
+ | ReadonlyArray<readonly [string, SortDirection]>
21
+ | readonly [string, SortDirection];
22
22
 
23
23
  /** Below stricter types were created for sort that correspond with type that the cmd takes */
24
24
 
25
- /** @internal */
25
+ /** @public */
26
26
  export type SortDirectionForCmd = 1 | -1 | { $meta: string };
27
27
 
28
- /** @internal */
28
+ /** @public */
29
29
  export type SortForCmd = Map<string, SortDirectionForCmd>;
30
30
 
31
31
  /** @internal */
@@ -55,7 +55,7 @@ function isMeta(t: SortDirection): t is { $meta: string } {
55
55
  }
56
56
 
57
57
  /** @internal */
58
- function isPair(t: Sort): t is [string, SortDirection] {
58
+ function isPair(t: Sort): t is readonly [string, SortDirection] {
59
59
  if (Array.isArray(t) && t.length === 2) {
60
60
  try {
61
61
  prepareDirection(t[1]);
@@ -67,33 +67,37 @@ function isPair(t: Sort): t is [string, SortDirection] {
67
67
  return false;
68
68
  }
69
69
 
70
- function isDeep(t: Sort): t is [string, SortDirection][] {
70
+ function isDeep(t: Sort): t is ReadonlyArray<readonly [string, SortDirection]> {
71
71
  return Array.isArray(t) && Array.isArray(t[0]);
72
72
  }
73
73
 
74
- function isMap(t: Sort): t is Map<string, SortDirection> {
74
+ function isMap(t: Sort): t is ReadonlyMap<string, SortDirection> {
75
75
  return t instanceof Map && t.size > 0;
76
76
  }
77
77
 
78
+ function isReadonlyArray<T>(value: any): value is readonly T[] {
79
+ return Array.isArray(value);
80
+ }
81
+
78
82
  /** @internal */
79
- function pairToMap(v: [string, SortDirection]): SortForCmd {
83
+ function pairToMap(v: readonly [string, SortDirection]): SortForCmd {
80
84
  return new Map([[`${v[0]}`, prepareDirection([v[1]])]]);
81
85
  }
82
86
 
83
87
  /** @internal */
84
- function deepToMap(t: [string, SortDirection][]): SortForCmd {
88
+ function deepToMap(t: ReadonlyArray<readonly [string, SortDirection]>): SortForCmd {
85
89
  const sortEntries: SortPairForCmd[] = t.map(([k, v]) => [`${k}`, prepareDirection(v)]);
86
90
  return new Map(sortEntries);
87
91
  }
88
92
 
89
93
  /** @internal */
90
- function stringsToMap(t: string[]): SortForCmd {
94
+ function stringsToMap(t: ReadonlyArray<string>): SortForCmd {
91
95
  const sortEntries: SortPairForCmd[] = t.map(key => [`${key}`, 1]);
92
96
  return new Map(sortEntries);
93
97
  }
94
98
 
95
99
  /** @internal */
96
- function objectToMap(t: { [key: string]: SortDirection }): SortForCmd {
100
+ function objectToMap(t: { readonly [key: string]: SortDirection }): SortForCmd {
97
101
  const sortEntries: SortPairForCmd[] = Object.entries(t).map(([k, v]) => [
98
102
  `${k}`,
99
103
  prepareDirection(v)
@@ -102,7 +106,7 @@ function objectToMap(t: { [key: string]: SortDirection }): SortForCmd {
102
106
  }
103
107
 
104
108
  /** @internal */
105
- function mapToMap(t: Map<string, SortDirection>): SortForCmd {
109
+ function mapToMap(t: ReadonlyMap<string, SortDirection>): SortForCmd {
106
110
  const sortEntries: SortPairForCmd[] = Array.from(t).map(([k, v]) => [
107
111
  `${k}`,
108
112
  prepareDirection(v)
@@ -116,17 +120,22 @@ export function formatSort(
116
120
  direction?: SortDirection
117
121
  ): SortForCmd | undefined {
118
122
  if (sort == null) return undefined;
119
- if (typeof sort === 'string') return new Map([[sort, prepareDirection(direction)]]);
123
+
124
+ if (typeof sort === 'string') return new Map([[sort, prepareDirection(direction)]]); // 'fieldName'
125
+
120
126
  if (typeof sort !== 'object') {
121
127
  throw new MongoInvalidArgumentError(
122
128
  `Invalid sort format: ${JSON.stringify(sort)} Sort must be a valid object`
123
129
  );
124
130
  }
125
- if (!Array.isArray(sort)) {
126
- return isMap(sort) ? mapToMap(sort) : Object.keys(sort).length ? objectToMap(sort) : undefined;
131
+
132
+ if (!isReadonlyArray(sort)) {
133
+ if (isMap(sort)) return mapToMap(sort); // Map<fieldName, SortDirection>
134
+ if (Object.keys(sort).length) return objectToMap(sort); // { [fieldName: string]: SortDirection }
135
+ return undefined;
127
136
  }
128
137
  if (!sort.length) return undefined;
129
- if (isDeep(sort)) return deepToMap(sort);
130
- if (isPair(sort)) return pairToMap(sort);
131
- return stringsToMap(sort);
138
+ if (isDeep(sort)) return deepToMap(sort); // [ [fieldName, sortDir], [fieldName, sortDir] ... ]
139
+ if (isPair(sort)) return pairToMap(sort); // [ fieldName, sortDir ]
140
+ return stringsToMap(sort); // [ fieldName, fieldName ]
132
141
  }