mongodb 4.5.0 → 4.7.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.
Files changed (134) hide show
  1. package/lib/admin.js +5 -5
  2. package/lib/admin.js.map +1 -1
  3. package/lib/bulk/common.js +7 -4
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/change_stream.js +264 -258
  6. package/lib/change_stream.js.map +1 -1
  7. package/lib/cmap/auth/mongodb_aws.js +3 -0
  8. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  9. package/lib/cmap/auth/scram.js +12 -1
  10. package/lib/cmap/auth/scram.js.map +1 -1
  11. package/lib/cmap/commands.js +12 -11
  12. package/lib/cmap/commands.js.map +1 -1
  13. package/lib/cmap/connect.js +8 -1
  14. package/lib/cmap/connect.js.map +1 -1
  15. package/lib/cmap/connection.js +9 -60
  16. package/lib/cmap/connection.js.map +1 -1
  17. package/lib/cmap/connection_pool.js +70 -57
  18. package/lib/cmap/connection_pool.js.map +1 -1
  19. package/lib/cmap/connection_pool_events.js.map +1 -1
  20. package/lib/cmap/message_stream.js +39 -6
  21. package/lib/cmap/message_stream.js.map +1 -1
  22. package/lib/cmap/wire_protocol/compression.js +18 -2
  23. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  24. package/lib/cmap/wire_protocol/constants.js +2 -2
  25. package/lib/cmap/wire_protocol/shared.js +3 -0
  26. package/lib/cmap/wire_protocol/shared.js.map +1 -1
  27. package/lib/collection.js +62 -31
  28. package/lib/collection.js.map +1 -1
  29. package/lib/connection_string.js +39 -11
  30. package/lib/connection_string.js.map +1 -1
  31. package/lib/constants.js +8 -1
  32. package/lib/constants.js.map +1 -1
  33. package/lib/cursor/abstract_cursor.js +16 -11
  34. package/lib/cursor/abstract_cursor.js.map +1 -1
  35. package/lib/cursor/aggregation_cursor.js +5 -5
  36. package/lib/cursor/aggregation_cursor.js.map +1 -1
  37. package/lib/cursor/find_cursor.js +12 -7
  38. package/lib/cursor/find_cursor.js.map +1 -1
  39. package/lib/db.js +21 -14
  40. package/lib/db.js.map +1 -1
  41. package/lib/deps.js +6 -1
  42. package/lib/deps.js.map +1 -1
  43. package/lib/encrypter.js +13 -3
  44. package/lib/encrypter.js.map +1 -1
  45. package/lib/error.js +37 -24
  46. package/lib/error.js.map +1 -1
  47. package/lib/gridfs/upload.js +1 -1
  48. package/lib/gridfs/upload.js.map +1 -1
  49. package/lib/index.js +4 -3
  50. package/lib/index.js.map +1 -1
  51. package/lib/mongo_client.js +18 -0
  52. package/lib/mongo_client.js.map +1 -1
  53. package/lib/operations/command.js +0 -3
  54. package/lib/operations/command.js.map +1 -1
  55. package/lib/operations/connect.js +1 -0
  56. package/lib/operations/connect.js.map +1 -1
  57. package/lib/operations/create_collection.js +56 -17
  58. package/lib/operations/create_collection.js.map +1 -1
  59. package/lib/operations/drop.js +48 -7
  60. package/lib/operations/drop.js.map +1 -1
  61. package/lib/operations/estimated_document_count.js +1 -20
  62. package/lib/operations/estimated_document_count.js.map +1 -1
  63. package/lib/operations/execute_operation.js +16 -9
  64. package/lib/operations/execute_operation.js.map +1 -1
  65. package/lib/operations/find.js +0 -51
  66. package/lib/operations/find.js.map +1 -1
  67. package/lib/operations/indexes.js +2 -2
  68. package/lib/operations/indexes.js.map +1 -1
  69. package/lib/operations/insert.js +5 -1
  70. package/lib/operations/insert.js.map +1 -1
  71. package/lib/operations/list_collections.js +2 -2
  72. package/lib/operations/list_collections.js.map +1 -1
  73. package/lib/sdam/monitor.js +10 -3
  74. package/lib/sdam/monitor.js.map +1 -1
  75. package/lib/sdam/server.js +29 -21
  76. package/lib/sdam/server.js.map +1 -1
  77. package/lib/sdam/srv_polling.js +2 -1
  78. package/lib/sdam/srv_polling.js.map +1 -1
  79. package/lib/sdam/topology.js +25 -24
  80. package/lib/sdam/topology.js.map +1 -1
  81. package/lib/sdam/topology_description.js +2 -1
  82. package/lib/sdam/topology_description.js.map +1 -1
  83. package/lib/sessions.js +29 -17
  84. package/lib/sessions.js.map +1 -1
  85. package/lib/utils.js +3 -2
  86. package/lib/utils.js.map +1 -1
  87. package/mongodb.d.ts +521 -88
  88. package/package.json +12 -11
  89. package/src/admin.ts +9 -5
  90. package/src/bulk/common.ts +7 -4
  91. package/src/change_stream.ts +761 -440
  92. package/src/cmap/auth/mongodb_aws.ts +5 -0
  93. package/src/cmap/auth/scram.ts +11 -1
  94. package/src/cmap/commands.ts +23 -22
  95. package/src/cmap/connect.ts +13 -2
  96. package/src/cmap/connection.ts +12 -74
  97. package/src/cmap/connection_pool.ts +90 -74
  98. package/src/cmap/connection_pool_events.ts +1 -1
  99. package/src/cmap/message_stream.ts +41 -7
  100. package/src/cmap/wire_protocol/compression.ts +27 -3
  101. package/src/cmap/wire_protocol/constants.ts +2 -2
  102. package/src/cmap/wire_protocol/shared.ts +5 -1
  103. package/src/collection.ts +74 -36
  104. package/src/connection_string.ts +49 -14
  105. package/src/constants.ts +6 -0
  106. package/src/cursor/abstract_cursor.ts +18 -15
  107. package/src/cursor/aggregation_cursor.ts +6 -6
  108. package/src/cursor/find_cursor.ts +16 -8
  109. package/src/db.ts +31 -20
  110. package/src/deps.ts +65 -7
  111. package/src/encrypter.ts +12 -3
  112. package/src/error.ts +41 -27
  113. package/src/gridfs/upload.ts +1 -1
  114. package/src/index.ts +23 -0
  115. package/src/mongo_client.ts +36 -11
  116. package/src/mongo_types.ts +1 -1
  117. package/src/operations/command.ts +0 -4
  118. package/src/operations/connect.ts +1 -0
  119. package/src/operations/create_collection.ts +95 -21
  120. package/src/operations/drop.ts +66 -6
  121. package/src/operations/estimated_document_count.ts +2 -29
  122. package/src/operations/execute_operation.ts +27 -27
  123. package/src/operations/find.ts +0 -80
  124. package/src/operations/indexes.ts +3 -9
  125. package/src/operations/insert.ts +8 -1
  126. package/src/operations/list_collections.ts +3 -3
  127. package/src/sdam/monitor.ts +10 -0
  128. package/src/sdam/server.ts +39 -36
  129. package/src/sdam/srv_polling.ts +1 -0
  130. package/src/sdam/topology.ts +36 -27
  131. package/src/sdam/topology_description.ts +2 -1
  132. package/src/sessions.ts +31 -20
  133. package/src/transactions.ts +1 -1
  134. package/src/utils.ts +3 -2
