mongodb 6.12.0 → 6.13.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 (92) hide show
  1. package/lib/beta.d.ts +176 -108
  2. package/lib/bulk/common.js +5 -7
  3. package/lib/bulk/common.js.map +1 -1
  4. package/lib/change_stream.js +16 -26
  5. package/lib/change_stream.js.map +1 -1
  6. package/lib/client-side-encryption/auto_encrypter.js +4 -2
  7. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  8. package/lib/client-side-encryption/client_encryption.js +4 -4
  9. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  10. package/lib/client-side-encryption/state_machine.js +56 -30
  11. package/lib/client-side-encryption/state_machine.js.map +1 -1
  12. package/lib/cmap/auth/mongodb_oidc.js +1 -1
  13. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  14. package/lib/cmap/command_monitoring_events.js +9 -50
  15. package/lib/cmap/command_monitoring_events.js.map +1 -1
  16. package/lib/cmap/connection.js +28 -22
  17. package/lib/cmap/connection.js.map +1 -1
  18. package/lib/cmap/connection_pool.js +88 -117
  19. package/lib/cmap/connection_pool.js.map +1 -1
  20. package/lib/cmap/wire_protocol/on_data.js +6 -1
  21. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  22. package/lib/collection.js.map +1 -1
  23. package/lib/connection_string.js +68 -86
  24. package/lib/connection_string.js.map +1 -1
  25. package/lib/cursor/abstract_cursor.js +47 -18
  26. package/lib/cursor/abstract_cursor.js.map +1 -1
  27. package/lib/cursor/aggregation_cursor.js +2 -1
  28. package/lib/cursor/aggregation_cursor.js.map +1 -1
  29. package/lib/cursor/find_cursor.js +2 -1
  30. package/lib/cursor/find_cursor.js.map +1 -1
  31. package/lib/cursor/list_collections_cursor.js +2 -1
  32. package/lib/cursor/list_collections_cursor.js.map +1 -1
  33. package/lib/db.js +2 -1
  34. package/lib/db.js.map +1 -1
  35. package/lib/encrypter.js +5 -9
  36. package/lib/encrypter.js.map +1 -1
  37. package/lib/error.js +10 -18
  38. package/lib/error.js.map +1 -1
  39. package/lib/index.js +5 -2
  40. package/lib/index.js.map +1 -1
  41. package/lib/mongo_client.js +46 -26
  42. package/lib/mongo_client.js.map +1 -1
  43. package/lib/mongo_logger.js +102 -3
  44. package/lib/mongo_logger.js.map +1 -1
  45. package/lib/operations/execute_operation.js +9 -5
  46. package/lib/operations/execute_operation.js.map +1 -1
  47. package/lib/operations/list_collections.js.map +1 -1
  48. package/lib/operations/operation.js +4 -5
  49. package/lib/operations/operation.js.map +1 -1
  50. package/lib/sdam/monitor.js +25 -31
  51. package/lib/sdam/monitor.js.map +1 -1
  52. package/lib/sdam/server.js +27 -17
  53. package/lib/sdam/server.js.map +1 -1
  54. package/lib/sdam/topology.js +20 -19
  55. package/lib/sdam/topology.js.map +1 -1
  56. package/lib/sessions.js +24 -48
  57. package/lib/sessions.js.map +1 -1
  58. package/lib/utils.js +64 -44
  59. package/lib/utils.js.map +1 -1
  60. package/mongodb.d.ts +176 -108
  61. package/package.json +1 -1
  62. package/src/bulk/common.ts +6 -9
  63. package/src/change_stream.ts +21 -33
  64. package/src/client-side-encryption/auto_encrypter.ts +12 -8
  65. package/src/client-side-encryption/client_encryption.ts +6 -4
  66. package/src/client-side-encryption/state_machine.ts +80 -36
  67. package/src/cmap/auth/mongodb_oidc.ts +1 -1
  68. package/src/cmap/command_monitoring_events.ts +10 -55
  69. package/src/cmap/connection.ts +37 -29
  70. package/src/cmap/connection_pool.ts +121 -145
  71. package/src/cmap/wire_protocol/on_data.ts +9 -2
  72. package/src/collection.ts +15 -8
  73. package/src/connection_string.ts +74 -99
  74. package/src/cursor/abstract_cursor.ts +71 -23
  75. package/src/cursor/aggregation_cursor.ts +5 -3
  76. package/src/cursor/find_cursor.ts +5 -3
  77. package/src/cursor/list_collections_cursor.ts +5 -3
  78. package/src/db.ts +11 -7
  79. package/src/encrypter.ts +6 -11
  80. package/src/error.ts +11 -23
  81. package/src/index.ts +3 -3
  82. package/src/mongo_client.ts +78 -47
  83. package/src/mongo_logger.ts +158 -11
  84. package/src/mongo_types.ts +38 -0
  85. package/src/operations/execute_operation.ts +11 -6
  86. package/src/operations/list_collections.ts +4 -1
  87. package/src/operations/operation.ts +8 -9
  88. package/src/sdam/monitor.ts +30 -38
  89. package/src/sdam/server.ts +33 -20
  90. package/src/sdam/topology.ts +29 -26
  91. package/src/sessions.ts +37 -58
  92. package/src/utils.ts +79 -43
