mongodb 7.0.0 → 7.1.0-dev.20260205.sha.d2ad07f2

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 (104) hide show
  1. package/lib/bson.js +2 -1
  2. package/lib/bson.js.map +1 -1
  3. package/lib/change_stream.js +3 -1
  4. package/lib/change_stream.js.map +1 -1
  5. package/lib/client-side-encryption/state_machine.js +1 -0
  6. package/lib/client-side-encryption/state_machine.js.map +1 -1
  7. package/lib/cmap/auth/aws4.js +161 -0
  8. package/lib/cmap/auth/aws4.js.map +1 -0
  9. package/lib/cmap/auth/aws_temporary_credentials.js +1 -0
  10. package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -1
  11. package/lib/cmap/auth/gssapi.js +2 -4
  12. package/lib/cmap/auth/gssapi.js.map +1 -1
  13. package/lib/cmap/auth/mongo_credentials.js +2 -1
  14. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  15. package/lib/cmap/auth/mongodb_aws.js +9 -14
  16. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  17. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +1 -0
  18. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js.map +1 -1
  19. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js +1 -0
  20. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js.map +1 -1
  21. package/lib/cmap/connect.js +15 -0
  22. package/lib/cmap/connect.js.map +1 -1
  23. package/lib/cmap/connection.js +3 -3
  24. package/lib/cmap/connection.js.map +1 -1
  25. package/lib/cmap/connection_pool.js +8 -8
  26. package/lib/cmap/connection_pool.js.map +1 -1
  27. package/lib/cmap/connection_pool_events.js +3 -3
  28. package/lib/cmap/connection_pool_events.js.map +1 -1
  29. package/lib/cmap/handshake/client_metadata.js +7 -7
  30. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  31. package/lib/cmap/wire_protocol/compression.js +18 -3
  32. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  33. package/lib/connection_string.js +6 -0
  34. package/lib/connection_string.js.map +1 -1
  35. package/lib/db.js +0 -1
  36. package/lib/db.js.map +1 -1
  37. package/lib/deps.js +0 -13
  38. package/lib/deps.js.map +1 -1
  39. package/lib/error.js +5 -8
  40. package/lib/error.js.map +1 -1
  41. package/lib/gridfs/upload.js +8 -8
  42. package/lib/gridfs/upload.js.map +1 -1
  43. package/lib/mongo_client.js +1 -1
  44. package/lib/mongo_client.js.map +1 -1
  45. package/lib/mongo_logger.js +11 -5
  46. package/lib/mongo_logger.js.map +1 -1
  47. package/lib/operations/execute_operation.js +5 -4
  48. package/lib/operations/execute_operation.js.map +1 -1
  49. package/lib/operations/rename.js.map +1 -1
  50. package/lib/runtime_adapters.js +16 -0
  51. package/lib/runtime_adapters.js.map +1 -0
  52. package/lib/sdam/monitor.js +8 -8
  53. package/lib/sdam/monitor.js.map +1 -1
  54. package/lib/sdam/server.js +41 -29
  55. package/lib/sdam/server.js.map +1 -1
  56. package/lib/sdam/server_description.js +1 -1
  57. package/lib/sdam/server_description.js.map +1 -1
  58. package/lib/sdam/server_selection.js +135 -72
  59. package/lib/sdam/server_selection.js.map +1 -1
  60. package/lib/sdam/topology.js +9 -7
  61. package/lib/sdam/topology.js.map +1 -1
  62. package/lib/sdam/topology_description.js +1 -1
  63. package/lib/sdam/topology_description.js.map +1 -1
  64. package/lib/sessions.js +87 -23
  65. package/lib/sessions.js.map +1 -1
  66. package/lib/utils.js +21 -47
  67. package/lib/utils.js.map +1 -1
  68. package/mongodb.d.ts +39 -2
  69. package/package.json +10 -12
  70. package/src/bson.ts +1 -0
  71. package/src/change_stream.ts +3 -1
  72. package/src/client-side-encryption/state_machine.ts +1 -0
  73. package/src/cmap/auth/aws4.ts +207 -0
  74. package/src/cmap/auth/aws_temporary_credentials.ts +2 -0
  75. package/src/cmap/auth/gssapi.ts +8 -4
  76. package/src/cmap/auth/mongo_credentials.ts +2 -1
  77. package/src/cmap/auth/mongodb_aws.ts +10 -17
  78. package/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +1 -0
  79. package/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts +1 -0
  80. package/src/cmap/connect.ts +19 -0
  81. package/src/cmap/connection.ts +7 -4
  82. package/src/cmap/connection_pool.ts +9 -9
  83. package/src/cmap/connection_pool_events.ts +4 -4
  84. package/src/cmap/handshake/client_metadata.ts +10 -9
  85. package/src/cmap/wire_protocol/compression.ts +17 -3
  86. package/src/connection_string.ts +7 -0
  87. package/src/db.ts +0 -1
  88. package/src/deps.ts +0 -60
  89. package/src/error.ts +4 -8
  90. package/src/gridfs/upload.ts +8 -8
  91. package/src/index.ts +2 -1
  92. package/src/mongo_client.ts +12 -2
  93. package/src/mongo_logger.ts +11 -6
  94. package/src/operations/execute_operation.ts +6 -5
  95. package/src/operations/rename.ts +6 -1
  96. package/src/runtime_adapters.ts +49 -0
  97. package/src/sdam/monitor.ts +10 -10
  98. package/src/sdam/server.ts +50 -32
  99. package/src/sdam/server_description.ts +8 -2
  100. package/src/sdam/server_selection.ts +188 -96
  101. package/src/sdam/topology.ts +20 -16
  102. package/src/sdam/topology_description.ts +1 -1
  103. package/src/sessions.ts +114 -30
  104. package/src/utils.ts +17 -74
