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
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ChangeStreamCursor = exports.ChangeStream = void 0;
4
4
  const Denque = require("denque");
5
+ const timers_1 = require("timers");
5
6
  const collection_1 = require("./collection");
7
+ const constants_1 = require("./constants");
6
8
  const abstract_cursor_1 = require("./cursor/abstract_cursor");
7
9
  const db_1 = require("./db");
8
10
  const error_1 = require("./error");
@@ -23,21 +25,17 @@ const CHANGE_STREAM_OPTIONS = [
23
25
  'resumeAfter',
24
26
  'startAfter',
25
27
  'startAtOperationTime',
26
- 'fullDocument'
27
- ];
28
- const CURSOR_OPTIONS = [
29
- 'batchSize',
30
- 'maxAwaitTimeMS',
31
- 'collation',
32
- 'readPreference',
33
- 'comment',
34
- ...CHANGE_STREAM_OPTIONS
28
+ 'fullDocument',
29
+ 'fullDocumentBeforeChange',
30
+ 'showExpandedEvents'
35
31
  ];
36
32
  const CHANGE_DOMAIN_TYPES = {
37
33
  COLLECTION: Symbol('Collection'),
38
34
  DATABASE: Symbol('Database'),
39
35
  CLUSTER: Symbol('Cluster')
40
36
  };
37
+ const SELECTION_TIMEOUT = 30000;
38
+ const CHANGE_STREAM_EVENTS = [constants_1.RESUME_TOKEN_CHANGED, constants_1.END, constants_1.CLOSE];
41
39
  const NO_RESUME_TOKEN_ERROR = 'A change stream document has been received that lacks a resume token (_id).';
42
40
  const NO_CURSOR_ERROR = 'ChangeStream has no cursor';
43
41
  const CHANGESTREAM_CLOSED_ERROR = 'ChangeStream is closed';
@@ -75,13 +73,13 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
75
73
  }
76
74
  this[kResumeQueue] = new Denque();
77
75
  // Create contained Change Stream cursor
78
- this.cursor = createChangeStreamCursor(this, options);
76
+ this.cursor = this._createChangeStreamCursor(options);
79
77
  this[kClosed] = false;
80
78
  this[kMode] = false;
81
79
  // Listen for any `change` listeners being added to ChangeStream
82
80
  this.on('newListener', eventName => {
83
81
  if (eventName === 'change' && this.cursor && this.listenerCount('change') === 0) {
84
- streamEvents(this, this.cursor);
82
+ this._streamEvents(this.cursor);
85
83
  }
86
84
  });
