CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
connection_string.ts1304 linesDownload Raw Back to src
1import * as dns from 'dns';2import ConnectionString from 'mongodb-connection-string-url';3import { URLSearchParams } from 'url';4 5import type { Document } from './bson';6import { MongoCredentials } from './cmap/auth/mongo_credentials';7import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './cmap/auth/providers';8import { Compressor, type CompressorName } from './cmap/wire_protocol/compression';9import { Encrypter } from './encrypter';10import {11  MongoAPIError,12  MongoInvalidArgumentError,13  MongoMissingCredentialsError,14  MongoParseError15} from './error';16import {17  MongoClient,18  type MongoClientOptions,19  type MongoOptions,20  type PkFactory,21  type ServerApi,22  ServerApiVersion23} from './mongo_client';24import { MongoLoggableComponent, MongoLogger, SeverityLevel } from './mongo_logger';25import { ReadConcern, type ReadConcernLevel } from './read_concern';26import { ReadPreference, type ReadPreferenceMode } from './read_preference';27import { ServerMonitoringMode } from './sdam/monitor';28import type { TagSet } from './sdam/server_description';29import {30  checkParentDomainMatch,31  DEFAULT_PK_FACTORY,32  emitWarning,33  HostAddress,34  isRecord,35  parseInteger,36  setDifference,37  squashError38} from './utils';39import { type W, WriteConcern } from './write_concern';40 41const VALID_TXT_RECORDS = ['authSource', 'replicaSet', 'loadBalanced'];42 43const LB_SINGLE_HOST_ERROR = 'loadBalanced option only supported with a single host in the URI';44const LB_REPLICA_SET_ERROR = 'loadBalanced option not supported with a replicaSet option';45const LB_DIRECT_CONNECTION_ERROR =46  'loadBalanced option not supported when directConnection is provided';47 48function retryDNSTimeoutFor(api: 'resolveSrv'): (a: string) => Promise<dns.SrvRecord[]>;49function retryDNSTimeoutFor(api: 'resolveTxt'): (a: string) => Promise<string[][]>;50function retryDNSTimeoutFor(51  api: 'resolveSrv' | 'resolveTxt'52): (a: string) => Promise<dns.SrvRecord[] | string[][]> {53  return async function dnsReqRetryTimeout(lookupAddress: string) {54    try {55      return await dns.promises[api](lookupAddress);56    } catch (firstDNSError) {57      if (firstDNSError.code === dns.TIMEOUT) {58        return await dns.promises[api](lookupAddress);59      } else {60        throw firstDNSError;61      }62    }63  };64}65 66const resolveSrv = retryDNSTimeoutFor('resolveSrv');67const resolveTxt = retryDNSTimeoutFor('resolveTxt');68 69/**70 * Lookup a `mongodb+srv` connection string, combine the parts and reparse it as a normal71 * connection string.72 *73 * @param uri - The connection string to parse74 * @param options - Optional user provided connection string options75 */76export async function resolveSRVRecord(options: MongoOptions): Promise<HostAddress[]> {77  if (typeof options.srvHost !== 'string') {78    throw new MongoAPIError('Option "srvHost" must not be empty');79  }80 81  // Asynchronously start TXT resolution so that we do not have to wait until82  // the SRV record is resolved before starting a second DNS query.83  const lookupAddress = options.srvHost;84  const txtResolutionPromise = resolveTxt(lookupAddress);85 86  txtResolutionPromise.then(undefined, squashError); // rejections will be handled later87 88  const hostname = `_${options.srvServiceName}._tcp.${lookupAddress}`;89  // Resolve the SRV record and use the result as the list of hosts to connect to.90  const addresses = await resolveSrv(hostname);91 92  if (addresses.length === 0) {93    throw new MongoAPIError('No addresses found at host');94  }95 96  for (const { name } of addresses) {97    checkParentDomainMatch(name, lookupAddress);98  }99 100  const hostAddresses = addresses.map(r => HostAddress.fromString(`${r.name}:${r.port ?? 27017}`));101 102  validateLoadBalancedOptions(hostAddresses, options, true);103 104  // Use the result of resolving the TXT record and add options from there if they exist.105  let record;106  try {107    record = await txtResolutionPromise;108  } catch (error) {109    if (error.code !== 'ENODATA' && error.code !== 'ENOTFOUND') {110      throw error;111    }112    return hostAddresses;113  }114 115  if (record.length > 1) {116    throw new MongoParseError('Multiple text records not allowed');117  }118 119  const txtRecordOptions = new URLSearchParams(record[0].join(''));120  const txtRecordOptionKeys = [...txtRecordOptions.keys()];121  if (txtRecordOptionKeys.some(key => !VALID_TXT_RECORDS.includes(key))) {122    throw new MongoParseError(`Text record may only set any of: ${VALID_TXT_RECORDS.join(', ')}`);123  }124 125  if (VALID_TXT_RECORDS.some(option => txtRecordOptions.get(option) === '')) {126    throw new MongoParseError('Cannot have empty URI params in DNS TXT Record');127  }128 129  const source = txtRecordOptions.get('authSource') ?? undefined;130  const replicaSet = txtRecordOptions.get('replicaSet') ?? undefined;131  const loadBalanced = txtRecordOptions.get('loadBalanced') ?? undefined;132 133  if (134    !options.userSpecifiedAuthSource &&135    source &&136    options.credentials &&137    !AUTH_MECHS_AUTH_SRC_EXTERNAL.has(options.credentials.mechanism)138  ) {139    options.credentials = MongoCredentials.merge(options.credentials, { source });140  }141 142  if (!options.userSpecifiedReplicaSet && replicaSet) {143    options.replicaSet = replicaSet;144  }145 146  if (loadBalanced === 'true') {147    options.loadBalanced = true;148  }149 150  if (options.replicaSet && options.srvMaxHosts > 0) {151    throw new MongoParseError('Cannot combine replicaSet option with srvMaxHosts');152  }153 154  validateLoadBalancedOptions(hostAddresses, options, true);155 156  return hostAddresses;157}158 159/**160 * Checks if TLS options are valid161 *162 * @param allOptions - All options provided by user or included in default options map163 * @throws MongoAPIError if TLS options are invalid164 */165function checkTLSOptions(allOptions: CaseInsensitiveMap): void {166  if (!allOptions) return;167  const check = (a: string, b: string) => {168    if (allOptions.has(a) && allOptions.has(b)) {169      throw new MongoAPIError(`The '${a}' option cannot be used with the '${b}' option`);170    }171  };172  check('tlsInsecure', 'tlsAllowInvalidCertificates');173  check('tlsInsecure', 'tlsAllowInvalidHostnames');174}175function getBoolean(name: string, value: unknown): boolean {176  if (typeof value === 'boolean') return value;177  switch (value) {178    case 'true':179      return true;180    case 'false':181      return false;182    default:183      throw new MongoParseError(`${name} must be either "true" or "false"`);184  }185}186 187function getIntFromOptions(name: string, value: unknown): number {188  const parsedInt = parseInteger(value);189  if (parsedInt != null) {190    return parsedInt;191  }192  throw new MongoParseError(`Expected ${name} to be stringified int value, got: ${value}`);193}194 195function getUIntFromOptions(name: string, value: unknown): number {196  const parsedValue = getIntFromOptions(name, value);197  if (parsedValue < 0) {198    throw new MongoParseError(`${name} can only be a positive int value, got: ${value}`);199  }200  return parsedValue;201}202 203function* entriesFromString(value: string): Generator<[string, string]> {204  if (value === '') {205    return;206  }207  const keyValuePairs = value.split(',');208  for (const keyValue of keyValuePairs) {209    const [key, value] = keyValue.split(/:(.*)/);210    if (value == null) {211      throw new MongoParseError('Cannot have undefined values in key value pairs');212    }213 214    yield [key, value];215  }216}217 218class CaseInsensitiveMap<Value = any> extends Map<string, Value> {219  constructor(entries: Array<[string, any]> = []) {220    super(entries.map(([k, v]) => [k.toLowerCase(), v]));221  }222  override has(k: string) {223    return super.has(k.toLowerCase());224  }225  override get(k: string) {226    return super.get(k.toLowerCase());227  }228  override set(k: string, v: any) {229    return super.set(k.toLowerCase(), v);230  }231  override delete(k: string): boolean {232    return super.delete(k.toLowerCase());233  }234}235 236export function parseOptions(237  uri: string,238  mongoClient: MongoClient | MongoClientOptions | undefined = undefined,239  options: MongoClientOptions = {}240): MongoOptions {241  if (mongoClient != null && !(mongoClient instanceof MongoClient)) {242    options = mongoClient;243    mongoClient = undefined;244  }245 246  // validate BSONOptions247  if (options.useBigInt64 && typeof options.promoteLongs === 'boolean' && !options.promoteLongs) {248    throw new MongoAPIError('Must request either bigint or Long for int64 deserialization');249  }250 251  if (options.useBigInt64 && typeof options.promoteValues === 'boolean' && !options.promoteValues) {252    throw new MongoAPIError('Must request either bigint or Long for int64 deserialization');253  }254 255  const url = new ConnectionString(uri);256  const { hosts, isSRV } = url;257 258  const mongoOptions = Object.create(null);259 260  mongoOptions.hosts = isSRV ? [] : hosts.map(HostAddress.fromString);261 262  const urlOptions = new CaseInsensitiveMap<unknown[]>();263 264  if (url.pathname !== '/' && url.pathname !== '') {265    const dbName = decodeURIComponent(266      url.pathname[0] === '/' ? url.pathname.slice(1) : url.pathname267    );268    if (dbName) {269      urlOptions.set('dbName', [dbName]);270    }271  }272 273  if (url.username !== '') {274    const auth: Document = {275      username: decodeURIComponent(url.username)276    };277 278    if (typeof url.password === 'string') {279      auth.password = decodeURIComponent(url.password);280    }281 282    urlOptions.set('auth', [auth]);283  }284 285  for (const key of url.searchParams.keys()) {286    const values = url.searchParams.getAll(key);287 288    const isReadPreferenceTags = /readPreferenceTags/i.test(key);289 290    if (!isReadPreferenceTags && values.length > 1) {291      throw new MongoInvalidArgumentError(292        `URI option "${key}" cannot appear more than once in the connection string`293      );294    }295 296    if (!isReadPreferenceTags && values.includes('')) {297      throw new MongoAPIError(`URI option "${key}" cannot be specified with no value`);298    }299 300    if (!urlOptions.has(key)) {301      urlOptions.set(key, values);302    }303  }304 305  const objectOptions = new CaseInsensitiveMap<unknown>(306    Object.entries(options).filter(([, v]) => v != null)307  );308 309  // Validate options that can only be provided by one of uri or object310 311  if (urlOptions.has('serverApi')) {312    throw new MongoParseError(313      'URI cannot contain `serverApi`, it can only be passed to the client'314    );315  }316 317  const uriMechanismProperties = urlOptions.get('authMechanismProperties');318  if (uriMechanismProperties) {319    for (const property of uriMechanismProperties) {320      if (/(^|,)ALLOWED_HOSTS:/.test(property as string)) {321        throw new MongoParseError(322          'Auth mechanism property ALLOWED_HOSTS is not allowed in the connection string.'323        );324      }325    }326  }327 328  if (objectOptions.has('loadBalanced')) {329    throw new MongoParseError('loadBalanced is only a valid option in the URI');330  }331 332  // All option collection333 334  const allProvidedOptions = new CaseInsensitiveMap<unknown[]>();335 336  const allProvidedKeys = new Set<string>([...urlOptions.keys(), ...objectOptions.keys()]);337 338  for (const key of allProvidedKeys) {339    const values = [];340    const objectOptionValue = objectOptions.get(key);341    if (objectOptionValue != null) {342      values.push(objectOptionValue);343    }344 345    const urlValues = urlOptions.get(key) ?? [];346    values.push(...urlValues);347    allProvidedOptions.set(key, values);348  }349 350  if (allProvidedOptions.has('tls') || allProvidedOptions.has('ssl')) {351    const tlsAndSslOpts = (allProvidedOptions.get('tls') || [])352      .concat(allProvidedOptions.get('ssl') || [])353      .map(getBoolean.bind(null, 'tls/ssl'));354    if (new Set(tlsAndSslOpts).size !== 1) {355      throw new MongoParseError('All values of tls/ssl must be the same.');356    }357  }358 359  checkTLSOptions(allProvidedOptions);360 361  const unsupportedOptions = setDifference(362    allProvidedKeys,363    Array.from(Object.keys(OPTIONS)).map(s => s.toLowerCase())364  );365  if (unsupportedOptions.size !== 0) {366    const optionWord = unsupportedOptions.size > 1 ? 'options' : 'option';367    const isOrAre = unsupportedOptions.size > 1 ? 'are' : 'is';368    throw new MongoParseError(369      `${optionWord} ${Array.from(unsupportedOptions).join(', ')} ${isOrAre} not supported`370    );371  }372 373  // Option parsing and setting374 375  for (const [key, descriptor] of Object.entries(OPTIONS)) {376    const values = allProvidedOptions.get(key);377    if (!values || values.length === 0) {378      if (DEFAULT_OPTIONS.has(key)) {379        setOption(mongoOptions, key, descriptor, [DEFAULT_OPTIONS.get(key)]);380      }381    } else {382      const { deprecated } = descriptor;383      if (deprecated) {384        const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';385        emitWarning(`${key} is a deprecated option${deprecatedMsg}`);386      }387 388      setOption(mongoOptions, key, descriptor, values);389    }390  }391 392  if (mongoOptions.credentials) {393    const isGssapi = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_GSSAPI;394    const isX509 = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_X509;395    const isAws = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_AWS;396    const isOidc = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_OIDC;397    if (398      (isGssapi || isX509) &&399      allProvidedOptions.has('authSource') &&400      mongoOptions.credentials.source !== '$external'401    ) {402      // If authSource was explicitly given and its incorrect, we error403      throw new MongoParseError(404        `authMechanism ${mongoOptions.credentials.mechanism} requires an authSource of '$external'`405      );406    }407 408    if (409      !(isGssapi || isX509 || isAws || isOidc) &&410      mongoOptions.dbName &&411      !allProvidedOptions.has('authSource')412    ) {413      // inherit the dbName unless GSSAPI or X509, then silently ignore dbName414      // and there was no specific authSource given415      mongoOptions.credentials = MongoCredentials.merge(mongoOptions.credentials, {416        source: mongoOptions.dbName417      });418    }419 420    if (isAws && mongoOptions.credentials.username && !mongoOptions.credentials.password) {421      throw new MongoMissingCredentialsError(422        `When using ${mongoOptions.credentials.mechanism} password must be set when a username is specified`423      );424    }425 426    mongoOptions.credentials.validate();427 428    // Check if the only auth related option provided was authSource, if so we can remove credentials429    if (430      mongoOptions.credentials.password === '' &&431      mongoOptions.credentials.username === '' &&432      mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_DEFAULT &&433      Object.keys(mongoOptions.credentials.mechanismProperties).length === 0434    ) {435      delete mongoOptions.credentials;436    }437  }438 439  if (!mongoOptions.dbName) {440    // dbName default is applied here because of the credential validation above441    mongoOptions.dbName = 'test';442  }443 444  validateLoadBalancedOptions(hosts, mongoOptions, isSRV);445 446  if (mongoClient && mongoOptions.autoEncryption) {447    Encrypter.checkForMongoCrypt();448    mongoOptions.encrypter = new Encrypter(mongoClient, uri, options);449    mongoOptions.autoEncrypter = mongoOptions.encrypter.autoEncrypter;450  }451 452  // Potential SRV Overrides and SRV connection string validations453 454  mongoOptions.userSpecifiedAuthSource =455    objectOptions.has('authSource') || urlOptions.has('authSource');456  mongoOptions.userSpecifiedReplicaSet =457    objectOptions.has('replicaSet') || urlOptions.has('replicaSet');458 459  if (isSRV) {460    // SRV Record is resolved upon connecting461    mongoOptions.srvHost = hosts[0];462 463    if (mongoOptions.directConnection) {464      throw new MongoAPIError('SRV URI does not support directConnection');465    }466 467    if (mongoOptions.srvMaxHosts > 0 && typeof mongoOptions.replicaSet === 'string') {468      throw new MongoParseError('Cannot use srvMaxHosts option with replicaSet');469    }470 471    // SRV turns on TLS by default, but users can override and turn it off472    const noUserSpecifiedTLS = !objectOptions.has('tls') && !urlOptions.has('tls');473    const noUserSpecifiedSSL = !objectOptions.has('ssl') && !urlOptions.has('ssl');474    if (noUserSpecifiedTLS && noUserSpecifiedSSL) {475      mongoOptions.tls = true;476    }477  } else {478    const userSpecifiedSrvOptions =479      urlOptions.has('srvMaxHosts') ||480      objectOptions.has('srvMaxHosts') ||481      urlOptions.has('srvServiceName') ||482      objectOptions.has('srvServiceName');483 484    if (userSpecifiedSrvOptions) {485      throw new MongoParseError(486        'Cannot use srvMaxHosts or srvServiceName with a non-srv connection string'487      );488    }489  }490 491  if (mongoOptions.directConnection && mongoOptions.hosts.length !== 1) {492    throw new MongoParseError('directConnection option requires exactly one host');493  }494 495  if (496    !mongoOptions.proxyHost &&497    (mongoOptions.proxyPort || mongoOptions.proxyUsername || mongoOptions.proxyPassword)498  ) {499    throw new MongoParseError('Must specify proxyHost if other proxy options are passed');500  }501 502  if (503    (mongoOptions.proxyUsername && !mongoOptions.proxyPassword) ||504    (!mongoOptions.proxyUsername && mongoOptions.proxyPassword)505  ) {506    throw new MongoParseError('Can only specify both of proxy username/password or neither');507  }508 509  const proxyOptions = ['proxyHost', 'proxyPort', 'proxyUsername', 'proxyPassword'].map(510    key => urlOptions.get(key) ?? []511  );512 513  if (proxyOptions.some(options => options.length > 1)) {514    throw new MongoParseError(515      'Proxy options cannot be specified multiple times in the connection string'516    );517  }518 519  mongoOptions.mongoLoggerOptions = MongoLogger.resolveOptions(520    {521      MONGODB_LOG_COMMAND: process.env.MONGODB_LOG_COMMAND,522      MONGODB_LOG_TOPOLOGY: process.env.MONGODB_LOG_TOPOLOGY,523      MONGODB_LOG_SERVER_SELECTION: process.env.MONGODB_LOG_SERVER_SELECTION,524      MONGODB_LOG_CONNECTION: process.env.MONGODB_LOG_CONNECTION,525      MONGODB_LOG_CLIENT: process.env.MONGODB_LOG_CLIENT,526      MONGODB_LOG_ALL: process.env.MONGODB_LOG_ALL,527      MONGODB_LOG_MAX_DOCUMENT_LENGTH: process.env.MONGODB_LOG_MAX_DOCUMENT_LENGTH,528      MONGODB_LOG_PATH: process.env.MONGODB_LOG_PATH529    },530    {531      mongodbLogPath: mongoOptions.mongodbLogPath,532      mongodbLogComponentSeverities: mongoOptions.mongodbLogComponentSeverities,533      mongodbLogMaxDocumentLength: mongoOptions.mongodbLogMaxDocumentLength534    }535  );536 537  return mongoOptions;538}539 540/**541 * #### Throws if LB mode is true:542 * - hosts contains more than one host543 * - there is a replicaSet name set544 * - directConnection is set545 * - if srvMaxHosts is used when an srv connection string is passed in546 *547 * @throws MongoParseError548 */549function validateLoadBalancedOptions(550  hosts: HostAddress[] | string[],551  mongoOptions: MongoOptions,552  isSrv: boolean553): void {554  if (mongoOptions.loadBalanced) {555    if (hosts.length > 1) {556      throw new MongoParseError(LB_SINGLE_HOST_ERROR);557    }558    if (mongoOptions.replicaSet) {559      throw new MongoParseError(LB_REPLICA_SET_ERROR);560    }561    if (mongoOptions.directConnection) {562      throw new MongoParseError(LB_DIRECT_CONNECTION_ERROR);563    }564 565    if (isSrv && mongoOptions.srvMaxHosts > 0) {566      throw new MongoParseError('Cannot limit srv hosts with loadBalanced enabled');567    }568  }569  return;570}571 572function setOption(573  mongoOptions: any,574  key: string,575  descriptor: OptionDescriptor,576  values: unknown[]577) {578  const { target, type, transform } = descriptor;579  const name = target ?? key;580 581  switch (type) {582    case 'boolean':583      mongoOptions[name] = getBoolean(name, values[0]);584      break;585    case 'int':586      mongoOptions[name] = getIntFromOptions(name, values[0]);587      break;588    case 'uint':589      mongoOptions[name] = getUIntFromOptions(name, values[0]);590      break;591    case 'string':592      if (values[0] == null) {593        break;594      }595      // The value should always be a string here, but since the array is typed as unknown596      // there still needs to be an explicit cast.597      // eslint-disable-next-line @typescript-eslint/no-base-to-string598      mongoOptions[name] = String(values[0]);599      break;600    case 'record':601      if (!isRecord(values[0])) {602        throw new MongoParseError(`${name} must be an object`);603      }604      mongoOptions[name] = values[0];605      break;606    case 'any':607      mongoOptions[name] = values[0];608      break;609    default: {610      if (!transform) {611        throw new MongoParseError('Descriptors missing a type must define a transform');612      }613      const transformValue = transform({ name, options: mongoOptions, values });614      mongoOptions[name] = transformValue;615      break;616    }617  }618}619 620interface OptionDescriptor {621  target?: string;622  type?: 'boolean' | 'int' | 'uint' | 'record' | 'string' | 'any';623  default?: any;624 625  deprecated?: boolean | string;626  /**627   * @param name - the original option name628   * @param options - the options so far for resolution629   * @param values - the possible values in precedence order630   */631  transform?: (args: { name: string; options: MongoOptions; values: unknown[] }) => unknown;632}633 634export const OPTIONS = {635  appName: {636    type: 'string'637  },638  auth: {639    target: 'credentials',640    transform({ name, options, values: [value] }): MongoCredentials {641      if (!isRecord(value, ['username', 'password'] as const)) {642        throw new MongoParseError(643          `${name} must be an object with 'username' and 'password' properties`644        );645      }646      return MongoCredentials.merge(options.credentials, {647        username: value.username,648        password: value.password649      });650    }651  },652  authMechanism: {653    target: 'credentials',654    transform({ options, values: [value] }): MongoCredentials {655      const mechanisms = Object.values(AuthMechanism);656      const [mechanism] = mechanisms.filter(m => m.match(RegExp(String.raw`\b${value}\b`, 'i')));657      if (!mechanism) {658        throw new MongoParseError(`authMechanism one of ${mechanisms}, got ${value}`);659      }660      let source = options.credentials?.source;661      if (662        mechanism === AuthMechanism.MONGODB_PLAIN ||663        AUTH_MECHS_AUTH_SRC_EXTERNAL.has(mechanism)664      ) {665        // some mechanisms have '$external' as the Auth Source666        source = '$external';667      }668 669      let password = options.credentials?.password;670      if (mechanism === AuthMechanism.MONGODB_X509 && password === '') {671        password = undefined;672      }673      return MongoCredentials.merge(options.credentials, {674        mechanism,675        source,676        password677      });678    }679  },680  // Note that if the authMechanismProperties contain a TOKEN_RESOURCE that has a681  // comma in it, it MUST be supplied as a MongoClient option instead of in the682  // connection string.683  authMechanismProperties: {684    target: 'credentials',685    transform({ options, values }): MongoCredentials {686      // We can have a combination of options passed in the URI and options passed687      // as an object to the MongoClient. So we must transform the string options688      // as well as merge them together with a potentially provided object.689      let mechanismProperties = Object.create(null);690 691      for (const optionValue of values) {692        if (typeof optionValue === 'string') {693          for (const [key, value] of entriesFromString(optionValue)) {694            try {695              mechanismProperties[key] = getBoolean(key, value);696            } catch {697              mechanismProperties[key] = value;698            }699          }700        } else {701          if (!isRecord(optionValue)) {702            throw new MongoParseError('AuthMechanismProperties must be an object');703          }704          mechanismProperties = { ...optionValue };705        }706      }707      return MongoCredentials.merge(options.credentials, {708        mechanismProperties709      });710    }711  },712  authSource: {713    target: 'credentials',714    transform({ options, values: [value] }): MongoCredentials {715      const source = String(value);716      return MongoCredentials.merge(options.credentials, { source });717    }718  },719  autoEncryption: {720    type: 'record'721  },722  autoSelectFamily: {723    type: 'boolean',724    default: true725  },726  autoSelectFamilyAttemptTimeout: {727    type: 'uint'728  },729  bsonRegExp: {730    type: 'boolean'731  },732  serverApi: {733    target: 'serverApi',734    transform({ values: [version] }): ServerApi {735      const serverApiToValidate =736        typeof version === 'string' ? ({ version } as ServerApi) : (version as ServerApi);737      const versionToValidate = serverApiToValidate && serverApiToValidate.version;738      if (!versionToValidate) {739        throw new MongoParseError(740          `Invalid \`serverApi\` property; must specify a version from the following enum: ["${Object.values(741            ServerApiVersion742          ).join('", "')}"]`743        );744      }745      if (!Object.values(ServerApiVersion).some(v => v === versionToValidate)) {746        throw new MongoParseError(747          `Invalid server API version=${versionToValidate}; must be in the following enum: ["${Object.values(748            ServerApiVersion749          ).join('", "')}"]`750        );751      }752      return serverApiToValidate;753    }754  },755  checkKeys: {756    type: 'boolean'757  },758  compressors: {759    default: 'none',760    target: 'compressors',761    transform({ values }) {762      const compressionList = new Set();763      for (const compVal of values as (CompressorName[] | string)[]) {764        const compValArray = typeof compVal === 'string' ? compVal.split(',') : compVal;765        if (!Array.isArray(compValArray)) {766          throw new MongoInvalidArgumentError(767            'compressors must be an array or a comma-delimited list of strings'768          );769        }770        for (const c of compValArray) {771          if (Object.keys(Compressor).includes(String(c))) {772            compressionList.add(String(c));773          } else {774            throw new MongoInvalidArgumentError(775              `${c} is not a valid compression mechanism. Must be one of: ${Object.keys(776                Compressor777              )}.`778            );779          }780        }781      }782      return [...compressionList];783    }784  },785  connectTimeoutMS: {786    default: 30000,787    type: 'uint'788  },789  dbName: {790    type: 'string'791  },792  directConnection: {793    default: false,794    type: 'boolean'795  },796  driverInfo: {797    default: {},798    type: 'record'799  },800  enableUtf8Validation: { type: 'boolean', default: true },801  family: {802    transform({ name, values: [value] }): 4 | 6 {803      const transformValue = getIntFromOptions(name, value);804      if (transformValue === 4 || transformValue === 6) {805        return transformValue;806      }807      throw new MongoParseError(`Option 'family' must be 4 or 6 got ${transformValue}.`);808    }809  },810  fieldsAsRaw: {811    type: 'record'812  },813  forceServerObjectId: {814    default: false,815    type: 'boolean'816  },817  fsync: {818    deprecated: 'Please use journal instead',819    target: 'writeConcern',820    transform({ name, options, values: [value] }): WriteConcern {821      const wc = WriteConcern.fromOptions({822        writeConcern: {823          ...options.writeConcern,824          fsync: getBoolean(name, value)825        }826      });827      if (!wc) throw new MongoParseError(`Unable to make a writeConcern from fsync=${value}`);828      return wc;829    }830  } as OptionDescriptor,831  heartbeatFrequencyMS: {832    default: 10000,833    type: 'uint'834  },835  ignoreUndefined: {836    type: 'boolean'837  },838  j: {839    deprecated: 'Please use journal instead',840    target: 'writeConcern',841    transform({ name, options, values: [value] }): WriteConcern {842      const wc = WriteConcern.fromOptions({843        writeConcern: {844          ...options.writeConcern,845          journal: getBoolean(name, value)846        }847      });848      if (!wc) throw new MongoParseError(`Unable to make a writeConcern from journal=${value}`);849      return wc;850    }851  } as OptionDescriptor,852  journal: {853    target: 'writeConcern',854    transform({ name, options, values: [value] }): WriteConcern {855      const wc = WriteConcern.fromOptions({856        writeConcern: {857          ...options.writeConcern,858          journal: getBoolean(name, value)859        }860      });861      if (!wc) throw new MongoParseError(`Unable to make a writeConcern from journal=${value}`);862      return wc;863    }864  },865  loadBalanced: {866    default: false,867    type: 'boolean'868  },869  localThresholdMS: {870    default: 15,871    type: 'uint'872  },873  maxConnecting: {874    default: 2,875    transform({ name, values: [value] }): number {876      const maxConnecting = getUIntFromOptions(name, value);877      if (maxConnecting === 0) {878        throw new MongoInvalidArgumentError('maxConnecting must be > 0 if specified');879      }880      return maxConnecting;881    }882  },883  maxIdleTimeMS: {884    default: 0,885    type: 'uint'886  },887  maxPoolSize: {888    default: 100,889    type: 'uint'890  },891  maxStalenessSeconds: {892    target: 'readPreference',893    transform({ name, options, values: [value] }) {894      const maxStalenessSeconds = getUIntFromOptions(name, value);895      if (options.readPreference) {896        return ReadPreference.fromOptions({897          readPreference: { ...options.readPreference, maxStalenessSeconds }898        });899      } else {900        return new ReadPreference('secondary', undefined, { maxStalenessSeconds });901      }902    }903  },904  minInternalBufferSize: {905    type: 'uint'906  },907  minPoolSize: {908    default: 0,909    type: 'uint'910  },911  minHeartbeatFrequencyMS: {912    default: 500,913    type: 'uint'914  },915  monitorCommands: {916    default: false,917    type: 'boolean'918  },919  name: {920    target: 'driverInfo',921    transform({ values: [value], options }) {922      return { ...options.driverInfo, name: String(value) };923    }924  } as OptionDescriptor,925  noDelay: {926    default: true,927    type: 'boolean'928  },929  pkFactory: {930    default: DEFAULT_PK_FACTORY,931    transform({ values: [value] }): PkFactory {932      if (isRecord(value, ['createPk'] as const) && typeof value.createPk === 'function') {933        return value as PkFactory;934      }935      throw new MongoParseError(936        `Option pkFactory must be an object with a createPk function, got ${value}`937      );938    }939  },940  promoteBuffers: {941    type: 'boolean'942  },943  promoteLongs: {944    type: 'boolean'945  },946  promoteValues: {947    type: 'boolean'948  },949  useBigInt64: {950    type: 'boolean'951  },952  proxyHost: {953    type: 'string'954  },955  proxyPassword: {956    type: 'string'957  },958  proxyPort: {959    type: 'uint'960  },961  proxyUsername: {962    type: 'string'963  },964  raw: {965    default: false,966    type: 'boolean'967  },968  readConcern: {969    transform({ values: [value], options }) {970      if (value instanceof ReadConcern || isRecord(value, ['level'] as const)) {971        return ReadConcern.fromOptions({ ...options.readConcern, ...value } as any);972      }973      throw new MongoParseError(`ReadConcern must be an object, got ${JSON.stringify(value)}`);974    }975  },976  readConcernLevel: {977    target: 'readConcern',978    transform({ values: [level], options }) {979      return ReadConcern.fromOptions({980        ...options.readConcern,981        level: level as ReadConcernLevel982      });983    }984  },985  readPreference: {986    default: ReadPreference.primary,987    transform({ values: [value], options }) {988      if (value instanceof ReadPreference) {989        return ReadPreference.fromOptions({990          readPreference: { ...options.readPreference, ...value },991          ...value992        } as any);993      }994      if (isRecord(value, ['mode'] as const)) {995        const rp = ReadPreference.fromOptions({996          readPreference: { ...options.readPreference, ...value },997          ...value998        } as any);999        if (rp) return rp;1000        else throw new MongoParseError(`Cannot make read preference from ${JSON.stringify(value)}`);1001      }1002      if (typeof value === 'string') {1003        const rpOpts = {1004          hedge: options.readPreference?.hedge,1005          maxStalenessSeconds: options.readPreference?.maxStalenessSeconds1006        };1007        return new ReadPreference(1008          value as ReadPreferenceMode,1009          options.readPreference?.tags,1010          rpOpts1011        );1012      }1013      throw new MongoParseError(`Unknown ReadPreference value: ${value}`);1014    }1015  },1016  readPreferenceTags: {1017    target: 'readPreference',1018    transform({1019      values,1020      options1021    }: {1022      values: Array<string | Record<string, string>[]>;1023      options: MongoClientOptions;1024    }) {1025      const tags: Array<string | Record<string, string>> = Array.isArray(values[0])1026        ? values[0]1027        : (values as Array<string>);1028      const readPreferenceTags = [];1029      for (const tag of tags) {1030        const readPreferenceTag: TagSet = Object.create(null);1031        if (typeof tag === 'string') {1032          for (const [k, v] of entriesFromString(tag)) {1033            readPreferenceTag[k] = v;1034          }1035        }1036        if (isRecord(tag)) {1037          for (const [k, v] of Object.entries(tag)) {1038            readPreferenceTag[k] = v;1039          }1040        }1041        readPreferenceTags.push(readPreferenceTag);1042      }1043      return ReadPreference.fromOptions({1044        readPreference: options.readPreference,1045        readPreferenceTags1046      });1047    }1048  },1049  replicaSet: {1050    type: 'string'1051  },1052  retryReads: {1053    default: true,1054    type: 'boolean'1055  },1056  retryWrites: {1057    default: true,1058    type: 'boolean'1059  },1060  serializeFunctions: {1061    type: 'boolean'1062  },1063  serverMonitoringMode: {1064    default: 'auto',1065    transform({ values: [value] }) {1066      if (!Object.values(ServerMonitoringMode).includes(value as any)) {1067        throw new MongoParseError(1068          'serverMonitoringMode must be one of `auto`, `poll`, or `stream`'1069        );1070      }1071      return value;1072    }1073  },1074  serverSelectionTimeoutMS: {1075    default: 30000,1076    type: 'uint'1077  },1078  servername: {1079    type: 'string'1080  },1081  socketTimeoutMS: {1082    // TODO(NODE-6491): deprecated: 'Please use timeoutMS instead',1083    default: 0,1084    type: 'uint'1085  },1086  srvMaxHosts: {1087    type: 'uint',1088    default: 01089  },1090  srvServiceName: {1091    type: 'string',1092    default: 'mongodb'1093  },1094  ssl: {1095    target: 'tls',1096    type: 'boolean'1097  },1098  timeoutMS: {1099    type: 'uint'1100  },1101  tls: {1102    type: 'boolean'1103  },1104  tlsAllowInvalidCertificates: {1105    target: 'rejectUnauthorized',1106    transform({ name, values: [value] }) {1107      // allowInvalidCertificates is the inverse of rejectUnauthorized1108      return !getBoolean(name, value);1109    }1110  },1111  tlsAllowInvalidHostnames: {1112    target: 'checkServerIdentity',1113    transform({ name, values: [value] }) {1114      // tlsAllowInvalidHostnames means setting the checkServerIdentity function to a noop1115      return getBoolean(name, value) ? () => undefined : undefined;1116    }1117  },1118  tlsCAFile: {1119    type: 'string'1120  },1121  tlsCRLFile: {1122    type: 'string'1123  },1124  tlsCertificateKeyFile: {1125    type: 'string'1126  },1127  tlsCertificateKeyFilePassword: {1128    target: 'passphrase',1129    type: 'any'1130  },1131  tlsInsecure: {1132    transform({ name, options, values: [value] }) {1133      const tlsInsecure = getBoolean(name, value);1134      if (tlsInsecure) {1135        options.checkServerIdentity = () => undefined;1136        options.rejectUnauthorized = false;1137      } else {1138        options.checkServerIdentity = options.tlsAllowInvalidHostnames1139          ? () => undefined1140          : undefined;1141        options.rejectUnauthorized = options.tlsAllowInvalidCertificates ? false : true;1142      }1143      return tlsInsecure;1144    }1145  },1146  w: {1147    target: 'writeConcern',1148    transform({ values: [value], options }) {1149      return WriteConcern.fromOptions({ writeConcern: { ...options.writeConcern, w: value as W } });1150    }1151  },1152  waitQueueTimeoutMS: {1153    // TODO(NODE-6491): deprecated: 'Please use timeoutMS instead',1154    default: 0,1155    type: 'uint'1156  },1157  writeConcern: {1158    target: 'writeConcern',1159    transform({ values: [value], options }) {1160      if (isRecord(value) || value instanceof WriteConcern) {1161        return WriteConcern.fromOptions({1162          writeConcern: {1163            ...options.writeConcern,1164            ...value1165          }1166        });1167      } else if (value === 'majority' || typeof value === 'number') {1168        return WriteConcern.fromOptions({1169          writeConcern: {1170            ...options.writeConcern,1171            w: value1172          }1173        });1174      }1175 1176      throw new MongoParseError(`Invalid WriteConcern cannot parse: ${JSON.stringify(value)}`);1177    }1178  },1179  wtimeout: {1180    deprecated: 'Please use wtimeoutMS instead',1181    target: 'writeConcern',1182    transform({ values: [value], options }) {1183      const wc = WriteConcern.fromOptions({1184        writeConcern: {1185          ...options.writeConcern,1186          wtimeout: getUIntFromOptions('wtimeout', value)1187        }1188      });1189      if (wc) return wc;1190      throw new MongoParseError(`Cannot make WriteConcern from wtimeout`);1191    }1192  } as OptionDescriptor,1193  wtimeoutMS: {1194    target: 'writeConcern',1195    transform({ values: [value], options }) {1196      const wc = WriteConcern.fromOptions({1197        writeConcern: {1198          ...options.writeConcern,1199          wtimeoutMS: getUIntFromOptions('wtimeoutMS', value)1200        }

Showing the first 1,200 of 1304 lines. Download the file for the rest.