mongodb 5.2.0 → 5.3.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 (60) hide show
  1. package/lib/admin.js +2 -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/command_monitoring_events.js +6 -0
  6. package/lib/cmap/command_monitoring_events.js.map +1 -1
  7. package/lib/cmap/connect.js +1 -0
  8. package/lib/cmap/connect.js.map +1 -1
  9. package/lib/cmap/connection.js.map +1 -1
  10. package/lib/cmap/connection_pool.js +7 -3
  11. package/lib/cmap/connection_pool.js.map +1 -1
  12. package/lib/cmap/connection_pool_events.js +28 -3
  13. package/lib/cmap/connection_pool_events.js.map +1 -1
  14. package/lib/cmap/handshake/client_metadata.js +173 -0
  15. package/lib/cmap/handshake/client_metadata.js.map +1 -0
  16. package/lib/connection_string.js +40 -53
  17. package/lib/connection_string.js.map +1 -1
  18. package/lib/constants.js +11 -0
  19. package/lib/constants.js.map +1 -1
  20. package/lib/cursor/abstract_cursor.js +1 -0
  21. package/lib/cursor/abstract_cursor.js.map +1 -1
  22. package/lib/db.js +2 -0
  23. package/lib/db.js.map +1 -1
  24. package/lib/mongo_client.js +1 -0
  25. package/lib/mongo_client.js.map +1 -1
  26. package/lib/mongo_logger.js +224 -27
  27. package/lib/mongo_logger.js.map +1 -1
  28. package/lib/operations/add_user.js.map +1 -1
  29. package/lib/operations/find.js +0 -7
  30. package/lib/operations/find.js.map +1 -1
  31. package/lib/operations/update.js.map +1 -1
  32. package/lib/sdam/srv_polling.js +1 -15
  33. package/lib/sdam/srv_polling.js.map +1 -1
  34. package/lib/sdam/topology.js.map +1 -1
  35. package/lib/utils.js +33 -35
  36. package/lib/utils.js.map +1 -1
  37. package/mongodb.d.ts +139 -23
  38. package/package.json +2 -2
  39. package/src/admin.ts +2 -0
  40. package/src/bulk/common.ts +28 -7
  41. package/src/cmap/command_monitoring_events.ts +13 -1
  42. package/src/cmap/connect.ts +3 -1
  43. package/src/cmap/connection.ts +3 -1
  44. package/src/cmap/connection_pool.ts +14 -4
  45. package/src/cmap/connection_pool_events.ts +68 -6
  46. package/src/cmap/handshake/client_metadata.ts +272 -0
  47. package/src/collection.ts +3 -3
  48. package/src/connection_string.ts +44 -55
  49. package/src/constants.ts +11 -0
  50. package/src/cursor/abstract_cursor.ts +1 -0
  51. package/src/db.ts +2 -0
  52. package/src/index.ts +20 -3
  53. package/src/mongo_client.ts +25 -4
  54. package/src/mongo_logger.ts +326 -43
  55. package/src/operations/add_user.ts +8 -2
  56. package/src/operations/find.ts +0 -10
  57. package/src/operations/update.ts +8 -4
  58. package/src/sdam/srv_polling.ts +1 -16
  59. package/src/sdam/topology.ts +1 -3
  60. package/src/utils.ts +37 -73
@@ -212,37 +212,58 @@ export class BulkWriteResult {
212
212
  return this.result.ok;
213
213
  }
214
214
 
215
- /** The number of inserted documents */
215
+ /**
216
+ * The number of inserted documents
217
+ * @deprecated Use insertedCount instead.
218
+ */
216
219
  get nInserted(): number {
217
220
  return this.result.nInserted;
218
221
  }
219
222
 
220
- /** Number of upserted documents */
223
+ /**
224
+ * Number of upserted documents
225
+ * @deprecated User upsertedCount instead.
226
+ */
221
227
  get nUpserted(): number {
222
228
  return this.result.nUpserted;
223
229
  }
224
230
 
225
- /** Number of matched documents */
231
+ /**
232
+ * Number of matched documents
233
+ * @deprecated Use matchedCount instead.
234
+ */
226
235
  get nMatched(): number {
227
236
  return this.result.nMatched;
228
237
  }