@@ -1,6 +1,24 @@
1
1
  import { inspect, promisify } from 'util';
2
+ import { isUint8Array } from 'util/types';
2
3
 
3
- import { type Document, EJSON, type EJSONOptions, type ObjectId } from './bson';
4
+ import {
5
+ type Binary,
6
+ type BSONRegExp,
7
+ type BSONSymbol,
8
+ type Code,
9
+ type DBRef,
10
+ type Decimal128,
11
+ type Document,
12
+ type Double,
13
+ EJSON,
14
+ type EJSONOptions,
15
+ type Int32,
16
+ type Long,
17
+ type MaxKey,
18
+ type MinKey,
19
+ type ObjectId,
20
+ type Timestamp
21
+ } from './bson';
4
22
  import type { CommandStartedEvent } from './cmap/command_monitoring_events';
5
23
  import type {
6
24
  ConnectionCheckedInEvent,
@@ -59,7 +77,11 @@ import type {
59
77
  } from './sdam/server_selection_events';
60
78
  import { HostAddress, isPromiseLike, parseUnsignedInteger } from './utils';
61
79
 
62
- /** @internal */
80
+ /**
81
+ * @public
82
+ * Severity levels align with unix syslog.
83
+ * Most typical driver functions will log to debug.
84
+ */
63
85
  export const SeverityLevel = Object.freeze({
64
86
  EMERGENCY: 'emergency',
65
87
  ALERT: 'alert',
@@ -75,7 +97,7 @@ export const SeverityLevel = Object.freeze({
75
97
 
76
98
  /** @internal */
77
99
  export const DEFAULT_MAX_DOCUMENT_LENGTH = 1000;
78
- /** @internal */
100
+ /** @public */
79
101
  export type SeverityLevel = (typeof SeverityLevel)[keyof typeof SeverityLevel];
80
102
 
81
103
  /** @internal */
@@ -113,7 +135,7 @@ export const SEVERITY_LEVEL_MAP = new SeverityLevelMap([
113
135
  [SeverityLevel.TRACE, 8]
114
136
  ]);
115
137
 
116
- /** @internal */
138
+ /** @public */
117
139
  export const MongoLoggableComponent = Object.freeze({
118
140
  COMMAND: 'command',
119
141
  TOPOLOGY: 'topology',
@@ -122,7 +144,7 @@ export const MongoLoggableComponent = Object.freeze({
122
144
  CLIENT: 'client'
123
145
  } as const);
124
146
 
125
- /** @internal */
147
+ /** @public */
126
148
  export type MongoLoggableComponent =
127
149
  (typeof MongoLoggableComponent)[keyof typeof MongoLoggableComponent];
128
150
 
@@ -146,13 +168,13 @@ export interface MongoLoggerEnvOptions {
146
168
  MONGODB_LOG_PATH?: string;
147
169
  }
148
170
 
149
- /** @internal */
171
+ /** @public */
150
172
  export interface LogComponentSeveritiesClientOptions {
151
173
  /** Optional severity level for command component */
152
174
  command?: SeverityLevel;
153
175
  /** Optional severity level for topology component */
154
176
  topology?: SeverityLevel;
155
- /** Optionsl severity level for server selection component */
177
+ /** Optional severity level for server selection component */
156
178
  serverSelection?: SeverityLevel;
157
179
  /** Optional severity level for connection component */
158
180
  connection?: SeverityLevel;
@@ -274,7 +296,7 @@ function resolveSeverityConfiguration(
274
296
  );
275
297
  }
276
298
 
277
- /** @internal */
299
+ /** @public */
278
300
  export interface Log extends Record<string, any> {
279
301
  t: Date;
280
302
  c: MongoLoggableComponent;
@@ -283,10 +305,27 @@ export interface Log extends Record<string, any> {
283
305
  }
284
306
 
285
307
  /**
286
- * @internal
287
- * TODO: NODE-5671 - remove internal flag and add API comments
308
+ * @public
309
+ *
310
+ * A custom destination for structured logging messages.
288
311
  */
289
312
  export interface MongoDBLogWritable {
313
+ /**
314
+ * This function will be called for every enabled log message.
315
+ *
316
+ * It can be sync or async:
317
+ * - If it is synchronous it will block the driver from proceeding until this method returns.
318
+ * - If it is asynchronous the driver will not await the returned promise. It will attach fulfillment handling (`.then`).
319
+ * If the promise rejects the logger will write an error message to stderr and stop functioning.
320
+ * If the promise resolves the driver proceeds to the next log message (or waits for new ones to occur).
321
+ *
322
+ * Tips:
323
+ * - We recommend writing an async `write` function that _never_ rejects.
324
+ * Instead handle logging errors as necessary to your use case and make the write function a noop, until it can be recovered.
325
+ * - The Log messages are structured but **subject to change** since the intended purpose is informational.
326
+ * Program against this defensively and err on the side of stringifying whatever is passed in to write in some form or another.
327
+ *
328
+ */
290
329
  write(log: Log): PromiseLike<unknown> | unknown;
291
330
  }
292
331
 
@@ -413,6 +452,20 @@ export interface LogConvertible extends Record<string, any> {
413
452
  toLog(): Record<string, any>;
414
453
  }
415
454
 
455
+ type BSONObject =
456
+ | BSONRegExp
457
+ | BSONSymbol
458
+ | Code
459
+ | DBRef
460
+ | Decimal128
461
+ | Double
462
+ | Int32
463
+ | Long
464
+ | MaxKey
465
+ | MinKey
466
+ | ObjectId
467
+ | Timestamp
468
+ | Binary;
416
469
  /** @internal */
417
470
  export function stringifyWithMaxLen(
418
471
  value: any,
@@ -421,13 +474,107 @@ export function stringifyWithMaxLen(
421
474
  ): string {
422
475
  let strToTruncate = '';
423
476
 
477
+ let currentLength = 0;
478
+ const maxDocumentLengthEnsurer = function maxDocumentLengthEnsurer(key: string, value: any) {
479
+ if (currentLength >= maxDocumentLength) {
480
+ return undefined;
481
+ }
482
+ // Account for root document
483
+ if (key === '') {
484
+ // Account for starting brace
485
+ currentLength += 1;
486
+ return value;
487
+ }
488
+
489
+ // +4 accounts for 2 quotation marks, colon and comma after value
490
+ // Note that this potentially undercounts since it does not account for escape sequences which
491
+ // will have an additional backslash added to them once passed through JSON.stringify.
492
+ currentLength += key.length + 4;
493
+
494
+ if (value == null) return value;
495
+
496
+ switch (typeof value) {
497
+ case 'string':
498
+ // +2 accounts for quotes
499
+ // Note that this potentially undercounts similarly to the key length calculation
500
+ currentLength += value.length + 2;
501
+ break;
502
+ case 'number':
503
+ case 'bigint':
504
+ currentLength += String(value).length;
505
+ break;
506
+ case 'boolean':
507
+ currentLength += value ? 4 : 5;
508
+ break;
509
+ case 'object':
510
+ if (isUint8Array(value)) {
511
+ // '{"$binary":{"base64":"<base64 string>","subType":"XX"}}'
512
+ // This is an estimate based on the fact that the base64 is approximately 1.33x the length of
513
+ // the actual binary sequence https://en.wikipedia.org/wiki/Base64
514
+ currentLength += (22 + value.byteLength + value.byteLength * 0.33 + 18) | 0;
515
+ } else if ('_bsontype' in value) {
516
+ const v = value as BSONObject;
517
+ switch (v._bsontype) {
518
+ case 'Int32':
519
+ currentLength += String(v.value).length;
520
+ break;
521
+ case 'Double':
522
+ // Account for representing integers as <value>.0
523
+ currentLength +=
524
+ (v.value | 0) === v.value ? String(v.value).length + 2 : String(v.value).length;
525
+ break;
526
+ case 'Long':
527
+ currentLength += v.toString().length;
528
+ break;
529
+ case 'ObjectId':
530
+ // '{"$oid":"XXXXXXXXXXXXXXXXXXXXXXXX"}'
531
+ currentLength += 35;
532
+ break;
533
+ case 'MaxKey':
534
+ case 'MinKey':
535
+ // '{"$maxKey":1}' or '{"$minKey":1}'
536
+ currentLength += 13;
537
+ break;
538
+ case 'Binary':
539
+ // '{"$binary":{"base64":"<base64 string>","subType":"XX"}}'
540
+ // This is an estimate based on the fact that the base64 is approximately 1.33x the length of
541
+ // the actual binary sequence https://en.wikipedia.org/wiki/Base64
542
+ currentLength += (22 + value.position + value.position * 0.33 + 18) | 0;
543
+ break;
544
+ case 'Timestamp':
545
+ // '{"$timestamp":{"t":<t>,"i":<i>}}'
546
+ currentLength += 19 + String(v.t).length + 5 + String(v.i).length + 2;
547
+ break;
548
+ case 'Code':
549
+ // '{"$code":"<code>"}' or '{"$code":"<code>","$scope":<scope>}'
550
+ if (v.scope == null) {
551
+ currentLength += v.code.length + 10 + 2;
552
+ } else {
553
+ // Ignoring actual scope object, so this undercounts by a significant amount
554
+ currentLength += v.code.length + 10 + 11;
555
+ }
556
+ break;
557
+ case 'BSONRegExp':
558
+ // '{"$regularExpression":{"pattern":"<pattern>","options":"<options>"}}'
559
+ currentLength += 34 + v.pattern.length + 13 + v.options.length + 3;
560
+ break;
561
+ }
562
+ }
563
+ }
564
+ return value;
565
+ };
566
+
424
567
  if (typeof value === 'string') {
425
568
  strToTruncate = value;
426
569
  } else if (typeof value === 'function') {
427
570
  strToTruncate = value.name;
428
571
  } else {
429
572
  try {
430
- strToTruncate = EJSON.stringify(value, options);
573
+ if (maxDocumentLength !== 0) {
574
+ strToTruncate = EJSON.stringify(value, maxDocumentLengthEnsurer, 0, options);
575
+ } else {
576
+ strToTruncate = EJSON.stringify(value, options);
577
+ }
431
578
  } catch (e) {
432
579
  strToTruncate = `Extended JSON serialization failed with: ${e.message}`;
433
580
  }
@@ -474,6 +474,44 @@ export class TypedEventEmitter<Events extends EventsDescription> extends EventEm
474
474
  /** @public */
475
475
  export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> {}
476
476
 
477
+ /** @public */
478
+ export type Abortable = {
479
+ /**
480
+ * @experimental
481
+ * When provided, the corresponding `AbortController` can be used to abort an asynchronous action.
482
+ *
483
+ * The `signal.reason` value is used as the error thrown.
484
+ *
485
+ * @remarks
486
+ * **NOTE:** If an abort signal aborts an operation while the driver is writing to the underlying
487
+ * socket or reading the response from the server, the socket will be closed.
488
+ * If signals are aborted at a high rate during socket read/writes this can lead to a high rate of connection reestablishment.
489
+ *
490
+ * We plan to mitigate this in a future release, please follow NODE-6062 (`timeoutMS` expiration suffers the same limitation).
491
+ *
492
+ * AbortSignals are likely a best fit for human interactive interruption (ex. ctrl-C) where the frequency
493
+ * of cancellation is reasonably low. If a signal is programmatically aborted for 100s of operations you can empty
494
+ * the driver's connection pool.
495
+ *
496
+ * @example
497
+ * ```js
498
+ * const controller = new AbortController();
499
+ * const { signal } = controller;
500
+ * process.on('SIGINT', () => controller.abort(new Error('^C pressed')));
501
+ *
502
+ * try {
503
+ * const res = await fetch('...', { signal });
504
+ * await collection.findOne(await res.json(), { signal });
505
+ * catch (error) {
506
+ * if (error === signal.reason) {
507
+ * // signal abort error handling
508
+ * }
509
+ * }
510
+ * ```
511
+ */
512
+ signal?: AbortSignal | undefined;
513
+ };
514
+
477
515
  /**
478
516
  * Helper types for dot-notation filter attributes
479
517
  */
@@ -25,7 +25,7 @@ import {
25
25
  import type { Topology } from '../sdam/topology';
26
26
  import type { ClientSession } from '../sessions';
27
27
  import { TimeoutContext } from '../timeout';
28
- import { supportsRetryableWrites } from '../utils';
28
+ import { abortable, supportsRetryableWrites } from '../utils';
29
29
  import { AbstractOperation, Aspect } from './operation';
30
30
 
31
31
  const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
@@ -64,7 +64,10 @@ export async function executeOperation<
64
64
  throw new MongoRuntimeError('This method requires a valid operation instance');
65
65
  }
66
66
 
67
- const topology = await autoConnect(client);
67
+ const topology =
68
+ client.topology == null
69
+ ? await abortable(autoConnect(client), operation.options)
70
+ : client.topology;
68
71
 
69
72
  // The driver sessions spec mandates that we implicitly create sessions for operations
70
73
  // that are not explicitly provided with a session.
@@ -131,7 +134,7 @@ async function autoConnect(client: MongoClient): Promise<Topology> {
131
134
  if (client.s.hasBeenClosed) {
132
135
  throw new MongoNotConnectedError('Client must be connected before running operations');
133
136
  }
134
- client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
137
+ client.s.options.__skipPingOnConnect = true;
135
138
  try {
136
139
  await client.connect();
137
140
  if (client.topology == null) {
@@ -141,7 +144,7 @@ async function autoConnect(client: MongoClient): Promise<Topology> {
141
144
  }
142
145
  return client.topology;
143
146
  } finally {
144
- delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
147
+ delete client.s.options.__skipPingOnConnect;
145
148
  }
146
149
  }
147
150
  return client.topology;
@@ -198,7 +201,8 @@ async function tryOperation<
198
201
  let server = await topology.selectServer(selector, {
199
202
  session,
200
203
  operationName: operation.commandName,
201
- timeoutContext
204
+ timeoutContext,
205
+ signal: operation.options.signal
202
206
  });
203
207
 
204
208
  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
@@ -260,7 +264,8 @@ async function tryOperation<
260
264
  server = await topology.selectServer(selector, {
261
265
  session,
262
266
  operationName: operation.commandName,
263
- previousServer
267
+ previousServer,
268
+ signal: operation.options.signal
264
269
  });
265
270
 
266
271
  if (hasWriteAspect && !supportsRetryableWrites(server)) {
@@ -2,6 +2,7 @@ import type { Binary, Document } from '../bson';
2
2
  import { CursorResponse } from '../cmap/wire_protocol/responses';
3
3
  import { type CursorTimeoutContext, type CursorTimeoutMode } from '../cursor/abstract_cursor';
4
4
  import type { Db } from '../db';
5
+ import { type Abortable } from '../mongo_types';
5
6
  import type { Server } from '../sdam/server';
6
7
  import type { ClientSession } from '../sessions';
7
8
  import { type TimeoutContext } from '../timeout';
@@ -10,7 +11,9 @@ import { CommandOperation, type CommandOperationOptions } from './command';
10
11
  import { Aspect, defineAspects } from './operation';
11
12
 
12
13
  /** @public */
13
- export interface ListCollectionsOptions extends Omit<CommandOperationOptions, 'writeConcern'> {
14
+ export interface ListCollectionsOptions
15
+ extends Omit<CommandOperationOptions, 'writeConcern'>,
16
+ Abortable {
14
17
  /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */
15
18
  nameOnly?: boolean;
16
19
  /** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
@@ -1,4 +1,5 @@
1
1
  import { type BSONSerializeOptions, type Document, resolveBSONOptions } from '../bson';
2
+ import { type Abortable } from '../mongo_types';
2
3
  import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
3
4
  import type { Server } from '../sdam/server';
4
5
  import type { ClientSession } from '../sessions';
@@ -42,9 +43,6 @@ export interface OperationOptions extends BSONSerializeOptions {
42
43
  timeoutMS?: number;
43
44
  }
44
45
 
45
- /** @internal */
46
- const kSession = Symbol('session');
47
-
48
46
  /**
49
47
  * This class acts as a parent class for any operation and is responsible for setting this.options,
50
48
  * as well as setting and getting a session.
@@ -62,16 +60,16 @@ export abstract class AbstractOperation<TResult = any> {
62
60
  // BSON serialization options
63
61
  bsonOptions?: BSONSerializeOptions;
64
62
 
65
- options: OperationOptions;
63
+ options: OperationOptions & Abortable;
66
64
 
67
65
  /** Specifies the time an operation will run until it throws a timeout error. */
68
66
  timeoutMS?: number;
69
67
 
70
- [kSession]: ClientSession | undefined;
68
+ private _session: ClientSession | undefined;
71
69
 
72
70
  static aspects?: Set<symbol>;
73
71
 
74
- constructor(options: OperationOptions = {}) {
72
+ constructor(options: OperationOptions & Abortable = {}) {
75
73
  this.readPreference = this.hasAspect(Aspect.WRITE_OPERATION)
76
74
  ? ReadPreference.primary
77
75
  : (ReadPreference.fromOptions(options) ?? ReadPreference.primary);
@@ -79,7 +77,7 @@ export abstract class AbstractOperation<TResult = any> {
79
77
  // Pull the BSON serialize options from the already-resolved options
80
78
  this.bsonOptions = resolveBSONOptions(options);
81
79
 
82
- this[kSession] = options.session != null ? options.session : undefined;
80
+ this._session = options.session != null ? options.session : undefined;
83
81
 
84
82
  this.options = options;
85
83
  this.bypassPinningCheck = !!options.bypassPinningCheck;
@@ -105,12 +103,13 @@ export abstract class AbstractOperation<TResult = any> {
105
103
  return ctor.aspects.has(aspect);
106
104
  }
107
105
 
106
+ // Make sure the session is not writable from outside this class.
108
107
  get session(): ClientSession | undefined {
109
- return this[kSession];
108
+ return this._session;
110
109
  }
111
110
 
112
111
  clearSession() {
113
- this[kSession] = undefined;
112
+ this._session = undefined;
114
113
  }
115
114
 
116
115
  resetBatch(): boolean {
@@ -25,13 +25,6 @@ import {
25
25
  import { Server } from './server';
26
26
  import type { TopologyVersion } from './server_description';
27
27
 
28
- /** @internal */
29
- const kServer = Symbol('server');
30
- /** @internal */
31
- const kMonitorId = Symbol('monitorId');
32
- /** @internal */
33
- const kCancellationToken = Symbol('cancellationToken');
34
-
35
28
  const STATE_IDLE = 'idle';
36
29
  const STATE_MONITORING = 'monitoring';
37
30
  const stateTransition = makeStateMachine({
@@ -96,11 +89,11 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
96
89
  >;
97
90
  connectOptions: ConnectionOptions;
98
91
  isRunningInFaasEnv: boolean;
99
- [kServer]: Server;
92
+ server: Server;
100
93
  connection: Connection | null;
101
- [kCancellationToken]: CancellationToken;
94
+ cancellationToken: CancellationToken;
102
95
  /** @internal */
103
- [kMonitorId]?: MonitorInterval;
96
+ monitorId?: MonitorInterval;
104
97
  rttPinger?: RTTPinger;
105
98
  /** @internal */
106
99
  override component = MongoLoggableComponent.TOPOLOGY;
@@ -110,11 +103,11 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
110
103
  constructor(server: Server, options: MonitorOptions) {
111
104
  super();
112
105
 
113
- this[kServer] = server;
106
+ this.server = server;
114
107
  this.connection = null;
115
- this[kCancellationToken] = new CancellationToken();
116
- this[kCancellationToken].setMaxListeners(Infinity);
117
- this[kMonitorId] = undefined;
108
+ this.cancellationToken = new CancellationToken();
109
+ this.cancellationToken.setMaxListeners(Infinity);
110
+ this.monitorId = undefined;
118
111
  this.s = {
119
112
  state: STATE_CLOSED
120
113
  };
@@ -126,10 +119,10 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
126
119
  serverMonitoringMode: options.serverMonitoringMode
127
120
  });
128
121
  this.isRunningInFaasEnv = getFAASEnv() != null;
129
- this.mongoLogger = this[kServer].topology.client?.mongoLogger;
122
+ this.mongoLogger = this.server.topology.client?.mongoLogger;
130
123
  this.rttSampler = new RTTSampler(10);
131
124
 
132
- const cancellationToken = this[kCancellationToken];
125
+ const cancellationToken = this.cancellationToken;
133
126
  // TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
134
127
  const connectOptions = {
135
128
  id: '<monitor>' as const,
@@ -162,7 +155,7 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
162
155
  // start
163
156
  const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
164
157
  const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
165
- this[kMonitorId] = new MonitorInterval(monitorServer(this), {
158
+ this.monitorId = new MonitorInterval(monitorServer(this), {
166
159
  heartbeatFrequencyMS: heartbeatFrequencyMS,
167
160
  minHeartbeatFrequencyMS: minHeartbeatFrequencyMS,
168
161
  immediate: true
@@ -174,11 +167,11 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
174
167
  return;
175
168
  }
176
169
 
177
- this[kMonitorId]?.wake();
170
+ this.monitorId?.wake();
178
171
  }
179
172
 
180
173
  reset(): void {
181
- const topologyVersion = this[kServer].description.topologyVersion;
174
+ const topologyVersion = this.server.description.topologyVersion;
182
175
  if (isInCloseState(this) || topologyVersion == null) {
183
176
  return;
184
177
  }
@@ -192,7 +185,7 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
192
185
  // restart monitoring
193
186
  const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
194
187
  const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
195
- this[kMonitorId] = new MonitorInterval(monitorServer(this), {
188
+ this.monitorId = new MonitorInterval(monitorServer(this), {
196
189
  heartbeatFrequencyMS: heartbeatFrequencyMS,
197
190
  minHeartbeatFrequencyMS: minHeartbeatFrequencyMS
198
191
  });
@@ -233,13 +226,13 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
233
226
  }
234
227
 
235
228
  function resetMonitorState(monitor: Monitor) {
236
- monitor[kMonitorId]?.stop();
237
- monitor[kMonitorId] = undefined;
229
+ monitor.monitorId?.stop();
230
+ monitor.monitorId = undefined;
238
231
 
239
232
  monitor.rttPinger?.close();
240
233
  monitor.rttPinger = undefined;
241
234
 
242
- monitor[kCancellationToken].emit('cancel');
235
+ monitor.cancellationToken.emit('cancel');
243
236
 
244
237
  monitor.connection?.destroy();
245
238
  monitor.connection = null;
@@ -266,11 +259,11 @@ function useStreamingProtocol(monitor: Monitor, topologyVersion: TopologyVersion
266
259
  function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
267
260
  let start: number;
268
261
  let awaited: boolean;
269
- const topologyVersion = monitor[kServer].description.topologyVersion;
262
+ const topologyVersion = monitor.server.description.topologyVersion;
270
263
  const isAwaitable = useStreamingProtocol(monitor, topologyVersion);
271
264
  monitor.emitAndLogHeartbeat(
272
265
  Server.SERVER_HEARTBEAT_STARTED,
273
- monitor[kServer].topology.s.id,
266
+ monitor.server.topology.s.id,
274
267
  undefined,
275
268
  new ServerHeartbeatStartedEvent(monitor.address, isAwaitable)
276
269
  );
@@ -280,7 +273,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
280
273
  monitor.connection = null;
281
274
  monitor.emitAndLogHeartbeat(
282
275
  Server.SERVER_HEARTBEAT_FAILED,
283
- monitor[kServer].topology.s.id,
276
+ monitor.server.topology.s.id,
284
277
  undefined,
285
278
  new ServerHeartbeatFailedEvent(monitor.address, calculateDurationInMs(start), err, awaited)
286
279
  );
@@ -315,7 +308,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
315
308
 
316
309
  monitor.emitAndLogHeartbeat(
317
310
  Server.SERVER_HEARTBEAT_SUCCEEDED,
318
- monitor[kServer].topology.s.id,
311
+ monitor.server.topology.s.id,
319
312
  hello.connectionId,
320
313
  new ServerHeartbeatSucceededEvent(monitor.address, duration, hello, isAwaitable)
321
314
  );
@@ -325,7 +318,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
325
318
  // event, otherwise the "check" is complete and return to the main monitor loop
326
319
  monitor.emitAndLogHeartbeat(
327
320
  Server.SERVER_HEARTBEAT_STARTED,
328
- monitor[kServer].topology.s.id,
321
+ monitor.server.topology.s.id,
329
322
  undefined,
330
323
  new ServerHeartbeatStartedEvent(monitor.address, true)
331
324
  );
@@ -378,7 +371,6 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
378
371
  awaited = false;
379
372
  connection
380
373
  .command(ns('admin.$cmd'), cmd, options)
381
-
382
374
  .then(onHeartbeatSucceeded, onHeartbeatFailed);
383
375
 
384
376
  return;
@@ -409,7 +401,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
409
401
  monitor.connection = connection;
410
402
  monitor.emitAndLogHeartbeat(
411
403
  Server.SERVER_HEARTBEAT_SUCCEEDED,
412
- monitor[kServer].topology.s.id,
404
+ monitor.server.topology.s.id,
413
405
  connection.hello?.connectionId,
414
406
  new ServerHeartbeatSucceededEvent(
415
407
  monitor.address,
@@ -447,7 +439,7 @@ function monitorServer(monitor: Monitor) {
447
439
  checkServer(monitor, (err, hello) => {
448
440
  if (err) {
449
441
  // otherwise an error occurred on initial discovery, also bail
450
- if (monitor[kServer].description.type === ServerType.Unknown) {
442
+ if (monitor.server.description.type === ServerType.Unknown) {
451
443
  return done();
452
444
  }
453
445
  }
@@ -456,7 +448,7 @@ function monitorServer(monitor: Monitor) {
456
448
  if (useStreamingProtocol(monitor, hello?.topologyVersion)) {
457
449
  setTimeout(() => {
458
450
  if (!isInCloseState(monitor)) {
459
- monitor[kMonitorId]?.wake();
451
+ monitor.monitorId?.wake();
460
452
  }
461
453
  }, 0);
462
454
  }
@@ -484,9 +476,9 @@ export interface RTTPingerOptions extends ConnectionOptions {
484
476
  export class RTTPinger {
485
477
  connection?: Connection;
486
478
  /** @internal */
487
- [kCancellationToken]: CancellationToken;
479
+ cancellationToken: CancellationToken;
488
480
  /** @internal */
489
- [kMonitorId]: NodeJS.Timeout;
481
+ monitorId: NodeJS.Timeout;
490
482
  /** @internal */
491
483
  monitor: Monitor;
492
484
  closed: boolean;
@@ -495,13 +487,13 @@ export class RTTPinger {
495
487
 
496
488
  constructor(monitor: Monitor) {
497
489
  this.connection = undefined;
498
- this[kCancellationToken] = monitor[kCancellationToken];
490
+ this.cancellationToken = monitor.cancellationToken;
499
491
  this.closed = false;
500
492
  this.monitor = monitor;
501
493
  this.latestRtt = monitor.latestRtt ?? undefined;
502
494
 
503
495
  const heartbeatFrequencyMS = monitor.options.heartbeatFrequencyMS;
504
- this[kMonitorId] = setTimeout(() => this.measureRoundTripTime(), heartbeatFrequencyMS);
496
+ this.monitorId = setTimeout(() => this.measureRoundTripTime(), heartbeatFrequencyMS);
505
497
  }
506
498
 
507
499
  get roundTripTime(): number {
@@ -514,7 +506,7 @@ export class RTTPinger {
514
506
 
515
507
  close(): void {
516
508
  this.closed = true;
517
- clearTimeout(this[kMonitorId]);
509
+ clearTimeout(this.monitorId);
518
510
 
519
511
  this.connection?.destroy();
520
512
  this.connection = undefined;
@@ -531,7 +523,7 @@ export class RTTPinger {
531
523
  }
532
524
 
533
525
  this.latestRtt = calculateDurationInMs(start);
534
- this[kMonitorId] = setTimeout(
526
+ this.monitorId = setTimeout(
535
527
  () => this.measureRoundTripTime(),
536
528
  this.monitor.options.heartbeatFrequencyMS
537
529
  );