@@ -36,7 +36,7 @@ import {
36
36
  List,
37
37
  makeCounter,
38
38
  noop,
39
- now,
39
+ processTimeMS,
40
40
  promiseWithResolvers
41
41
  } from '../utils';
42
42
  import { connect } from './connect';
@@ -231,7 +231,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
231
231
  this.mongoLogger = this.server.topology.client?.mongoLogger;
232
232
  this.component = 'connection';
233
233
 
234
- process.nextTick(() => {
234
+ queueMicrotask(() => {
235
235
  this.emitAndLog(ConnectionPool.CONNECTION_POOL_CREATED, new ConnectionPoolCreatedEvent(this));
236
236
  });
237
237
  }
@@ -319,7 +319,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
319
319
  * explicitly destroyed by the new owner.
320
320
  */
321
321
  async checkOut(options: { timeoutContext: TimeoutContext } & Abortable): Promise<Connection> {
322
- const checkoutTime = now();
322
+ const checkoutTime = processTimeMS();
323
323
  this.emitAndLog(
324
324
  ConnectionPool.CONNECTION_CHECK_OUT_STARTED,
325
325
  new ConnectionCheckOutStartedEvent(this)
@@ -342,7 +342,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
342
342
  });
343
343
 
344
344
  this.waitQueue.push(waitQueueMember);
345
- process.nextTick(() => this.processWaitQueue());
345
+ queueMicrotask(() => this.processWaitQueue());
346
346
 
347
347
  try {
348
348
  timeout?.throwIfExpired();
@@ -405,7 +405,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
405
405
  this.destroyConnection(connection, reason);
406
406
  }
407
407
 
408
- process.nextTick(() => this.processWaitQueue());
408
+ queueMicrotask(() => this.processWaitQueue());
409
409
  }
410
410
 
