opusdev/vector-similarity-api
1
1import { promises as fs } from 'fs';2import type { TcpNetConnectOpts } from 'net';3import type { ConnectionOptions as TLSConnectionOptions, TLSSocketOptions } from 'tls';4 5import { type ServerCommandOptions, type TimeoutContext } from '.';6import { type BSONSerializeOptions, type Document, resolveBSONOptions } from './bson';7import { ChangeStream, type ChangeStreamDocument, type ChangeStreamOptions } from './change_stream';8import type { AutoEncrypter, AutoEncryptionOptions } from './client-side-encryption/auto_encrypter';9import {10 type AuthMechanismProperties,11 DEFAULT_ALLOWED_HOSTS,12 type MongoCredentials13} from './cmap/auth/mongo_credentials';14import { type TokenCache } from './cmap/auth/mongodb_oidc/token_cache';15import { AuthMechanism } from './cmap/auth/providers';16import type { LEGAL_TCP_SOCKET_OPTIONS, LEGAL_TLS_SOCKET_OPTIONS } from './cmap/connect';17import type { Connection } from './cmap/connection';18import {19 addContainerMetadata,20 type ClientMetadata,21 isDriverInfoEqual,22 makeClientMetadata23} from './cmap/handshake/client_metadata';24import type { CompressorName } from './cmap/wire_protocol/compression';25import { MongoDBResponse } from './cmap/wire_protocol/responses';26import { parseOptions, resolveSRVRecord } from './connection_string';27import { MONGO_CLIENT_EVENTS } from './constants';28import { type AbstractCursor } from './cursor/abstract_cursor';29import { Db, type DbOptions } from './db';30import type { Encrypter } from './encrypter';31import { MongoInvalidArgumentError } from './error';32import { MongoClientAuthProviders } from './mongo_client_auth_providers';33import {34 type LogComponentSeveritiesClientOptions,35 type MongoDBLogWritable,36 MongoLogger,37 type MongoLoggerOptions,38 SeverityLevel39} from './mongo_logger';40import { TypedEventEmitter } from './mongo_types';41import {42 type ClientBulkWriteModel,43 type ClientBulkWriteOptions,44 type ClientBulkWriteResult45} from './operations/client_bulk_write/common';46import { ClientBulkWriteExecutor } from './operations/client_bulk_write/executor';47import { executeOperation } from './operations/execute_operation';48import { AbstractOperation } from './operations/operation';49import type { ReadConcern, ReadConcernLevel, ReadConcernLike } from './read_concern';50import { ReadPreference, type ReadPreferenceMode } from './read_preference';51import { type AsyncDisposable, configureResourceManagement } from './resource_management';52import type { ServerMonitoringMode } from './sdam/monitor';53import type { TagSet } from './sdam/server_description';54import { readPreferenceServerSelector } from './sdam/server_selection';55import type { SrvPoller } from './sdam/srv_polling';56import { Topology, type TopologyEvents } from './sdam/topology';57import { ClientSession, type ClientSessionOptions, ServerSessionPool } from './sessions';58import {59 COSMOS_DB_CHECK,60 COSMOS_DB_MSG,61 DOCUMENT_DB_CHECK,62 DOCUMENT_DB_MSG,63 type HostAddress,64 hostMatchesWildcards,65 isHostMatch,66 MongoDBNamespace,67 noop,68 ns,69 resolveOptions,70 squashError71} from './utils';72import type { W, WriteConcern, WriteConcernSettings } from './write_concern';73 74/** @public */75export const ServerApiVersion = Object.freeze({76 v1: '1'77} as const);78 79/** @public */80export type ServerApiVersion = (typeof ServerApiVersion)[keyof typeof ServerApiVersion];81 82/** @public */83export interface ServerApi {84 version: ServerApiVersion;85 strict?: boolean;86 deprecationErrors?: boolean;87}88 89/** @public */90export interface DriverInfo {91 name?: string;92 version?: string;93 platform?: string;94}95 96/** @public */97export interface Auth {98 /** The username for auth */99 username?: string;100 /** The password for auth */101 password?: string;102}103 104/** @public */105export interface PkFactory {106 createPk(): any;107}108 109/** @public */110export type SupportedTLSConnectionOptions = Pick<111 TLSConnectionOptions & {112 allowPartialTrustChain?: boolean;113 },114 (typeof LEGAL_TLS_SOCKET_OPTIONS)[number]115>;116 117/** @public */118export type SupportedTLSSocketOptions = Pick<119 TLSSocketOptions,120 Extract<keyof TLSSocketOptions, (typeof LEGAL_TLS_SOCKET_OPTIONS)[number]>121>;122 123/** @public */124export type SupportedSocketOptions = Pick<125 TcpNetConnectOpts & {126 autoSelectFamily?: boolean;127 autoSelectFamilyAttemptTimeout?: number;128 /** Node.JS socket option to set the time the first keepalive probe is sent on an idle socket. Defaults to 120000ms */129 keepAliveInitialDelay?: number;130 },131 (typeof LEGAL_TCP_SOCKET_OPTIONS)[number]132>;133 134/** @public */135export type SupportedNodeConnectionOptions = SupportedTLSConnectionOptions &136 SupportedTLSSocketOptions &137 SupportedSocketOptions;138 139/**140 * Describes all possible URI query options for the mongo client141 * @public142 * @see https://www.mongodb.com/docs/manual/reference/connection-string143 */144export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeConnectionOptions {145 /** Specifies the name of the replica set, if the mongod is a member of a replica set. */146 replicaSet?: string;147 /**148 * @experimental149 * Specifies the time an operation will run until it throws a timeout error150 */151 timeoutMS?: number;152 /** Enables or disables TLS/SSL for the connection. */153 tls?: boolean;154 /** A boolean to enable or disables TLS/SSL for the connection. (The ssl option is equivalent to the tls option.) */155 ssl?: boolean;156 /** Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key. */157 tlsCertificateKeyFile?: string;158 /** Specifies the password to de-crypt the tlsCertificateKeyFile. */159 tlsCertificateKeyFilePassword?: string;160 /** Specifies the location of a local .pem file that contains the root certificate chain from the Certificate Authority. This file is used to validate the certificate presented by the mongod/mongos instance. */161 tlsCAFile?: string;162 /** Specifies the location of a local CRL .pem file that contains the client revokation list. */163 tlsCRLFile?: string;164 /** Bypasses validation of the certificates presented by the mongod/mongos instance */165 tlsAllowInvalidCertificates?: boolean;166 /** Disables hostname validation of the certificate presented by the mongod/mongos instance. */167 tlsAllowInvalidHostnames?: boolean;168 /** Disables various certificate validations. */169 tlsInsecure?: boolean;170 /** The time in milliseconds to attempt a connection before timing out. */171 connectTimeoutMS?: number;172 /** The time in milliseconds to attempt a send or receive on a socket before the attempt times out. */173 socketTimeoutMS?: number;174 /** An array or comma-delimited string of compressors to enable network compression for communication between this client and a mongod/mongos instance. */175 compressors?: CompressorName[] | string;176 /** An integer that specifies the compression level if using zlib for network compression. */177 zlibCompressionLevel?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;178 /** The maximum number of hosts to connect to when using an srv connection string, a setting of `0` means unlimited hosts */179 srvMaxHosts?: number;180 /**181 * Modifies the srv URI to look like:182 *183 * `_{srvServiceName}._tcp.{hostname}.{domainname}`184 *185 * Querying this DNS URI is expected to respond with SRV records186 */187 srvServiceName?: string;188 /** The maximum number of connections in the connection pool. */189 maxPoolSize?: number;190 /** The minimum number of connections in the connection pool. */191 minPoolSize?: number;192 /** The maximum number of connections that may be in the process of being established concurrently by the connection pool. */193 maxConnecting?: number;194 /**195 * The maximum amount of time a connection should remain idle in the connection pool before being marked idle, in milliseconds.196 * If specified, this must be a number greater than or equal to 0, where 0 means there is no limit. Defaults to 0. After this197 * time passes, the idle collection can be automatically cleaned up in the background.198 */199 maxIdleTimeMS?: number;200 /** The maximum time in milliseconds that a thread can wait for a connection to become available. */201 waitQueueTimeoutMS?: number;202 /** Specify a read concern for the collection (only MongoDB 3.2 or higher supported) */203 readConcern?: ReadConcernLike;204 /** The level of isolation */205 readConcernLevel?: ReadConcernLevel;206 /** Specifies the read preferences for this connection */207 readPreference?: ReadPreferenceMode | ReadPreference;208 /** Specifies, in seconds, how stale a secondary can be before the client stops using it for read operations. */209 maxStalenessSeconds?: number;210 /** Specifies the tags document as a comma-separated list of colon-separated key-value pairs. */211 readPreferenceTags?: TagSet[];212 /** The auth settings for when connection to server. */213 auth?: Auth;214 /** Specify the database name associated with the user’s credentials. */215 authSource?: string;216 /** Specify the authentication mechanism that MongoDB will use to authenticate the connection. */217 authMechanism?: AuthMechanism;218 /** Specify properties for the specified authMechanism as a comma-separated list of colon-separated key-value pairs. */219 authMechanismProperties?: AuthMechanismProperties;220 /** The size (in milliseconds) of the latency window for selecting among multiple suitable MongoDB instances. */221 localThresholdMS?: number;222 /** Specifies how long (in milliseconds) to block for server selection before throwing an exception. */223 serverSelectionTimeoutMS?: number;224 /** heartbeatFrequencyMS controls when the driver checks the state of the MongoDB deployment. Specify the interval (in milliseconds) between checks, counted from the end of the previous check until the beginning of the next one. */225 heartbeatFrequencyMS?: number;226 /** Sets the minimum heartbeat frequency. In the event that the driver has to frequently re-check a server's availability, it will wait at least this long since the previous check to avoid wasted effort. */227 minHeartbeatFrequencyMS?: number;228 /** The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections */229 appName?: string;230 /** Enables retryable reads. */231 retryReads?: boolean;232 /** Enable retryable writes. */233 retryWrites?: boolean;234 /** Allow a driver to force a Single topology type with a connection string containing one host */235 directConnection?: boolean;236 /** Instruct the driver it is connecting to a load balancer fronting a mongos like service */237 loadBalanced?: boolean;238 /**239 * The write concern w value240 * @deprecated Please use the `writeConcern` option instead241 */242 w?: W;243 /**244 * The write concern timeout245 * @deprecated Please use the `writeConcern` option instead246 */247 wtimeoutMS?: number;248 /**249 * The journal write concern250 * @deprecated Please use the `writeConcern` option instead251 */252 journal?: boolean;253 /**254 * A MongoDB WriteConcern, which describes the level of acknowledgement255 * requested from MongoDB for write operations.256 *257 * @see https://www.mongodb.com/docs/manual/reference/write-concern/258 */259 writeConcern?: WriteConcern | WriteConcernSettings;260 /** TCP Connection no delay */261 noDelay?: boolean;262 /** Force server to assign `_id` values instead of driver */263 forceServerObjectId?: boolean;264 /** A primary key factory function for generation of custom `_id` keys */265 pkFactory?: PkFactory;266 /** Enable command monitoring for this client */267 monitorCommands?: boolean;268 /** Server API version */269 serverApi?: ServerApi | ServerApiVersion;270 /**271 * Optionally enable in-use auto encryption272 *273 * @remarks274 * Automatic encryption is an enterprise only feature that only applies to operations on a collection. Automatic encryption is not supported for operations on a database or view, and operations that are not bypassed will result in error275 * (see [libmongocrypt: Auto Encryption Allow-List](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md#libmongocrypt-auto-encryption-allow-list)). To bypass automatic encryption for all operations, set bypassAutoEncryption=true in AutoEncryptionOpts.276 *277 * Automatic encryption requires the authenticated user to have the [listCollections privilege action](https://www.mongodb.com/docs/manual/reference/command/listCollections/#dbcmd.listCollections).278 *279 * If a MongoClient with a limited connection pool size (i.e a non-zero maxPoolSize) is configured with AutoEncryptionOptions, a separate internal MongoClient is created if any of the following are true:280 * - AutoEncryptionOptions.keyVaultClient is not passed.281 * - AutoEncryptionOptions.bypassAutomaticEncryption is false.282 *283 * If an internal MongoClient is created, it is configured with the same options as the parent MongoClient except minPoolSize is set to 0 and AutoEncryptionOptions is omitted.284 */285 autoEncryption?: AutoEncryptionOptions;286 /**287 * Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver288 /* @deprecated - Will be made internal in a future major release.289 */290 driverInfo?: DriverInfo;291 /** Configures a Socks5 proxy host used for creating TCP connections. */292 proxyHost?: string;293 /** Configures a Socks5 proxy port used for creating TCP connections. */294 proxyPort?: number;295 /** Configures a Socks5 proxy username when the proxy in proxyHost requires username/password authentication. */296 proxyUsername?: string;297 /** Configures a Socks5 proxy password when the proxy in proxyHost requires username/password authentication. */298 proxyPassword?: string;299 /** Instructs the driver monitors to use a specific monitoring mode */300 serverMonitoringMode?: ServerMonitoringMode;301 /**302 * @public303 * Specifies the destination of the driver's logging. The default is stderr.304 */305 mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;306 /**307 * @public308 * Enable logging level per component or use `default` to control any unset components.309 */310 mongodbLogComponentSeverities?: LogComponentSeveritiesClientOptions;311 /**312 * @public313 * All BSON documents are stringified to EJSON. This controls the maximum length of those strings.314 * It is defaulted to 1000.315 */316 mongodbLogMaxDocumentLength?: number;317 318 /** @internal */319 srvPoller?: SrvPoller;320 /** @internal */321 connectionType?: typeof Connection;322 /** @internal */323 __skipPingOnConnect?: boolean;324}325 326/** @public */327export type WithSessionCallback<T = unknown> = (session: ClientSession) => Promise<T>;328 329/** @internal */330export interface MongoClientPrivate {331 url: string;332 bsonOptions: BSONSerializeOptions;333 namespace: MongoDBNamespace;334 hasBeenClosed: boolean;335 authProviders: MongoClientAuthProviders;336 /**337 * We keep a reference to the sessions that are acquired from the pool.338 * - used to track and close all sessions in client.close() (which is non-standard behavior)339 * - used to notify the leak checker in our tests if test author forgot to clean up explicit sessions340 */341 readonly activeSessions: Set<ClientSession>;342 /**343 * We keep a reference to the cursors that are created from this client.344 * - used to track and close all cursors in client.close().345 * Cursors in this set are ones that still need to have their close method invoked (no other conditions are considered)346 */347 readonly activeCursors: Set<AbstractCursor>;348 readonly sessionPool: ServerSessionPool;349 readonly options: MongoOptions;350 readonly readConcern?: ReadConcern;351 readonly writeConcern?: WriteConcern;352 readonly readPreference: ReadPreference;353 readonly isMongoClient: true;354}355 356/** @public */357export type MongoClientEvents = Pick<TopologyEvents, (typeof MONGO_CLIENT_EVENTS)[number]> & {358 // In previous versions the open event emitted a topology, in an effort to no longer359 // expose internals but continue to expose this useful event API, it now emits a mongoClient360 open(mongoClient: MongoClient): void;361};362 363/**364 * @public365 *366 * The **MongoClient** class is a class that allows for making Connections to MongoDB.367 *368 * **NOTE:** The programmatically provided options take precedence over the URI options.369 *370 * @remarks371 *372 * A MongoClient is the entry point to connecting to a MongoDB server.373 *374 * It handles a multitude of features on your application's behalf:375 * - **Server Host Connection Configuration**: A MongoClient is responsible for reading TLS cert, ca, and crl files if provided.376 * - **SRV Record Polling**: A "`mongodb+srv`" style connection string is used to have the MongoClient resolve DNS SRV records of all server hostnames which the driver periodically monitors for changes and adjusts its current view of hosts correspondingly.377 * - **Server Monitoring**: The MongoClient automatically keeps monitoring the health of server nodes in your cluster to reach out to the correct and lowest latency one available.378 * - **Connection Pooling**: To avoid paying the cost of rebuilding a connection to the server on every operation the MongoClient keeps idle connections preserved for reuse.379 * - **Session Pooling**: The MongoClient creates logical sessions that enable retryable writes, causal consistency, and transactions. It handles pooling these sessions for reuse in subsequent operations.380 * - **Cursor Operations**: A MongoClient's cursors use the health monitoring system to send the request for more documents to the same server the query began on.381 * - **Mongocryptd process**: When using auto encryption, a MongoClient will launch a `mongocryptd` instance for handling encryption if the mongocrypt shared library isn't in use.382 *383 * There are many more features of a MongoClient that are not listed above.384 *385 * In order to enable these features, a number of asynchronous Node.js resources are established by the driver: Timers, FS Requests, Sockets, etc.386 * For details on cleanup, please refer to the MongoClient `close()` documentation.387 *388 * @example389 * ```ts390 * import { MongoClient } from 'mongodb';391 * // Enable command monitoring for debugging392 * const client = new MongoClient('mongodb://localhost:27017?appName=mflix', { monitorCommands: true });393 * ```394 */395export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements AsyncDisposable {396 /** @internal */397 s: MongoClientPrivate;398 /** @internal */399 topology?: Topology;400 /** @internal */401 override readonly mongoLogger: MongoLogger | undefined;402 /** @internal */403 private connectionLock?: Promise<this>;404 /** @internal */405 private closeLock?: Promise<void>;406 407 /**408 * The consolidate, parsed, transformed and merged options.409 */410 public readonly options: Readonly<411 Omit<412 MongoOptions,413 | 'monitorCommands'414 | 'ca'415 | 'crl'416 | 'key'417 | 'cert'418 | 'driverInfo'419 | 'additionalDriverInfo'420 | 'metadata'421 | 'extendedMetadata'422 >423 > &424 Pick<425 MongoOptions,426 | 'monitorCommands'427 | 'ca'428 | 'crl'429 | 'key'430 | 'cert'431 | 'driverInfo'432 | 'additionalDriverInfo'433 | 'metadata'434 | 'extendedMetadata'435 >;436 437 private driverInfoList: DriverInfo[] = [];438 439 constructor(url: string, options?: MongoClientOptions) {440 super();441 this.on('error', noop);442 443 this.options = parseOptions(url, this, options);444 445 this.appendMetadata(this.options.driverInfo);446 447 const shouldSetLogger = Object.values(this.options.mongoLoggerOptions.componentSeverities).some(448 value => value !== SeverityLevel.OFF449 );450 this.mongoLogger = shouldSetLogger451 ? new MongoLogger(this.options.mongoLoggerOptions)452 : undefined;453 454 // eslint-disable-next-line @typescript-eslint/no-this-alias455 const client = this;456 457 // The internal state458 this.s = {459 url,460 bsonOptions: resolveBSONOptions(this.options),461 namespace: ns('admin'),462 hasBeenClosed: false,463 sessionPool: new ServerSessionPool(this),464 activeSessions: new Set(),465 activeCursors: new Set(),466 authProviders: new MongoClientAuthProviders(),467 468 get options() {469 return client.options;470 },471 get readConcern() {472 return client.options.readConcern;473 },474 get writeConcern() {475 return client.options.writeConcern;476 },477 get readPreference() {478 return client.options.readPreference;479 },480 get isMongoClient(): true {481 return true;482 }483 };484 this.checkForNonGenuineHosts();485 }486 487 /**488 * @beta489 * @experimental490 * An alias for {@link MongoClient.close|MongoClient.close()}.491 */492 declare [Symbol.asyncDispose]: () => Promise<void>;493 /** @internal */494 async asyncDispose() {495 await this.close();496 }497 498 /**499 * Append metadata to the client metadata after instantiation.500 * @param driverInfo - Information about the application or library.501 */502 appendMetadata(driverInfo: DriverInfo) {503 const isDuplicateDriverInfo = this.driverInfoList.some(info =>504 isDriverInfoEqual(info, driverInfo)505 );506 if (isDuplicateDriverInfo) return;507 508 this.driverInfoList.push(driverInfo);509 this.options.metadata = makeClientMetadata(this.driverInfoList, this.options);510 this.options.extendedMetadata = addContainerMetadata(this.options.metadata)511 .then(undefined, squashError)512 .then(result => result ?? {}); // ensure Promise<Document>513 }514 515 /** @internal */516 private checkForNonGenuineHosts() {517 const documentDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) =>518 isHostMatch(DOCUMENT_DB_CHECK, hostAddress.host)519 );520 const srvHostIsDocumentDB = isHostMatch(DOCUMENT_DB_CHECK, this.options.srvHost);521 522 const cosmosDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) =>523 isHostMatch(COSMOS_DB_CHECK, hostAddress.host)524 );525 const srvHostIsCosmosDB = isHostMatch(COSMOS_DB_CHECK, this.options.srvHost);526 527 if (documentDBHostnames.length !== 0 || srvHostIsDocumentDB) {528 this.mongoLogger?.info('client', DOCUMENT_DB_MSG);529 } else if (cosmosDBHostnames.length !== 0 || srvHostIsCosmosDB) {530 this.mongoLogger?.info('client', COSMOS_DB_MSG);531 }532 }533 534 get serverApi(): Readonly<ServerApi | undefined> {535 return this.options.serverApi && Object.freeze({ ...this.options.serverApi });536 }537 /**538 * Intended for APM use only539 * @internal540 */541 get monitorCommands(): boolean {542 return this.options.monitorCommands;543 }544 set monitorCommands(value: boolean) {545 this.options.monitorCommands = value;546 }547 548 /** @internal */549 get autoEncrypter(): AutoEncrypter | undefined {550 return this.options.autoEncrypter;551 }552 553 get readConcern(): ReadConcern | undefined {554 return this.s.readConcern;555 }556 557 get writeConcern(): WriteConcern | undefined {558 return this.s.writeConcern;559 }560 561 get readPreference(): ReadPreference {562 return this.s.readPreference;563 }564 565 get bsonOptions(): BSONSerializeOptions {566 return this.s.bsonOptions;567 }568 569 get timeoutMS(): number | undefined {570 return this.s.options.timeoutMS;571 }572 573 /**574 * Executes a client bulk write operation, available on server 8.0+.575 * @param models - The client bulk write models.576 * @param options - The client bulk write options.577 * @returns A ClientBulkWriteResult for acknowledged writes and ok: 1 for unacknowledged writes.578 */579 async bulkWrite<SchemaMap extends Record<string, Document> = Record<string, Document>>(580 models: ReadonlyArray<ClientBulkWriteModel<SchemaMap>>,581 options?: ClientBulkWriteOptions582 ): Promise<ClientBulkWriteResult> {583 if (this.autoEncrypter) {584 throw new MongoInvalidArgumentError(585 'MongoClient bulkWrite does not currently support automatic encryption.'586 );587 }588 // We do not need schema type information past this point ("as any" is fine)589 return await new ClientBulkWriteExecutor(590 this,591 models as any,592 resolveOptions(this, options)593 ).execute();594 }595 596 /**597 * Connect to MongoDB using a url598 *599 * @remarks600 * Calling `connect` is optional since the first operation you perform will call `connect` if it's needed.601 * `timeoutMS` will bound the time any operation can take before throwing a timeout error.602 * However, when the operation being run is automatically connecting your `MongoClient` the `timeoutMS` will not apply to the time taken to connect the MongoClient.603 * This means the time to setup the `MongoClient` does not count against `timeoutMS`.604 * If you are using `timeoutMS` we recommend connecting your client explicitly in advance of any operation to avoid this inconsistent execution time.605 *606 * @remarks607 * The driver will look up corresponding SRV and TXT records if the connection string starts with `mongodb+srv://`.608 * If those look ups throw a DNS Timeout error, the driver will retry the look up once.609 *610 * @see docs.mongodb.org/manual/reference/connection-string/611 */612 async connect(): Promise<this> {613 if (this.connectionLock) {614 return await this.connectionLock;615 }616 617 try {618 this.connectionLock = this._connect();619 await this.connectionLock;620 } finally {621 // release622 this.connectionLock = undefined;623 }624 625 return this;626 }627 628 /**629 * Create a topology to open the connection, must be locked to avoid topology leaks in concurrency scenario.630 * Locking is enforced by the connect method.631 *632 * @internal633 */634 private async _connect(): Promise<this> {635 if (this.topology && this.topology.isConnected()) {636 return this;637 }638 639 const options = this.options;640 641 if (options.tls) {642 if (typeof options.tlsCAFile === 'string') {643 options.ca ??= await fs.readFile(options.tlsCAFile);644 }645 if (typeof options.tlsCRLFile === 'string') {646 options.crl ??= await fs.readFile(options.tlsCRLFile);647 }648 if (typeof options.tlsCertificateKeyFile === 'string') {649 if (!options.key || !options.cert) {650 const contents = await fs.readFile(options.tlsCertificateKeyFile);651 options.key ??= contents;652 options.cert ??= contents;653 }654 }655 }656 if (typeof options.srvHost === 'string') {657 const hosts = await resolveSRVRecord(options);658 659 for (const [index, host] of hosts.entries()) {660 options.hosts[index] = host;661 }662 }663 664 // It is important to perform validation of hosts AFTER SRV resolution, to check the real hostname,665 // but BEFORE we even attempt connecting with a potentially not allowed hostname666 if (options.credentials?.mechanism === AuthMechanism.MONGODB_OIDC) {667 const allowedHosts =668 options.credentials?.mechanismProperties?.ALLOWED_HOSTS || DEFAULT_ALLOWED_HOSTS;669 const isServiceAuth = !!options.credentials?.mechanismProperties?.ENVIRONMENT;670 if (!isServiceAuth) {671 for (const host of options.hosts) {672 if (!hostMatchesWildcards(host.toHostPort().host, allowedHosts)) {673 throw new MongoInvalidArgumentError(674 `Host '${host}' is not valid for OIDC authentication with ALLOWED_HOSTS of '${allowedHosts.join(675 ','676 )}'`677 );678 }679 }680 }681 }682 683 this.topology = new Topology(this, options.hosts, options);684 // Events can be emitted before initialization is complete so we have to685 // save the reference to the topology on the client ASAP if the event handlers need to access it686 687 this.topology.once(Topology.OPEN, () => this.emit('open', this));688 689 for (const event of MONGO_CLIENT_EVENTS) {690 this.topology.on(event, (...args: any[]) => this.emit(event, ...(args as any)));691 }692 693 const topologyConnect = async () => {694 try {695 await this.topology?.connect(options);696 } catch (error) {697 this.topology?.close();698 throw error;699 }700 };701 702 if (this.autoEncrypter) {703 await this.autoEncrypter?.init();704 await topologyConnect();705 await options.encrypter.connectInternalClient();706 } else {707 await topologyConnect();708 }709 710 return this;711 }712 713 /**714 * Cleans up resources managed by the MongoClient.715 *716 * The close method clears and closes all resources whose lifetimes are managed by the MongoClient.717 * Please refer to the `MongoClient` class documentation for a high level overview of the client's key features and responsibilities.718 *719 * **However,** the close method does not handle the cleanup of resources explicitly created by the user.720 * Any user-created driver resource with its own `close()` method should be explicitly closed by the user before calling MongoClient.close().721 * This method is written as a "best effort" attempt to leave behind the least amount of resources server-side when possible.722 *723 * The following list defines ideal preconditions and consequent pitfalls if they are not met.724 * The MongoClient, ClientSession, Cursors and ChangeStreams all support [explicit resource management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html).725 * By using explicit resource management to manage the lifetime of driver resources instead of manually managing their lifetimes, the pitfalls outlined below can be avoided.726 *727 * The close method performs the following in the order listed:728 * - Client-side:729 * - **Close in-use connections**: Any connections that are currently waiting on a response from the server will be closed.730 * This is performed _first_ to avoid reaching the next step (server-side clean up) and having no available connections to check out.731 * - _Ideal_: All operations have been awaited or cancelled, and the outcomes, regardless of success or failure, have been processed before closing the client servicing the operation.732 * - _Pitfall_: When `client.close()` is called and all connections are in use, after closing them, the client must create new connections for cleanup operations, which comes at the cost of new TLS/TCP handshakes and authentication steps.733 * - Server-side:734 * - **Close active cursors**: All cursors that haven't been completed will have a `killCursor` operation sent to the server they were initialized on, freeing the server-side resource.735 * - _Ideal_: Cursors are explicitly closed or completed before `client.close()` is called.736 * - _Pitfall_: `killCursors` may have to build a new connection if the in-use closure ended all pooled connections.737 * - **End active sessions**: In-use sessions created with `client.startSession()` or `client.withSession()` or implicitly by the driver will have their `.endSession()` method called.738 * Contrary to the name of the method, `endSession()` returns the session to the client's pool of sessions rather than end them on the server.739 * - _Ideal_: Transaction outcomes are awaited and their corresponding explicit sessions are ended before `client.close()` is called.740 * - _Pitfall_: **This step aborts in-progress transactions**. It is advisable to observe the outcome of a transaction before closing your client.741 * - **End all pooled sessions**: The `endSessions` command with all session IDs the client has pooled is sent to the server to inform the cluster it can clean them up.742 * - _Ideal_: No user intervention is expected.743 * - _Pitfall_: None.744 *745 * The remaining shutdown is of the MongoClient resources that are intended to be entirely internal but is documented here as their existence relates to the JS event loop.746 *747 * - Client-side (again):748 * - **Stop all server monitoring**: Connections kept live for detecting cluster changes and roundtrip time measurements are shutdown.749 * - **Close all pooled connections**: Each server node in the cluster has a corresponding connection pool and all connections in the pool are closed. Any operations waiting to check out a connection will have an error thrown instead of a connection returned.750 * - **Clear out server selection queue**: Any operations that are in the process of waiting for a server to be selected will have an error thrown instead of a server returned.751 * - **Close encryption-related resources**: An internal MongoClient created for communicating with `mongocryptd` or other encryption purposes is closed. (Using this same method of course!)752 *753 * After the close method completes there should be no MongoClient related resources [ref-ed in Node.js' event loop](https://docs.libuv.org/en/v1.x/handle.html#reference-counting).754 * This should allow Node.js to exit gracefully if MongoClient resources were the only active handles in the event loop.755 *756 * @param _force - currently an unused flag that has no effect. Defaults to `false`.757 */758 async close(_force = false): Promise<void> {759 if (this.closeLock) {760 return await this.closeLock;761 }762 763 try {764 this.closeLock = this._close();765 await this.closeLock;766 } finally {767 // release768 this.closeLock = undefined;769 }770 }771 772 /* @internal */773 private async _close(): Promise<void> {774 // There's no way to set hasBeenClosed back to false775 Object.defineProperty(this.s, 'hasBeenClosed', {776 value: true,777 enumerable: true,778 configurable: false,779 writable: false780 });781 782 this.topology?.closeCheckedOutConnections();783 784 const activeCursorCloses = Array.from(this.s.activeCursors, cursor => cursor.close());785 this.s.activeCursors.clear();786 787 await Promise.all(activeCursorCloses);788 789 const activeSessionEnds = Array.from(this.s.activeSessions, session => session.endSession());790 this.s.activeSessions.clear();791 792 await Promise.all(activeSessionEnds);793 794 if (this.topology == null) {795 return;796 }797 798 // If we would attempt to select a server and get nothing back we short circuit799 // to avoid the server selection timeout.800 const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred);801 const topologyDescription = this.topology.description;802 const serverDescriptions = Array.from(topologyDescription.servers.values());803 const servers = selector(topologyDescription, serverDescriptions);804 if (servers.length !== 0) {805 const endSessions = Array.from(this.s.sessionPool.sessions, ({ id }) => id);806 if (endSessions.length !== 0) {807 try {808 class EndSessionsOperation extends AbstractOperation<void> {809 override ns = MongoDBNamespace.fromString('admin.$cmd');810 override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;811 override buildCommand(_connection: Connection, _session?: ClientSession): Document {812 return {813 endSessions814 };815 }816 override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {817 return {818 timeoutContext,819 readPreference: ReadPreference.primaryPreferred,820 noResponse: true821 };822 }823 override get commandName(): string {824 return 'endSessions';825 }826 }827 await executeOperation(this, new EndSessionsOperation());828 } catch (error) {829 squashError(error);830 }831 }832 }833 834 // clear out references to old topology835 const topology = this.topology;836 this.topology = undefined;837 838 topology.close();839 840 const { encrypter } = this.options;841 if (encrypter) {842 await encrypter.close(this);843 }844 }845 846 /**847 * Create a new Db instance sharing the current socket connections.848 *849 * @param dbName - The name of the database we want to use. If not provided, use database name from connection string.850 * @param options - Optional settings for Db construction851 */852 db(dbName?: string, options?: DbOptions): Db {853 options = options ?? {};854 855 // Default to db from connection string if not provided856 if (!dbName) {857 dbName = this.s.options.dbName;858 }859 860 // Copy the options and add out internal override of the not shared flag861 const finalOptions = Object.assign({}, this.options, options);862 863 // Return the db object864 const db = new Db(this, dbName, finalOptions);865 866 // Return the database867 return db;868 }869 870 /**871 * Connect to MongoDB using a url872 *873 * @remarks874 * Calling `connect` is optional since the first operation you perform will call `connect` if it's needed.875 * `timeoutMS` will bound the time any operation can take before throwing a timeout error.876 * However, when the operation being run is automatically connecting your `MongoClient` the `timeoutMS` will not apply to the time taken to connect the MongoClient.877 * This means the time to setup the `MongoClient` does not count against `timeoutMS`.878 * If you are using `timeoutMS` we recommend connecting your client explicitly in advance of any operation to avoid this inconsistent execution time.879 *880 * @remarks881 * The programmatically provided options take precedence over the URI options.882 *883 * @remarks884 * The driver will look up corresponding SRV and TXT records if the connection string starts with `mongodb+srv://`.885 * If those look ups throw a DNS Timeout error, the driver will retry the look up once.886 *887 * @see https://www.mongodb.com/docs/manual/reference/connection-string/888 */889 static async connect(url: string, options?: MongoClientOptions): Promise<MongoClient> {890 const client = new this(url, options);891 return await client.connect();892 }893 894 /**895 * Creates a new ClientSession. When using the returned session in an operation896 * a corresponding ServerSession will be created.897 *898 * @remarks899 * A ClientSession instance may only be passed to operations being performed on the same900 * MongoClient it was started from.901 */902 startSession(options?: ClientSessionOptions): ClientSession {903 const session = new ClientSession(904 this,905 this.s.sessionPool,906 { explicit: true, ...options },907 this.options908 );909 this.s.activeSessions.add(session);910 session.once('ended', () => {911 this.s.activeSessions.delete(session);912 });913 return session;914 }915 916 /**917 * A convenience method for creating and handling the clean up of a ClientSession.918 * The session will always be ended when the executor finishes.919 *920 * @param executor - An executor function that all operations using the provided session must be invoked in921 * @param options - optional settings for the session922 */923 async withSession<T = any>(executor: WithSessionCallback<T>): Promise<T>;924 async withSession<T = any>(925 options: ClientSessionOptions,926 executor: WithSessionCallback<T>927 ): Promise<T>;928 async withSession<T = any>(929 optionsOrExecutor: ClientSessionOptions | WithSessionCallback<T>,930 executor?: WithSessionCallback<T>931 ): Promise<T> {932 const options = {933 // Always define an owner934 owner: Symbol(),935 // If it's an object inherit the options936 ...(typeof optionsOrExecutor === 'object' ? optionsOrExecutor : {})937 };938 939 const withSessionCallback =940 typeof optionsOrExecutor === 'function' ? optionsOrExecutor : executor;941 942 if (withSessionCallback == null) {943 throw new MongoInvalidArgumentError('Missing required callback parameter');944 }945 946 const session = this.startSession(options);947 948 try {949 return await withSessionCallback(session);950 } finally {951 try {952 await session.endSession();953 } catch (error) {954 squashError(error);955 }956 }957 }958 959 /**960 * Create a new Change Stream, watching for new changes (insertions, updates,961 * replacements, deletions, and invalidations) in this cluster. Will ignore all962 * changes to system collections, as well as the local, admin, and config databases.963 *964 * @remarks965 * watch() accepts two generic arguments for distinct use cases:966 * - The first is to provide the schema that may be defined for all the data within the current cluster967 * - The second is to override the shape of the change stream document entirely, if it is not provided the type will default to ChangeStreamDocument of the first argument968 *969 * @remarks970 * When `timeoutMS` is configured for a change stream, it will have different behaviour depending971 * on whether the change stream is in iterator mode or emitter mode. In both cases, a change972 * stream will time out if it does not receive a change event within `timeoutMS` of the last change973 * event.974 *975 * Note that if a change stream is consistently timing out when watching a collection, database or976 * client that is being changed, then this may be due to the server timing out before it can finish977 * processing the existing oplog. To address this, restart the change stream with a higher978 * `timeoutMS`.979 *980 * If the change stream times out the initial aggregate operation to establish the change stream on981 * the server, then the client will close the change stream. If the getMore calls to the server982 * time out, then the change stream will be left open, but will throw a MongoOperationTimeoutError983 * when in iterator mode and emit an error event that returns a MongoOperationTimeoutError in984 * emitter mode.985 *986 * To determine whether or not the change stream is still open following a timeout, check the987 * {@link ChangeStream.closed} getter.988 *989 * @example990 * In iterator mode, if a next() call throws a timeout error, it will attempt to resume the change stream.991 * The next call can just be retried after this succeeds.992 * ```ts993 * const changeStream = collection.watch([], { timeoutMS: 100 });994 * try {995 * await changeStream.next();996 * } catch (e) {997 * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {998 * await changeStream.next();999 * }1000 * throw e;1001 * }1002 * ```1003 *1004 * @example1005 * In emitter mode, if the change stream goes `timeoutMS` without emitting a change event, it will1006 * emit an error event that returns a MongoOperationTimeoutError, but will not close the change1007 * stream unless the resume attempt fails. There is no need to re-establish change listeners as1008 * this will automatically continue emitting change events once the resume attempt completes.1009 *1010 * ```ts1011 * const changeStream = collection.watch([], { timeoutMS: 100 });1012 * changeStream.on('change', console.log);1013 * changeStream.on('error', e => {1014 * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {1015 * // do nothing1016 * } else {1017 * changeStream.close();1018 * }1019 * });1020 * ```1021 * @param pipeline - An array of {@link https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.1022 * @param options - Optional settings for the command1023 * @typeParam TSchema - Type of the data being detected by the change stream1024 * @typeParam TChange - Type of the whole change stream document emitted1025 */1026 watch<1027 TSchema extends Document = Document,1028 TChange extends Document = ChangeStreamDocument<TSchema>1029 >(pipeline: Document[] = [], options: ChangeStreamOptions = {}): ChangeStream<TSchema, TChange> {1030 // Allow optionally not specifying a pipeline1031 if (!Array.isArray(pipeline)) {1032 options = pipeline;1033 pipeline = [];1034 }1035 1036 return new ChangeStream<TSchema, TChange>(this, pipeline, resolveOptions(this, options));1037 }1038}1039 1040configureResourceManagement(MongoClient.prototype);1041 1042/**1043 * Parsed Mongo Client Options.1044 *1045 * User supplied options are documented by `MongoClientOptions`.1046 *1047 * **NOTE:** The client's options parsing is subject to change to support new features.1048 * This type is provided to aid with inspection of options after parsing, it should not be relied upon programmatically.1049 *1050 * Options are sourced from:1051 * - connection string1052 * - options object passed to the MongoClient constructor1053 * - file system (ex. tls settings)1054 * - environment variables1055 * - DNS SRV records and TXT records1056 *1057 * Not all options may be present after client construction as some are obtained from asynchronous operations.1058 *1059 * @public1060 */1061export interface MongoOptions1062 extends Required<1063 Pick<1064 MongoClientOptions,1065 | 'autoEncryption'1066 | 'connectTimeoutMS'1067 | 'directConnection'1068 | 'driverInfo'1069 | 'forceServerObjectId'1070 | 'minHeartbeatFrequencyMS'1071 | 'heartbeatFrequencyMS'1072 | 'localThresholdMS'1073 | 'maxConnecting'1074 | 'maxIdleTimeMS'1075 | 'maxPoolSize'1076 | 'minPoolSize'1077 | 'monitorCommands'1078 | 'noDelay'1079 | 'pkFactory'1080 | 'raw'1081 | 'replicaSet'1082 | 'retryReads'1083 | 'retryWrites'1084 | 'serverSelectionTimeoutMS'1085 | 'socketTimeoutMS'1086 | 'srvMaxHosts'1087 | 'srvServiceName'1088 | 'tlsAllowInvalidCertificates'1089 | 'tlsAllowInvalidHostnames'1090 | 'tlsInsecure'1091 | 'waitQueueTimeoutMS'1092 | 'zlibCompressionLevel'1093 >1094 >,1095 SupportedNodeConnectionOptions {1096 appName?: string;1097 hosts: HostAddress[];1098 srvHost?: string;1099 credentials?: MongoCredentials;1100 readPreference: ReadPreference;1101 readConcern: ReadConcern;1102 loadBalanced: boolean;1103 directConnection: boolean;1104 serverApi: ServerApi;1105 compressors: CompressorName[];1106 writeConcern: WriteConcern;1107 dbName: string;1108 /** @deprecated - Will be made internal in a future major release. */1109 metadata: ClientMetadata;1110 /** @deprecated - Will be made internal in a future major release. */1111 extendedMetadata: Promise<Document>;1112 /** @deprecated - Will be made internal in a future major release. */1113 additionalDriverInfo: DriverInfo[];1114 /** @internal */1115 autoEncrypter?: AutoEncrypter;1116 /** @internal */1117 tokenCache?: TokenCache;1118 proxyHost?: string;1119 proxyPort?: number;1120 proxyUsername?: string;1121 proxyPassword?: string;1122 serverMonitoringMode: ServerMonitoringMode;1123 /** @internal */1124 connectionType?: typeof Connection;1125 /** @internal */1126 authProviders: MongoClientAuthProviders;1127 /** @internal */1128 encrypter: Encrypter;1129 /** @internal */1130 userSpecifiedAuthSource: boolean;1131 /** @internal */1132 userSpecifiedReplicaSet: boolean;1133 1134 /**1135 * # NOTE ABOUT TLS Options1136 *1137 * If `tls` is provided as an option, it is equivalent to setting the `ssl` option.1138 *1139 * NodeJS native TLS options are passed through to the socket and retain their original types.1140 *1141 * ### Additional options:1142 *1143 * | nodejs native option | driver spec equivalent option name | driver option type |1144 * |:----------------------|:----------------------------------------------|:-------------------|1145 * | `ca` | `tlsCAFile` | `string` |1146 * | `crl` | `tlsCRLFile` | `string` |1147 * | `cert` | `tlsCertificateKeyFile` | `string` |1148 * | `key` | `tlsCertificateKeyFile` | `string` |1149 * | `passphrase` | `tlsCertificateKeyFilePassword` | `string` |1150 * | `rejectUnauthorized` | `tlsAllowInvalidCertificates` | `boolean` |1151 * | `checkServerIdentity` | `tlsAllowInvalidHostnames` | `boolean` |1152 * | see note below | `tlsInsecure` | `boolean` |1153 *1154 * If `tlsInsecure` is set to `true`, then it will set the node native options `checkServerIdentity`1155 * to a no-op and `rejectUnauthorized` to `false`.1156 *1157 * If `tlsInsecure` is set to `false`, then it will set the node native options `checkServerIdentity`1158 * to a no-op and `rejectUnauthorized` to the inverse value of `tlsAllowInvalidCertificates`. If1159 * `tlsAllowInvalidCertificates` is not set, then `rejectUnauthorized` will be set to `true`.1160 *1161 * ### Note on `tlsCAFile`, `tlsCertificateKeyFile` and `tlsCRLFile`1162 *1163 * The files specified by the paths passed in to the `tlsCAFile`, `tlsCertificateKeyFile` and `tlsCRLFile`1164 * fields are read lazily on the first call to `MongoClient.connect`. Once these files have been read and1165 * the `ca`, `cert`, `crl` and `key` fields are populated, they will not be read again on subsequent calls to1166 * `MongoClient.connect`. As a result, until the first call to `MongoClient.connect`, the `ca`,1167 * `cert`, `crl` and `key` fields will be undefined.1168 */1169 tls: boolean;1170 tlsCAFile?: string;1171 tlsCRLFile?: string;1172 tlsCertificateKeyFile?: string;1173 1174 /**1175 * @internal1176 * TODO: NODE-5671 - remove internal flag1177 */1178 mongoLoggerOptions: MongoLoggerOptions;1179 /**1180 * @internal1181 * TODO: NODE-5671 - remove internal flag1182 */1183 mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;1184 timeoutMS?: number;1185 /** @internal */1186 __skipPingOnConnect?: boolean;1187}1188 