229
238
 
230
- /** Number of documents updated physically on disk */
239
+ /**
240
+ * Number of documents updated physically on disk
241
+ * @deprecated Use modifiedCount instead.
242
+ */
231
243
  get nModified(): number {
232
244
  return this.result.nModified;
233
245
  }
234
246
 
235
- /** Number of removed documents */
247
+ /**
248
+ * Number of removed documents
249
+ * @deprecated Use deletedCount instead.
250
+ */
236
251
  get nRemoved(): number {
237
252
  return this.result.nRemoved;
238
253
  }
239
254
 
240
- /** Returns an array of all inserted ids */
255
+ /**
256
+ * Returns an array of all inserted ids
257
+ * @deprecated Use insertedIds instead.
258
+ */
241
259
  getInsertedIds(): Document[] {
242
260
  return this.result.insertedIds;
243
261
  }
244
262
 
245
- /** Returns an array of all upserted ids */
263
+ /**
264
+ * Returns an array of all upserted ids
265
+ * @deprecated Use upsertedIds instead.
266
+ */
246
267
  getUpsertedIds(): Document[] {
247
268
  return this.result.upserted;
248
269
  }
@@ -1,5 +1,11 @@
1
1
  import type { Document, ObjectId } from '../bson';
2
- import { LEGACY_HELLO_COMMAND, LEGACY_HELLO_COMMAND_CAMEL_CASE } from '../constants';
2
+ import {
3
+ COMMAND_FAILED,
4
+ COMMAND_STARTED,
5
+ COMMAND_SUCCEEDED,
6
+ LEGACY_HELLO_COMMAND,
7
+ LEGACY_HELLO_COMMAND_CAMEL_CASE
8
+ } from '../constants';
3
9
  import { calculateDurationInMs, deepCopy } from '../utils';
4
10
  import { Msg, WriteProtocolMessageType } from './commands';
5
11
  import type { Connection } from './connection';
@@ -18,6 +24,8 @@ export class CommandStartedEvent {
18
24
  address: string;
19
25
  connectionId?: string | number;
20
26
  serviceId?: ObjectId;
27
+ /** @internal */
28
+ name = COMMAND_STARTED;
21
29
 
22
30
  /**
23
31
  * Create a started event
@@ -65,6 +73,8 @@ export class CommandSucceededEvent {
65
73
  commandName: string;
66
74
  reply: unknown;
67
75
  serviceId?: ObjectId;
76
+ /** @internal */
77
+ name = COMMAND_SUCCEEDED;
68
78
 
69
79
  /**
70
80
  * Create a succeeded event
@@ -113,6 +123,8 @@ export class CommandFailedEvent {
113
123
  commandName: string;
114
124
  failure: Error;
115
125
  serviceId?: ObjectId;
126
+ /** @internal */
127
+ name = COMMAND_FAILED;
116
128
 
117
129
  /**
118
130
  * Create a failure event
@@ -17,7 +17,7 @@ import {
17
17
  MongoRuntimeError,
18
18
  needsRetryableWriteLabel
19
19
  } from '../error';
20
- import { Callback, ClientMetadata, HostAddress, ns } from '../utils';
20
+ import { Callback, HostAddress, ns } from '../utils';
21
21
  import { AuthContext, AuthProvider } from './auth/auth_provider';
22
22
  import { GSSAPI } from './auth/gssapi';
23
23
  import { MongoCR } from './auth/mongocr';
@@ -28,6 +28,7 @@ import { AuthMechanism } from './auth/providers';
28
28
  import { ScramSHA1, ScramSHA256 } from './auth/scram';
29
29
  import { X509 } from './auth/x509';
30
30
  import { CommandOptions, Connection, ConnectionOptions, CryptoConnection } from './connection';
31
+ import type { ClientMetadata } from './handshake/client_metadata';
31
32
  import {
32
33
  MAX_SUPPORTED_SERVER_VERSION,
33
34
  MAX_SUPPORTED_WIRE_VERSION,
@@ -400,6 +401,7 @@ function makeConnection(options: MakeConnectionOptions, _callback: Callback<Stre
400
401
 
401
402
  if ('authorizationError' in socket) {
402
403
  if (socket.authorizationError && rejectUnauthorized) {
404
+ // TODO(NODE-5192): wrap this with a MongoError subclass
403
405
  return callback(socket.authorizationError);
404
406
  }
405
407
  }
@@ -29,7 +29,6 @@ import { applySession, ClientSession, updateSessionFromResponse } from '../sessi
29
29
  import {
30
30
  calculateDurationInMs,
31
31
  Callback,
32
- ClientMetadata,
33
32
  HostAddress,
34
33
  maxWireVersion,
35
34
  MongoDBNamespace,
@@ -46,6 +45,7 @@ import {
46
45
  } from './command_monitoring_events';
47
46
  import { BinMsg, Msg, Query, Response, WriteProtocolMessageType } from './commands';
48
47
  import type { Stream } from './connect';
48
+ import type { ClientMetadata } from './handshake/client_metadata';
49
49
  import { MessageStream, OperationDescription } from './message_stream';
50
50
  import { StreamDescription, StreamDescriptionOptions } from './stream_description';
51
51
  import { getReadPreference, isSharded } from './wire_protocol/shared';
@@ -122,7 +122,9 @@ export interface ConnectionOptions
122
122
  credentials?: MongoCredentials;
123
123
  connectTimeoutMS?: number;
124
124
  tls: boolean;
125
+ /** @deprecated - Will not be able to turn off in the future. */
125
126
  keepAlive?: boolean;
127
+ /** @deprecated - Will not be configurable in the future. */
126
128
  keepAliveInitialDelay?: number;
127
129
  noDelay?: boolean;
128
130
  socketTimeoutMS?: number;
@@ -641,7 +641,10 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
641
641
  }
