mongodb 5.2.0 → 5.4.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 (100) hide show
  1. package/lib/admin.js +18 -0
  2. package/lib/admin.js.map +1 -1
  3. package/lib/bulk/common.js +28 -7
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/cmap/auth/mongo_credentials.js +29 -2
  6. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  7. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +5 -3
  8. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +1 -1
  9. package/lib/cmap/auth/mongodb_oidc/cache.js +28 -0
  10. package/lib/cmap/auth/mongodb_oidc/cache.js.map +1 -0
  11. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js +83 -0
  12. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js.map +1 -0
  13. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +138 -112
  14. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
  15. package/lib/cmap/auth/mongodb_oidc/service_workflow.js +4 -2
  16. package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +1 -1
  17. package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js +12 -56
  18. package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js.map +1 -1
  19. package/lib/cmap/auth/mongodb_oidc.js +17 -13
  20. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  21. package/lib/cmap/command_monitoring_events.js +6 -0
  22. package/lib/cmap/command_monitoring_events.js.map +1 -1
  23. package/lib/cmap/connect.js +1 -0
  24. package/lib/cmap/connect.js.map +1 -1
  25. package/lib/cmap/connection.js +12 -12
  26. package/lib/cmap/connection.js.map +1 -1
  27. package/lib/cmap/connection_pool.js +7 -3
  28. package/lib/cmap/connection_pool.js.map +1 -1
  29. package/lib/cmap/connection_pool_events.js +28 -3
  30. package/lib/cmap/connection_pool_events.js.map +1 -1
  31. package/lib/cmap/handshake/client_metadata.js +173 -0
  32. package/lib/cmap/handshake/client_metadata.js.map +1 -0
  33. package/lib/cmap/wire_protocol/constants.js +2 -2
  34. package/lib/cmap/wire_protocol/shared.js +2 -2
  35. package/lib/cmap/wire_protocol/shared.js.map +1 -1
  36. package/lib/collection.js +3 -0
  37. package/lib/collection.js.map +1 -1
  38. package/lib/connection_string.js +48 -53
  39. package/lib/connection_string.js.map +1 -1
  40. package/lib/constants.js +11 -0
  41. package/lib/constants.js.map +1 -1
  42. package/lib/cursor/abstract_cursor.js +1 -0
  43. package/lib/cursor/abstract_cursor.js.map +1 -1
  44. package/lib/db.js +18 -0
  45. package/lib/db.js.map +1 -1
  46. package/lib/mongo_client.js +16 -0
  47. package/lib/mongo_client.js.map +1 -1
  48. package/lib/mongo_logger.js +258 -27
  49. package/lib/mongo_logger.js.map +1 -1
  50. package/lib/operations/add_user.js.map +1 -1
  51. package/lib/operations/find.js +0 -7
  52. package/lib/operations/find.js.map +1 -1
  53. package/lib/operations/run_command.js.map +1 -1
  54. package/lib/operations/stats.js.map +1 -1
  55. package/lib/operations/update.js.map +1 -1
  56. package/lib/sdam/srv_polling.js +1 -15
  57. package/lib/sdam/srv_polling.js.map +1 -1
  58. package/lib/sdam/topology.js.map +1 -1
  59. package/lib/utils.js +48 -35
  60. package/lib/utils.js.map +1 -1
  61. package/mongodb.d.ts +247 -47
  62. package/package.json +3 -3
  63. package/src/admin.ts +18 -0
  64. package/src/bulk/common.ts +28 -7
  65. package/src/change_stream.ts +1 -1
  66. package/src/cmap/auth/mongo_credentials.ts +35 -2
  67. package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +6 -3
  68. package/src/cmap/auth/mongodb_oidc/cache.ts +27 -0
  69. package/src/cmap/auth/mongodb_oidc/callback_lock_cache.ts +107 -0
  70. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +208 -171
  71. package/src/cmap/auth/mongodb_oidc/service_workflow.ts +5 -3
  72. package/src/cmap/auth/mongodb_oidc/token_entry_cache.ts +17 -96
  73. package/src/cmap/auth/mongodb_oidc.ts +61 -37
  74. package/src/cmap/command_monitoring_events.ts +13 -1
  75. package/src/cmap/connect.ts +3 -1
  76. package/src/cmap/connection.ts +16 -13
  77. package/src/cmap/connection_pool.ts +14 -4
  78. package/src/cmap/connection_pool_events.ts +68 -6
  79. package/src/cmap/handshake/client_metadata.ts +272 -0
  80. package/src/cmap/wire_protocol/constants.ts +2 -2
  81. package/src/cmap/wire_protocol/shared.ts +2 -3
  82. package/src/collection.ts +6 -3
  83. package/src/connection_string.ts +55 -55
  84. package/src/constants.ts +11 -0
  85. package/src/cursor/abstract_cursor.ts +1 -0
  86. package/src/db.ts +18 -0
  87. package/src/index.ts +24 -6
  88. package/src/mongo_client.ts +50 -6
  89. package/src/mongo_logger.ts +363 -44
  90. package/src/operations/add_user.ts +8 -2
  91. package/src/operations/find.ts +0 -10
  92. package/src/operations/run_command.ts +40 -3
  93. package/src/operations/stats.ts +11 -2
  94. package/src/operations/update.ts +8 -4
  95. package/src/sdam/srv_polling.ts +1 -16
  96. package/src/sdam/topology.ts +1 -3
  97. package/src/utils.ts +54 -73
  98. package/lib/cmap/auth/mongodb_oidc/workflow.js +0 -3
  99. package/lib/cmap/auth/mongodb_oidc/workflow.js.map +0 -1
  100. package/src/cmap/auth/mongodb_oidc/workflow.ts +0 -21
