mongodb 3.2.5 → 3.3.0-beta2

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 (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -0,0 +1,453 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const f = require('util').format;
5
+ const ReadPreference = require('./read_preference');
6
+ const Buffer = require('safe-buffer').Buffer;
7
+ const TopologyType = require('../sdam/topology_description').TopologyType;
8
+
9
+ /**
10
+ * Emit event if it exists
11
+ * @method
12
+ */
13
+ function emitSDAMEvent(self, event, description) {
14
+ if (self.listeners(event).length > 0) {
15
+ self.emit(event, description);
16
+ }
17
+ }
18
+
19
+ // Get package.json variable
20
+ var driverVersion = require('../../../package.json').version;
21
+ var nodejsversion = f('Node.js %s, %s', process.version, os.endianness());
22
+ var type = os.type();
23
+ var name = process.platform;
24
+ var architecture = process.arch;
25
+ var release = os.release();
26
+
27
+ function createClientInfo(options) {
28
+ // Build default client information
29
+ var clientInfo = options.clientInfo
30
+ ? clone(options.clientInfo)
31
+ : {
32
+ driver: {
33
+ name: 'nodejs-core',
34
+ version: driverVersion
35
+ },
36
+ os: {
37
+ type: type,
38
+ name: name,
39
+ architecture: architecture,
40
+ version: release
41
+ }
42
+ };
43
+
44
+ // Is platform specified
45
+ if (clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') === -1) {
46
+ clientInfo.platform = f('%s, mongodb-core: %s', clientInfo.platform, driverVersion);
47
+ } else if (!clientInfo.platform) {
48
+ clientInfo.platform = nodejsversion;
49
+ }
50
+
51
+ // Do we have an application specific string
52
+ if (options.appname) {
53
+ // Cut at 128 bytes
54
+ var buffer = Buffer.from(options.appname);
55
+ // Return the truncated appname
56
+ var appname = buffer.length > 128 ? buffer.slice(0, 128).toString('utf8') : options.appname;
57
+ // Add to the clientInfo
58
+ clientInfo.application = { name: appname };
59
+ }
60
+
61
+ return clientInfo;
62
+ }
63
+
64
+ function createCompressionInfo(options) {
65
+ if (!options.compression || !options.compression.compressors) {
66
+ return [];
67
+ }
68
+
69
+ // Check that all supplied compressors are valid
70
+ options.compression.compressors.forEach(function(compressor) {
71
+ if (compressor !== 'snappy' && compressor !== 'zlib') {
72
+ throw new Error('compressors must be at least one of snappy or zlib');
73
+ }
74
+ });
75
+
76
+ return options.compression.compressors;
77
+ }
78
+
79
+ function clone(object) {
80
+ return JSON.parse(JSON.stringify(object));
81
+ }
82
+
83
+ var getPreviousDescription = function(self) {
84
+ if (!self.s.serverDescription) {
85
+ self.s.serverDescription = {
86
+ address: self.name,
87
+ arbiters: [],
88
+ hosts: [],
89
+ passives: [],
90
+ type: 'Unknown'
91
+ };
92
+ }
93
+
94
+ return self.s.serverDescription;
95
+ };
96
+
97
+ var emitServerDescriptionChanged = function(self, description) {
98
+ if (self.listeners('serverDescriptionChanged').length > 0) {
99
+ // Emit the server description changed events
100
+ self.emit('serverDescriptionChanged', {
101
+ topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
102
+ address: self.name,
103
+ previousDescription: getPreviousDescription(self),
104
+ newDescription: description
105
+ });
106
+
107
+ self.s.serverDescription = description;
108
+ }
109
+ };
110
+
111
+ var getPreviousTopologyDescription = function(self) {
112
+ if (!self.s.topologyDescription) {
113
+ self.s.topologyDescription = {
114
+ topologyType: 'Unknown',
115
+ servers: [
116
+ {
117
+ address: self.name,
118
+ arbiters: [],
119
+ hosts: [],
120
+ passives: [],
121
+ type: 'Unknown'
122
+ }
123
+ ]
124
+ };
125
+ }
126
+
127
+ return self.s.topologyDescription;
128
+ };
129
+
130
+ var emitTopologyDescriptionChanged = function(self, description) {
131
+ if (self.listeners('topologyDescriptionChanged').length > 0) {
132
+ // Emit the server description changed events
133
+ self.emit('topologyDescriptionChanged', {
134
+ topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
135
+ address: self.name,
136
+ previousDescription: getPreviousTopologyDescription(self),
137
+ newDescription: description
138
+ });
139
+
140
+ self.s.serverDescription = description;
141
+ }
142
+ };
143
+
144
+ var changedIsMaster = function(self, currentIsmaster, ismaster) {
145
+ var currentType = getTopologyType(self, currentIsmaster);
146
+ var newType = getTopologyType(self, ismaster);
147
+ if (newType !== currentType) return true;
148
+ return false;
149
+ };
150
+
151
+ var getTopologyType = function(self, ismaster) {
152
+ if (!ismaster) {
153
+ ismaster = self.ismaster;
154
+ }
155
+
156
+ if (!ismaster) return 'Unknown';
157
+ if (ismaster.ismaster && ismaster.msg === 'isdbgrid') return 'Mongos';
158
+ if (ismaster.ismaster && !ismaster.hosts) return 'Standalone';
159
+ if (ismaster.ismaster) return 'RSPrimary';
160
+ if (ismaster.secondary) return 'RSSecondary';
161
+ if (ismaster.arbiterOnly) return 'RSArbiter';
162
+ return 'Unknown';
163
+ };
164
+
165
+ var inquireServerState = function(self) {
166
+ return function(callback) {
167
+ if (self.s.state === 'destroyed') return;
168
+ // Record response time
169
+ var start = new Date().getTime();
170
+
171
+ // emitSDAMEvent
172
+ emitSDAMEvent(self, 'serverHeartbeatStarted', { connectionId: self.name });
173
+
174
+ // Attempt to execute ismaster command
175
+ self.command('admin.$cmd', { ismaster: true }, { monitoring: true }, function(err, r) {
176
+ if (!err) {
177
+ // Legacy event sender
178
+ self.emit('ismaster', r, self);
179
+
180
+ // Calculate latencyMS
181
+ var latencyMS = new Date().getTime() - start;
182
+
183
+ // Server heart beat event
184
+ emitSDAMEvent(self, 'serverHeartbeatSucceeded', {
185
+ durationMS: latencyMS,
186
+ reply: r.result,
187
+ connectionId: self.name
188
+ });
189
+
190
+ // Did the server change
191
+ if (changedIsMaster(self, self.s.ismaster, r.result)) {
192
+ // Emit server description changed if something listening
193
+ emitServerDescriptionChanged(self, {
194
+ address: self.name,
195
+ arbiters: [],
196
+ hosts: [],
197
+ passives: [],
198
+ type: !self.s.inTopology ? 'Standalone' : getTopologyType(self)
199
+ });
200
+ }
201
+
202
+ // Updat ismaster view
203
+ self.s.ismaster = r.result;
204
+
205
+ // Set server response time
206
+ self.s.isMasterLatencyMS = latencyMS;
207
+ } else {
208
+ emitSDAMEvent(self, 'serverHeartbeatFailed', {
209
+ durationMS: latencyMS,
210
+ failure: err,
211
+ connectionId: self.name
212
+ });
213
+ }
214
+
215
+ // Peforming an ismaster monitoring callback operation
216
+ if (typeof callback === 'function') {
217
+ return callback(err, r);
218
+ }
219
+
220
+ // Perform another sweep
221
+ self.s.inquireServerStateTimeout = setTimeout(inquireServerState(self), self.s.haInterval);
222
+ });
223
+ };
224
+ };
225
+
226
+ //
227
+ // Clone the options
228
+ var cloneOptions = function(options) {
229
+ var opts = {};
230
+ for (var name in options) {
231
+ opts[name] = options[name];
232
+ }
233
+ return opts;
234
+ };
235
+
236
+ function Interval(fn, time) {
237
+ var timer = false;
238
+
239
+ this.start = function() {
240
+ if (!this.isRunning()) {
241
+ timer = setInterval(fn, time);
242
+ }
243
+
244
+ return this;
245
+ };
246
+
247
+ this.stop = function() {
248
+ clearInterval(timer);
249
+ timer = false;
250
+ return this;
251
+ };
252
+
253
+ this.isRunning = function() {
254
+ return timer !== false;
255
+ };
256
+ }
257
+
258
+ function Timeout(fn, time) {
259
+ var timer = false;
260
+
261
+ this.start = function() {
262
+ if (!this.isRunning()) {
263
+ timer = setTimeout(fn, time);
264
+ }
265
+ return this;
266
+ };
267
+
268
+ this.stop = function() {
269
+ clearTimeout(timer);
270
+ timer = false;
271
+ return this;
272
+ };
273
+
274
+ this.isRunning = function() {
275
+ if (timer && timer._called) return false;
276
+ return timer !== false;
277
+ };
278
+ }
279
+
280
+ function diff(previous, current) {
281
+ // Difference document
282
+ var diff = {
283
+ servers: []
284
+ };
285
+
286
+ // Previous entry
287
+ if (!previous) {
288
+ previous = { servers: [] };
289
+ }
290
+
291
+ // Check if we have any previous servers missing in the current ones
292
+ for (var i = 0; i < previous.servers.length; i++) {
293
+ var found = false;
294
+
295
+ for (var j = 0; j < current.servers.length; j++) {
296
+ if (current.servers[j].address.toLowerCase() === previous.servers[i].address.toLowerCase()) {
297
+ found = true;
298
+ break;
299
+ }
300
+ }
301
+
302
+ if (!found) {
303
+ // Add to the diff
304
+ diff.servers.push({
305
+ address: previous.servers[i].address,
306
+ from: previous.servers[i].type,
307
+ to: 'Unknown'
308
+ });
309
+ }
310
+ }
311
+
312
+ // Check if there are any severs that don't exist
313
+ for (j = 0; j < current.servers.length; j++) {
314
+ found = false;
315
+
316
+ // Go over all the previous servers
317
+ for (i = 0; i < previous.servers.length; i++) {
318
+ if (previous.servers[i].address.toLowerCase() === current.servers[j].address.toLowerCase()) {
319
+ found = true;
320
+ break;
321
+ }
322
+ }
323
+
324
+ // Add the server to the diff
325
+ if (!found) {
326
+ diff.servers.push({
327
+ address: current.servers[j].address,
328
+ from: 'Unknown',
329
+ to: current.servers[j].type
330
+ });
331
+ }
332
+ }
333
+
334
+ // Got through all the servers
335
+ for (i = 0; i < previous.servers.length; i++) {
336
+ var prevServer = previous.servers[i];
337
+
338
+ // Go through all current servers
339
+ for (j = 0; j < current.servers.length; j++) {
340
+ var currServer = current.servers[j];
341
+
342
+ // Matching server
343
+ if (prevServer.address.toLowerCase() === currServer.address.toLowerCase()) {
344
+ // We had a change in state
345
+ if (prevServer.type !== currServer.type) {
346
+ diff.servers.push({
347
+ address: prevServer.address,
348
+ from: prevServer.type,
349
+ to: currServer.type
350
+ });
351
+ }
352
+ }
353
+ }
354
+ }
355
+
356
+ // Return difference
357
+ return diff;
358
+ }
359
+
360
+ /**
361
+ * Shared function to determine clusterTime for a given topology
362
+ *
363
+ * @param {*} topology
364
+ * @param {*} clusterTime
365
+ */
366
+ function resolveClusterTime(topology, $clusterTime) {
367
+ if (topology.clusterTime == null) {
368
+ topology.clusterTime = $clusterTime;
369
+ } else {
370
+ if ($clusterTime.clusterTime.greaterThan(topology.clusterTime.clusterTime)) {
371
+ topology.clusterTime = $clusterTime;
372
+ }
373
+ }
374
+ }
375
+
376
+ // NOTE: this is a temporary move until the topologies can be more formally refactored
377
+ // to share code.
378
+ const SessionMixins = {
379
+ endSessions: function(sessions, callback) {
380
+ if (!Array.isArray(sessions)) {
381
+ sessions = [sessions];
382
+ }
383
+
384
+ // TODO:
385
+ // When connected to a sharded cluster the endSessions command
386
+ // can be sent to any mongos. When connected to a replica set the
387
+ // endSessions command MUST be sent to the primary if the primary
388
+ // is available, otherwise it MUST be sent to any available secondary.
389
+ // Is it enough to use: ReadPreference.primaryPreferred ?
390
+ this.command(
391
+ 'admin.$cmd',
392
+ { endSessions: sessions },
393
+ { readPreference: ReadPreference.primaryPreferred },
394
+ () => {
395
+ // intentionally ignored, per spec
396
+ if (typeof callback === 'function') callback();
397
+ }
398
+ );
399
+ }
400
+ };
401
+
402
+ function topologyType(topology) {
403
+ if (topology.description) {
404
+ return topology.description.type;
405
+ }
406
+
407
+ if (topology.type === 'mongos') {
408
+ return TopologyType.Sharded;
409
+ } else if (topology.type === 'replset') {
410
+ return TopologyType.ReplicaSetWithPrimary;
411
+ }
412
+
413
+ return TopologyType.Single;
414
+ }
415
+
416
+ const RETRYABLE_WIRE_VERSION = 6;
417
+
418
+ /**
419
+ * Determines whether the provided topology supports retryable writes
420
+ *
421
+ * @param {Mongos|Replset} topology
422
+ */
423
+ const isRetryableWritesSupported = function(topology) {
424
+ const maxWireVersion = topology.lastIsMaster().maxWireVersion;
425
+ if (maxWireVersion < RETRYABLE_WIRE_VERSION) {
426
+ return false;
427
+ }
428
+
429
+ if (!topology.logicalSessionTimeoutMinutes) {
430
+ return false;
431
+ }
432
+
433
+ if (topologyType(topology) === TopologyType.Single) {
434
+ return false;
435
+ }
436
+
437
+ return true;
438
+ };
439
+
440
+ module.exports.SessionMixins = SessionMixins;
441
+ module.exports.resolveClusterTime = resolveClusterTime;
442
+ module.exports.inquireServerState = inquireServerState;
443
+ module.exports.getTopologyType = getTopologyType;
444
+ module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;
445
+ module.exports.emitTopologyDescriptionChanged = emitTopologyDescriptionChanged;
446
+ module.exports.cloneOptions = cloneOptions;
447
+ module.exports.createClientInfo = createClientInfo;
448
+ module.exports.createCompressionInfo = createCompressionInfo;
449
+ module.exports.clone = clone;
450
+ module.exports.diff = diff;
451
+ module.exports.Interval = Interval;
452
+ module.exports.Timeout = Timeout;
453
+ module.exports.isRetryableWritesSupported = isRetryableWritesSupported;
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+ const MongoError = require('./error').MongoError;
3
+
4
+ let TxnState;
5
+ let stateMachine;
6
+
7
+ (() => {
8
+ const NO_TRANSACTION = 'NO_TRANSACTION';
9
+ const STARTING_TRANSACTION = 'STARTING_TRANSACTION';
10
+ const TRANSACTION_IN_PROGRESS = 'TRANSACTION_IN_PROGRESS';
11
+ const TRANSACTION_COMMITTED = 'TRANSACTION_COMMITTED';
12
+ const TRANSACTION_COMMITTED_EMPTY = 'TRANSACTION_COMMITTED_EMPTY';
13
+ const TRANSACTION_ABORTED = 'TRANSACTION_ABORTED';
14
+
15
+ TxnState = {
16
+ NO_TRANSACTION,
17
+ STARTING_TRANSACTION,
18
+ TRANSACTION_IN_PROGRESS,
19
+ TRANSACTION_COMMITTED,
20
+ TRANSACTION_COMMITTED_EMPTY,
21
+ TRANSACTION_ABORTED
22
+ };
23
+
24
+ stateMachine = {
25
+ [NO_TRANSACTION]: [NO_TRANSACTION, STARTING_TRANSACTION],
26
+ [STARTING_TRANSACTION]: [
27
+ TRANSACTION_IN_PROGRESS,
28
+ TRANSACTION_COMMITTED,
29
+ TRANSACTION_COMMITTED_EMPTY,
30
+ TRANSACTION_ABORTED
31
+ ],
32
+ [TRANSACTION_IN_PROGRESS]: [
33
+ TRANSACTION_IN_PROGRESS,
34
+ TRANSACTION_COMMITTED,
35
+ TRANSACTION_ABORTED
36
+ ],
37
+ [TRANSACTION_COMMITTED]: [
38
+ TRANSACTION_COMMITTED,
39
+ TRANSACTION_COMMITTED_EMPTY,
40
+ STARTING_TRANSACTION,
41
+ NO_TRANSACTION
42
+ ],
43
+ [TRANSACTION_ABORTED]: [STARTING_TRANSACTION, NO_TRANSACTION],
44
+ [TRANSACTION_COMMITTED_EMPTY]: [TRANSACTION_COMMITTED_EMPTY, NO_TRANSACTION]
45
+ };
46
+ })();
47
+
48
+ /**
49
+ * The MongoDB ReadConcern, which allows for control of the consistency and isolation properties
50
+ * of the data read from replica sets and replica set shards.
51
+ * @typedef {Object} ReadConcern
52
+ * @property {'local'|'available'|'majority'|'linearizable'|'snapshot'} level The readConcern Level
53
+ * @see https://docs.mongodb.com/manual/reference/read-concern/
54
+ */
55
+
56
+ /**
57
+ * A MongoDB WriteConcern, which describes the level of acknowledgement
58
+ * requested from MongoDB for write operations.
59
+ * @typedef {Object} WriteConcern
60
+ * @property {number|'majority'|string} [w=1] requests acknowledgement that the write operation has
61
+ * propagated to a specified number of mongod hosts
62
+ * @property {boolean} [j=false] requests acknowledgement from MongoDB that the write operation has
63
+ * been written to the journal
64
+ * @property {number} [wtimeout] a time limit, in milliseconds, for the write concern
65
+ * @see https://docs.mongodb.com/manual/reference/write-concern/
66
+ */
67
+
68
+ /**
69
+ * Configuration options for a transaction.
70
+ * @typedef {Object} TransactionOptions
71
+ * @property {ReadConcern} [readConcern] A default read concern for commands in this transaction
72
+ * @property {WriteConcern} [writeConcern] A default writeConcern for commands in this transaction
73
+ * @property {ReadPreference} [readPreference] A default read preference for commands in this transaction
74
+ */
75
+
76
+ /**
77
+ * A class maintaining state related to a server transaction. Internal Only
78
+ * @ignore
79
+ */
80
+ class Transaction {
81
+ /**
82
+ * Create a transaction
83
+ *
84
+ * @ignore
85
+ * @param {TransactionOptions} [options] Optional settings
86
+ */
87
+ constructor(options) {
88
+ options = options || {};
89
+
90
+ this.state = TxnState.NO_TRANSACTION;
91
+ this.options = {};
92
+
93
+ if (options.writeConcern || typeof options.w !== 'undefined') {
94
+ const w = options.writeConcern ? options.writeConcern.w : options.w;
95
+ if (w <= 0) {
96
+ throw new MongoError('Transactions do not support unacknowledged write concern');
97
+ }
98
+
99
+ this.options.writeConcern = options.writeConcern ? options.writeConcern : { w: options.w };
100
+ }
101
+
102
+ if (options.readConcern) this.options.readConcern = options.readConcern;
103
+ if (options.readPreference) this.options.readPreference = options.readPreference;
104
+
105
+ // TODO: This isn't technically necessary
106
+ this._pinnedServer = undefined;
107
+ this._recoveryToken = undefined;
108
+ }
109
+
110
+ get server() {
111
+ return this._pinnedServer;
112
+ }
113
+
114
+ get recoveryToken() {
115
+ return this._recoveryToken;
116
+ }
117
+
118
+ get isPinned() {
119
+ return !!this.server;
120
+ }
121
+
122
+ /**
123
+ * @ignore
124
+ * @return Whether this session is presently in a transaction
125
+ */
126
+ get isActive() {
127
+ return (
128
+ [TxnState.STARTING_TRANSACTION, TxnState.TRANSACTION_IN_PROGRESS].indexOf(this.state) !== -1
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Transition the transaction in the state machine
134
+ * @ignore
135
+ * @param {TxnState} state The new state to transition to
136
+ */
137
+ transition(nextState) {
138
+ const nextStates = stateMachine[this.state];
139
+ if (nextStates && nextStates.indexOf(nextState) !== -1) {
140
+ this.state = nextState;
141
+ if (this.state === TxnState.NO_TRANSACTION || this.state === TxnState.STARTING_TRANSACTION) {
142
+ this.unpinServer();
143
+ }
144
+ return;
145
+ }
146
+
147
+ throw new MongoError(
148
+ `Attempted illegal state transition from [${this.state}] to [${nextState}]`
149
+ );
150
+ }
151
+
152
+ pinServer(server) {
153
+ if (this.isActive) {
154
+ this._pinnedServer = server;
155
+ }
156
+ }
157
+
158
+ unpinServer() {
159
+ this._pinnedServer = undefined;
160
+ }
161
+ }
162
+
163
+ function isTransactionCommand(command) {
164
+ return !!(command.commitTransaction || command.abortTransaction);
165
+ }
166
+
167
+ module.exports = { TxnState, Transaction, isTransactionCommand };