@@ -1,7 +1,7 @@
1
1
  import * as zlib from 'zlib';
2
2
 
3
3
  import { LEGACY_HELLO_COMMAND } from '../../constants';
4
- import { PKG_VERSION, Snappy } from '../../deps';
4
+ import { PKG_VERSION, Snappy, ZStandard } from '../../deps';
5
5
  import { MongoDecompressionError, MongoInvalidArgumentError } from '../../error';
6
6
  import type { Callback } from '../../utils';
7
7
  import type { OperationDescription } from '../message_stream';
@@ -10,7 +10,8 @@ import type { OperationDescription } from '../message_stream';
10
10
  export const Compressor = Object.freeze({
11
11
  none: 0,
12
12
  snappy: 1,
13
- zlib: 2
13
+ zlib: 2,
14
+ zstd: 3
14
15
  } as const);
15
16
 
16
17
  /** @public */
@@ -32,6 +33,9 @@ export const uncompressibleCommands = new Set([
32
33
  'copydb'
33
34
  ]);
34
35
 
36
+ const MAX_COMPRESSOR_ID = 3;
37
+ const ZSTD_COMPRESSION_LEVEL = 3;
38
+
35
39
  // Facilitate compressing a message using an agreed compressor
36
40
  export function compress(
37
41
  self: { options: OperationDescription & zlib.ZlibOptions },
@@ -61,6 +65,15 @@ export function compress(
61
65
  }
62
66
  zlib.deflate(dataToBeCompressed, zlibOptions, callback as zlib.CompressCallback);
63
67
  break;
68
+ case 'zstd':
69
+ if ('kModuleError' in ZStandard) {
70
+ return callback(ZStandard['kModuleError']);
71
+ }
72
+ ZStandard.compress(dataToBeCompressed, ZSTD_COMPRESSION_LEVEL).then(
73
+ buffer => callback(undefined, buffer),
74
+ error => callback(error)
75
+ );
76
+ break;
64
77
  default:
65
78
  throw new MongoInvalidArgumentError(
66
79
  `Unknown compressor ${self.options.agreedCompressor} failed to compress`
@@ -74,7 +87,7 @@ export function decompress(
74
87
  compressedData: Buffer,
75
88
  callback: Callback<Buffer>
76
89
  ): void {
77
- if (compressorID < 0 || compressorID > Math.max(2)) {
90
+ if (compressorID < 0 || compressorID > MAX_COMPRESSOR_ID) {
78
91
  throw new MongoDecompressionError(
79
92
  `Server sent message compressed using an unsupported compressor. (Received compressor ID ${compressorID})`
80
93
  );
@@ -95,6 +108,17 @@ export function decompress(
95
108
  }
96
109
  break;
97
110
  }
111
+ case Compressor.zstd: {
112
+ if ('kModuleError' in ZStandard) {
113
+ return callback(ZStandard['kModuleError']);
114
+ }
115
+
116
+ ZStandard.decompress(compressedData).then(
117
+ buffer => callback(undefined, buffer),
118
+ error => callback(error)
119
+ );
120
+ break;
121
+ }
98
122
  case Compressor.zlib:
99
123
  zlib.inflate(compressedData, callback as zlib.CompressCallback);
100
124
  break;
@@ -1,7 +1,7 @@
1
1
  export const MIN_SUPPORTED_SERVER_VERSION = '3.6';
2
- export const MAX_SUPPORTED_SERVER_VERSION = '5.1';
2
+ export const MAX_SUPPORTED_SERVER_VERSION = '6.0';
3
3
  export const MIN_SUPPORTED_WIRE_VERSION = 6;
4
- export const MAX_SUPPORTED_WIRE_VERSION = 14;
4
+ export const MAX_SUPPORTED_WIRE_VERSION = 17;
5
5
  export const OP_REPLY = 1;
6
6
  export const OP_UPDATE = 2001;
7
7
  export const OP_INSERT = 2002;
@@ -56,7 +56,11 @@ export function applyCommonQueryOptions(
56
56
  return queryOptions;
57
57
  }
58
58
 
59
- export function isSharded(topologyOrServer: Topology | Server | Connection): boolean {
59
+ export function isSharded(topologyOrServer?: Topology | Server | Connection): boolean {
60
+ if (topologyOrServer == null) {
61
+ return false;
62
+ }
63
+
60
64
  if (topologyOrServer.description && topologyOrServer.description.type === ServerType.Mongos) {
61
65
  return true;
62
66
  }
package/src/collection.ts CHANGED
@@ -2,7 +2,7 @@ import { BSONSerializeOptions, Document, resolveBSONOptions } from './bson';
2
2
  import type { AnyBulkWriteOperation, BulkWriteOptions, BulkWriteResult } from './bulk/common';
3
3
  import { OrderedBulkOperation } from './bulk/ordered';
4
4
  import { UnorderedBulkOperation } from './bulk/unordered';
5
- import { ChangeStream, ChangeStreamOptions } from './change_stream';
5
+ import { ChangeStream, ChangeStreamDocument, ChangeStreamOptions } from './change_stream';
6
6
  import { AggregationCursor } from './cursor/aggregation_cursor';
7
7
  import { FindCursor } from './cursor/find_cursor';
8
8
  import type { Db } from './db';
@@ -93,7 +93,6 @@ import {
93
93
  checkCollectionName,
94
94
  DEFAULT_PK_FACTORY,
95
95
  emitWarningOnce,
96
- getTopology,
97
96
  MongoDBNamespace,
98
97
  normalizeHintField,
99
98
  resolveOptions
@@ -296,7 +295,7 @@ export class Collection<TSchema extends Document = Document> {
296
295
  }
297
296
 
298
297
  return executeOperation(
299
- this,
298
+ this.s.db.s.client,
300
299
  new InsertOneOperation(
301
300
  this as TODO_NODE_3286,
302
301
  doc,
@@ -338,7 +337,7 @@ export class Collection<TSchema extends Document = Document> {
338
337
  options = options ? Object.assign({}, options) : { ordered: true };
339
338
 
340
339
  return executeOperation(
341
- this,
340
+ this.s.db.s.client,
342
341
  new InsertManyOperation(
343
342
  this as TODO_NODE_3286,
344
343
  docs,
@@ -406,7 +405,7 @@ export class Collection<TSchema extends Document = Document> {
406
405
  }
407
406
 
408
407
  return executeOperation(
409
- this,
408
+ this.s.db.s.client,
410
409
  new BulkWriteOperation(
411
410
  this as TODO_NODE_3286,
412
411
  operations as TODO_NODE_3286,
@@ -453,7 +452,7 @@ export class Collection<TSchema extends Document = Document> {
453
452
  if (typeof options === 'function') (callback = options), (options = {});
454
453
 
455
454
  return executeOperation(
456
- this,
455
+ this.s.db.s.client,
457
456
  new UpdateOneOperation(
458
457
  this as TODO_NODE_3286,
459
458
  filter,
@@ -501,7 +500,7 @@ export class Collection<TSchema extends Document = Document> {
501
500
  if (typeof options === 'function') (callback = options), (options = {});
502
501
 
503
502
  return executeOperation(
504
- this,
503
+ this.s.db.s.client,
505
504
  new ReplaceOneOperation(
506
505
  this as TODO_NODE_3286,
507
506
  filter,
@@ -549,7 +548,7 @@ export class Collection<TSchema extends Document = Document> {
549
548
  if (typeof options === 'function') (callback = options), (options = {});
550
549
 
551
550
  return executeOperation(
552
- this,
551
+ this.s.db.s.client,
553
552
  new UpdateManyOperation(
554
553
  this as TODO_NODE_3286,
555
554
  filter,
@@ -583,7 +582,7 @@ export class Collection<TSchema extends Document = Document> {
583
582
  if (typeof options === 'function') (callback = options), (options = {});
584
583
 
585
584
  return executeOperation(
586
- this,
585
+ this.s.db.s.client,
587
586
  new DeleteOneOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)),
588
587
  callback
589
588
  );
@@ -623,7 +622,7 @@ export class Collection<TSchema extends Document = Document> {
623
622
  }
624
623
 
625
624
  return executeOperation(
626
- this,
625
+ this.s.db.s.client,
627
626
  new DeleteManyOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)),
628
627
  callback
629
628
  );
@@ -652,7 +651,7 @@ export class Collection<TSchema extends Document = Document> {
652
651
 
653
652
  // Intentionally, we do not inherit options from parent for this operation.
654
653
  return executeOperation(
655
- this,
654
+ this.s.db.s.client,
656
655
  new RenameOperation(this as TODO_NODE_3286, newName, {
657
656
  ...options,
658
657
  readPreference: ReadPreference.PRIMARY
@@ -679,7 +678,7 @@ export class Collection<TSchema extends Document = Document> {
679
678
  options = options ?? {};
680
679
 
681
680
  return executeOperation(
682
- this,
681
+ this.s.db.s.client,
683
682
  new DropCollectionOperation(this.s.db, this.collectionName, options),
684
683
  callback
685
684
  );
@@ -759,7 +758,7 @@ export class Collection<TSchema extends Document = Document> {
759
758
  }
760
759
 
761
760
  return new FindCursor<WithId<TSchema>>(
762
- getTopology(this),
761
+ this.s.db.s.client,
763
762
  this.s.namespace,
764
763
  filter,
765
764
  resolveOptions(this as TODO_NODE_3286, options)
@@ -783,7 +782,7 @@ export class Collection<TSchema extends Document = Document> {
783
782
  if (typeof options === 'function') (callback = options), (options = {});
784
783
 
785
784
  return executeOperation(
786
- this,
785
+ this.s.db.s.client,
787
786
  new OptionsOperation(this as TODO_NODE_3286, resolveOptions(this, options)),
788
787
  callback
789
788
  );
@@ -806,7 +805,7 @@ export class Collection<TSchema extends Document = Document> {
806
805
  if (typeof options === 'function') (callback = options), (options = {});
807
806
 
808
807
  return executeOperation(
809
- this,
808
+ this.s.db.s.client,
810
809
  new IsCappedOperation(this as TODO_NODE_3286, resolveOptions(this, options)),
811
810
  callback
812
811
  );
@@ -857,7 +856,7 @@ export class Collection<TSchema extends Document = Document> {
857
856
  if (typeof options === 'function') (callback = options), (options = {});
858
857
 
859
858
  return executeOperation(
860
- this,
859
+ this.s.db.s.client,
861
860
  new CreateIndexOperation(
862
861
  this as TODO_NODE_3286,
863
862
  this.collectionName,
@@ -918,7 +917,7 @@ export class Collection<TSchema extends Document = Document> {
918
917
  if (typeof options.maxTimeMS !== 'number') delete options.maxTimeMS;
919
918
 
920
919
  return executeOperation(
921
- this,
920
+ this.s.db.s.client,
922
921
  new CreateIndexesOperation(
923
922
  this as TODO_NODE_3286,
924
923
  this.collectionName,
@@ -952,7 +951,7 @@ export class Collection<TSchema extends Document = Document> {
952
951
  options.readPreference = ReadPreference.primary;
953
952
 
954
953
  return executeOperation(
955
- this,
954
+ this.s.db.s.client,
956
955
  new DropIndexOperation(this as TODO_NODE_3286, indexName, options),
957
956
  callback
958
957
  );
@@ -975,7 +974,7 @@ export class Collection<TSchema extends Document = Document> {
975
974
  if (typeof options === 'function') (callback = options), (options = {});
976
975
 
977
976
  return executeOperation(
978
- this,
977
+ this.s.db.s.client,
979
978
  new DropIndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)),
980
979
  callback
981
980
  );
@@ -1013,7 +1012,7 @@ export class Collection<TSchema extends Document = Document> {
1013
1012
  if (typeof options === 'function') (callback = options), (options = {});
1014
1013
 
1015
1014
  return executeOperation(
1016
- this,
1015
+ this.s.db.s.client,
1017
1016
  new IndexExistsOperation(this as TODO_NODE_3286, indexes, resolveOptions(this, options)),
1018
1017
  callback
1019
1018
  );
@@ -1036,7 +1035,7 @@ export class Collection<TSchema extends Document = Document> {
1036
1035
  if (typeof options === 'function') (callback = options), (options = {});
1037
1036
 
1038
1037
  return executeOperation(
1039
- this,
1038
+ this.s.db.s.client,
1040
1039
  new IndexInformationOperation(this.s.db, this.collectionName, resolveOptions(this, options)),
1041
1040
  callback
1042
1041
  );
@@ -1044,7 +1043,15 @@ export class Collection<TSchema extends Document = Document> {
1044
1043
 
1045
1044
  /**
1046
1045
  * Gets an estimate of the count of documents in a collection using collection metadata.
1046
+ * This will always run a count command on all server versions.
1047
1047
  *
1048
+ * due to an oversight in versions 5.0.0-5.0.8 of MongoDB, the count command,
1049
+ * which estimatedDocumentCount uses in its implementation, was not included in v1 of
1050
+ * the Stable API, and so users of the Stable API with estimatedDocumentCount are
1051
+ * recommended to upgrade their server version to 5.0.9+ or set apiStrict: false to avoid
1052
+ * encountering errors.
1053
+ *
1054
+ * @see {@link https://www.mongodb.com/docs/manual/reference/command/count/#behavior|Count: Behavior}
1048
1055
  * @param options - Optional settings for the command
1049
1056
  * @param callback - An optional callback, a Promise will be returned if none is provided
1050
1057
  */
@@ -1058,7 +1065,7 @@ export class Collection<TSchema extends Document = Document> {
1058
1065
  ): Promise<number> | void {
1059
1066
  if (typeof options === 'function') (callback = options), (options = {});
1060
1067
  return executeOperation(
1061
- this,
1068
+ this.s.db.s.client,
1062
1069
  new EstimatedDocumentCountOperation(this as TODO_NODE_3286, resolveOptions(this, options)),
1063
1070
  callback
1064
1071
  );
@@ -1118,7 +1125,7 @@ export class Collection<TSchema extends Document = Document> {
1118
1125
 
1119
1126
  filter ??= {};
1120
1127
  return executeOperation(
1121
- this,
1128
+ this.s.db.s.client,
1122
1129
  new CountDocumentsOperation(
1123
1130
  this as TODO_NODE_3286,
1124
1131
  filter as Document,
@@ -1193,7 +1200,7 @@ export class Collection<TSchema extends Document = Document> {
1193
1200
 
1194
1201
  filter ??= {};
1195
1202
  return executeOperation(
1196
- this,
1203
+ this.s.db.s.client,
1197
1204
  new DistinctOperation(
1198
1205
  this as TODO_NODE_3286,
1199
1206
  key as TODO_NODE_3286,
@@ -1221,7 +1228,7 @@ export class Collection<TSchema extends Document = Document> {
1221
1228
  if (typeof options === 'function') (callback = options), (options = {});
1222
1229
 
1223
1230
  return executeOperation(
1224
- this,
1231
+ this.s.db.s.client,
1225
1232
  new IndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)),
1226
1233
  callback
1227
1234
  );
@@ -1245,7 +1252,7 @@ export class Collection<TSchema extends Document = Document> {
1245
1252
  options = options ?? {};
1246
1253
 
1247
1254
  return executeOperation(
1248
- this,
1255
+ this.s.db.s.client,
1249
1256
  new CollStatsOperation(this as TODO_NODE_3286, options),
1250
1257
  callback
1251
1258
  );
@@ -1277,7 +1284,7 @@ export class Collection<TSchema extends Document = Document> {
1277
1284
  if (typeof options === 'function') (callback = options), (options = {});
1278
1285
 
1279
1286
  return executeOperation(
1280
- this,
1287
+ this.s.db.s.client,
1281
1288
  new FindOneAndDeleteOperation(
1282
1289
  this as TODO_NODE_3286,
1283
1290
  filter,
@@ -1324,7 +1331,7 @@ export class Collection<TSchema extends Document = Document> {
1324
1331
  if (typeof options === 'function') (callback = options), (options = {});
1325
1332
 
1326
1333
  return executeOperation(
1327
- this,
1334
+ this.s.db.s.client,
1328
1335
  new FindOneAndReplaceOperation(
1329
1336
  this as TODO_NODE_3286,
1330
1337
  filter,
@@ -1372,7 +1379,7 @@ export class Collection<TSchema extends Document = Document> {
1372
1379
  if (typeof options === 'function') (callback = options), (options = {});
1373
1380
 
1374
1381
  return executeOperation(
1375
- this,
1382
+ this.s.db.s.client,
1376
1383
  new FindOneAndUpdateOperation(
1377
1384
  this as TODO_NODE_3286,
1378
1385
  filter,
@@ -1408,7 +1415,7 @@ export class Collection<TSchema extends Document = Document> {
1408
1415
  }
1409
1416
 
1410
1417
  return new AggregationCursor(
1411
- getTopology(this),
1418
+ this.s.db.s.client,
1412
1419
  this.s.namespace,
1413
1420
  pipeline,
1414
1421
  resolveOptions(this, options)
@@ -1418,21 +1425,52 @@ export class Collection<TSchema extends Document = Document> {
1418
1425
  /**
1419
1426
  * Create a new Change Stream, watching for new changes (insertions, updates, replacements, deletions, and invalidations) in this collection.
1420
1427
  *
1421
- * @since 3.0.0
1428
+ * @remarks
1429
+ * watch() accepts two generic arguments for distinct usecases:
1430
+ * - The first is to override the schema that may be defined for this specific collection
1431
+ * - The second is to override the shape of the change stream document entirely, if it is not provided the type will default to ChangeStreamDocument of the first argument
1432
+ * @example
1433
+ * By just providing the first argument I can type the change to be `ChangeStreamDocument<{ _id: number }>`
1434
+ * ```ts
1435
+ * collection.watch<{ _id: number }>()
1436
+ * .on('change', change => console.log(change._id.toFixed(4)));
1437
+ * ```
1438
+ *
1439
+ * @example
1440
+ * Passing a second argument provides a way to reflect the type changes caused by an advanced pipeline.
1441
+ * Here, we are using a pipeline to have MongoDB filter for insert changes only and add a comment.
1442
+ * No need start from scratch on the ChangeStreamInsertDocument type!
1443
+ * By using an intersection we can save time and ensure defaults remain the same type!
1444
+ * ```ts
1445
+ * collection
1446
+ * .watch<Schema, ChangeStreamInsertDocument<Schema> & { comment: string }>([
1447
+ * { $addFields: { comment: 'big changes' } },
1448
+ * { $match: { operationType: 'insert' } }
1449
+ * ])
1450
+ * .on('change', change => {
1451
+ * change.comment.startsWith('big');
1452
+ * change.operationType === 'insert';
1453
+ * // No need to narrow in code because the generics did that for us!
1454
+ * expectType<Schema>(change.fullDocument);
1455
+ * });
1456
+ * ```
1457
+ *
1422
1458
  * @param pipeline - An array of {@link https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.
1423
1459
  * @param options - Optional settings for the command
1460
+ * @typeParam TLocal - Type of the data being detected by the change stream
1461
+ * @typeParam TChange - Type of the whole change stream document emitted
1424
1462
  */
1425
- watch<TLocal extends Document = TSchema>(
1463
+ watch<TLocal extends Document = TSchema, TChange extends Document = ChangeStreamDocument<TLocal>>(
1426
1464
  pipeline: Document[] = [],
1427
1465
  options: ChangeStreamOptions = {}
1428
- ): ChangeStream<TLocal> {
1466
+ ): ChangeStream<TLocal, TChange> {
1429
1467
  // Allow optionally not specifying a pipeline
1430
1468
  if (!Array.isArray(pipeline)) {
1431
1469
  options = pipeline;
1432
1470
  pipeline = [];
1433
1471
  }
1434
1472
 
1435
- return new ChangeStream<TLocal>(this, pipeline, resolveOptions(this, options));
1473
+ return new ChangeStream<TLocal, TChange>(this, pipeline, resolveOptions(this, options));
1436
1474
  }
1437
1475
 
1438
1476
  /**
@@ -1495,7 +1533,7 @@ export class Collection<TSchema extends Document = Document> {
1495
1533
  }
1496
1534
 
1497
1535
  return executeOperation(
1498
- this,
1536
+ this.s.db.s.client,
1499
1537
  new MapReduceOperation(
1500
1538
  this as TODO_NODE_3286,
1501
1539
  map,
@@ -1636,7 +1674,7 @@ export class Collection<TSchema extends Document = Document> {
1636
1674
 
1637
1675
  filter ??= {};
1638
1676
  return executeOperation(
1639
- this,
1677
+ this.s.db.s.client,
1640
1678
  new CountOperation(
1641
1679
  MongoDBNamespace.fromString(this.namespace),
1642
1680
  filter,
@@ -19,7 +19,6 @@ import {
19
19
  ServerApi,
20
20
  ServerApiVersion
21
21
  } from './mongo_client';
22
- import type { OneOrMore } from './mongo_types';
23
22
  import { PromiseProvider } from './promise_provider';
24
23
  import { ReadConcern, ReadConcernLevel } from './read_concern';
25
24
  import { ReadPreference, ReadPreferenceMode } from './read_preference';
@@ -220,12 +219,7 @@ function getUint(name: string, value: unknown): number {
220
219
  return parsedValue;
221
220
  }
222
221
 
223
- /** Wrap a single value in an array if the value is not an array */
224
- function toArray<T>(value: OneOrMore<T>): T[] {
225
- return Array.isArray(value) ? value : [value];
226
- }
227
-
228
- function* entriesFromString(value: string) {
222
+ function* entriesFromString(value: string): Generator<[string, string]> {
229
223
  const keyValuePairs = value.split(',');
230
224
  for (const keyValue of keyValuePairs) {
231
225
  const [key, value] = keyValue.split(':');
@@ -269,6 +263,14 @@ export function parseOptions(
269
263
  const { hosts, isSRV } = url;
270
264
 
271
265
  const mongoOptions = Object.create(null);
266
+
267
+ // Feature flags
268
+ for (const flag of Object.getOwnPropertySymbols(options)) {
269
+ if (FEATURE_FLAGS.has(flag)) {
270
+ mongoOptions[flag] = options[flag];
271
+ }
272
+ }
273
+
272
274
  mongoOptions.hosts = isSRV ? [] : hosts.map(HostAddress.fromString);
273
275
 
274
276
  const urlOptions = new CaseInsensitiveMap<any[]>();
@@ -333,11 +335,19 @@ export function parseOptions(
333
335
  ]);
334
336
 
335
337
  for (const key of allKeys) {
336
- const values = [objectOptions, urlOptions, DEFAULT_OPTIONS].flatMap(optionsObject => {
337
- const options = optionsObject.get(key) ?? [];
338
- return toArray(options);
339
- });
340
-
338
+ const values = [];
339
+ const objectOptionValue = objectOptions.get(key);
340
+ if (objectOptionValue != null) {
341
+ values.push(objectOptionValue);
342
+ }
343
+ const urlValue = urlOptions.get(key);
344
+ if (urlValue != null) {
345
+ values.push(...urlValue);
346
+ }
347
+ const defaultOptionsValue = DEFAULT_OPTIONS.get(key);
348
+ if (defaultOptionsValue != null) {
349
+ values.push(defaultOptionsValue);
350
+ }
341
351
  allOptions.set(key, values);
342
352
  }
343
353
 
@@ -846,6 +856,16 @@ export const OPTIONS = {
846
856
  return new Logger('MongoClient', { loggerLevel: value as LoggerLevel });
847
857
  }
848
858
  },
859
+ maxConnecting: {
860
+ default: 2,
861
+ transform({ name, values: [value] }): number {
862
+ const maxConnecting = getUint(name, value);
863
+ if (maxConnecting === 0) {
864
+ throw new MongoInvalidArgumentError('maxConnecting must be > 0 if specified');
865
+ }
866
+ return maxConnecting;
867
+ }
868
+ },
849
869
  maxIdleTimeMS: {
850
870
  default: 0,
851
871
  type: 'uint'
@@ -982,9 +1002,18 @@ export const OPTIONS = {
982
1002
  },
983
1003
  readPreferenceTags: {
984
1004
  target: 'readPreference',
985
- transform({ values, options }) {
1005
+ transform({
1006
+ values,
1007
+ options
1008
+ }: {
1009
+ values: Array<string | Record<string, string>[]>;
1010
+ options: MongoClientOptions;
1011
+ }) {
1012
+ const tags: Array<string | Record<string, string>> = Array.isArray(values[0])
1013
+ ? values[0]
1014
+ : (values as Array<string>);
986
1015
  const readPreferenceTags = [];
987
- for (const tag of values) {
1016
+ for (const tag of tags) {
988
1017
  const readPreferenceTag: TagSet = Object.create(null);
989
1018
  if (typeof tag === 'string') {
990
1019
  for (const [k, v] of entriesFromString(tag)) {
@@ -1230,3 +1259,9 @@ export const DEFAULT_OPTIONS = new CaseInsensitiveMap(
1230
1259
  .filter(([, descriptor]) => descriptor.default != null)
1231
1260
  .map(([k, d]) => [k, d.default])
1232
1261
  );
1262
+
1263
+ /**
1264
+ * Set of permitted feature flags
1265
+ * @internal
1266
+ */
1267
+ export const FEATURE_FLAGS = new Set([Symbol.for('@@mdb.skipPingOnConnect')]);
package/src/constants.ts CHANGED
@@ -40,6 +40,12 @@ export const COMMAND_FAILED = 'commandFailed' as const;
40
40
  export const SERVER_HEARTBEAT_STARTED = 'serverHeartbeatStarted' as const;
41
41
  export const SERVER_HEARTBEAT_SUCCEEDED = 'serverHeartbeatSucceeded' as const;
42
42
  export const SERVER_HEARTBEAT_FAILED = 'serverHeartbeatFailed' as const;
43
+ export const RESPONSE = 'response' as const;
44
+ export const MORE = 'more' as const;
45
+ export const INIT = 'init' as const;
46
+ export const CHANGE = 'change' as const;
47
+ export const END = 'end' as const;
48
+ export const RESUME_TOKEN_CHANGED = 'resumeTokenChanged' as const;
43
49
 
44
50
  /** @public */
45
51
  export const HEARTBEAT_EVENTS = Object.freeze([
@@ -10,13 +10,13 @@ import {
10
10
  MongoRuntimeError,
11
11
  MongoTailableCursorError
12
12
  } from '../error';
13
+ import type { MongoClient } from '../mongo_client';
13
14
  import { TODO_NODE_3286, TypedEventEmitter } from '../mongo_types';
14
15
  import { executeOperation, ExecutionResult } from '../operations/execute_operation';
15
16
  import { GetMoreOperation } from '../operations/get_more';
16
17
  import { ReadConcern, ReadConcernLike } from '../read_concern';
17
18
  import { ReadPreference, ReadPreferenceLike } from '../read_preference';
18
19
  import type { Server } from '../sdam/server';
19
- import type { Topology } from '../sdam/topology';
20
20
  import { ClientSession, maybeClearPinnedConnection } from '../sessions';
21
21
  import { Callback, maybePromise, MongoDBNamespace, ns } from '../utils';
22
22
 
@@ -29,7 +29,7 @@ const kServer = Symbol('server');
29
29
  /** @internal */
30
30
  const kNamespace = Symbol('namespace');
31
31
  /** @internal */
32
- const kTopology = Symbol('topology');
32
+ const kClient = Symbol('client');
33
33
  /** @internal */
34
34
  const kSession = Symbol('session');
35
35
  /** @internal */
@@ -126,7 +126,7 @@ export abstract class AbstractCursor<
126
126
  /** @internal */
127
127
  [kDocuments]: TSchema[];
128
128
  /** @internal */
129
- [kTopology]: Topology;
129
+ [kClient]: MongoClient;
130
130
  /** @internal */
131
131
  [kTransform]?: (doc: TSchema) => any;
132
132
  /** @internal */
@@ -143,13 +143,16 @@ export abstract class AbstractCursor<
143
143
 
144
144
  /** @internal */
145
145
  constructor(
146
- topology: Topology,
146
+ client: MongoClient,
147
147
  namespace: MongoDBNamespace,
148
148
  options: AbstractCursorOptions = {}
149
149
  ) {
150
150
  super();
151
151
 
152
- this[kTopology] = topology;
152
+ if (!client.s.isMongoClient) {
153
+ throw new MongoRuntimeError('Cursor must be constructed with MongoClient');
154
+ }
155
+ this[kClient] = client;
153
156
  this[kNamespace] = namespace;
154
157
  this[kDocuments] = []; // TODO: https://github.com/microsoft/TypeScript/issues/36230
155
158
  this[kInitialized] = false;
@@ -192,8 +195,8 @@ export abstract class AbstractCursor<
192
195
  }
193
196
 
194
197
  /** @internal */
195
- get topology(): Topology {
196
- return this[kTopology];
198
+ get client(): MongoClient {
199
+ return this[kClient];
197
200
  }
198
201
 
199
202
  /** @internal */
@@ -236,7 +239,7 @@ export abstract class AbstractCursor<
236
239
  }
237
240
 
238
241
  get loadBalanced(): boolean {
239
- return this[kTopology].loadBalanced;
242
+ return !!this[kClient].topology?.loadBalanced;
240
243
  }
241
244
 
242
245
  /** Returns current buffered documents length */
@@ -258,7 +261,7 @@ export abstract class AbstractCursor<
258
261
  };
259
262
  }
260
263
 
261
- stream(options?: CursorStreamOptions): Readable {
264
+ stream(options?: CursorStreamOptions): Readable & AsyncIterable<TSchema> {
262
265
  if (options?.transform) {
263
266
  const transform = options.transform;
264
267
  const readable = makeCursorStream(this);
@@ -388,7 +391,7 @@ export abstract class AbstractCursor<
388
391
  });
389
392
  }
390
393
 
391
- close(): void;
394
+ close(): Promise<void>;
392
395
  close(callback: Callback): void;
393
396
  /**
394
397
  * @deprecated options argument is deprecated
@@ -630,7 +633,7 @@ export abstract class AbstractCursor<
630
633
  batchSize
631
634
  });
632
635
 
633
- executeOperation(this, getMoreOperation, callback);
636
+ executeOperation(this[kClient], getMoreOperation, callback);
634
637
  }
635
638
 
636
639
  /**
@@ -642,13 +645,13 @@ export abstract class AbstractCursor<
642
645
  */
643
646
  [kInit](callback: Callback<TSchema | null>): void {
644
647
  if (this[kSession] == null) {
645
- if (this[kTopology].shouldCheckForSessionSupport()) {
646
- return this[kTopology].selectServer(ReadPreference.primaryPreferred, {}, err => {
648
+ if (this[kClient].topology?.shouldCheckForSessionSupport()) {
649
+ return this[kClient].topology?.selectServer(ReadPreference.primaryPreferred, {}, err => {
647
650
  if (err) return callback(err);
648
651
  return this[kInit](callback);
649
652
  });
650
- } else if (this[kTopology].hasSessionSupport()) {
651
- this[kSession] = this[kTopology].startSession({ owner: this, explicit: false });
653
+ } else if (this[kClient].topology?.hasSessionSupport()) {
654
+ this[kSession] = this[kClient].topology?.startSession({ owner: this, explicit: false });
652
655
  }
653
656
  }
654
657