@@ -0,0 +1,272 @@
1
+ import * as os from 'os';
2
+ import * as process from 'process';
3
+
4
+ import { BSON, Int32 } from '../../bson';
5
+ import { MongoInvalidArgumentError } from '../../error';
6
+ import type { MongoOptions } from '../../mongo_client';
7
+
8
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
9
+ const NODE_DRIVER_VERSION = require('../../../package.json').version;
10
+
11
+ /**
12
+ * @public
13
+ * @see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst#hello-command
14
+ */
15
+ export interface ClientMetadata {
16
+ driver: {
17
+ name: string;
18
+ version: string;
19
+ };
20
+ os: {
21
+ type: string;
22
+ name?: NodeJS.Platform;
23
+ architecture?: string;
24
+ version?: string;
25
+ };
26
+ platform: string;
27
+ application?: {
28
+ name: string;
29
+ };
30
+ /** FaaS environment information */
31
+ env?: {
32
+ name: 'aws.lambda' | 'gcp.func' | 'azure.func' | 'vercel';
33
+ timeout_sec?: Int32;
34
+ memory_mb?: Int32;
35
+ region?: string;
36
+ url?: string;
37
+ };
38
+ }
39
+
40
+ /** @public */
41
+ export interface ClientMetadataOptions {
42
+ driverInfo?: {
43
+ name?: string;
44
+ version?: string;
45
+ platform?: string;
46
+ };
47
+ appName?: string;
48
+ }
49
+
50
+ /** @internal */
51
+ export class LimitedSizeDocument {
52
+ private document = new Map();
53
+ /** BSON overhead: Int32 + Null byte */
54
+ private documentSize = 5;
55
+ constructor(private maxSize: number) {}
56
+
57
+ /** Only adds key/value if the bsonByteLength is less than MAX_SIZE */
58
+ public ifItFitsItSits(key: string, value: Record<string, any> | string): boolean {
59
+ // The BSON byteLength of the new element is the same as serializing it to its own document
60
+ // subtracting the document size int32 and the null terminator.
61
+ const newElementSize = BSON.serialize(new Map().set(key, value)).byteLength - 5;
62
+
63
+ if (newElementSize + this.documentSize > this.maxSize) {
64
+ return false;
65
+ }
66
+
67
+ this.documentSize += newElementSize;
68
+
69
+ this.document.set(key, value);
70
+
71
+ return true;
72
+ }
73
+
74
+ toObject(): ClientMetadata {
75
+ return BSON.deserialize(BSON.serialize(this.document), {
76
+ promoteLongs: false,
77
+ promoteBuffers: false,
78
+ promoteValues: false,
79
+ useBigInt64: false
80
+ }) as ClientMetadata;
81
+ }
82
+ }
83
+
84
+ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'driverInfo'>;
85
+ /**
86
+ * From the specs:
87
+ * Implementors SHOULD cumulatively update fields in the following order until the document is under the size limit:
88
+ * 1. Omit fields from `env` except `env.name`.
89
+ * 2. Omit fields from `os` except `os.type`.
90
+ * 3. Omit the `env` document entirely.
91
+ * 4. Truncate `platform`. -- special we do not truncate this field
92
+ */
93
+ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMetadata {
94
+ const metadataDocument = new LimitedSizeDocument(512);
95
+
96
+ const { appName = '' } = options;
97
+ // Add app name first, it must be sent
98
+ if (appName.length > 0) {
99
+ const name =
100
+ Buffer.byteLength(appName, 'utf8') <= 128
101
+ ? options.appName
102
+ : Buffer.from(appName, 'utf8').subarray(0, 128).toString('utf8');
103
+ metadataDocument.ifItFitsItSits('application', { name });
104
+ }
105
+
106
+ const { name = '', version = '', platform = '' } = options.driverInfo;
107
+
108
+ const driverInfo = {
109
+ name: name.length > 0 ? `nodejs|${name}` : 'nodejs',
110
+ version: version.length > 0 ? `${NODE_DRIVER_VERSION}|${version}` : NODE_DRIVER_VERSION
111
+ };
112
+
113
+ if (!metadataDocument.ifItFitsItSits('driver', driverInfo)) {
114
+ throw new MongoInvalidArgumentError(
115
+ 'Unable to include driverInfo name and version, metadata cannot exceed 512 bytes'
116
+ );
117
+ }
118
+
119
+ let runtimeInfo = getRuntimeInfo();
120
+ if (platform.length > 0) {
121
+ runtimeInfo = `${runtimeInfo}|${platform}`;
122
+ }
123
+
124
+ if (!metadataDocument.ifItFitsItSits('platform', runtimeInfo)) {
125
+ throw new MongoInvalidArgumentError(
126
+ 'Unable to include driverInfo platform, metadata cannot exceed 512 bytes'
127
+ );
128
+ }
129
+
130
+ // Note: order matters, os.type is last so it will be removed last if we're at maxSize
131
+ const osInfo = new Map()
132
+ .set('name', process.platform)
133
+ .set('architecture', process.arch)
134
+ .set('version', os.release())
135
+ .set('type', os.type());
136
+
137
+ if (!metadataDocument.ifItFitsItSits('os', osInfo)) {
138
+ for (const key of osInfo.keys()) {
139
+ osInfo.delete(key);
140
+ if (osInfo.size === 0) break;
141
+ if (metadataDocument.ifItFitsItSits('os', osInfo)) break;
142
+ }
143
+ }
144
+
145
+ const faasEnv = getFAASEnv();
146
+ if (faasEnv != null) {
147
+ if (!metadataDocument.ifItFitsItSits('env', faasEnv)) {
148
+ for (const key of faasEnv.keys()) {
149
+ faasEnv.delete(key);
150
+ if (faasEnv.size === 0) break;
151
+ if (metadataDocument.ifItFitsItSits('env', faasEnv)) break;
152
+ }
153
+ }
154
+ }
155
+
156
+ return metadataDocument.toObject();
157
+ }
158
+
159
+ /**
160
+ * Collects FaaS metadata.
161
+ * - `name` MUST be the last key in the Map returned.
162
+ */
163
+ export function getFAASEnv(): Map<string, string | Int32> | null {
164
+ const {
165
+ AWS_EXECUTION_ENV = '',
166
+ AWS_LAMBDA_RUNTIME_API = '',
167
+ FUNCTIONS_WORKER_RUNTIME = '',
168
+ K_SERVICE = '',
169
+ FUNCTION_NAME = '',
170
+ VERCEL = '',
171
+ AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '',
172
+ AWS_REGION = '',
173
+ FUNCTION_MEMORY_MB = '',
174
+ FUNCTION_REGION = '',
175
+ FUNCTION_TIMEOUT_SEC = '',
176
+ VERCEL_REGION = ''
177
+ } = process.env;
178
+
179
+ const isAWSFaaS = AWS_EXECUTION_ENV.length > 0 || AWS_LAMBDA_RUNTIME_API.length > 0;
180
+ const isAzureFaaS = FUNCTIONS_WORKER_RUNTIME.length > 0;
181
+ const isGCPFaaS = K_SERVICE.length > 0 || FUNCTION_NAME.length > 0;
182
+ const isVercelFaaS = VERCEL.length > 0;
183
+
184
+ // Note: order matters, name must always be the last key
185
+ const faasEnv = new Map();
186
+
187
+ // When isVercelFaaS is true so is isAWSFaaS; Vercel inherits the AWS env
188
+ if (isVercelFaaS && !(isAzureFaaS || isGCPFaaS)) {
189
+ if (VERCEL_REGION.length > 0) {
190
+ faasEnv.set('region', VERCEL_REGION);
191
+ }
192
+
193
+ faasEnv.set('name', 'vercel');
194
+ return faasEnv;
195
+ }
196
+
197
+ if (isAWSFaaS && !(isAzureFaaS || isGCPFaaS || isVercelFaaS)) {
198
+ if (AWS_REGION.length > 0) {
199
+ faasEnv.set('region', AWS_REGION);
200
+ }
201
+
202
+ if (
203
+ AWS_LAMBDA_FUNCTION_MEMORY_SIZE.length > 0 &&
204
+ Number.isInteger(+AWS_LAMBDA_FUNCTION_MEMORY_SIZE)
205
+ ) {
206
+ faasEnv.set('memory_mb', new Int32(AWS_LAMBDA_FUNCTION_MEMORY_SIZE));
207
+ }
208
+
209
+ faasEnv.set('name', 'aws.lambda');
210
+ return faasEnv;
211
+ }
212
+
213
+ if (isAzureFaaS && !(isGCPFaaS || isAWSFaaS || isVercelFaaS)) {
214
+ faasEnv.set('name', 'azure.func');
215
+ return faasEnv;
216
+ }
217
+
218
+ if (isGCPFaaS && !(isAzureFaaS || isAWSFaaS || isVercelFaaS)) {
219
+ if (FUNCTION_REGION.length > 0) {
220
+ faasEnv.set('region', FUNCTION_REGION);
221
+ }
222
+
223
+ if (FUNCTION_MEMORY_MB.length > 0 && Number.isInteger(+FUNCTION_MEMORY_MB)) {
224
+ faasEnv.set('memory_mb', new Int32(FUNCTION_MEMORY_MB));
225
+ }
226
+
227
+ if (FUNCTION_TIMEOUT_SEC.length > 0 && Number.isInteger(+FUNCTION_TIMEOUT_SEC)) {
228
+ faasEnv.set('timeout_sec', new Int32(FUNCTION_TIMEOUT_SEC));
229
+ }
230
+
231
+ faasEnv.set('name', 'gcp.func');
232
+ return faasEnv;
233
+ }
234
+
235
+ return null;
236
+ }
237
+
238
+ /**
239
+ * @internal
240
+ * This type represents the global Deno object and the minimal type contract we expect it to satisfy.
241
+ */
242
+ declare const Deno: { version?: { deno?: string } } | undefined;
243
+
244
+ /**
245
+ * @internal
246
+ * This type represents the global Bun object and the minimal type contract we expect it to satisfy.
247
+ */
248
+ declare const Bun: { (): void; version?: string } | undefined;
249
+
250
+ /**
251
+ * @internal
252
+ * Get current JavaScript runtime platform
253
+ *
254
+ * NOTE: The version information fetching is intentionally written defensively
255
+ * to avoid having a released driver version that becomes incompatible
256
+ * with a future change to these global objects.
257
+ */
258
+ function getRuntimeInfo(): string {
259
+ if ('Deno' in globalThis) {
260
+ const version = typeof Deno?.version?.deno === 'string' ? Deno?.version?.deno : '0.0.0-unknown';
261
+
262
+ return `Deno v${version}, ${os.endianness()}`;
263
+ }
264
+
265
+ if ('Bun' in globalThis) {
266
+ const version = typeof Bun?.version === 'string' ? Bun?.version : '0.0.0-unknown';
267
+
268
+ return `Bun v${version}, ${os.endianness()}`;
269
+ }
270
+
271
+ return `Node.js ${process.version}, ${os.endianness()}`;
272
+ }
@@ -1,7 +1,7 @@
1
1
  export const MIN_SUPPORTED_SERVER_VERSION = '3.6';