411
411
  /**
@@ -461,7 +461,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
461
461
  }
462
462
 
463
463
  if (interruptInUseConnections) {
464
- process.nextTick(() => this.interruptInUseConnections(oldGeneration));
464
+ queueMicrotask(() => this.interruptInUseConnections(oldGeneration));
465
465
  }
466
466
 
467
467
  this.processWaitQueue();
@@ -616,7 +616,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
616
616
 
617
617
  this.pending++;
618
618
  // This is our version of a "virtual" no-I/O connection as the spec requires
619
- const connectionCreatedTime = now();
619
+ const connectionCreatedTime = processTimeMS();
620
620
  this.emitAndLog(
621
621
  ConnectionPool.CONNECTION_CREATED,
622
622
  new ConnectionCreatedEvent(this, { id: connectOptions.id })
@@ -702,7 +702,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
702
702
  this.createConnection((err, connection) => {
703
703
  if (!err && connection) {
704
704
  this.connections.push(connection);
705
- process.nextTick(() => this.processWaitQueue());
705
+ queueMicrotask(() => this.processWaitQueue());
706
706
  }
707
707
  if (this.poolState === PoolState.ready) {
708
708
  clearTimeout(this.minPoolSizeTimer);
@@ -809,7 +809,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
809
809
  waitQueueMember.resolve(connection);
810
810
  }
811
811
  }
812
- process.nextTick(() => this.processWaitQueue());
812
+ queueMicrotask(() => this.processWaitQueue());
813
813
  });
814
814
  }
815
815
  this.processingWaitQueue = false;
@@ -13,7 +13,7 @@ import {
13
13
  CONNECTION_READY
14
14
  } from '../constants';
15
15
  import type { MongoError } from '../error';
16
- import { now } from '../utils';
16
+ import { processTimeMS } from '../utils';
17
17
  import type { Connection } from './connection';
18
18
  import type { ConnectionPool, ConnectionPoolOptions } from './connection_pool';
19
19
 
@@ -145,7 +145,7 @@ export class ConnectionReadyEvent extends ConnectionPoolMonitoringEvent {
145
145
  /** @internal */
146
146
  constructor(pool: ConnectionPool, connection: Connection, connectionCreatedEventTime: number) {
147
147
  super(pool);
148
- this.durationMS = now() - connectionCreatedEventTime;
148
+ this.durationMS = processTimeMS() - connectionCreatedEventTime;
149
149
  this.connectionId = connection.id;
150
150
  }
151
151
  }
@@ -224,7 +224,7 @@ export class ConnectionCheckOutFailedEvent extends ConnectionPoolMonitoringEvent
224
224
  error?: MongoError
225
225
  ) {
226
226
  super(pool);
227
- this.durationMS = now() - checkoutTime;
227
+ this.durationMS = processTimeMS() - checkoutTime;
228
228
  this.reason = reason;
229
229
  this.error = error;
230
230
  }
@@ -252,7 +252,7 @@ export class ConnectionCheckedOutEvent extends ConnectionPoolMonitoringEvent {
252
252
  /** @internal */
253
253
  constructor(pool: ConnectionPool, connection: Connection, checkoutTime: number) {
254
254
  super(pool);
255
- this.durationMS = now() - checkoutTime;
255
+ this.durationMS = processTimeMS() - checkoutTime;
256
256
  this.connectionId = connection.id;
257
257
  }
258
258
  }
@@ -1,7 +1,6 @@
1
- import * as os from 'os';
2
1
  import * as process from 'process';
3
2
 
4
- import { BSON, type Document, Int32 } from '../../bson';
3
+ import { BSON, type Document, Int32, NumberUtils } from '../../bson';
5
4
  import { MongoInvalidArgumentError } from '../../error';
6
5
  import type { DriverInfo, MongoOptions } from '../../mongo_client';
7
6
  import { fileIsAccessible } from '../../utils';
@@ -96,7 +95,8 @@ export class LimitedSizeDocument {
96
95
  }
97
96
  }
98
97
 
99
- type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
98
+ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'runtime'>;
99
+
100
100
  /**
101
101
  * From the specs:
102
102
  * Implementors SHOULD cumulatively update fields in the following order until the document is under the size limit:
@@ -107,7 +107,7 @@ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
107
107
  */
