mongodb 6.10.0 → 6.11.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 (228) hide show
  1. package/README.md +2 -0
  2. package/lib/admin.js +3 -2
  3. package/lib/admin.js.map +1 -1
  4. package/lib/beta.d.ts +562 -45
  5. package/lib/bulk/common.js +4 -4
  6. package/lib/bulk/common.js.map +1 -1
  7. package/lib/change_stream.js +111 -51
  8. package/lib/change_stream.js.map +1 -1
  9. package/lib/client-side-encryption/auto_encrypter.js +8 -5
  10. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  11. package/lib/client-side-encryption/client_encryption.js +48 -18
  12. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  13. package/lib/client-side-encryption/state_machine.js +43 -29
  14. package/lib/client-side-encryption/state_machine.js.map +1 -1
  15. package/lib/cmap/auth/mongo_credentials.js +5 -2
  16. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  17. package/lib/cmap/auth/mongodb_aws.js +1 -1
  18. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  19. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +38 -0
  20. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js.map +1 -0
  21. package/lib/cmap/auth/mongodb_oidc.js +2 -0
  22. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  23. package/lib/cmap/connect.js +13 -1
  24. package/lib/cmap/connect.js.map +1 -1
  25. package/lib/cmap/connection.js +75 -17
  26. package/lib/cmap/connection.js.map +1 -1
  27. package/lib/cmap/connection_pool.js +14 -12
  28. package/lib/cmap/connection_pool.js.map +1 -1
  29. package/lib/cmap/wire_protocol/on_data.js +5 -1
  30. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  31. package/lib/cmap/wire_protocol/responses.js +30 -0
  32. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  33. package/lib/collection.js +62 -3
  34. package/lib/collection.js.map +1 -1
  35. package/lib/connection_string.js +2 -0
  36. package/lib/connection_string.js.map +1 -1
  37. package/lib/cursor/abstract_cursor.js +221 -38
  38. package/lib/cursor/abstract_cursor.js.map +1 -1
  39. package/lib/cursor/aggregation_cursor.js +29 -7
  40. package/lib/cursor/aggregation_cursor.js.map +1 -1
  41. package/lib/cursor/change_stream_cursor.js +2 -2
  42. package/lib/cursor/change_stream_cursor.js.map +1 -1
  43. package/lib/cursor/client_bulk_write_cursor.js +1 -1
  44. package/lib/cursor/client_bulk_write_cursor.js.map +1 -1
  45. package/lib/cursor/find_cursor.js +18 -8
  46. package/lib/cursor/find_cursor.js.map +1 -1
  47. package/lib/cursor/list_collections_cursor.js +1 -1
  48. package/lib/cursor/list_collections_cursor.js.map +1 -1
  49. package/lib/cursor/list_indexes_cursor.js +1 -1
  50. package/lib/cursor/list_indexes_cursor.js.map +1 -1
  51. package/lib/cursor/run_command_cursor.js +6 -4
  52. package/lib/cursor/run_command_cursor.js.map +1 -1
  53. package/lib/db.js +63 -3
  54. package/lib/db.js.map +1 -1
  55. package/lib/error.js +34 -9
  56. package/lib/error.js.map +1 -1
  57. package/lib/explain.js +57 -1
  58. package/lib/explain.js.map +1 -1
  59. package/lib/gridfs/download.js +31 -3
  60. package/lib/gridfs/download.js.map +1 -1
  61. package/lib/gridfs/index.js +49 -14
  62. package/lib/gridfs/index.js.map +1 -1
  63. package/lib/gridfs/upload.js +80 -22
  64. package/lib/gridfs/upload.js.map +1 -1
  65. package/lib/index.js +9 -5
  66. package/lib/index.js.map +1 -1
  67. package/lib/mongo_client.js +70 -1
  68. package/lib/mongo_client.js.map +1 -1
  69. package/lib/operations/aggregate.js +2 -2
  70. package/lib/operations/aggregate.js.map +1 -1
  71. package/lib/operations/bulk_write.js +7 -2
  72. package/lib/operations/bulk_write.js.map +1 -1
  73. package/lib/operations/client_bulk_write/client_bulk_write.js +3 -3
  74. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -1
  75. package/lib/operations/client_bulk_write/executor.js +14 -3
  76. package/lib/operations/client_bulk_write/executor.js.map +1 -1
  77. package/lib/operations/command.js +5 -2
  78. package/lib/operations/command.js.map +1 -1
  79. package/lib/operations/count.js +2 -2
  80. package/lib/operations/count.js.map +1 -1
  81. package/lib/operations/create_collection.js +8 -7
  82. package/lib/operations/create_collection.js.map +1 -1
  83. package/lib/operations/delete.js +6 -6
  84. package/lib/operations/delete.js.map +1 -1
  85. package/lib/operations/distinct.js +2 -2
  86. package/lib/operations/distinct.js.map +1 -1
  87. package/lib/operations/drop.js +8 -8
  88. package/lib/operations/drop.js.map +1 -1
  89. package/lib/operations/estimated_document_count.js +2 -2
  90. package/lib/operations/estimated_document_count.js.map +1 -1
  91. package/lib/operations/execute_operation.js +16 -10
  92. package/lib/operations/execute_operation.js.map +1 -1
  93. package/lib/operations/find.js +6 -3
  94. package/lib/operations/find.js.map +1 -1
  95. package/lib/operations/find_and_modify.js +2 -2
  96. package/lib/operations/find_and_modify.js.map +1 -1
  97. package/lib/operations/get_more.js +2 -1
  98. package/lib/operations/get_more.js.map +1 -1
  99. package/lib/operations/indexes.js +6 -6
  100. package/lib/operations/indexes.js.map +1 -1
  101. package/lib/operations/insert.js +6 -6
  102. package/lib/operations/insert.js.map +1 -1
  103. package/lib/operations/kill_cursors.js +5 -2
  104. package/lib/operations/kill_cursors.js.map +1 -1
  105. package/lib/operations/list_collections.js +2 -2
  106. package/lib/operations/list_collections.js.map +1 -1
  107. package/lib/operations/list_databases.js +2 -2
  108. package/lib/operations/list_databases.js.map +1 -1
  109. package/lib/operations/operation.js.map +1 -1
  110. package/lib/operations/profiling_level.js +2 -2
  111. package/lib/operations/profiling_level.js.map +1 -1
  112. package/lib/operations/remove_user.js +2 -2
  113. package/lib/operations/remove_user.js.map +1 -1
  114. package/lib/operations/rename.js +2 -2
  115. package/lib/operations/rename.js.map +1 -1
  116. package/lib/operations/run_command.js +6 -4
  117. package/lib/operations/run_command.js.map +1 -1
  118. package/lib/operations/search_indexes/create.js +5 -2
  119. package/lib/operations/search_indexes/create.js.map +1 -1
  120. package/lib/operations/search_indexes/drop.js +2 -2
  121. package/lib/operations/search_indexes/drop.js.map +1 -1
  122. package/lib/operations/search_indexes/update.js +2 -2
  123. package/lib/operations/search_indexes/update.js.map +1 -1
  124. package/lib/operations/set_profiling_level.js +2 -2
  125. package/lib/operations/set_profiling_level.js.map +1 -1
  126. package/lib/operations/stats.js +2 -2
  127. package/lib/operations/stats.js.map +1 -1
  128. package/lib/operations/update.js +8 -8
  129. package/lib/operations/update.js.map +1 -1
  130. package/lib/operations/validate_collection.js +2 -2
  131. package/lib/operations/validate_collection.js.map +1 -1
  132. package/lib/read_concern.js +1 -1
  133. package/lib/sdam/common.js +0 -7
  134. package/lib/sdam/common.js.map +1 -1
  135. package/lib/sdam/server.js +4 -1
  136. package/lib/sdam/server.js.map +1 -1
  137. package/lib/sdam/server_description.js +4 -2
  138. package/lib/sdam/server_description.js.map +1 -1
  139. package/lib/sdam/server_selection.js +5 -2
  140. package/lib/sdam/server_selection.js.map +1 -1
  141. package/lib/sdam/topology.js +38 -15
  142. package/lib/sdam/topology.js.map +1 -1
  143. package/lib/sessions.js +157 -98
  144. package/lib/sessions.js.map +1 -1
  145. package/lib/timeout.js +231 -16
  146. package/lib/timeout.js.map +1 -1
  147. package/lib/utils.js +36 -19
  148. package/lib/utils.js.map +1 -1
  149. package/lib/write_concern.js.map +1 -1
  150. package/mongodb.d.ts +562 -45
  151. package/package.json +17 -16
  152. package/src/admin.ts +6 -2
  153. package/src/bulk/common.ts +17 -5
  154. package/src/change_stream.ts +127 -52
  155. package/src/client-side-encryption/auto_encrypter.ts +12 -5
  156. package/src/client-side-encryption/client_encryption.ts +103 -20
  157. package/src/client-side-encryption/state_machine.ts +66 -32
  158. package/src/cmap/auth/mongo_credentials.ts +6 -3
  159. package/src/cmap/auth/mongodb_aws.ts +1 -1
  160. package/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +38 -0
  161. package/src/cmap/auth/mongodb_oidc.ts +3 -1
  162. package/src/cmap/connect.ts +18 -1
  163. package/src/cmap/connection.ts +105 -17
  164. package/src/cmap/connection_pool.ts +15 -17
  165. package/src/cmap/handshake/client_metadata.ts +1 -1
  166. package/src/cmap/wire_protocol/on_data.ts +11 -1
  167. package/src/cmap/wire_protocol/responses.ts +35 -1
  168. package/src/collection.ts +81 -9
  169. package/src/connection_string.ts +2 -0
  170. package/src/cursor/abstract_cursor.ts +287 -39
  171. package/src/cursor/aggregation_cursor.ts +54 -8
  172. package/src/cursor/change_stream_cursor.ts +6 -2
  173. package/src/cursor/client_bulk_write_cursor.ts +6 -2
  174. package/src/cursor/find_cursor.ts +40 -9
  175. package/src/cursor/list_collections_cursor.ts +1 -1
  176. package/src/cursor/list_indexes_cursor.ts +1 -1
  177. package/src/cursor/run_command_cursor.ts +50 -5
  178. package/src/db.ts +75 -7
  179. package/src/error.ts +33 -8
  180. package/src/explain.ts +85 -0
  181. package/src/gridfs/download.ts +43 -4
  182. package/src/gridfs/index.ts +64 -16
  183. package/src/gridfs/upload.ts +153 -45
  184. package/src/index.ts +27 -5
  185. package/src/mongo_client.ts +76 -4
  186. package/src/operations/aggregate.ts +10 -2
  187. package/src/operations/bulk_write.ts +9 -2
  188. package/src/operations/client_bulk_write/client_bulk_write.ts +11 -3
  189. package/src/operations/client_bulk_write/executor.ts +15 -3
  190. package/src/operations/command.ts +18 -8
  191. package/src/operations/count.ts +10 -3
  192. package/src/operations/create_collection.ts +14 -7
  193. package/src/operations/delete.ts +15 -6
  194. package/src/operations/distinct.ts +7 -2
  195. package/src/operations/drop.ts +18 -8
  196. package/src/operations/estimated_document_count.ts +7 -2
  197. package/src/operations/execute_operation.ts +22 -13
  198. package/src/operations/find.ts +17 -5
  199. package/src/operations/find_and_modify.ts +7 -2
  200. package/src/operations/get_more.ts +4 -1
  201. package/src/operations/indexes.ts +20 -7
  202. package/src/operations/insert.ts +13 -6
  203. package/src/operations/kill_cursors.ts +10 -2
  204. package/src/operations/list_collections.ts +10 -1
  205. package/src/operations/list_databases.ts +9 -2
  206. package/src/operations/operation.ts +16 -2
  207. package/src/operations/profiling_level.ts +7 -2
  208. package/src/operations/remove_user.ts +7 -2
  209. package/src/operations/rename.ts +7 -2
  210. package/src/operations/run_command.ts +23 -4
  211. package/src/operations/search_indexes/create.ts +10 -2
  212. package/src/operations/search_indexes/drop.ts +7 -2
  213. package/src/operations/search_indexes/update.ts +7 -2
  214. package/src/operations/set_profiling_level.ts +4 -2
  215. package/src/operations/stats.ts +7 -2
  216. package/src/operations/update.ts +16 -8
  217. package/src/operations/validate_collection.ts +7 -2
  218. package/src/read_concern.ts +1 -1
  219. package/src/sdam/common.ts +0 -11
  220. package/src/sdam/server.ts +14 -4
  221. package/src/sdam/server_description.ts +6 -2
  222. package/src/sdam/server_selection.ts +5 -2
  223. package/src/sdam/topology.ts +43 -27
  224. package/src/sessions.ts +206 -120
  225. package/src/timeout.ts +327 -23
  226. package/src/transactions.ts +1 -1
  227. package/src/utils.ts +47 -30
  228. package/src/write_concern.ts +6 -3