2
- export const MAX_SUPPORTED_SERVER_VERSION = '6.0';
2
+ export const MAX_SUPPORTED_SERVER_VERSION = '7.0';
3
3
  export const MIN_SUPPORTED_WIRE_VERSION = 6;
4
- export const MAX_SUPPORTED_WIRE_VERSION = 17;
4
+ export const MAX_SUPPORTED_WIRE_VERSION = 21;
5
5
  export const OP_REPLY = 1;
6
6
  export const OP_UPDATE = 2001;
7
7
  export const OP_INSERT = 2002;
@@ -1,4 +1,3 @@
1
- import type { Document } from '../../bson';
2
1
  import { MongoInvalidArgumentError } from '../../error';
3
2
  import type { ReadPreferenceLike } from '../../read_preference';
4
3
  import { ReadPreference } from '../../read_preference';
@@ -13,9 +12,9 @@ export interface ReadPreferenceOption {
13
12
  readPreference?: ReadPreferenceLike;
14
13
  }
15
14
 
16
- export function getReadPreference(cmd: Document, options?: ReadPreferenceOption): ReadPreference {
15
+ export function getReadPreference(options?: ReadPreferenceOption): ReadPreference {
17
16
  // Default to command version of the readPreference
18
- let readPreference = cmd.readPreference || ReadPreference.primary;
17
+ let readPreference = options?.readPreference ?? ReadPreference.primary;
19
18
  // If we have an option readPreference override the command one
20
19
  if (options?.readPreference) {
21
20
  readPreference = options.readPreference;
package/src/collection.ts CHANGED
@@ -334,7 +334,7 @@ export class Collection<TSchema extends Document = Document> {
334
334
  filter: Filter<TSchema>,
335
335
  update: UpdateFilter<TSchema> | Partial<TSchema>,
336
336
  options?: UpdateOptions
337
- ): Promise<UpdateResult> {
337
+ ): Promise<UpdateResult<TSchema>> {
338
338
  return executeOperation(
339
339
  this.s.db.s.client,
340
340
  new UpdateOneOperation(
@@ -357,7 +357,7 @@ export class Collection<TSchema extends Document = Document> {
357
357
  filter: Filter<TSchema>,
358
358
  replacement: WithoutId<TSchema>,
359
359
  options?: ReplaceOptions
360
- ): Promise<UpdateResult | Document> {
360
+ ): Promise<UpdateResult<TSchema> | Document> {
361
361
  return executeOperation(
362
362
  this.s.db.s.client,
363
363
  new ReplaceOneOperation(
@@ -380,7 +380,7 @@ export class Collection<TSchema extends Document = Document> {
380
380
  filter: Filter<TSchema>,
381
381
  update: UpdateFilter<TSchema>,
382
382
  options?: UpdateOptions
383
- ): Promise<UpdateResult> {
383
+ ): Promise<UpdateResult<TSchema>> {
384
384
  return executeOperation(
385
385
  this.s.db.s.client,
386
386
  new UpdateManyOperation(
@@ -784,6 +784,9 @@ export class Collection<TSchema extends Document = Document> {
784
784
  /**
785
785
  * Get all the collection statistics.
786
786
  *
787
+ * @deprecated the `collStats` operation will be removed in the next major release. Please
788
+ * use an aggregation pipeline with the [`$collStats`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/) stage instead
789
+ *
787
790
  * @param options - Optional settings for the command
788
791
  */
789
792
  async stats(options?: CollStatsOptions): Promise<CollStats> {
@@ -6,6 +6,7 @@ import { URLSearchParams } from 'url';
6
6
  import type { Document } from './bson';
7
7
  import { MongoCredentials } from './cmap/auth/mongo_credentials';
8
8
  import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './cmap/auth/providers';
9
+ import { makeClientMetadata } from './cmap/handshake/client_metadata';
9
10
  import { Compressor, CompressorName } from './cmap/wire_protocol/compression';
10
11
  import { Encrypter } from './encrypter';
11
12
  import {
@@ -32,7 +33,7 @@ import {
32
33
  emitWarningOnce,
33
34
  HostAddress,
34
35
  isRecord,
35
- makeClientMetadata,
36
+ matchesParentDomain,
36
37
  parseInteger,
37
38
  setDifference
38
39
  } from './utils';
@@ -45,21 +46,6 @@ const LB_REPLICA_SET_ERROR = 'loadBalanced option not supported with a replicaSe
45
46
  const LB_DIRECT_CONNECTION_ERROR =
46
47
  'loadBalanced option not supported when directConnection is provided';
47
48
 
48
- /**
49
- * Determines whether a provided address matches the provided parent domain in order
50
- * to avoid certain attack vectors.
51
- *
52
- * @param srvAddress - The address to check against a domain
53
- * @param parentDomain - The domain to check the provided address against
54
- * @returns Whether the provided address matches the parent domain
55
- */
56
- function matchesParentDomain(srvAddress: string, parentDomain: string): boolean {
57
- const regex = /^.*?\./;
58
- const srv = `.${srvAddress.replace(regex, '')}`;
59
- const parent = `.${parentDomain.replace(regex, '')}`;
60
- return srv.endsWith(parent);
61
- }
62
-
63
49
  /**
64
50
  * Lookup a `mongodb+srv` connection string, combine the parts and reparse it as a normal
65
51
  * connection string.
@@ -277,7 +263,7 @@ export function parseOptions(
277
263
 
278
264
  mongoOptions.hosts = isSRV ? [] : hosts.map(HostAddress.fromString);
279
265
 
280
- const urlOptions = new CaseInsensitiveMap<any[]>();
266
+ const urlOptions = new CaseInsensitiveMap<unknown[]>();
281
267
 
282
268
  if (url.pathname !== '/' && url.pathname !== '') {
283
269
  const dbName = decodeURIComponent(
@@ -312,7 +298,7 @@ export function parseOptions(
312
298
  }
313
299
  }
314
300
 
315
- const objectOptions = new CaseInsensitiveMap(
301
+ const objectOptions = new CaseInsensitiveMap<unknown>(
316
302
  Object.entries(options).filter(([, v]) => v != null)
317
303
  );
318
304
 
@@ -324,54 +310,59 @@ export function parseOptions(
324
310
  );
325
311
  }
326
312
 
313
+ const uriMechanismProperties = urlOptions.get('authMechanismProperties');
314
+ if (uriMechanismProperties) {
315
+ for (const property of uriMechanismProperties) {
316
+ if (/(^|,)ALLOWED_HOSTS:/.test(property as string)) {
317
+ throw new MongoParseError(
318
+ 'Auth mechanism property ALLOWED_HOSTS is not allowed in the connection string.'
319
+ );
320
+ }
321
+ }
322
+ }
323
+
327
324
  if (objectOptions.has('loadBalanced')) {
328
325
  throw new MongoParseError('loadBalanced is only a valid option in the URI');
329
326
  }
330
327
 
331
328
  // All option collection
332
329
 
333
- const allOptions = new CaseInsensitiveMap();
330
+ const allProvidedOptions = new CaseInsensitiveMap<unknown[]>();
334
331
 
335
- const allKeys = new Set<string>([
336
- ...urlOptions.keys(),
337
- ...objectOptions.keys(),
338
- ...DEFAULT_OPTIONS.keys()
339
- ]);
332
+ const allProvidedKeys = new Set<string>([...urlOptions.keys(), ...objectOptions.keys()]);
340
333
 
341
- for (const key of allKeys) {
334
+ for (const key of allProvidedKeys) {
342
335
  const values = [];
343
336
  const objectOptionValue = objectOptions.get(key);
344
337
  if (objectOptionValue != null) {
345
338
  values.push(objectOptionValue);
346
339
  }
347
- const urlValue = urlOptions.get(key);
348
- if (urlValue != null) {
349
- values.push(...urlValue);
350
- }
351
- const defaultOptionsValue = DEFAULT_OPTIONS.get(key);
352
- if (defaultOptionsValue != null) {
353
- values.push(defaultOptionsValue);
354
- }
355
- allOptions.set(key, values);
340
+
341
+ const urlValues = urlOptions.get(key) ?? [];
342
+ values.push(...urlValues);
343
+ allProvidedOptions.set(key, values);
356
344
  }
357
345
 
358
- if (allOptions.has('tlsCertificateKeyFile') && !allOptions.has('tlsCertificateFile')) {
359
- allOptions.set('tlsCertificateFile', allOptions.get('tlsCertificateKeyFile'));
346
+ if (
347
+ allProvidedOptions.has('tlsCertificateKeyFile') &&
348
+ !allProvidedOptions.has('tlsCertificateFile')
349
+ ) {
350
+ allProvidedOptions.set('tlsCertificateFile', allProvidedOptions.get('tlsCertificateKeyFile'));
360
351
  }
361
352
 
362
- if (allOptions.has('tls') || allOptions.has('ssl')) {
363
- const tlsAndSslOpts = (allOptions.get('tls') || [])
364
- .concat(allOptions.get('ssl') || [])
353
+ if (allProvidedOptions.has('tls') || allProvidedOptions.has('ssl')) {
354
+ const tlsAndSslOpts = (allProvidedOptions.get('tls') || [])
355
+ .concat(allProvidedOptions.get('ssl') || [])
365
356
  .map(getBoolean.bind(null, 'tls/ssl'));
366
357
  if (new Set(tlsAndSslOpts).size !== 1) {
367
358
  throw new MongoParseError('All values of tls/ssl must be the same.');
368
359
  }
369
360
  }
370
361
 
371
- checkTLSOptions(allOptions);
362
+ checkTLSOptions(allProvidedOptions);
372
363
 
373
364
  const unsupportedOptions = setDifference(
374
- allKeys,
365
+ allProvidedKeys,
375
366
  Array.from(Object.keys(OPTIONS)).map(s => s.toLowerCase())
376
367
  );
377
368
  if (unsupportedOptions.size !== 0) {
@@ -385,9 +376,20 @@ export function parseOptions(
385
376
  // Option parsing and setting
386
377
 
387
378
  for (const [key, descriptor] of Object.entries(OPTIONS)) {
388
- const values = allOptions.get(key);
389
- if (!values || values.length === 0) continue;
390
- setOption(mongoOptions, key, descriptor, values);
379
+ const values = allProvidedOptions.get(key);
380
+ if (!values || values.length === 0) {
381
+ if (DEFAULT_OPTIONS.has(key)) {
382
+ setOption(mongoOptions, key, descriptor, [DEFAULT_OPTIONS.get(key)]);
383
+ }
384
+ } else {
385
+ const { deprecated } = descriptor;
386
+ if (deprecated) {
387
+ const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';
388
+ emitWarning(`${key} is a deprecated option${deprecatedMsg}`);
389
+ }
390
+
391
+ setOption(mongoOptions, key, descriptor, values);
392
+ }
391
393
  }
392
394
 
393
395
  if (mongoOptions.credentials) {
@@ -397,7 +399,7 @@ export function parseOptions(
397
399
  const isOidc = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_OIDC;
398
400
  if (
399
401
  (isGssapi || isX509) &&
400
- allOptions.has('authSource') &&
402
+ allProvidedOptions.has('authSource') &&
401
403
  mongoOptions.credentials.source !== '$external'
402
404
  ) {
403
405
  // If authSource was explicitly given and its incorrect, we error
@@ -409,7 +411,7 @@ export function parseOptions(
409
411
  if (
410
412
  !(isGssapi || isX509 || isAws || isOidc) &&
411
413
  mongoOptions.dbName &&
412
- !allOptions.has('authSource')
414
+ !allProvidedOptions.has('authSource')
413
415
  ) {
414
416
  // inherit the dbName unless GSSAPI or X509, then silently ignore dbName
415
417
  // and there was no specific authSource given
@@ -585,14 +587,9 @@ function setOption(
585
587
  descriptor: OptionDescriptor,
586
588
  values: unknown[]
587
589
  ) {
588
- const { target, type, transform, deprecated } = descriptor;
590
+ const { target, type, transform } = descriptor;
589
591
  const name = target ?? key;
590
592
 
591
- if (deprecated) {
592
- const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';
593
- emitWarning(`${key} is a deprecated option${deprecatedMsg}`);
594
- }
595
-
596
593
  switch (type) {
597
594
  case 'boolean':
598
595
  mongoOptions[name] = getBoolean(name, values[0]);
@@ -866,11 +863,13 @@ export const OPTIONS = {
866
863
  },
867
864
  keepAlive: {
868
865
  default: true,
869
- type: 'boolean'
866
+ type: 'boolean',
867
+ deprecated: 'Will not be able to turn off in the future.'
870
868
  },
871
869
  keepAliveInitialDelay: {
872
870
  default: 120000,
873
- type: 'uint'
871
+ type: 'uint',
872
+ deprecated: 'Will not be configurable in the future.'
874
873
  },
875
874
  loadBalanced: {
876
875
  default: false,
@@ -1292,5 +1291,6 @@ export const DEFAULT_OPTIONS = new CaseInsensitiveMap(
1292
1291
  */
1293
1292
  export const FEATURE_FLAGS = new Set([
1294
1293
  Symbol.for('@@mdb.skipPingOnConnect'),
1295
- Symbol.for('@@mdb.enableMongoLogger')
1294
+ Symbol.for('@@mdb.enableMongoLogger'),
1295
+ Symbol.for('@@mdb.internalLoggerConfig')
1296
1296
  ]);
package/src/constants.ts CHANGED
@@ -23,16 +23,27 @@ export const SERVER_DESCRIPTION_CHANGED = 'serverDescriptionChanged' as const;
23
23
  export const TOPOLOGY_OPENING = 'topologyOpening' as const;
24
24
  export const TOPOLOGY_CLOSED = 'topologyClosed' as const;
25
25
  export const TOPOLOGY_DESCRIPTION_CHANGED = 'topologyDescriptionChanged' as const;
26
+ /** @internal */
26
27
  export const CONNECTION_POOL_CREATED = 'connectionPoolCreated' as const;
28
+ /** @internal */
27
29
  export const CONNECTION_POOL_CLOSED = 'connectionPoolClosed' as const;
30
+ /** @internal */
28
31
  export const CONNECTION_POOL_CLEARED = 'connectionPoolCleared' as const;
32
+ /** @internal */
29
33
  export const CONNECTION_POOL_READY = 'connectionPoolReady' as const;
34
+ /** @internal */
30
35
  export const CONNECTION_CREATED = 'connectionCreated' as const;
36
+ /** @internal */
31
37
  export const CONNECTION_READY = 'connectionReady' as const;
38
+ /** @internal */
32
39
  export const CONNECTION_CLOSED = 'connectionClosed' as const;
40
+ /** @internal */
33
41
  export const CONNECTION_CHECK_OUT_STARTED = 'connectionCheckOutStarted' as const;
42
+ /** @internal */
34
43
  export const CONNECTION_CHECK_OUT_FAILED = 'connectionCheckOutFailed' as const;
44
+ /** @internal */
35
45
  export const CONNECTION_CHECKED_OUT = 'connectionCheckedOut' as const;
46
+ /** @internal */
36
47
  export const CONNECTION_CHECKED_IN = 'connectionCheckedIn' as const;
37
48
  export const CLUSTER_TIME_RECEIVED = 'clusterTimeReceived' as const;
38
49
  export const COMMAND_STARTED = 'commandStarted' as const;
@@ -397,6 +397,7 @@ export abstract class AbstractCursor<
397
397
  * If the iterator returns `false`, iteration will stop.
398
398
  *
399
399
  * @param iterator - The iteration callback.
400
+ * @deprecated - Will be removed in a future release. Use for await...of instead.
400
401
  */
401
402
  async forEach(iterator: (doc: TSchema) => boolean | void): Promise<void> {
402
403
  if (typeof iterator !== 'function') {
package/src/db.ts CHANGED
@@ -232,6 +232,22 @@ export class Db {
232
232
  * @remarks
233
233
  * This command does not inherit options from the MongoClient.
234
234
  *
235
+ * The driver will ensure the following fields are attached to the command sent to the server:
236
+ * - `lsid` - sourced from an implicit session or options.session
237
+ * - `$readPreference` - defaults to primary or can be configured by options.readPreference
238
+ * - `$db` - sourced from the name of this database
239
+ *
240
+ * If the client has a serverApi setting:
241
+ * - `apiVersion`
242
+ * - `apiStrict`
243
+ * - `apiDeprecationErrors`
244
+ *
245
+ * When in a transaction:
246
+ * - `readConcern` - sourced from readConcern set on the TransactionOptions
247
+ * - `writeConcern` - sourced from writeConcern set on the TransactionOptions
248
+ *
249
+ * Attaching any of the above fields to the command will have no effect as the driver will overwrite the value.
250
+ *
235
251
  * @param command - The command to run
236
252
  * @param options - Optional settings for the command
237
253
  */
@@ -405,6 +421,8 @@ export class Db {
405
421
  * @param username - The username for the new user
406
422
  * @param passwordOrOptions - An optional password for the new user, or the options for the command
407
423
  * @param options - Optional settings for the command
424
+ * @deprecated Use the createUser command in `db.command()` instead.
425
+ * @see https://www.mongodb.com/docs/manual/reference/command/createUser/
408
426
  */
409
427
  async addUser(
410
428
  username: string,
package/src/index.ts CHANGED
@@ -204,10 +204,11 @@ export type {
204
204
  MongoCredentialsOptions
205
205
  } from './cmap/auth/mongo_credentials';
206
206
  export type {
207
- OIDCMechanismServerStep1,
207
+ IdPServerInfo,
208
+ IdPServerResponse,
209
+ OIDCCallbackContext,
208
210
  OIDCRefreshFunction,
209
- OIDCRequestFunction,
210
- OIDCRequestTokenResult
211
+ OIDCRequestFunction
211
212
  } from './cmap/auth/mongodb_oidc';
212
213
  export type {
213
214
  BinMsg,
@@ -238,6 +239,7 @@ export type {
238
239
  WaitQueueMember,
239
240
  WithConnectionCallback
240
241
  } from './cmap/connection_pool';
242
+ export type { ClientMetadata, ClientMetadataOptions } from './cmap/handshake/client_metadata';
241
243
  export type {
242
244
  MessageStream,
243
245
  MessageStreamOptions,
@@ -247,7 +249,20 @@ export type { ConnectionPoolMetrics } from './cmap/metrics';
247
249
  export type { StreamDescription, StreamDescriptionOptions } from './cmap/stream_description';
248
250
  export type { CompressorName } from './cmap/wire_protocol/compression';
249
251
  export type { CollectionOptions, CollectionPrivate, ModifyResult } from './collection';
250
- export type { MONGO_CLIENT_EVENTS } from './constants';
252
+ export type {
253
+ CONNECTION_CHECK_OUT_FAILED,
254
+ CONNECTION_CHECK_OUT_STARTED,
255
+ CONNECTION_CHECKED_IN,
256
+ CONNECTION_CHECKED_OUT,
257
+ CONNECTION_CLOSED,
258
+ CONNECTION_CREATED,
259
+ CONNECTION_POOL_CLEARED,
260
+ CONNECTION_POOL_CLOSED,
261
+ CONNECTION_POOL_CREATED,
262
+ CONNECTION_POOL_READY,
263
+ CONNECTION_READY,
264
+ MONGO_CLIENT_EVENTS
265
+ } from './constants';
251
266
  export type {
252
267
  AbstractCursorEvents,
253
268
  AbstractCursorOptions,
@@ -289,6 +304,11 @@ export type {
289
304
  WithSessionCallback
290
305
  } from './mongo_client';
291
306
  export type {
307
+ Log,
308
+ LogConvertible,
309
+ Loggable,
310
+ LoggableEvent,
311
+ MongoDBLogWritable,
292
312
  MongoLoggableComponent,
293
313
  MongoLogger,
294
314
  MongoLoggerEnvOptions,
@@ -463,8 +483,6 @@ export type { Transaction, TransactionOptions, TxnState } from './transactions';
463
483
  export type {
464
484
  BufferPool,
465
485
  Callback,
466
- ClientMetadata,
467
- ClientMetadataOptions,
468
486
  EventEmitterWithState,
469
487
  HostAddress,
470
488
  List,