108
108
  export async function makeClientMetadata(
109
109
  driverInfoList: DriverInfo[],
110
- { appName = '' }: MakeClientMetadataOptions
110
+ { appName = '', runtime: { os } }: MakeClientMetadataOptions
111
111
  ): Promise<ClientMetadata> {
112
112
  const metadataDocument = new LimitedSizeDocument(512);
113
113
 
@@ -157,8 +157,8 @@ export async function makeClientMetadata(
157
157
 
158
158
  // Note: order matters, os.type is last so it will be removed last if we're at maxSize
159
159
  const osInfo = new Map()
160
- .set('name', process.platform)
161
- .set('architecture', process.arch)
160
+ .set('name', os.platform())
161
+ .set('architecture', os.arch())
162
162
  .set('version', os.release())
163
163
  .set('type', os.type());
164
164
 
@@ -336,17 +336,18 @@ declare const Bun: { (): void; version?: string } | undefined;
336
336
  * with a future change to these global objects.
337
337
  */
338
338
  function getRuntimeInfo(): string {
339
+ const endianness = NumberUtils.isBigEndian ? 'BE' : 'LE';
339
340
  if ('Deno' in globalThis) {
340
341
  const version = typeof Deno?.version?.deno === 'string' ? Deno?.version?.deno : '0.0.0-unknown';
341
342
 
342
- return `Deno v${version}, ${os.endianness()}`;
343
+ return `Deno v${version}, ${endianness}`;
343
344
  }
344
345
 
345
346
  if ('Bun' in globalThis) {
346
347
  const version = typeof Bun?.version === 'string' ? Bun?.version : '0.0.0-unknown';
347
348
 
348
- return `Bun v${version}, ${os.endianness()}`;
349
+ return `Bun v${version}, ${endianness}`;
349
350
  }
350
351
 
351
- return `Node.js ${process.version}, ${os.endianness()}`;
352
+ return `Node.js ${process.version}, ${endianness}`;
352
353
  }
@@ -1,4 +1,3 @@
1
- import { promisify } from 'util';
2
1
  import * as zlib from 'zlib';
3
2
 
4
3
  import { LEGACY_HELLO_COMMAND } from '../../constants';
@@ -43,8 +42,23 @@ export const uncompressibleCommands = new Set([
43
42
 
44
43
  const ZSTD_COMPRESSION_LEVEL = 3;
45
44
 
46
- const zlibInflate = promisify(zlib.inflate.bind(zlib));
47
- const zlibDeflate = promisify(zlib.deflate.bind(zlib));
45
+ const zlibInflate = (buf: zlib.InputType) => {
46
+ return new Promise<Buffer>((resolve, reject) => {
47
+ zlib.inflate(buf, (error, result) => {
48
+ if (error) return reject(error);
49
+ resolve(result);
50
+ });
51
+ });
52
+ };
53
+
54
+ const zlibDeflate = (buf: zlib.InputType, options: zlib.ZlibOptions) => {
55
+ return new Promise<Buffer>((resolve, reject) => {
56
+ zlib.deflate(buf, options, (error, result) => {
57
+ if (error) return reject(error);
58
+ resolve(result);
59
+ });
60
+ });
61
+ };
48
62
 
49
63
  let zstd: ZStandard;
50
64
  let Snappy: SnappyLib | null = null;
@@ -1,5 +1,6 @@
1
1
  import * as dns from 'dns';
2
2
  import ConnectionString from 'mongodb-connection-string-url';
3
+ import * as process from 'process';
3
4
  import { URLSearchParams } from 'url';
4
5
 
5
6
  import type { Document } from './bson';
@@ -19,6 +20,7 @@ import {
19
20
  import { MongoLoggableComponent, MongoLogger, SeverityLevel } from './mongo_logger';
20
21
  import { ReadConcern, type ReadConcernLevel } from './read_concern';
21
22
  import { ReadPreference, type ReadPreferenceMode } from './read_preference';
23
+ import { resolveRuntimeAdapters } from './runtime_adapters';
22
24
  import { ServerMonitoringMode } from './sdam/monitor';
23
25
  import type { TagSet } from './sdam/server_description';
24
26
  import {
@@ -537,6 +539,8 @@ export function parseOptions(
537
539
  }
538
540
  );
539
541
 
542
+ mongoOptions.runtime = resolveRuntimeAdapters(options);
543
+
540
544
  return mongoOptions;
541
545
  }
542
546
 
@@ -1060,6 +1064,9 @@ export const OPTIONS = {
1060
1064
  default: true,
1061
1065
  type: 'boolean'
1062
1066
  },
1067
+ runtimeAdapters: {
1068
+ type: 'record'
1069
+ },
1063
1070
  serializeFunctions: {
1064
1071
  type: 'boolean'
1065
1072
  },
package/src/db.ts CHANGED
@@ -394,7 +394,6 @@ export class Db {
394
394
  toCollection,
395
395
  resolveOptions(undefined, {
396
396
  ...options,
397
- new_collection: true,
398
397
  readPreference: ReadPreference.primary
399
398
  })
400
399
  ) as TODO_NODE_3286
package/src/deps.ts CHANGED
@@ -203,66 +203,6 @@ export function getSocks(): SocksLib | { kModuleError: MongoMissingDependencyErr
203
203
  }
204
204
  }
205
205
 
206
- interface AWS4 {
207
- /**
208
- * Created these inline types to better assert future usage of this API
209
- * @param options - options for request
210
- * @param credentials - AWS credential details, sessionToken should be omitted entirely if its false-y
211
- */
212
- sign(
213
- this: void,
214
- options: {
215
- path: '/';
216
- body: string;
217
- host: string;
218
- method: 'POST';
219
- headers: {
220
- 'Content-Type': 'application/x-www-form-urlencoded';
221
- 'Content-Length': number;
222
- 'X-MongoDB-Server-Nonce': string;
223
- 'X-MongoDB-GS2-CB-Flag': 'n';
224
- };
225
- service: string;
226
- region: string;
227
- },
228
- credentials:
229
- | {
230
- accessKeyId: string;
231
- secretAccessKey: string;
232
- sessionToken: string;
233
- }
234
- | {
235
- accessKeyId: string;
236
- secretAccessKey: string;
237
- }
238
- | undefined
239
- ): {
240
- headers: {
241
- Authorization: string;
242
- 'X-Amz-Date': string;
243
- };
244
- };
245
- }
246
-
247
- export const aws4: AWS4 | { kModuleError: MongoMissingDependencyError } = loadAws4();
248
-
249
- function loadAws4() {
250
- let aws4: AWS4 | { kModuleError: MongoMissingDependencyError };
251
- try {
252
- // eslint-disable-next-line @typescript-eslint/no-require-imports
253
- aws4 = require('aws4');
254
- } catch (error) {
255
- aws4 = makeErrorModule(
256
- new MongoMissingDependencyError(
257
- 'Optional module `aws4` not found. Please install it to enable AWS authentication',
258
- { cause: error, dependencyName: 'aws4' }
259
- )
260
- );
261
- }
262
-
263
- return aws4;
264
- }
265
-
266
206
  /** A utility function to get the instance of mongodb-client-encryption, if it exists. */
267
207
  export function getMongoDBClientEncryption():
268
208
  | typeof import('mongodb-client-encryption')
package/src/error.ts CHANGED
@@ -99,7 +99,9 @@ export const MongoErrorLabel = Object.freeze({
99
99
  ResetPool: 'ResetPool',
100
100
  PoolRequestedRetry: 'PoolRequestedRetry',
101
101
  InterruptInUseConnections: 'InterruptInUseConnections',
102
- NoWritesPerformed: 'NoWritesPerformed'
102
+ NoWritesPerformed: 'NoWritesPerformed',
103
+ RetryableError: 'RetryableError',
104
+ SystemOverloadedError: 'SystemOverloadedError'
103
105
  } as const);
104
106
 
105
107
  /** @public */
@@ -1528,13 +1530,7 @@ export function isNodeShuttingDownError(err: MongoError): boolean {
1528
1530
  *
1529
1531
  * @see https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md#not-writable-primary-and-node-is-recovering
1530
1532
  */
1531
- export function isSDAMUnrecoverableError(error: MongoError): boolean {
1532
- // NOTE: null check is here for a strictly pre-CMAP world, a timeout or
1533
- // close event are considered unrecoverable
1534
- if (error instanceof MongoParseError || error == null) {
1535
- return true;
1536
- }
1537
-
1533
+ export function isStateChangeError(error: MongoError): boolean {
1538
1534
  return isRecoveringError(error) || isNotWritablePrimaryError(error);
1539
1535
  }
1540
1536
 
@@ -165,7 +165,7 @@ export class GridFSBucketWriteStream extends Writable {
165
165
  }
166
166
  );
167
167
  } else {
168
- return process.nextTick(callback);
168
+ return queueMicrotask(callback);
169
169
  }
170
170
  }
171
171
 
@@ -188,7 +188,7 @@ export class GridFSBucketWriteStream extends Writable {
188
188
  /** @internal */
189
189
  override _final(callback: (error?: Error | null) => void): void {
190
190
  if (this.state.streamEnd) {
191
- return process.nextTick(callback);
191
+ return queueMicrotask(callback);
192
192
  }
193
193
  this.state.streamEnd = true;
194
194
  writeRemnant(this, callback);
@@ -220,11 +220,11 @@ export class GridFSBucketWriteStream extends Writable {
220
220
 
221
221
  function handleError(stream: GridFSBucketWriteStream, error: Error, callback: Callback): void {
222
222
  if (stream.state.errored) {
223
- process.nextTick(callback);
223
+ queueMicrotask(callback);
224
224
  return;
225
225
  }
226
226
  stream.state.errored = true;
227
- process.nextTick(callback, error);
227
+ queueMicrotask(() => callback(error));
228
228
  }
229
229
 
230
230
  function createChunkDoc(filesId: ObjectId, n: number, data: Buffer): GridFSChunk {
@@ -283,7 +283,7 @@ async function checkChunksIndex(stream: GridFSBucketWriteStream): Promise<void>
283
283
 
284
284
  function checkDone(stream: GridFSBucketWriteStream, callback: Callback): void {
285
285
  if (stream.done) {
286
- return process.nextTick(callback);
286
+ return queueMicrotask(callback);
287
287
  }
288
288
 
289
289
  if (stream.state.streamEnd && stream.state.outstandingRequests === 0 && !stream.state.errored) {
@@ -327,7 +327,7 @@ function checkDone(stream: GridFSBucketWriteStream, callback: Callback): void {
327
327
  return;
328
328
  }
329
329
 
330
- process.nextTick(callback);
330
+ queueMicrotask(callback);
331
331
  }
332
332
 
333
333
  async function checkIndexes(stream: GridFSBucketWriteStream): Promise<void> {
@@ -425,7 +425,7 @@ function doWrite(
425
425
  if (stream.pos + inputBuf.length < stream.chunkSizeBytes) {
426
426
  inputBuf.copy(stream.bufToStore, stream.pos);
427
427
  stream.pos += inputBuf.length;
428
- process.nextTick(callback);
428
+ queueMicrotask(callback);
429
429
  return;
430
430
  }
431
431
 
@@ -530,7 +530,7 @@ function writeRemnant(stream: GridFSBucketWriteStream, callback: Callback): void
530
530
 
531
531
  function isAborted(stream: GridFSBucketWriteStream, callback: Callback<void>): boolean {
532
532
  if (stream.state.aborted) {
533
- process.nextTick(callback, new MongoAPIError('Stream has been aborted'));
533
+ queueMicrotask(() => callback(new MongoAPIError('Stream has been aborted')));
534
534
  return true;
535
535
  }
536
536
  return false;
package/src/index.ts CHANGED
@@ -562,6 +562,7 @@ export type {
562
562
  ReadPreferenceLikeOptions,
563
563
  ReadPreferenceOptions
564
564
  } from './read_preference';
565
+ export type { OsAdapter, Runtime, RuntimeAdapters } from './runtime_adapters';
565
566
  export type { ClusterTime } from './sdam/common';
566
567
  export type {
567
568
  Monitor,
@@ -588,7 +589,7 @@ export type {
588
589
  TagSet,
589
590
  TopologyVersion
590
591
  } from './sdam/server_description';
591
- export type { ServerSelector } from './sdam/server_selection';
592
+ export type { DeprioritizedServers, ServerSelector } from './sdam/server_selection';
592
593
  export type { SrvPoller, SrvPollerEvents, SrvPollerOptions } from './sdam/srv_polling';
593
594
  export type {
594
595
  ConnectOptions,
@@ -46,9 +46,10 @@ import { EndSessionsOperation } from './operations/end_sessions';
46
46
  import { executeOperation } from './operations/execute_operation';
47
47
  import type { ReadConcern, ReadConcernLevel, ReadConcernLike } from './read_concern';
48
48
  import { ReadPreference, type ReadPreferenceMode } from './read_preference';
49
+ import { type Runtime, type RuntimeAdapters } from './runtime_adapters';
49
50
  import type { ServerMonitoringMode } from './sdam/monitor';
50
51
  import type { TagSet } from './sdam/server_description';
51
- import { readPreferenceServerSelector } from './sdam/server_selection';
52
+ import { DeprioritizedServers, readPreferenceServerSelector } from './sdam/server_selection';
52
53
  import type { SrvPoller } from './sdam/srv_polling';
53
54
  import { Topology, type TopologyEvents } from './sdam/topology';
54
55
  import { ClientSession, type ClientSessionOptions, ServerSessionPool } from './sessions';
@@ -318,6 +319,12 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
318
319
  connectionType?: typeof Connection;
319
320
  /** @internal */
320
321
  __skipPingOnConnect?: boolean;
322
+ /**
323
+ * @experimental
324
+ *
325
+ * If provided, any adapters provided will be used in place of the corresponding Node.js module.
326
+ */
327
+ runtimeAdapters?: RuntimeAdapters;
321
328
  }
322
329
 
323
330
  /** @public */
@@ -789,7 +796,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
789
796
  // to avoid the server selection timeout.
790
797
  const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred);
791
798
  const serverDescriptions = Array.from(topologyDescription.servers.values());
792
- const servers = selector(topologyDescription, serverDescriptions);
799
+ const servers = selector(topologyDescription, serverDescriptions, new DeprioritizedServers());
793
800
  if (servers.length !== 0) {
794
801
  const endSessions = Array.from(client.s.sessionPool.sessions, ({ id }) => id);
795
802
  if (endSessions.length !== 0) {
@@ -1152,4 +1159,7 @@ export interface MongoOptions
1152
1159
  timeoutMS?: number;
1153
1160
  /** @internal */
1154
1161
  __skipPingOnConnect?: boolean;
1162
+
1163
+ /** @internal */
1164
+ runtime: Runtime;
1155
1165
  }
@@ -1,4 +1,5 @@
1
- import { inspect, promisify } from 'util';
1
+ import * as process from 'process';
2
+ import { inspect } from 'util';
2
3
 
3
4
  import {
4
5
  type Binary,
@@ -240,11 +241,15 @@ export function createStdioLogger(stream: {
240
241
  write: NodeJS.WriteStream['write'];
241
242
  }): MongoDBLogWritable {
242
243
  return {
243
- write: promisify((log: Log, cb: (error?: Error | null) => void): unknown => {
244
- const logLine = inspect(log, { compact: true, breakLength: Infinity });
245
- stream.write(`${logLine}\n`, 'utf-8', cb);
246
- return;
247
- })
244
+ write: (log: Log): Promise<unknown> => {
245
+ return new Promise((resolve, reject) => {
246
+ const logLine = inspect(log, { compact: true, breakLength: Infinity });
247
+ stream.write(`${logLine}\n`, 'utf-8', error => {
248
+ if (error) return reject(error);
249
+ resolve(true);
250
+ });
251
+ });
252
+ }
248
253
  };
249
254
  }
250
255
 
@@ -17,8 +17,8 @@ import {
17
17
  } from '../error';
18
18
  import type { MongoClient } from '../mongo_client';
19
19
  import { ReadPreference } from '../read_preference';
20
- import type { ServerDescription } from '../sdam/server_description';
21
20
  import {
21
+ DeprioritizedServers,
22
22
  sameServerSelector,
23
23
  secondaryWritableServerSelector,
24
24
  type ServerSelector
@@ -207,7 +207,8 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
207
207
  session,
208
208
  operationName: operation.commandName,
209
209
  timeoutContext,
210
- signal: operation.options.signal
210
+ signal: operation.options.signal,
211
+ deprioritizedServers: new DeprioritizedServers()
211
212
  });
212
213
 
213
214
  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
@@ -234,7 +235,7 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
234
235
 
235
236
  const maxTries = willRetry ? (timeoutContext.csotEnabled() ? Infinity : 2) : 1;
236
237
  let previousOperationError: MongoError | undefined;
237
- let previousServer: ServerDescription | undefined;
238
+ const deprioritizedServers = new DeprioritizedServers();
238
239
 
239
240
  for (let tries = 0; tries < maxTries; tries++) {
240
241
  if (previousOperationError) {
@@ -270,7 +271,7 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
270
271
  server = await topology.selectServer(selector, {
271
272
  session,
272
273
  operationName: operation.commandName,
273
- previousServer,
274
+ deprioritizedServers,
274
275
  signal: operation.options.signal
275
276
  });
276
277
 
@@ -303,7 +304,7 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
303
304
  ) {
304
305
  throw previousOperationError;
305
306
  }
306
- previousServer = server.description;
307
+ deprioritizedServers.add(server.description);
307
308
  previousOperationError = operationError;
308
309
 
309
310
  // Reset timeouts
@@ -11,7 +11,12 @@ import { Aspect, defineAspects } from './operation';
11
11
  export interface RenameOptions extends Omit<CommandOperationOptions, 'rawData'> {
12
12
  /** Drop the target name collection if it previously exists. */
13
13
  dropTarget?: boolean;
14
- /** Unclear */
14
+ /**
15
+ * @deprecated
16
+ *
17
+ * This option has been dead code since at least Node driver version 4.x. It will
18
+ * be removed in a future major release.
19
+ */
15
20
  new_collection?: boolean;
16
21
  }
17
22
 
@@ -0,0 +1,49 @@
1
+ /* eslint-disable no-restricted-imports, @typescript-eslint/no-require-imports */
2
+
3
+ // We squash the restricted import errors here because we are using type-only imports, which
4
+ // do not impact the driver's actual runtime dependencies.
5
+ // We also allow restricted imports in this file, because we expect this file to be the only place actually importing restricted Node APIs.
6
+
7
+ import type * as os from 'os';
8
+
9
+ import { type MongoClientOptions } from './mongo_client';
10
+
11
+ /**
12
+ * @public
13
+ * @experimental
14
+ *
15
+ * Represents the set of dependencies that the driver uses from the [Node.js OS module](https://nodejs.org/api/os.html).
16
+ */
17
+ export type OsAdapter = Pick<typeof os, 'release' | 'platform' | 'arch' | 'type'>;
18
+
19
+ /**
20
+ * @public
21
+ * @experimental
22
+ *
23
+ * This type represents the set of dependencies that the driver needs from the Javascript runtime in order to function.
24
+ */
25
+ export interface RuntimeAdapters {
26
+ os?: OsAdapter;
27
+ }
28
+
29
+ /**
30
+ * @internal
31
+ *
32
+ * Represents a complete, parsed set of runtime adapters. After options parsing, all adapters
33
+ * are always present (either using the user's provided adapter, or defaulting to the Node.js module).
34
+ */
35
+ export interface Runtime {
36
+ os: OsAdapter;
37
+ }
38
+
39
+ /**
40
+ * @internal
41
+ *
42
+ * Given a MongoClientOptions, this function resolves the set of runtime options, providing Nodejs implementations if
43
+ * not provided by in `options`, and returns a `Runtime`.
44
+ */
45
+ export function resolveRuntimeAdapters(options: MongoClientOptions): Runtime {
46
+ return {
47
+ os: options.runtimeAdapters?.os ?? require('os')
48
+ };
49
+ }