package/src/timeout.ts CHANGED
@@ -1,16 +1,20 @@
1
1
  import { clearTimeout, setTimeout } from 'timers';
2
2
 
3
- import { MongoInvalidArgumentError } from './error';
4
- import { noop } from './utils';
3
+ import { type Document } from './bson';
4
+ import { MongoInvalidArgumentError, MongoOperationTimeoutError, MongoRuntimeError } from './error';
5
+ import { type ClientSession } from './sessions';
6
+ import { csotMin, noop } from './utils';
5
7
 
6
8
  /** @internal */
7
9
  export class TimeoutError extends Error {
10
+ duration: number;
8
11
  override get name(): 'TimeoutError' {
9
12
  return 'TimeoutError';
10
13
  }
11
14
 
12
- constructor(message: string, options?: { cause?: Error }) {
15
+ constructor(message: string, options: { cause?: Error; duration: number }) {
13
16
  super(message, options);
17
+ this.duration = options.duration;
14
18
  }
15
19
 
16
20
  static is(error: unknown): error is TimeoutError {
@@ -29,25 +33,38 @@ type Reject = Parameters<ConstructorParameters<typeof Promise<never>>[0]>[1];
29
33
  * if interacted with exclusively through its public API
30
34
  * */
31
35
  export class Timeout extends Promise<never> {
32
- get [Symbol.toStringTag](): 'MongoDBTimeout' {
33
- return 'MongoDBTimeout';
34
- }
35
-
36
36
  private id?: NodeJS.Timeout;
37
37
 
38
38
  public readonly start: number;
39
39
  public ended: number | null = null;
40
40
  public duration: number;
41
- public timedOut = false;
41
+ private timedOut = false;
42
+ public cleared = false;
43
+
44
+ get remainingTime(): number {
45
+ if (this.timedOut) return 0;
46
+ if (this.duration === 0) return Infinity;
47
+ return this.start + this.duration - Math.trunc(performance.now());
48
+ }
49
+
50
+ get timeElapsed(): number {
51
+ return Math.trunc(performance.now()) - this.start;
52
+ }
42
53
 
43
54
  /** Create a new timeout that expires in `duration` ms */
44
- private constructor(executor: Executor = () => null, duration: number, unref = false) {
45
- let reject!: Reject;
55
+ private constructor(
56
+ executor: Executor = () => null,
57
+ options?: { duration: number; unref?: true; rejection?: Error }
58
+ ) {
59
+ const duration = options?.duration ?? 0;
60
+ const unref = !!options?.unref;
61
+ const rejection = options?.rejection;
46
62
 
47
63
  if (duration < 0) {
48
64
  throw new MongoInvalidArgumentError('Cannot create a Timeout with a negative duration');
49
65
  }
50
66
 
67
+ let reject!: Reject;
51
68
  super((_, promiseReject) => {
52
69
  reject = promiseReject;
53
70
 
@@ -57,16 +74,20 @@ export class Timeout extends Promise<never> {
57
74
  this.duration = duration;
58
75
  this.start = Math.trunc(performance.now());
59
76
 
60
- if (this.duration > 0) {
77
+ if (rejection == null && this.duration > 0) {
61
78
  this.id = setTimeout(() => {
62
79
  this.ended = Math.trunc(performance.now());
63
80
  this.timedOut = true;
64
- reject(new TimeoutError(`Expired after ${duration}ms`));
81
+ reject(new TimeoutError(`Expired after ${duration}ms`, { duration }));
65
82
  }, this.duration);
66
83
  if (typeof this.id.unref === 'function' && unref) {
67
84
  // Ensure we do not keep the Node.js event loop running
68
85
  this.id.unref();
69
86
  }
87
+ } else if (rejection != null) {
88
+ this.ended = Math.trunc(performance.now());
89
+ this.timedOut = true;
90
+ reject(rejection);
70
91
  }
71
92
  }
72
93
 
@@ -76,20 +97,303 @@ export class Timeout extends Promise<never> {
76
97
  clear(): void {
77
98
  clearTimeout(this.id);
78
99
  this.id = undefined;
100
+ this.timedOut = false;
101
+ this.cleared = true;
79
102
  }
80
103
 
81
- public static expires(durationMS: number, unref?: boolean): Timeout {
82
- return new Timeout(undefined, durationMS, unref);
104
+ throwIfExpired(): void {
105
+ if (this.timedOut) throw new TimeoutError('Timed out', { duration: this.duration });
83
106
  }
84
107
 
85
- static is(timeout: unknown): timeout is Timeout {
86
- return (
87
- typeof timeout === 'object' &&
88
- timeout != null &&
89
- Symbol.toStringTag in timeout &&
90
- timeout[Symbol.toStringTag] === 'MongoDBTimeout' &&
91
- 'then' in timeout &&
92
- typeof timeout.then === 'function'
93
- );
108
+ public static expires(duration: number, unref?: true): Timeout {
109
+ return new Timeout(undefined, { duration, unref });
110
+ }
111
+
112
+ static override reject(rejection?: Error): Timeout {
113
+ return new Timeout(undefined, { duration: 0, unref: true, rejection });
114
+ }
115
+ }
116
+
117
+ /** @internal */
118
+ export type TimeoutContextOptions = (LegacyTimeoutContextOptions | CSOTTimeoutContextOptions) & {
119
+ session?: ClientSession;
120
+ };
121
+
122
+ /** @internal */
123
+ export type LegacyTimeoutContextOptions = {
124
+ serverSelectionTimeoutMS: number;
125
+ waitQueueTimeoutMS: number;
126
+ socketTimeoutMS?: number;
127
+ };
128
+
129
+ /** @internal */
130
+ export type CSOTTimeoutContextOptions = {
131
+ timeoutMS: number;
132
+ serverSelectionTimeoutMS: number;
133
+ socketTimeoutMS?: number;
134
+ };
135
+
136
+ function isLegacyTimeoutContextOptions(v: unknown): v is LegacyTimeoutContextOptions {
137
+ return (
138
+ v != null &&
139
+ typeof v === 'object' &&
140
+ 'serverSelectionTimeoutMS' in v &&
141
+ typeof v.serverSelectionTimeoutMS === 'number' &&
142
+ 'waitQueueTimeoutMS' in v &&
143
+ typeof v.waitQueueTimeoutMS === 'number'
144
+ );
145
+ }
146
+
147
+ function isCSOTTimeoutContextOptions(v: unknown): v is CSOTTimeoutContextOptions {
148
+ return (
149
+ v != null &&
150
+ typeof v === 'object' &&
151
+ 'serverSelectionTimeoutMS' in v &&
152
+ typeof v.serverSelectionTimeoutMS === 'number' &&
153
+ 'timeoutMS' in v &&
154
+ typeof v.timeoutMS === 'number'
155
+ );
156
+ }
157
+
158
+ /** @internal */
159
+ export abstract class TimeoutContext {
160
+ static create(options: TimeoutContextOptions): TimeoutContext {
161
+ if (options.session?.timeoutContext != null) return options.session?.timeoutContext;
162
+ if (isCSOTTimeoutContextOptions(options)) return new CSOTTimeoutContext(options);
163
+ else if (isLegacyTimeoutContextOptions(options)) return new LegacyTimeoutContext(options);
164
+ else throw new MongoRuntimeError('Unrecognized options');
165
+ }
166
+
167
+ abstract get maxTimeMS(): number | null;
168
+
169
+ abstract get serverSelectionTimeout(): Timeout | null;
170
+
171
+ abstract get connectionCheckoutTimeout(): Timeout | null;
172
+
173
+ abstract get clearServerSelectionTimeout(): boolean;
174
+
175
+ abstract get timeoutForSocketWrite(): Timeout | null;
176
+
177
+ abstract get timeoutForSocketRead(): Timeout | null;
178
+
179
+ abstract csotEnabled(): this is CSOTTimeoutContext;
180
+
181
+ abstract refresh(): void;
182
+
183
+ abstract clear(): void;
184
+
185
+ /** Returns a new instance of the TimeoutContext, with all timeouts refreshed and restarted. */
186
+ abstract refreshed(): TimeoutContext;
187
+
188
+ abstract addMaxTimeMSToCommand(command: Document, options: { omitMaxTimeMS?: boolean }): void;
189
+
190
+ abstract getSocketTimeoutMS(): number | undefined;
191
+ }
192
+
193
+ /** @internal */
194
+ export class CSOTTimeoutContext extends TimeoutContext {
195
+ timeoutMS: number;
196
+ serverSelectionTimeoutMS: number;
197
+ socketTimeoutMS?: number;
198
+
199
+ clearServerSelectionTimeout: boolean;
200
+
201
+ private _serverSelectionTimeout?: Timeout | null;
202
+ private _connectionCheckoutTimeout?: Timeout | null;
203
+ public minRoundTripTime = 0;
204
+ public start: number;
205
+
206
+ constructor(options: CSOTTimeoutContextOptions) {
207
+ super();
208
+ this.start = Math.trunc(performance.now());
209
+
210
+ this.timeoutMS = options.timeoutMS;
211
+
212
+ this.serverSelectionTimeoutMS = options.serverSelectionTimeoutMS;
213
+
214
+ this.socketTimeoutMS = options.socketTimeoutMS;
215
+
216
+ this.clearServerSelectionTimeout = false;
217
+ }
218
+
219
+ get maxTimeMS(): number {
220
+ return this.remainingTimeMS - this.minRoundTripTime;
221
+ }
222
+
223
+ get remainingTimeMS() {
224
+ const timePassed = Math.trunc(performance.now()) - this.start;
225
+ return this.timeoutMS <= 0 ? Infinity : this.timeoutMS - timePassed;
226
+ }
227
+
228
+ csotEnabled(): this is CSOTTimeoutContext {
229
+ return true;
230
+ }
231
+
232
+ get serverSelectionTimeout(): Timeout | null {
233
+ // check for undefined
234
+ if (typeof this._serverSelectionTimeout !== 'object' || this._serverSelectionTimeout?.cleared) {
235
+ const { remainingTimeMS, serverSelectionTimeoutMS } = this;
236
+ if (remainingTimeMS <= 0)
237
+ return Timeout.reject(
238
+ new MongoOperationTimeoutError(`Timed out in server selection after ${this.timeoutMS}ms`)
239
+ );
240
+ const usingServerSelectionTimeoutMS =
241
+ serverSelectionTimeoutMS !== 0 &&
242
+ csotMin(remainingTimeMS, serverSelectionTimeoutMS) === serverSelectionTimeoutMS;
243
+ if (usingServerSelectionTimeoutMS) {
244
+ this._serverSelectionTimeout = Timeout.expires(serverSelectionTimeoutMS);
245
+ } else {
246
+ if (remainingTimeMS > 0 && Number.isFinite(remainingTimeMS)) {
247
+ this._serverSelectionTimeout = Timeout.expires(remainingTimeMS);
248
+ } else {
249
+ this._serverSelectionTimeout = null;
250
+ }
251
+ }
252
+ }
253
+
254
+ return this._serverSelectionTimeout;
255
+ }
256
+
257
+ get connectionCheckoutTimeout(): Timeout | null {
258
+ if (
259
+ typeof this._connectionCheckoutTimeout !== 'object' ||
260
+ this._connectionCheckoutTimeout?.cleared
261
+ ) {
262
+ if (typeof this._serverSelectionTimeout === 'object') {
263
+ // null or Timeout
264
+ this._connectionCheckoutTimeout = this._serverSelectionTimeout;
265
+ } else {
266
+ throw new MongoRuntimeError(
267
+ 'Unreachable. If you are seeing this error, please file a ticket on the NODE driver project on Jira'
268
+ );
269
+ }
270
+ }
271
+ return this._connectionCheckoutTimeout;
272
+ }
273
+
274
+ get timeoutForSocketWrite(): Timeout | null {
275
+ const { remainingTimeMS } = this;
276
+ if (!Number.isFinite(remainingTimeMS)) return null;
277
+ if (remainingTimeMS > 0) return Timeout.expires(remainingTimeMS);
278
+ return Timeout.reject(new MongoOperationTimeoutError('Timed out before socket write'));
279
+ }
280
+
281
+ get timeoutForSocketRead(): Timeout | null {
282
+ const { remainingTimeMS } = this;
283
+ if (!Number.isFinite(remainingTimeMS)) return null;
284
+ if (remainingTimeMS > 0) return Timeout.expires(remainingTimeMS);
285
+ return Timeout.reject(new MongoOperationTimeoutError('Timed out before socket read'));
286
+ }
287
+
288
+ refresh(): void {
289
+ this.start = Math.trunc(performance.now());
290
+ this.minRoundTripTime = 0;
291
+ this._serverSelectionTimeout?.clear();
292
+ this._connectionCheckoutTimeout?.clear();
293
+ }
294
+
295
+ clear(): void {
296
+ this._serverSelectionTimeout?.clear();
297
+ this._connectionCheckoutTimeout?.clear();
298
+ }
299
+
300
+ /**
301
+ * @internal
302
+ * Throws a MongoOperationTimeoutError if the context has expired.
303
+ * If the context has not expired, returns the `remainingTimeMS`
304
+ **/
305
+ getRemainingTimeMSOrThrow(message?: string): number {
306
+ const { remainingTimeMS } = this;
307
+ if (remainingTimeMS <= 0)
308
+ throw new MongoOperationTimeoutError(message ?? `Expired after ${this.timeoutMS}ms`);
309
+ return remainingTimeMS;
310
+ }
311
+
312
+ /**
313
+ * @internal
314
+ * This method is intended to be used in situations where concurrent operation are on the same deadline, but cannot share a single `TimeoutContext` instance.
315
+ * Returns a new instance of `CSOTTimeoutContext` constructed with identical options, but setting the `start` property to `this.start`.
316
+ */
317
+ clone(): CSOTTimeoutContext {
318
+ const timeoutContext = new CSOTTimeoutContext({
319
+ timeoutMS: this.timeoutMS,
320
+ serverSelectionTimeoutMS: this.serverSelectionTimeoutMS
321
+ });
322
+ timeoutContext.start = this.start;
323
+ return timeoutContext;
324
+ }
325
+
326
+ override refreshed(): CSOTTimeoutContext {
327
+ return new CSOTTimeoutContext(this);
328
+ }
329
+
330
+ override addMaxTimeMSToCommand(command: Document, options: { omitMaxTimeMS?: boolean }): void {
331
+ if (options.omitMaxTimeMS) return;
332
+ const maxTimeMS = this.remainingTimeMS - this.minRoundTripTime;
333
+ if (maxTimeMS > 0 && Number.isFinite(maxTimeMS)) command.maxTimeMS = maxTimeMS;
334
+ }
335
+
336
+ override getSocketTimeoutMS(): number | undefined {
337
+ return 0;
338
+ }
339
+ }
340
+
341
+ /** @internal */
342
+ export class LegacyTimeoutContext extends TimeoutContext {
343
+ options: LegacyTimeoutContextOptions;
344
+ clearServerSelectionTimeout: boolean;
345
+
346
+ constructor(options: LegacyTimeoutContextOptions) {
347
+ super();
348
+ this.options = options;
349
+ this.clearServerSelectionTimeout = true;
350
+ }
351
+
352
+ csotEnabled(): this is CSOTTimeoutContext {
353
+ return false;
354
+ }
355
+
356
+ get serverSelectionTimeout(): Timeout | null {
357
+ if (this.options.serverSelectionTimeoutMS != null && this.options.serverSelectionTimeoutMS > 0)
358
+ return Timeout.expires(this.options.serverSelectionTimeoutMS);
359
+ return null;
360
+ }
361
+
362
+ get connectionCheckoutTimeout(): Timeout | null {
363
+ if (this.options.waitQueueTimeoutMS != null && this.options.waitQueueTimeoutMS > 0)
364
+ return Timeout.expires(this.options.waitQueueTimeoutMS);
365
+ return null;
366
+ }
367
+
368
+ get timeoutForSocketWrite(): Timeout | null {
369
+ return null;
370
+ }
371
+
372
+ get timeoutForSocketRead(): Timeout | null {
373
+ return null;
374
+ }
375
+
376
+ refresh(): void {
377
+ return;
378
+ }
379
+
380
+ clear(): void {
381
+ return;
382
+ }
383
+
384
+ get maxTimeMS() {
385
+ return null;
386
+ }
387
+
388
+ override refreshed(): LegacyTimeoutContext {
389
+ return new LegacyTimeoutContext(this.options);
390
+ }
391
+
392
+ override addMaxTimeMSToCommand(_command: Document, _options: { omitMaxTimeMS?: boolean }): void {
393
+ // No max timeMS is added to commands in legacy timeout mode.
394
+ }
395
+
396
+ override getSocketTimeoutMS(): number | undefined {
397
+ return this.options.socketTimeoutMS;
94
398
  }
95
399
  }
@@ -60,7 +60,7 @@ const COMMITTED_STATES: Set<TxnState> = new Set([
60
60
  * Configuration options for a transaction.
61
61
  * @public
62
62
  */
63
- export interface TransactionOptions extends CommandOperationOptions {
63
+ export interface TransactionOptions extends Omit<CommandOperationOptions, 'timeoutMS'> {
64
64
  // TODO(NODE-3344): These options use the proper class forms of these settings, it should accept the basic enum values too
65
65
  /** A default read concern for commands in this transaction */
66
66
  readConcern?: ReadConcernLike;
package/src/utils.ts CHANGED
@@ -26,7 +26,6 @@ import {
26
26
  MongoParseError,
27
27
  MongoRuntimeError
28
28
  } from './error';
29
- import type { Explain, ExplainVerbosity } from './explain';
30
29
  import type { MongoClient } from './mongo_client';
31
30
  import type { CommandOperationOptions, OperationParent } from './operations/command';
32
31
  import type { Hint, OperationOptions } from './operations/operation';
@@ -36,6 +35,7 @@ import { ServerType } from './sdam/common';
36
35
  import type { Server } from './sdam/server';
37
36
  import type { Topology } from './sdam/topology';
38
37
  import type { ClientSession } from './sessions';
38
+ import { type TimeoutContextOptions } from './timeout';
39
39
  import { WriteConcern } from './write_concern';
40
40
 
41
41
  /**
@@ -245,32 +245,6 @@ export function decorateWithReadConcern(
245
245
  }
246
246
  }
247
247
 
248
- /**
249
- * Applies an explain to a given command.
250
- * @internal
251
- *
252
- * @param command - the command on which to apply the explain
253
- * @param options - the options containing the explain verbosity
254
- */
255
- export function decorateWithExplain(
256
- command: Document,
257
- explain: Explain
258
- ): {
259
- explain: Document;
260
- verbosity: ExplainVerbosity;
261
- maxTimeMS?: number;
262
- } {
263
- type ExplainCommand = ReturnType<typeof decorateWithExplain>;
264
- const { verbosity, maxTimeMS } = explain;
265
- const baseCommand: ExplainCommand = { explain: command, verbosity };
266
-
267
- if (typeof maxTimeMS === 'number') {
268
- baseCommand.maxTimeMS = maxTimeMS;
269
- }
270
-
271
- return baseCommand;
272
- }
273
-
274
248
  /**
275
249
  * @internal
276
250
  */
@@ -515,9 +489,25 @@ export function hasAtomicOperators(doc: Document | Document[]): boolean {
515
489
  return keys.length > 0 && keys[0][0] === '$';
516
490
  }
517
491
 
492
+ export function resolveTimeoutOptions<T extends Partial<TimeoutContextOptions>>(
493
+ client: MongoClient,
494
+ options: T
495
+ ): T &
496
+ Pick<
497
+ MongoClient['s']['options'],
498
+ 'timeoutMS' | 'serverSelectionTimeoutMS' | 'waitQueueTimeoutMS' | 'socketTimeoutMS'
499
+ > {
500
+ const { socketTimeoutMS, serverSelectionTimeoutMS, waitQueueTimeoutMS, timeoutMS } =
501
+ client.s.options;
502
+ return { socketTimeoutMS, serverSelectionTimeoutMS, waitQueueTimeoutMS, timeoutMS, ...options };
503
+ }
518
504
  /**
519
505
  * Merge inherited properties from parent into options, prioritizing values from options,
520
506
  * then values from parent.
507
+ *
508
+ * @param parent - An optional owning class of the operation being run. ex. Db/Collection/MongoClient.
509
+ * @param options - The options passed to the operation method.
510
+ *
521
511
  * @internal
522
512
  */
523
513
  export function resolveOptions<T extends CommandOperationOptions>(
@@ -526,25 +516,43 @@ export function resolveOptions<T extends CommandOperationOptions>(
526
516
  ): T {
527
517
  const result: T = Object.assign({}, options, resolveBSONOptions(options, parent));
528
518
 
519
+ const timeoutMS = options?.timeoutMS ?? parent?.timeoutMS;
529
520
  // Users cannot pass a readConcern/writeConcern to operations in a transaction
530
521
  const session = options?.session;
522
+
531
523
  if (!session?.inTransaction()) {
532
524
  const readConcern = ReadConcern.fromOptions(options) ?? parent?.readConcern;
533
525
  if (readConcern) {
534
526
  result.readConcern = readConcern;
535
527
  }
536
528
 
537
- const writeConcern = WriteConcern.fromOptions(options) ?? parent?.writeConcern;
529
+ let writeConcern = WriteConcern.fromOptions(options) ?? parent?.writeConcern;
538
530
  if (writeConcern) {
531
+ if (timeoutMS != null) {
532
+ writeConcern = WriteConcern.fromOptions({
533
+ ...writeConcern,
534
+ wtimeout: undefined,
535
+ wtimeoutMS: undefined
536
+ });
537
+ }
539
538
  result.writeConcern = writeConcern;
540
539
  }
541
540
  }
542
541
 
542
+ result.timeoutMS = timeoutMS;
543
+
543
544
  const readPreference = ReadPreference.fromOptions(options) ?? parent?.readPreference;
544
545
  if (readPreference) {
545
546
  result.readPreference = readPreference;
546
547
  }
547
548
 
549
+ const isConvenientTransaction = session?.explicit && session?.timeoutContext != null;
550
+ if (isConvenientTransaction && options?.timeoutMS != null) {
551
+ throw new MongoInvalidArgumentError(
552
+ 'An operation cannot be given a timeoutMS setting when inside a withTransaction call that has a timeoutMS setting'
553
+ );
554
+ }
555
+
548
556
  return result;
549
557
  }
550
558
 
@@ -1097,8 +1105,11 @@ export function shuffle<T>(sequence: Iterable<T>, limit = 0): Array<T> {
1097
1105
  return limit % items.length === 0 ? items : items.slice(lowerBound);
1098
1106
  }
1099
1107
 
1100
- // TODO(NODE-4936): read concern eligibility for commands should be codified in command construction
1101
- // @see https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.rst#read-concern
1108
+ /**
1109
+ * TODO(NODE-4936): read concern eligibility for commands should be codified in command construction
1110
+ * @internal
1111
+ * @see https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.md#read-concern
1112
+ */
1102
1113
  export function commandSupportsReadConcern(command: Document): boolean {
1103
1114
  if (command.aggregate || command.count || command.distinct || command.find || command.geoNear) {
1104
1115
  return true;
@@ -1398,6 +1409,12 @@ export async function fileIsAccessible(fileName: string, mode?: number) {
1398
1409
  }
1399
1410
  }
1400
1411
 
1412
+ export function csotMin(duration1: number, duration2: number): number {
1413
+ if (duration1 === 0) return duration2;
1414
+ if (duration2 === 0) return duration1;
1415
+ return Math.min(duration1, duration2);
1416
+ }
1417
+
1401
1418
  export function noop() {
1402
1419
  return;
1403
1420
  }
@@ -15,7 +15,9 @@ export interface WriteConcernOptions {
15
15
  export interface WriteConcernSettings {
16
16
  /** The write concern */
17
17
  w?: W;
18
- /** The write concern timeout */
18
+ /**
19
+ * The write concern timeout.
20
+ */
19
21
  wtimeoutMS?: number;
20
22
  /** The journal write concern */
21
23
  journal?: boolean;
@@ -28,7 +30,6 @@ export interface WriteConcernSettings {
28
30
  j?: boolean;
29
31
  /**
30
32
  * The write concern timeout.
31
- * @deprecated Will be removed in the next major version. Please use the wtimeoutMS option.
32
33
  */
33
34
  wtimeout?: number;
34
35
  /**
@@ -65,7 +66,9 @@ export class WriteConcern {
65
66
  readonly w?: W;
66
67
  /** Request acknowledgment that the write operation has been written to the on-disk journal */
67
68
  readonly journal?: boolean;
68
- /** Specify a time limit to prevent write operations from blocking indefinitely */
69
+ /**
70
+ * Specify a time limit to prevent write operations from blocking indefinitely.
71
+ */
69
72
  readonly wtimeoutMS?: number;
70
73
  /**
71
74
  * Specify a time limit to prevent write operations from blocking indefinitely.