87
85
  this.on('removeListener', eventName => {
@@ -101,9 +99,9 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
101
99
  return (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.resumeToken;
102
100
  }
103
101
  hasNext(callback) {
104
- setIsIterator(this);
102
+ this._setIsIterator();
105
103
  return (0, utils_1.maybePromise)(callback, cb => {
106
- getCursor(this, (err, cursor) => {
104
+ this._getCursor((err, cursor) => {
107
105
  if (err || !cursor)
108
106
  return cb(err); // failed to resume, raise an error
109
107
  cursor.hasNext(cb);
@@ -111,18 +109,18 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
111
109
  });
112
110
  }
113
111
  next(callback) {
114
- setIsIterator(this);
112
+ this._setIsIterator();
115
113
  return (0, utils_1.maybePromise)(callback, cb => {
116
- getCursor(this, (err, cursor) => {
114
+ this._getCursor((err, cursor) => {
117
115
  if (err || !cursor)
118
116
  return cb(err); // failed to resume, raise an error
119
117
  cursor.next((error, change) => {
120
118
  if (error) {
121
119
  this[kResumeQueue].push(() => this.next(cb));
122
- processError(this, error, cb);
120
+ this._processError(error, cb);
123
121
  return;
124
122
  }
125
- processNewChange(this, change, cb);
123
+ this._processNewChange(change !== null && change !== void 0 ? change : null, cb);
126
124
  });
127
125
  });
128
126
  });
@@ -141,7 +139,7 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
141
139
  }
142
140
  const cursor = this.cursor;
143
141
  return cursor.close(err => {
144
- endStream(this);
142
+ this._endStream();
145
143
  this.cursor = undefined;
146
144
  return cb(err);
147
145
  });
@@ -158,45 +156,257 @@ class ChangeStream extends mongo_types_1.TypedEventEmitter {
158
156
  return this.cursor.stream(options);
159
157
  }
160
158
  tryNext(callback) {
161
- setIsIterator(this);
159
+ this._setIsIterator();
162
160
  return (0, utils_1.maybePromise)(callback, cb => {
163
- getCursor(this, (err, cursor) => {
161
+ this._getCursor((err, cursor) => {
164
162
  if (err || !cursor)
165
163
  return cb(err); // failed to resume, raise an error
166
164
  return cursor.tryNext(cb);
167
165
  });
168
166
  });
169
167
  }
168
+ /** @internal */
169
+ _setIsEmitter() {
170
+ if (this[kMode] === 'iterator') {
171
+ // TODO(NODE-3485): Replace with MongoChangeStreamModeError
172
+ throw new error_1.MongoAPIError('ChangeStream cannot be used as an EventEmitter after being used as an iterator');
173
+ }
174
+ this[kMode] = 'emitter';
175
+ }
176
+ /** @internal */
177
+ _setIsIterator() {
178
+ if (this[kMode] === 'emitter') {
179
+ // TODO(NODE-3485): Replace with MongoChangeStreamModeError
180
+ throw new error_1.MongoAPIError('ChangeStream cannot be used as an iterator after being used as an EventEmitter');
181
+ }
182
+ this[kMode] = 'iterator';
183
+ }
184
+ /**
185
+ * Create a new change stream cursor based on self's configuration
186
+ * @internal
187
+ */
188
+ _createChangeStreamCursor(options) {
189
+ const changeStreamStageOptions = (0, utils_1.filterOptions)(options, CHANGE_STREAM_OPTIONS);
190
+ if (this.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
191
+ changeStreamStageOptions.allChangesForCluster = true;
192
+ }
193
+ const pipeline = [{ $changeStream: changeStreamStageOptions }, ...this.pipeline];
194
+ const client = this.type === CHANGE_DOMAIN_TYPES.CLUSTER
195
+ ? this.parent
196
+ : this.type === CHANGE_DOMAIN_TYPES.DATABASE
197
+ ? this.parent.s.client
198
+ : this.type === CHANGE_DOMAIN_TYPES.COLLECTION
199
+ ? this.parent.s.db.s.client
200
+ : null;
201
+ if (client == null) {
202
+ // This should never happen because of the assertion in the constructor
203
+ throw new error_1.MongoRuntimeError(`Changestream type should only be one of cluster, database, collection. Found ${this.type.toString()}`);
204
+ }
205
+ const changeStreamCursor = new ChangeStreamCursor(client, this.namespace, pipeline, options);
206
+ for (const event of CHANGE_STREAM_EVENTS) {
207
+ changeStreamCursor.on(event, e => this.emit(event, e));
208
+ }
209
+ if (this.listenerCount(ChangeStream.CHANGE) > 0) {
210
+ this._streamEvents(changeStreamCursor);
211
+ }
212
+ return changeStreamCursor;
213
+ }
214
+ /**
215
+ * This method performs a basic server selection loop, satisfying the requirements of
216
+ * ChangeStream resumability until the new SDAM layer can be used.
217
+ * @internal
218
+ */
219
+ _waitForTopologyConnected(topology, options, callback) {
220
+ (0, timers_1.setTimeout)(() => {
221
+ if (options && options.start == null) {
222
+ options.start = (0, utils_1.now)();
223
+ }
224
+ const start = options.start || (0, utils_1.now)();
225
+ const timeout = options.timeout || SELECTION_TIMEOUT;
226
+ if (topology.isConnected()) {
227
+ return callback();
228
+ }
229
+ if ((0, utils_1.calculateDurationInMs)(start) > timeout) {
230
+ // TODO(NODE-3497): Replace with MongoNetworkTimeoutError
231
+ return callback(new error_1.MongoRuntimeError('Timed out waiting for connection'));
232
+ }
233
+ this._waitForTopologyConnected(topology, options, callback);
234
+ }, 500); // this is an arbitrary wait time to allow SDAM to transition
235
+ }
236
+ /** @internal */
237
+ _closeWithError(error, callback) {
238
+ if (!callback) {
239
+ this.emit(ChangeStream.ERROR, error);
240
+ }
241
+ this.close(() => callback && callback(error));
242
+ }
243
+ /** @internal */
244
+ _streamEvents(cursor) {
245
+ var _a;
246
+ this._setIsEmitter();
247
+ const stream = (_a = this[kCursorStream]) !== null && _a !== void 0 ? _a : cursor.stream();
248
+ this[kCursorStream] = stream;
249
+ stream.on('data', change => this._processNewChange(change));
250
+ stream.on('error', error => this._processError(error));
251
+ }
252
+ /** @internal */
253
+ _endStream() {
254
+ const cursorStream = this[kCursorStream];
255
+ if (cursorStream) {
256
+ ['data', 'close', 'end', 'error'].forEach(event => cursorStream.removeAllListeners(event));
257
+ cursorStream.destroy();
258
+ }
259
+ this[kCursorStream] = undefined;
260
+ }
261
+ /** @internal */
262
+ _processNewChange(change, callback) {
263
+ var _a;
264
+ if (this[kClosed]) {
265
+ // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
266
+ if (callback)
267
+ callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
268
+ return;
269
+ }
270
+ // a null change means the cursor has been notified, implicitly closing the change stream
271
+ if (change == null) {
272
+ // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
273
+ return this._closeWithError(new error_1.MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR), callback);
274
+ }
275
+ if (change && !change._id) {
276
+ return this._closeWithError(new error_1.MongoChangeStreamError(NO_RESUME_TOKEN_ERROR), callback);
277
+ }
278
+ // cache the resume token
279
+ (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.cacheResumeToken(change._id);
280
+ // wipe the startAtOperationTime if there was one so that there won't be a conflict
281
+ // between resumeToken and startAtOperationTime if we need to reconnect the cursor
282
+ this.options.startAtOperationTime = undefined;
283
+ // Return the change
284
+ if (!callback)
285
+ return this.emit(ChangeStream.CHANGE, change);
286
+ return callback(undefined, change);
287
+ }
288
+ /** @internal */
289
+ _processError(error, callback) {
290
+ const cursor = this.cursor;
291
+ // If the change stream has been closed explicitly, do not process error.
292
+ if (this[kClosed]) {
293
+ // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
294
+ if (callback)
295
+ callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
296
+ return;
297
+ }
298
+ // if the resume succeeds, continue with the new cursor
299
+ const resumeWithCursor = (newCursor) => {
300
+ this.cursor = newCursor;
301
+ this._processResumeQueue();
302
+ };
303
+ // otherwise, raise an error and close the change stream
304
+ const unresumableError = (err) => {
305
+ if (!callback) {
306
+ this.emit(ChangeStream.ERROR, err);
307
+ }
308
+ this.close(() => this._processResumeQueue(err));
309
+ };
310
+ if (cursor && (0, error_1.isResumableError)(error, (0, utils_1.maxWireVersion)(cursor.server))) {
311
+ this.cursor = undefined;
312
+ // stop listening to all events from old cursor
313
+ this._endStream();
314
+ // close internal cursor, ignore errors
315
+ cursor.close();
316
+ const topology = (0, utils_1.getTopology)(this.parent);
317
+ this._waitForTopologyConnected(topology, { readPreference: cursor.readPreference }, err => {
318
+ // if the topology can't reconnect, close the stream
319
+ if (err)
320
+ return unresumableError(err);
321
+ // create a new cursor, preserving the old cursor's options
322
+ const newCursor = this._createChangeStreamCursor(cursor.resumeOptions);
323
+ // attempt to continue in emitter mode
324
+ if (!callback)
325
+ return resumeWithCursor(newCursor);
326
+ // attempt to continue in iterator mode
327
+ newCursor.hasNext(err => {
328
+ // if there's an error immediately after resuming, close the stream
329
+ if (err)
330
+ return unresumableError(err);
331
+ resumeWithCursor(newCursor);
332
+ });
333
+ });
334
+ return;
335
+ }
336
+ // if initial error wasn't resumable, raise an error and close the change stream
337
+ return this._closeWithError(error, callback);
338
+ }
339
+ /** @internal */
340
+ _getCursor(callback) {
341
+ if (this[kClosed]) {
342
+ // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
343
+ callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
344
+ return;
345
+ }
346
+ // if a cursor exists and it is open, return it
347
+ if (this.cursor) {
348
+ callback(undefined, this.cursor);
349
+ return;
350
+ }
351
+ // no cursor, queue callback until topology reconnects
352
+ this[kResumeQueue].push(callback);
353
+ }
354
+ /**
355
+ * Drain the resume queue when a new has become available
356
+ * @internal
357
+ *
358
+ * @param error - error getting a new cursor
359
+ */
360
+ _processResumeQueue(error) {
361
+ var _a;
362
+ while (this[kResumeQueue].length) {
363
+ const request = this[kResumeQueue].pop();
364
+ if (!request)
365
+ break; // Should never occur but TS can't use the length check in the while condition
366
+ if (!error) {
367
+ if (this[kClosed]) {
368
+ // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
369
+ request(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
370
+ return;
371
+ }
372
+ if (!this.cursor) {
373
+ request(new error_1.MongoChangeStreamError(NO_CURSOR_ERROR));
374
+ return;
375
+ }
376
+ }
377
+ request(error, (_a = this.cursor) !== null && _a !== void 0 ? _a : undefined);
378
+ }
379
+ }
170
380
  }
171
381
  exports.ChangeStream = ChangeStream;
172
382
  /** @event */
173
- ChangeStream.RESPONSE = 'response';
383
+ ChangeStream.RESPONSE = constants_1.RESPONSE;
174
384
  /** @event */
175
- ChangeStream.MORE = 'more';
385
+ ChangeStream.MORE = constants_1.MORE;
176
386
  /** @event */
177
- ChangeStream.INIT = 'init';
387
+ ChangeStream.INIT = constants_1.INIT;
178
388
  /** @event */
179
- ChangeStream.CLOSE = 'close';
389
+ ChangeStream.CLOSE = constants_1.CLOSE;
180
390
  /**
181
391
  * Fired for each new matching change in the specified namespace. Attaching a `change`
182
392
  * event listener to a Change Stream will switch the stream into flowing mode. Data will
183
393
  * then be passed as soon as it is available.
184
394
  * @event
185
395
  */
186
- ChangeStream.CHANGE = 'change';
396
+ ChangeStream.CHANGE = constants_1.CHANGE;
187
397
  /** @event */
188
- ChangeStream.END = 'end';
398
+ ChangeStream.END = constants_1.END;
189
399
  /** @event */
190
- ChangeStream.ERROR = 'error';
400
+ ChangeStream.ERROR = constants_1.ERROR;
191
401
  /**
192
402
  * Emitted each time the change stream stores a new resume token.
193
403
  * @event
194
404
  */
195
- ChangeStream.RESUME_TOKEN_CHANGED = 'resumeTokenChanged';
405
+ ChangeStream.RESUME_TOKEN_CHANGED = constants_1.RESUME_TOKEN_CHANGED;
196
406
  /** @internal */
197
407
  class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
198
- constructor(topology, namespace, pipeline = [], options = {}) {
199
- super(topology, namespace, options);
408
+ constructor(client, namespace, pipeline = [], options = {}) {
409
+ super(client, namespace, options);
200
410
  this.pipeline = pipeline;
201
411
  this.options = options;
202
412
  this._resumeToken = null;
@@ -216,20 +426,24 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
216
426
  return this._resumeToken;
217
427
  }
218
428
  get resumeOptions() {
219
- const result = applyKnownOptions(this.options, CURSOR_OPTIONS);
220
- if (this.resumeToken || this.startAtOperationTime) {
221
- for (const key of ['resumeAfter', 'startAfter', 'startAtOperationTime']) {
222
- Reflect.deleteProperty(result, key);
223
- }
224
- if (this.resumeToken) {
225
- const resumeKey = this.options.startAfter && !this.hasReceived ? 'startAfter' : 'resumeAfter';
226
- result[resumeKey] = this.resumeToken;
429
+ const options = {
430
+ ...this.options
431
+ };
432
+ for (const key of ['resumeAfter', 'startAfter', 'startAtOperationTime']) {
433
+ delete options[key];
434
+ }
435
+ if (this.resumeToken != null) {
436
+ if (this.options.startAfter && !this.hasReceived) {
437
+ options.startAfter = this.resumeToken;
227
438
  }
228
- else if (this.startAtOperationTime && (0, utils_1.maxWireVersion)(this.server) >= 7) {
229
- result.startAtOperationTime = this.startAtOperationTime;
439
+ else {
440
+ options.resumeAfter = this.resumeToken;
230
441
  }
231
442
  }
232
- return result;
443
+ else if (this.startAtOperationTime != null && (0, utils_1.maxWireVersion)(this.server) >= 7) {
444
+ options.startAtOperationTime = this.startAtOperationTime;
445
+ }
446
+ return options;
233
447
  }
234
448
  cacheResumeToken(resumeToken) {
235
449
  if (this.bufferedCount() === 0 && this.postBatchResumeToken) {
@@ -240,17 +454,18 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
240
454
  }
241
455
  this.hasReceived = true;
242
456
  }
243
- _processBatch(batchName, response) {
244
- const cursor = (response === null || response === void 0 ? void 0 : response.cursor) || {};
457
+ _processBatch(response) {
458
+ const cursor = response.cursor;
245
459
  if (cursor.postBatchResumeToken) {
246
- this.postBatchResumeToken = cursor.postBatchResumeToken;
247
- if (cursor[batchName].length === 0) {
460
+ this.postBatchResumeToken = response.cursor.postBatchResumeToken;
461
+ const batch = 'firstBatch' in response.cursor ? response.cursor.firstBatch : response.cursor.nextBatch;
462
+ if (batch.length === 0) {
248
463
  this.resumeToken = cursor.postBatchResumeToken;
249
464
  }
250
465
  }
251
466
  }
252
467
  clone() {
253
- return new ChangeStreamCursor(this.topology, this.namespace, this.pipeline, {
468
+ return new ChangeStreamCursor(this.client, this.namespace, this.pipeline, {
254
469
  ...this.cursorOptions
255
470
  });
256
471
  }
@@ -260,7 +475,7 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
260
475
  ...this.options,
261
476
  session
262
477
  });
263
- (0, execute_operation_1.executeOperation)(session, aggregateOperation, (err, response) => {
478
+ (0, execute_operation_1.executeOperation)(session.client, aggregateOperation, (err, response) => {
264
479
  if (err || response == null) {
265
480
  return callback(err);
266
481
  }
@@ -271,7 +486,7 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
271
486
  (0, utils_1.maxWireVersion)(server) >= 7) {
272
487
  this.startAtOperationTime = response.operationTime;
273
488
  }
274
- this._processBatch('firstBatch', response);
489
+ this._processBatch(response);
275
490
  this.emit(ChangeStream.INIT, response);
276
491
  this.emit(ChangeStream.RESPONSE);
277
492
  // TODO: NODE-2882
@@ -283,7 +498,7 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
283
498
  if (err) {
284
499
  return callback(err);
285
500
  }
286
- this._processBatch('nextBatch', response);
501
+ this._processBatch(response);
287
502
  this.emit(ChangeStream.MORE, response);
288
503
  this.emit(ChangeStream.RESPONSE);
289
504
  callback(err, response);
@@ -291,213 +506,4 @@ class ChangeStreamCursor extends abstract_cursor_1.AbstractCursor {
291
506
  }
292
507
  }
293
508
  exports.ChangeStreamCursor = ChangeStreamCursor;
294
- const CHANGE_STREAM_EVENTS = [
295
- ChangeStream.RESUME_TOKEN_CHANGED,
296
- ChangeStream.END,
297
- ChangeStream.CLOSE
298
- ];
299
- function setIsEmitter(changeStream) {
300
- if (changeStream[kMode] === 'iterator') {
301
- // TODO(NODE-3485): Replace with MongoChangeStreamModeError
302
- throw new error_1.MongoAPIError('ChangeStream cannot be used as an EventEmitter after being used as an iterator');
303
- }
304
- changeStream[kMode] = 'emitter';
305
- }
306
- function setIsIterator(changeStream) {
307
- if (changeStream[kMode] === 'emitter') {
308
- // TODO(NODE-3485): Replace with MongoChangeStreamModeError
309
- throw new error_1.MongoAPIError('ChangeStream cannot be used as an iterator after being used as an EventEmitter');
310
- }
311
- changeStream[kMode] = 'iterator';
312
- }
313
- /**
314
- * Create a new change stream cursor based on self's configuration
315
- * @internal
316
- */
317
- function createChangeStreamCursor(changeStream, options) {
318
- const changeStreamStageOptions = applyKnownOptions(options, CHANGE_STREAM_OPTIONS);
319
- if (changeStream.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
320
- changeStreamStageOptions.allChangesForCluster = true;
321
- }
322
- const pipeline = [{ $changeStream: changeStreamStageOptions }].concat(changeStream.pipeline);
323
- const cursorOptions = applyKnownOptions(options, CURSOR_OPTIONS);
324
- const changeStreamCursor = new ChangeStreamCursor((0, utils_1.getTopology)(changeStream.parent), changeStream.namespace, pipeline, cursorOptions);
325
- for (const event of CHANGE_STREAM_EVENTS) {
326
- changeStreamCursor.on(event, e => changeStream.emit(event, e));
327
- }
328
- if (changeStream.listenerCount(ChangeStream.CHANGE) > 0) {
329
- streamEvents(changeStream, changeStreamCursor);
330
- }
331
- return changeStreamCursor;
332
- }
333
- function applyKnownOptions(source, options) {
334
- const result = {};
335
- for (const option of options) {
336
- if (option in source) {
337
- result[option] = source[option];
338
- }
339
- }
340
- return result;
341
- }
342
- // This method performs a basic server selection loop, satisfying the requirements of
343
- // ChangeStream resumability until the new SDAM layer can be used.
344
- const SELECTION_TIMEOUT = 30000;
345
- function waitForTopologyConnected(topology, options, callback) {
346
- setTimeout(() => {
347
- if (options && options.start == null) {
348
- options.start = (0, utils_1.now)();
349
- }
350
- const start = options.start || (0, utils_1.now)();
351
- const timeout = options.timeout || SELECTION_TIMEOUT;
352
- if (topology.isConnected()) {
353
- return callback();
354
- }
355
- if ((0, utils_1.calculateDurationInMs)(start) > timeout) {
356
- // TODO(NODE-3497): Replace with MongoNetworkTimeoutError
357
- return callback(new error_1.MongoRuntimeError('Timed out waiting for connection'));
358
- }
359
- waitForTopologyConnected(topology, options, callback);
360
- }, 500); // this is an arbitrary wait time to allow SDAM to transition
361
- }
362
- function closeWithError(changeStream, error, callback) {
363
- if (!callback) {
364
- changeStream.emit(ChangeStream.ERROR, error);
365
- }
366
- changeStream.close(() => callback && callback(error));
367
- }
368
- function streamEvents(changeStream, cursor) {
369
- setIsEmitter(changeStream);
370
- const stream = changeStream[kCursorStream] || cursor.stream();
371
- changeStream[kCursorStream] = stream;
372
- stream.on('data', change => processNewChange(changeStream, change));
373
- stream.on('error', error => processError(changeStream, error));
374
- }
375
- function endStream(changeStream) {
376
- const cursorStream = changeStream[kCursorStream];
377
- if (cursorStream) {
378
- ['data', 'close', 'end', 'error'].forEach(event => cursorStream.removeAllListeners(event));
379
- cursorStream.destroy();
380
- }
381
- changeStream[kCursorStream] = undefined;
382
- }
383
- function processNewChange(changeStream, change, callback) {
384
- var _a;
385
- if (changeStream[kClosed]) {
386
- // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
387
- if (callback)
388
- callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
389
- return;
390
- }
391
- // a null change means the cursor has been notified, implicitly closing the change stream
392
- if (change == null) {
393
- // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
394
- return closeWithError(changeStream, new error_1.MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR), callback);
395
- }
396
- if (change && !change._id) {
397
- return closeWithError(changeStream, new error_1.MongoChangeStreamError(NO_RESUME_TOKEN_ERROR), callback);
398
- }
399
- // cache the resume token
400
- (_a = changeStream.cursor) === null || _a === void 0 ? void 0 : _a.cacheResumeToken(change._id);
401
- // wipe the startAtOperationTime if there was one so that there won't be a conflict
402
- // between resumeToken and startAtOperationTime if we need to reconnect the cursor
403
- changeStream.options.startAtOperationTime = undefined;
404
- // Return the change
405
- if (!callback)
406
- return changeStream.emit(ChangeStream.CHANGE, change);
407
- return callback(undefined, change);
408
- }
409
- function processError(changeStream, error, callback) {
410
- const cursor = changeStream.cursor;
411
- // If the change stream has been closed explicitly, do not process error.
412
- if (changeStream[kClosed]) {
413
- // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
414
- if (callback)
415
- callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
416
- return;
417
- }
418
- // if the resume succeeds, continue with the new cursor
419
- function resumeWithCursor(newCursor) {
420
- changeStream.cursor = newCursor;
421
- processResumeQueue(changeStream);
422
- }
423
- // otherwise, raise an error and close the change stream
424
- function unresumableError(err) {
425
- if (!callback) {
426
- changeStream.emit(ChangeStream.ERROR, err);
427
- }
428
- changeStream.close(() => processResumeQueue(changeStream, err));
429
- }
430
- if (cursor && (0, error_1.isResumableError)(error, (0, utils_1.maxWireVersion)(cursor.server))) {
431
- changeStream.cursor = undefined;
432
- // stop listening to all events from old cursor
433
- endStream(changeStream);
434
- // close internal cursor, ignore errors
435
- cursor.close();
436
- const topology = (0, utils_1.getTopology)(changeStream.parent);
437
- waitForTopologyConnected(topology, { readPreference: cursor.readPreference }, err => {
438
- // if the topology can't reconnect, close the stream
439
- if (err)
440
- return unresumableError(err);
441
- // create a new cursor, preserving the old cursor's options
442
- const newCursor = createChangeStreamCursor(changeStream, cursor.resumeOptions);
443
- // attempt to continue in emitter mode
444
- if (!callback)
445
- return resumeWithCursor(newCursor);
446
- // attempt to continue in iterator mode
447
- newCursor.hasNext(err => {
448
- // if there's an error immediately after resuming, close the stream
449
- if (err)
450
- return unresumableError(err);
451
- resumeWithCursor(newCursor);
452
- });
453
- });
454
- return;
455
- }
456
- // if initial error wasn't resumable, raise an error and close the change stream
457
- return closeWithError(changeStream, error, callback);
458
- }
459
- /**
460
- * Safely provides a cursor across resume attempts
461
- *
462
- * @param changeStream - the parent ChangeStream
463
- */
464
- function getCursor(changeStream, callback) {
465
- if (changeStream[kClosed]) {
466
- // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
467
- callback(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
468
- return;
469
- }
470
- // if a cursor exists and it is open, return it
471
- if (changeStream.cursor) {
472
- callback(undefined, changeStream.cursor);
473
- return;
474
- }
475
- // no cursor, queue callback until topology reconnects
476
- changeStream[kResumeQueue].push(callback);
477
- }
478
- /**
479
- * Drain the resume queue when a new has become available
480
- *
481
- * @param changeStream - the parent ChangeStream
482
- * @param err - error getting a new cursor
483
- */
484
- function processResumeQueue(changeStream, err) {
485
- while (changeStream[kResumeQueue].length) {
486
- const request = changeStream[kResumeQueue].pop();
487
- if (!request)
488
- break; // Should never occur but TS can't use the length check in the while condition
489
- if (!err) {
490
- if (changeStream[kClosed]) {
491
- // TODO(NODE-3485): Replace with MongoChangeStreamClosedError
492
- request(new error_1.MongoAPIError(CHANGESTREAM_CLOSED_ERROR));
493
- return;
494
- }
495
- if (!changeStream.cursor) {
496
- request(new error_1.MongoChangeStreamError(NO_CURSOR_ERROR));
497
- return;
498
- }
499
- }
500
- request(err, changeStream.cursor);
501
- }
502
- }
503
509
  //# sourceMappingURL=change_stream.js.map