642
642
  }
643
643
 
644
- private destroyConnection(connection: Connection, reason: string) {
644
+ private destroyConnection(
645
+ connection: Connection,
646
+ reason: 'error' | 'idle' | 'stale' | 'poolClosed'
647
+ ) {
645
648
  this.emit(
646
649
  ConnectionPool.CONNECTION_CLOSED,
647
650
  new ConnectionClosedEvent(this, connection, reason)
@@ -701,7 +704,13 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
701
704
  this[kPending]--;
702
705
  this.emit(
703
706
  ConnectionPool.CONNECTION_CLOSED,
704
- new ConnectionClosedEvent(this, { id: connectOptions.id, serviceId: undefined }, 'error')
707
+ new ConnectionClosedEvent(
708
+ this,
709
+ { id: connectOptions.id, serviceId: undefined },
710
+ 'error',
711
+ // TODO(NODE-5192): Remove this cast
712
+ err as MongoError
713
+ )
705
714
  );
706
715
  if (err instanceof MongoNetworkError || err instanceof MongoServerError) {
707
716
  err.connectionGeneration = connectOptions.generation;
@@ -812,7 +821,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
812
821
  const error = this.closed ? new PoolClosedError(this) : new PoolClearedError(this);
813
822
  this.emit(
814
823
  ConnectionPool.CONNECTION_CHECK_OUT_FAILED,
815
- new ConnectionCheckOutFailedEvent(this, reason)
824
+ new ConnectionCheckOutFailedEvent(this, reason, error)
816
825
  );
817
826
  if (waitQueueMember.timer) {
818
827
  clearTimeout(waitQueueMember.timer);
@@ -865,7 +874,8 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
865
874
  if (err) {
866
875
  this.emit(
867
876
  ConnectionPool.CONNECTION_CHECK_OUT_FAILED,
868
- new ConnectionCheckOutFailedEvent(this, 'connectionError')
877
+ // TODO(NODE-5192): Remove this cast
878
+ new ConnectionCheckOutFailedEvent(this, 'connectionError', err as MongoError)
869
879
  );
870
880
  } else if (connection) {
871
881
  this[kCheckedOut].add(connection);
@@ -1,5 +1,18 @@
1
1
  import type { ObjectId } from '../bson';
2
- import type { AnyError } from '../error';
2
+ import {
3
+ CONNECTION_CHECK_OUT_FAILED,
4
+ CONNECTION_CHECK_OUT_STARTED,
5
+ CONNECTION_CHECKED_IN,
6
+ CONNECTION_CHECKED_OUT,
7
+ CONNECTION_CLOSED,
8
+ CONNECTION_CREATED,
9
+ CONNECTION_POOL_CLEARED,
10
+ CONNECTION_POOL_CLOSED,
11
+ CONNECTION_POOL_CREATED,
12
+ CONNECTION_POOL_READY,
13
+ CONNECTION_READY
14
+ } from '../constants';
15
+ import type { MongoError } from '../error';
3
16
  import type { Connection } from './connection';
4
17
  import type { ConnectionPool, ConnectionPoolOptions } from './connection_pool';
5
18
 
@@ -8,11 +21,24 @@ import type { ConnectionPool, ConnectionPoolOptions } from './connection_pool';
8
21
  * @public
9
22
  * @category Event
10
23
  */
11
- export class ConnectionPoolMonitoringEvent {
24
+ export abstract class ConnectionPoolMonitoringEvent {
12
25
  /** A timestamp when the event was created */
13
26
  time: Date;
14
27
  /** The address (host/port pair) of the pool */
15
28
  address: string;
29
+ /** @internal */
30
+ abstract name:
31
+ | typeof CONNECTION_CHECK_OUT_FAILED
32
+ | typeof CONNECTION_CHECK_OUT_STARTED
33
+ | typeof CONNECTION_CHECKED_IN
34
+ | typeof CONNECTION_CHECKED_OUT
35
+ | typeof CONNECTION_CLOSED
36
+ | typeof CONNECTION_CREATED
37
+ | typeof CONNECTION_POOL_CLEARED
38
+ | typeof CONNECTION_POOL_CLOSED
39
+ | typeof CONNECTION_POOL_CREATED
40
+ | typeof CONNECTION_POOL_READY
41
+ | typeof CONNECTION_READY;
16
42
 
17
43
  /** @internal */
18
44
  constructor(pool: ConnectionPool) {
@@ -29,6 +55,8 @@ export class ConnectionPoolMonitoringEvent {
29
55
  export class ConnectionPoolCreatedEvent extends ConnectionPoolMonitoringEvent {
30
56
  /** The options used to create this connection pool */
31
57
  options?: ConnectionPoolOptions;
58
+ /** @internal */
59
+ name = CONNECTION_POOL_CREATED;
32
60
 
33
61
  /** @internal */
34
62
  constructor(pool: ConnectionPool) {
@@ -43,6 +71,9 @@ export class ConnectionPoolCreatedEvent extends ConnectionPoolMonitoringEvent {
43
71
  * @category Event
44
72
  */
45
73
  export class ConnectionPoolReadyEvent extends ConnectionPoolMonitoringEvent {
74
+ /** @internal */
75
+ name = CONNECTION_POOL_READY;
76
+
46
77
  /** @internal */
47
78
  constructor(pool: ConnectionPool) {
48
79
  super(pool);
@@ -55,6 +86,9 @@ export class ConnectionPoolReadyEvent extends ConnectionPoolMonitoringEvent {
55
86
  * @category Event
56
87
  */
57
88
  export class ConnectionPoolClosedEvent extends ConnectionPoolMonitoringEvent {
89
+ /** @internal */
90
+ name = CONNECTION_POOL_CLOSED;
91
+
58
92
  /** @internal */
59
93
  constructor(pool: ConnectionPool) {
60
94
  super(pool);
@@ -69,6 +103,8 @@ export class ConnectionPoolClosedEvent extends ConnectionPoolMonitoringEvent {
69
103
  export class ConnectionCreatedEvent extends ConnectionPoolMonitoringEvent {
70
104
  /** A monotonically increasing, per-pool id for the newly created connection */
71
105
  connectionId: number | '<monitor>';
106
+ /** @internal */
107
+ name = CONNECTION_CREATED;
72
108
 
73
109
  /** @internal */
74
110
  constructor(pool: ConnectionPool, connection: { id: number | '<monitor>' }) {
@@ -85,6 +121,8 @@ export class ConnectionCreatedEvent extends ConnectionPoolMonitoringEvent {
85
121
  export class ConnectionReadyEvent extends ConnectionPoolMonitoringEvent {
86
122
  /** The id of the connection */
87
123
  connectionId: number | '<monitor>';
124
+ /** @internal */
125
+ name = CONNECTION_READY;
88
126
 
89
127
  /** @internal */
90
128
  constructor(pool: ConnectionPool, connection: Connection) {
@@ -104,17 +142,23 @@ export class ConnectionClosedEvent extends ConnectionPoolMonitoringEvent {
104
142
  /** The reason the connection was closed */
105
143
  reason: string;
106
144
  serviceId?: ObjectId;
145
+ /** @internal */
146
+ name = CONNECTION_CLOSED;
147
+ /** @internal */
148
+ error: MongoError | null;
107
149
 
108
150
  /** @internal */
109
151
  constructor(
110
152
  pool: ConnectionPool,
111
153
  connection: Pick<Connection, 'id' | 'serviceId'>,
112
- reason: string
154
+ reason: 'idle' | 'stale' | 'poolClosed' | 'error',
155
+ error?: MongoError
113
156
  ) {
114
157
  super(pool);
115
158
  this.connectionId = connection.id;
116
- this.reason = reason || 'unknown';
159
+ this.reason = reason;
117
160
  this.serviceId = connection.serviceId;
161
+ this.error = error ?? null;
118
162
  }
119
163
  }
120
164
 
@@ -124,6 +168,9 @@ export class ConnectionClosedEvent extends ConnectionPoolMonitoringEvent {
124
168
  * @category Event
125
169
  */
126
170
  export class ConnectionCheckOutStartedEvent extends ConnectionPoolMonitoringEvent {
171
+ /** @internal */
172
+ name = CONNECTION_CHECK_OUT_STARTED;
173
+
127
174
  /** @internal */
128
175
  constructor(pool: ConnectionPool) {
129
176
  super(pool);
@@ -137,12 +184,21 @@ export class ConnectionCheckOutStartedEvent extends ConnectionPoolMonitoringEven
137
184
  */
138
185
  export class ConnectionCheckOutFailedEvent extends ConnectionPoolMonitoringEvent {
139
186
  /** The reason the attempt to check out failed */
140
- reason: AnyError | string;
187
+ reason: string;
188
+ /** @internal */
189
+ error?: MongoError;
190
+ /** @internal */
191
+ name = CONNECTION_CHECK_OUT_FAILED;
141
192
 
142
193
  /** @internal */
143
- constructor(pool: ConnectionPool, reason: AnyError | string) {
194
+ constructor(
195
+ pool: ConnectionPool,
196
+ reason: 'poolClosed' | 'timeout' | 'connectionError',
197
+ error?: MongoError
198
+ ) {
144
199
  super(pool);
145
200
  this.reason = reason;
201
+ this.error = error;
146
202
  }
147
203
  }
148
204
 
@@ -154,6 +210,8 @@ export class ConnectionCheckOutFailedEvent extends ConnectionPoolMonitoringEvent
154
210
  export class ConnectionCheckedOutEvent extends ConnectionPoolMonitoringEvent {
155
211
  /** The id of the connection */
156
212
  connectionId: number | '<monitor>';
213
+ /** @internal */
214
+ name = CONNECTION_CHECKED_OUT;
157
215
 
158
216
  /** @internal */
159
217
  constructor(pool: ConnectionPool, connection: Connection) {
@@ -170,6 +228,8 @@ export class ConnectionCheckedOutEvent extends ConnectionPoolMonitoringEvent {
170
228
  export class ConnectionCheckedInEvent extends ConnectionPoolMonitoringEvent {
171
229
  /** The id of the connection */
172
230
  connectionId: number | '<monitor>';
231
+ /** @internal */
232
+ name = CONNECTION_CHECKED_IN;
173
233
 
174
234
  /** @internal */
175
235
  constructor(pool: ConnectionPool, connection: Connection) {
@@ -188,6 +248,8 @@ export class ConnectionPoolClearedEvent extends ConnectionPoolMonitoringEvent {
188
248
  serviceId?: ObjectId;
189
249
 
190
250
  interruptInUseConnections?: boolean;
251
+ /** @internal */
252
+ name = CONNECTION_POOL_CLEARED;
191
253
 
192
254
  /** @internal */
193
255
  constructor(
@@ -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
+ }
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(