| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043 | "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.FEATURE_FLAGS = exports.DEFAULT_OPTIONS = exports.OPTIONS = exports.parseOptions = exports.resolveSRVRecord = void 0;const dns = require("dns");const mongodb_connection_string_url_1 = require("mongodb-connection-string-url");const url_1 = require("url");const mongo_credentials_1 = require("./cmap/auth/mongo_credentials");const providers_1 = require("./cmap/auth/providers");const client_metadata_1 = require("./cmap/handshake/client_metadata");const compression_1 = require("./cmap/wire_protocol/compression");const encrypter_1 = require("./encrypter");const error_1 = require("./error");const mongo_client_1 = require("./mongo_client");const mongo_logger_1 = require("./mongo_logger");const read_concern_1 = require("./read_concern");const read_preference_1 = require("./read_preference");const utils_1 = require("./utils");const write_concern_1 = require("./write_concern");const VALID_TXT_RECORDS = ['authSource', 'replicaSet', 'loadBalanced'];const LB_SINGLE_HOST_ERROR = 'loadBalanced option only supported with a single host in the URI';const LB_REPLICA_SET_ERROR = 'loadBalanced option not supported with a replicaSet option';const LB_DIRECT_CONNECTION_ERROR = 'loadBalanced option not supported when directConnection is provided';/** * Lookup a `mongodb+srv` connection string, combine the parts and reparse it as a normal * connection string. * * @param uri - The connection string to parse * @param options - Optional user provided connection string options */async function resolveSRVRecord(options) {    if (typeof options.srvHost !== 'string') {        throw new error_1.MongoAPIError('Option "srvHost" must not be empty');    }    if (options.srvHost.split('.').length < 3) {        // TODO(NODE-3484): Replace with MongoConnectionStringError        throw new error_1.MongoAPIError('URI must include hostname, domain name, and tld');    }    // Resolve the SRV record and use the result as the list of hosts to connect to.    const lookupAddress = options.srvHost;    const addresses = await dns.promises.resolveSrv(`_${options.srvServiceName}._tcp.${lookupAddress}`);    if (addresses.length === 0) {        throw new error_1.MongoAPIError('No addresses found at host');    }    for (const { name } of addresses) {        if (!(0, utils_1.matchesParentDomain)(name, lookupAddress)) {            throw new error_1.MongoAPIError('Server record does not share hostname with parent URI');        }    }    const hostAddresses = addresses.map(r => utils_1.HostAddress.fromString(`${r.name}:${r.port ?? 27017}`));    validateLoadBalancedOptions(hostAddresses, options, true);    // Resolve TXT record and add options from there if they exist.    let record;    try {        record = await dns.promises.resolveTxt(lookupAddress);    }    catch (error) {        if (error.code !== 'ENODATA' && error.code !== 'ENOTFOUND') {            throw error;        }        return hostAddresses;    }    if (record.length > 1) {        throw new error_1.MongoParseError('Multiple text records not allowed');    }    const txtRecordOptions = new url_1.URLSearchParams(record[0].join(''));    const txtRecordOptionKeys = [...txtRecordOptions.keys()];    if (txtRecordOptionKeys.some(key => !VALID_TXT_RECORDS.includes(key))) {        throw new error_1.MongoParseError(`Text record may only set any of: ${VALID_TXT_RECORDS.join(', ')}`);    }    if (VALID_TXT_RECORDS.some(option => txtRecordOptions.get(option) === '')) {        throw new error_1.MongoParseError('Cannot have empty URI params in DNS TXT Record');    }    const source = txtRecordOptions.get('authSource') ?? undefined;    const replicaSet = txtRecordOptions.get('replicaSet') ?? undefined;    const loadBalanced = txtRecordOptions.get('loadBalanced') ?? undefined;    if (!options.userSpecifiedAuthSource &&        source &&        options.credentials &&        !providers_1.AUTH_MECHS_AUTH_SRC_EXTERNAL.has(options.credentials.mechanism)) {        options.credentials = mongo_credentials_1.MongoCredentials.merge(options.credentials, { source });    }    if (!options.userSpecifiedReplicaSet && replicaSet) {        options.replicaSet = replicaSet;    }    if (loadBalanced === 'true') {        options.loadBalanced = true;    }    if (options.replicaSet && options.srvMaxHosts > 0) {        throw new error_1.MongoParseError('Cannot combine replicaSet option with srvMaxHosts');    }    validateLoadBalancedOptions(hostAddresses, options, true);    return hostAddresses;}exports.resolveSRVRecord = resolveSRVRecord;/** * Checks if TLS options are valid * * @param allOptions - All options provided by user or included in default options map * @throws MongoAPIError if TLS options are invalid */function checkTLSOptions(allOptions) {    if (!allOptions)        return;    const check = (a, b) => {        if (allOptions.has(a) && allOptions.has(b)) {            throw new error_1.MongoAPIError(`The '${a}' option cannot be used with the '${b}' option`);        }    };    check('tlsInsecure', 'tlsAllowInvalidCertificates');    check('tlsInsecure', 'tlsAllowInvalidHostnames');    check('tlsInsecure', 'tlsDisableCertificateRevocationCheck');    check('tlsInsecure', 'tlsDisableOCSPEndpointCheck');    check('tlsAllowInvalidCertificates', 'tlsDisableCertificateRevocationCheck');    check('tlsAllowInvalidCertificates', 'tlsDisableOCSPEndpointCheck');    check('tlsDisableCertificateRevocationCheck', 'tlsDisableOCSPEndpointCheck');}function getBoolean(name, value) {    if (typeof value === 'boolean')        return value;    switch (value) {        case 'true':            return true;        case 'false':            return false;        default:            throw new error_1.MongoParseError(`${name} must be either "true" or "false"`);    }}function getIntFromOptions(name, value) {    const parsedInt = (0, utils_1.parseInteger)(value);    if (parsedInt != null) {        return parsedInt;    }    throw new error_1.MongoParseError(`Expected ${name} to be stringified int value, got: ${value}`);}function getUIntFromOptions(name, value) {    const parsedValue = getIntFromOptions(name, value);    if (parsedValue < 0) {        throw new error_1.MongoParseError(`${name} can only be a positive int value, got: ${value}`);    }    return parsedValue;}function* entriesFromString(value) {    if (value === '') {        return;    }    const keyValuePairs = value.split(',');    for (const keyValue of keyValuePairs) {        const [key, value] = keyValue.split(/:(.*)/);        if (value == null) {            throw new error_1.MongoParseError('Cannot have undefined values in key value pairs');        }        yield [key, value];    }}class CaseInsensitiveMap extends Map {    constructor(entries = []) {        super(entries.map(([k, v]) => [k.toLowerCase(), v]));    }    has(k) {        return super.has(k.toLowerCase());    }    get(k) {        return super.get(k.toLowerCase());    }    set(k, v) {        return super.set(k.toLowerCase(), v);    }    delete(k) {        return super.delete(k.toLowerCase());    }}function parseOptions(uri, mongoClient = undefined, options = {}) {    if (mongoClient != null && !(mongoClient instanceof mongo_client_1.MongoClient)) {        options = mongoClient;        mongoClient = undefined;    }    // validate BSONOptions    if (options.useBigInt64 && typeof options.promoteLongs === 'boolean' && !options.promoteLongs) {        throw new error_1.MongoAPIError('Must request either bigint or Long for int64 deserialization');    }    if (options.useBigInt64 && typeof options.promoteValues === 'boolean' && !options.promoteValues) {        throw new error_1.MongoAPIError('Must request either bigint or Long for int64 deserialization');    }    const url = new mongodb_connection_string_url_1.default(uri);    const { hosts, isSRV } = url;    const mongoOptions = Object.create(null);    // Feature flags    for (const flag of Object.getOwnPropertySymbols(options)) {        if (exports.FEATURE_FLAGS.has(flag)) {            mongoOptions[flag] = options[flag];        }    }    mongoOptions.hosts = isSRV ? [] : hosts.map(utils_1.HostAddress.fromString);    const urlOptions = new CaseInsensitiveMap();    if (url.pathname !== '/' && url.pathname !== '') {        const dbName = decodeURIComponent(url.pathname[0] === '/' ? url.pathname.slice(1) : url.pathname);        if (dbName) {            urlOptions.set('dbName', [dbName]);        }    }    if (url.username !== '') {        const auth = {            username: decodeURIComponent(url.username)        };        if (typeof url.password === 'string') {            auth.password = decodeURIComponent(url.password);        }        urlOptions.set('auth', [auth]);    }    for (const key of url.searchParams.keys()) {        const values = url.searchParams.getAll(key);        const isReadPreferenceTags = /readPreferenceTags/i.test(key);        if (!isReadPreferenceTags && values.length > 1) {            throw new error_1.MongoInvalidArgumentError(`URI option "${key}" cannot appear more than once in the connection string`);        }        if (!isReadPreferenceTags && values.includes('')) {            throw new error_1.MongoAPIError(`URI option "${key}" cannot be specified with no value`);        }        if (!urlOptions.has(key)) {            urlOptions.set(key, values);        }    }    const objectOptions = new CaseInsensitiveMap(Object.entries(options).filter(([, v]) => v != null));    // Validate options that can only be provided by one of uri or object    if (urlOptions.has('serverApi')) {        throw new error_1.MongoParseError('URI cannot contain `serverApi`, it can only be passed to the client');    }    const uriMechanismProperties = urlOptions.get('authMechanismProperties');    if (uriMechanismProperties) {        for (const property of uriMechanismProperties) {            if (/(^|,)ALLOWED_HOSTS:/.test(property)) {                throw new error_1.MongoParseError('Auth mechanism property ALLOWED_HOSTS is not allowed in the connection string.');            }        }    }    if (objectOptions.has('loadBalanced')) {        throw new error_1.MongoParseError('loadBalanced is only a valid option in the URI');    }    // All option collection    const allProvidedOptions = new CaseInsensitiveMap();    const allProvidedKeys = new Set([...urlOptions.keys(), ...objectOptions.keys()]);    for (const key of allProvidedKeys) {        const values = [];        const objectOptionValue = objectOptions.get(key);        if (objectOptionValue != null) {            values.push(objectOptionValue);        }        const urlValues = urlOptions.get(key) ?? [];        values.push(...urlValues);        allProvidedOptions.set(key, values);    }    if (allProvidedOptions.has('tls') || allProvidedOptions.has('ssl')) {        const tlsAndSslOpts = (allProvidedOptions.get('tls') || [])            .concat(allProvidedOptions.get('ssl') || [])            .map(getBoolean.bind(null, 'tls/ssl'));        if (new Set(tlsAndSslOpts).size !== 1) {            throw new error_1.MongoParseError('All values of tls/ssl must be the same.');        }    }    checkTLSOptions(allProvidedOptions);    const unsupportedOptions = (0, utils_1.setDifference)(allProvidedKeys, Array.from(Object.keys(exports.OPTIONS)).map(s => s.toLowerCase()));    if (unsupportedOptions.size !== 0) {        const optionWord = unsupportedOptions.size > 1 ? 'options' : 'option';        const isOrAre = unsupportedOptions.size > 1 ? 'are' : 'is';        throw new error_1.MongoParseError(`${optionWord} ${Array.from(unsupportedOptions).join(', ')} ${isOrAre} not supported`);    }    // Option parsing and setting    for (const [key, descriptor] of Object.entries(exports.OPTIONS)) {        const values = allProvidedOptions.get(key);        if (!values || values.length === 0) {            if (exports.DEFAULT_OPTIONS.has(key)) {                setOption(mongoOptions, key, descriptor, [exports.DEFAULT_OPTIONS.get(key)]);            }        }        else {            const { deprecated } = descriptor;            if (deprecated) {                const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';                (0, utils_1.emitWarning)(`${key} is a deprecated option${deprecatedMsg}`);            }            setOption(mongoOptions, key, descriptor, values);        }    }    if (mongoOptions.credentials) {        const isGssapi = mongoOptions.credentials.mechanism === providers_1.AuthMechanism.MONGODB_GSSAPI;        const isX509 = mongoOptions.credentials.mechanism === providers_1.AuthMechanism.MONGODB_X509;        const isAws = mongoOptions.credentials.mechanism === providers_1.AuthMechanism.MONGODB_AWS;        const isOidc = mongoOptions.credentials.mechanism === providers_1.AuthMechanism.MONGODB_OIDC;        if ((isGssapi || isX509) &&            allProvidedOptions.has('authSource') &&            mongoOptions.credentials.source !== '$external') {            // If authSource was explicitly given and its incorrect, we error            throw new error_1.MongoParseError(`authMechanism ${mongoOptions.credentials.mechanism} requires an authSource of '$external'`);        }        if (!(isGssapi || isX509 || isAws || isOidc) &&            mongoOptions.dbName &&            !allProvidedOptions.has('authSource')) {            // inherit the dbName unless GSSAPI or X509, then silently ignore dbName            // and there was no specific authSource given            mongoOptions.credentials = mongo_credentials_1.MongoCredentials.merge(mongoOptions.credentials, {                source: mongoOptions.dbName            });        }        if (isAws && mongoOptions.credentials.username && !mongoOptions.credentials.password) {            throw new error_1.MongoMissingCredentialsError(`When using ${mongoOptions.credentials.mechanism} password must be set when a username is specified`);        }        mongoOptions.credentials.validate();        // Check if the only auth related option provided was authSource, if so we can remove credentials        if (mongoOptions.credentials.password === '' &&            mongoOptions.credentials.username === '' &&            mongoOptions.credentials.mechanism === providers_1.AuthMechanism.MONGODB_DEFAULT &&            Object.keys(mongoOptions.credentials.mechanismProperties).length === 0) {            delete mongoOptions.credentials;        }    }    if (!mongoOptions.dbName) {        // dbName default is applied here because of the credential validation above        mongoOptions.dbName = 'test';    }    validateLoadBalancedOptions(hosts, mongoOptions, isSRV);    if (mongoClient && mongoOptions.autoEncryption) {        encrypter_1.Encrypter.checkForMongoCrypt();        mongoOptions.encrypter = new encrypter_1.Encrypter(mongoClient, uri, options);        mongoOptions.autoEncrypter = mongoOptions.encrypter.autoEncrypter;    }    // Potential SRV Overrides and SRV connection string validations    mongoOptions.userSpecifiedAuthSource =        objectOptions.has('authSource') || urlOptions.has('authSource');    mongoOptions.userSpecifiedReplicaSet =        objectOptions.has('replicaSet') || urlOptions.has('replicaSet');    if (isSRV) {        // SRV Record is resolved upon connecting        mongoOptions.srvHost = hosts[0];        if (mongoOptions.directConnection) {            throw new error_1.MongoAPIError('SRV URI does not support directConnection');        }        if (mongoOptions.srvMaxHosts > 0 && typeof mongoOptions.replicaSet === 'string') {            throw new error_1.MongoParseError('Cannot use srvMaxHosts option with replicaSet');        }        // SRV turns on TLS by default, but users can override and turn it off        const noUserSpecifiedTLS = !objectOptions.has('tls') && !urlOptions.has('tls');        const noUserSpecifiedSSL = !objectOptions.has('ssl') && !urlOptions.has('ssl');        if (noUserSpecifiedTLS && noUserSpecifiedSSL) {            mongoOptions.tls = true;        }    }    else {        const userSpecifiedSrvOptions = urlOptions.has('srvMaxHosts') ||            objectOptions.has('srvMaxHosts') ||            urlOptions.has('srvServiceName') ||            objectOptions.has('srvServiceName');        if (userSpecifiedSrvOptions) {            throw new error_1.MongoParseError('Cannot use srvMaxHosts or srvServiceName with a non-srv connection string');        }    }    if (mongoOptions.directConnection && mongoOptions.hosts.length !== 1) {        throw new error_1.MongoParseError('directConnection option requires exactly one host');    }    if (!mongoOptions.proxyHost &&        (mongoOptions.proxyPort || mongoOptions.proxyUsername || mongoOptions.proxyPassword)) {        throw new error_1.MongoParseError('Must specify proxyHost if other proxy options are passed');    }    if ((mongoOptions.proxyUsername && !mongoOptions.proxyPassword) ||        (!mongoOptions.proxyUsername && mongoOptions.proxyPassword)) {        throw new error_1.MongoParseError('Can only specify both of proxy username/password or neither');    }    const proxyOptions = ['proxyHost', 'proxyPort', 'proxyUsername', 'proxyPassword'].map(key => urlOptions.get(key) ?? []);    if (proxyOptions.some(options => options.length > 1)) {        throw new error_1.MongoParseError('Proxy options cannot be specified multiple times in the connection string');    }    const loggerFeatureFlag = Symbol.for('@@mdb.enableMongoLogger');    mongoOptions[loggerFeatureFlag] = mongoOptions[loggerFeatureFlag] ?? false;    let loggerEnvOptions = {};    let loggerClientOptions = {};    if (mongoOptions[loggerFeatureFlag]) {        loggerEnvOptions = {            MONGODB_LOG_COMMAND: process.env.MONGODB_LOG_COMMAND,            MONGODB_LOG_TOPOLOGY: process.env.MONGODB_LOG_TOPOLOGY,            MONGODB_LOG_SERVER_SELECTION: process.env.MONGODB_LOG_SERVER_SELECTION,            MONGODB_LOG_CONNECTION: process.env.MONGODB_LOG_CONNECTION,            MONGODB_LOG_ALL: process.env.MONGODB_LOG_ALL,            MONGODB_LOG_MAX_DOCUMENT_LENGTH: process.env.MONGODB_LOG_MAX_DOCUMENT_LENGTH,            MONGODB_LOG_PATH: process.env.MONGODB_LOG_PATH,            ...mongoOptions[Symbol.for('@@mdb.internalLoggerConfig')]        };        loggerClientOptions = {            mongodbLogPath: mongoOptions.mongodbLogPath        };    }    mongoOptions.mongoLoggerOptions = mongo_logger_1.MongoLogger.resolveOptions(loggerEnvOptions, loggerClientOptions);    mongoOptions.metadata = (0, client_metadata_1.makeClientMetadata)(mongoOptions);    return mongoOptions;}exports.parseOptions = parseOptions;/** * #### Throws if LB mode is true: * - hosts contains more than one host * - there is a replicaSet name set * - directConnection is set * - if srvMaxHosts is used when an srv connection string is passed in * * @throws MongoParseError */function validateLoadBalancedOptions(hosts, mongoOptions, isSrv) {    if (mongoOptions.loadBalanced) {        if (hosts.length > 1) {            throw new error_1.MongoParseError(LB_SINGLE_HOST_ERROR);        }        if (mongoOptions.replicaSet) {            throw new error_1.MongoParseError(LB_REPLICA_SET_ERROR);        }        if (mongoOptions.directConnection) {            throw new error_1.MongoParseError(LB_DIRECT_CONNECTION_ERROR);        }        if (isSrv && mongoOptions.srvMaxHosts > 0) {            throw new error_1.MongoParseError('Cannot limit srv hosts with loadBalanced enabled');        }    }    return;}function setOption(mongoOptions, key, descriptor, values) {    const { target, type, transform } = descriptor;    const name = target ?? key;    switch (type) {        case 'boolean':            mongoOptions[name] = getBoolean(name, values[0]);            break;        case 'int':            mongoOptions[name] = getIntFromOptions(name, values[0]);            break;        case 'uint':            mongoOptions[name] = getUIntFromOptions(name, values[0]);            break;        case 'string':            if (values[0] == null) {                break;            }            mongoOptions[name] = String(values[0]);            break;        case 'record':            if (!(0, utils_1.isRecord)(values[0])) {                throw new error_1.MongoParseError(`${name} must be an object`);            }            mongoOptions[name] = values[0];            break;        case 'any':            mongoOptions[name] = values[0];            break;        default: {            if (!transform) {                throw new error_1.MongoParseError('Descriptors missing a type must define a transform');            }            const transformValue = transform({ name, options: mongoOptions, values });            mongoOptions[name] = transformValue;            break;        }    }}exports.OPTIONS = {    appName: {        type: 'string'    },    auth: {        target: 'credentials',        transform({ name, options, values: [value] }) {            if (!(0, utils_1.isRecord)(value, ['username', 'password'])) {                throw new error_1.MongoParseError(`${name} must be an object with 'username' and 'password' properties`);            }            return mongo_credentials_1.MongoCredentials.merge(options.credentials, {                username: value.username,                password: value.password            });        }    },    authMechanism: {        target: 'credentials',        transform({ options, values: [value] }) {            const mechanisms = Object.values(providers_1.AuthMechanism);            const [mechanism] = mechanisms.filter(m => m.match(RegExp(String.raw `\b${value}\b`, 'i')));            if (!mechanism) {                throw new error_1.MongoParseError(`authMechanism one of ${mechanisms}, got ${value}`);            }            let source = options.credentials?.source;            if (mechanism === providers_1.AuthMechanism.MONGODB_PLAIN ||                providers_1.AUTH_MECHS_AUTH_SRC_EXTERNAL.has(mechanism)) {                // some mechanisms have '$external' as the Auth Source                source = '$external';            }            let password = options.credentials?.password;            if (mechanism === providers_1.AuthMechanism.MONGODB_X509 && password === '') {                password = undefined;            }            return mongo_credentials_1.MongoCredentials.merge(options.credentials, {                mechanism,                source,                password            });        }    },    authMechanismProperties: {        target: 'credentials',        transform({ options, values }) {            // We can have a combination of options passed in the URI and options passed            // as an object to the MongoClient. So we must transform the string options            // as well as merge them together with a potentially provided object.            let mechanismProperties = Object.create(null);            for (const optionValue of values) {                if (typeof optionValue === 'string') {                    for (const [key, value] of entriesFromString(optionValue)) {                        try {                            mechanismProperties[key] = getBoolean(key, value);                        }                        catch {                            mechanismProperties[key] = value;                        }                    }                }                else {                    if (!(0, utils_1.isRecord)(optionValue)) {                        throw new error_1.MongoParseError('AuthMechanismProperties must be an object');                    }                    mechanismProperties = { ...optionValue };                }            }            return mongo_credentials_1.MongoCredentials.merge(options.credentials, {                mechanismProperties            });        }    },    authSource: {        target: 'credentials',        transform({ options, values: [value] }) {            const source = String(value);            return mongo_credentials_1.MongoCredentials.merge(options.credentials, { source });        }    },    autoEncryption: {        type: 'record'    },    bsonRegExp: {        type: 'boolean'    },    serverApi: {        target: 'serverApi',        transform({ values: [version] }) {            const serverApiToValidate = typeof version === 'string' ? { version } : version;            const versionToValidate = serverApiToValidate && serverApiToValidate.version;            if (!versionToValidate) {                throw new error_1.MongoParseError(`Invalid \`serverApi\` property; must specify a version from the following enum: ["${Object.values(mongo_client_1.ServerApiVersion).join('", "')}"]`);            }            if (!Object.values(mongo_client_1.ServerApiVersion).some(v => v === versionToValidate)) {                throw new error_1.MongoParseError(`Invalid server API version=${versionToValidate}; must be in the following enum: ["${Object.values(mongo_client_1.ServerApiVersion).join('", "')}"]`);            }            return serverApiToValidate;        }    },    checkKeys: {        type: 'boolean'    },    compressors: {        default: 'none',        target: 'compressors',        transform({ values }) {            const compressionList = new Set();            for (const compVal of values) {                const compValArray = typeof compVal === 'string' ? compVal.split(',') : compVal;                if (!Array.isArray(compValArray)) {                    throw new error_1.MongoInvalidArgumentError('compressors must be an array or a comma-delimited list of strings');                }                for (const c of compValArray) {                    if (Object.keys(compression_1.Compressor).includes(String(c))) {                        compressionList.add(String(c));                    }                    else {                        throw new error_1.MongoInvalidArgumentError(`${c} is not a valid compression mechanism. Must be one of: ${Object.keys(compression_1.Compressor)}.`);                    }                }            }            return [...compressionList];        }    },    connectTimeoutMS: {        default: 30000,        type: 'uint'    },    dbName: {        type: 'string'    },    directConnection: {        default: false,        type: 'boolean'    },    driverInfo: {        default: {},        type: 'record'    },    enableUtf8Validation: { type: 'boolean', default: true },    family: {        transform({ name, values: [value] }) {            const transformValue = getIntFromOptions(name, value);            if (transformValue === 4 || transformValue === 6) {                return transformValue;            }            throw new error_1.MongoParseError(`Option 'family' must be 4 or 6 got ${transformValue}.`);        }    },    fieldsAsRaw: {        type: 'record'    },    forceServerObjectId: {        default: false,        type: 'boolean'    },    fsync: {        deprecated: 'Please use journal instead',        target: 'writeConcern',        transform({ name, options, values: [value] }) {            const wc = write_concern_1.WriteConcern.fromOptions({                writeConcern: {                    ...options.writeConcern,                    fsync: getBoolean(name, value)                }            });            if (!wc)                throw new error_1.MongoParseError(`Unable to make a writeConcern from fsync=${value}`);            return wc;        }    },    heartbeatFrequencyMS: {        default: 10000,        type: 'uint'    },    ignoreUndefined: {        type: 'boolean'    },    j: {        deprecated: 'Please use journal instead',        target: 'writeConcern',        transform({ name, options, values: [value] }) {            const wc = write_concern_1.WriteConcern.fromOptions({                writeConcern: {                    ...options.writeConcern,                    journal: getBoolean(name, value)                }            });            if (!wc)                throw new error_1.MongoParseError(`Unable to make a writeConcern from journal=${value}`);            return wc;        }    },    journal: {        target: 'writeConcern',        transform({ name, options, values: [value] }) {            const wc = write_concern_1.WriteConcern.fromOptions({                writeConcern: {                    ...options.writeConcern,                    journal: getBoolean(name, value)                }            });            if (!wc)                throw new error_1.MongoParseError(`Unable to make a writeConcern from journal=${value}`);            return wc;        }    },    loadBalanced: {        default: false,        type: 'boolean'    },    localThresholdMS: {        default: 15,        type: 'uint'    },    maxConnecting: {        default: 2,        transform({ name, values: [value] }) {            const maxConnecting = getUIntFromOptions(name, value);            if (maxConnecting === 0) {                throw new error_1.MongoInvalidArgumentError('maxConnecting must be > 0 if specified');            }            return maxConnecting;        }    },    maxIdleTimeMS: {        default: 0,        type: 'uint'    },    maxPoolSize: {        default: 100,        type: 'uint'    },    maxStalenessSeconds: {        target: 'readPreference',        transform({ name, options, values: [value] }) {            const maxStalenessSeconds = getUIntFromOptions(name, value);            if (options.readPreference) {                return read_preference_1.ReadPreference.fromOptions({                    readPreference: { ...options.readPreference, maxStalenessSeconds }                });            }            else {                return new read_preference_1.ReadPreference('secondary', undefined, { maxStalenessSeconds });            }        }    },    minInternalBufferSize: {        type: 'uint'    },    minPoolSize: {        default: 0,        type: 'uint'    },    minHeartbeatFrequencyMS: {        default: 500,        type: 'uint'    },    monitorCommands: {        default: false,        type: 'boolean'    },    name: {        target: 'driverInfo',        transform({ values: [value], options }) {            return { ...options.driverInfo, name: String(value) };        }    },    noDelay: {        default: true,        type: 'boolean'    },    pkFactory: {        default: utils_1.DEFAULT_PK_FACTORY,        transform({ values: [value] }) {            if ((0, utils_1.isRecord)(value, ['createPk']) && typeof value.createPk === 'function') {                return value;            }            throw new error_1.MongoParseError(`Option pkFactory must be an object with a createPk function, got ${value}`);        }    },    promoteBuffers: {        type: 'boolean'    },    promoteLongs: {        type: 'boolean'    },    promoteValues: {        type: 'boolean'    },    useBigInt64: {        type: 'boolean'    },    proxyHost: {        type: 'string'    },    proxyPassword: {        type: 'string'    },    proxyPort: {        type: 'uint'    },    proxyUsername: {        type: 'string'    },    raw: {        default: false,        type: 'boolean'    },    readConcern: {        transform({ values: [value], options }) {            if (value instanceof read_concern_1.ReadConcern || (0, utils_1.isRecord)(value, ['level'])) {                return read_concern_1.ReadConcern.fromOptions({ ...options.readConcern, ...value });            }            throw new error_1.MongoParseError(`ReadConcern must be an object, got ${JSON.stringify(value)}`);        }    },    readConcernLevel: {        target: 'readConcern',        transform({ values: [level], options }) {            return read_concern_1.ReadConcern.fromOptions({                ...options.readConcern,                level: level            });        }    },    readPreference: {        default: read_preference_1.ReadPreference.primary,        transform({ values: [value], options }) {            if (value instanceof read_preference_1.ReadPreference) {                return read_preference_1.ReadPreference.fromOptions({                    readPreference: { ...options.readPreference, ...value },                    ...value                });            }            if ((0, utils_1.isRecord)(value, ['mode'])) {                const rp = read_preference_1.ReadPreference.fromOptions({                    readPreference: { ...options.readPreference, ...value },                    ...value                });                if (rp)                    return rp;                else                    throw new error_1.MongoParseError(`Cannot make read preference from ${JSON.stringify(value)}`);            }            if (typeof value === 'string') {                const rpOpts = {                    hedge: options.readPreference?.hedge,                    maxStalenessSeconds: options.readPreference?.maxStalenessSeconds                };                return new read_preference_1.ReadPreference(value, options.readPreference?.tags, rpOpts);            }            throw new error_1.MongoParseError(`Unknown ReadPreference value: ${value}`);        }    },    readPreferenceTags: {        target: 'readPreference',        transform({ values, options }) {            const tags = Array.isArray(values[0])                ? values[0]                : values;            const readPreferenceTags = [];            for (const tag of tags) {                const readPreferenceTag = Object.create(null);                if (typeof tag === 'string') {                    for (const [k, v] of entriesFromString(tag)) {                        readPreferenceTag[k] = v;                    }                }                if ((0, utils_1.isRecord)(tag)) {                    for (const [k, v] of Object.entries(tag)) {                        readPreferenceTag[k] = v;                    }                }                readPreferenceTags.push(readPreferenceTag);            }            return read_preference_1.ReadPreference.fromOptions({                readPreference: options.readPreference,                readPreferenceTags            });        }    },    replicaSet: {        type: 'string'    },    retryReads: {        default: true,        type: 'boolean'    },    retryWrites: {        default: true,        type: 'boolean'    },    serializeFunctions: {        type: 'boolean'    },    serverSelectionTimeoutMS: {        default: 30000,        type: 'uint'    },    servername: {        type: 'string'    },    socketTimeoutMS: {        default: 0,        type: 'uint'    },    srvMaxHosts: {        type: 'uint',        default: 0    },    srvServiceName: {        type: 'string',        default: 'mongodb'    },    ssl: {        target: 'tls',        type: 'boolean'    },    tls: {        type: 'boolean'    },    tlsAllowInvalidCertificates: {        target: 'rejectUnauthorized',        transform({ name, values: [value] }) {            // allowInvalidCertificates is the inverse of rejectUnauthorized            return !getBoolean(name, value);        }    },    tlsAllowInvalidHostnames: {        target: 'checkServerIdentity',        transform({ name, values: [value] }) {            // tlsAllowInvalidHostnames means setting the checkServerIdentity function to a noop            return getBoolean(name, value) ? () => undefined : undefined;        }    },    tlsCAFile: {        type: 'string'    },    tlsCRLFile: {        type: 'string'    },    tlsCertificateKeyFile: {        type: 'string'    },    tlsCertificateKeyFilePassword: {        target: 'passphrase',        type: 'any'    },    tlsInsecure: {        transform({ name, options, values: [value] }) {            const tlsInsecure = getBoolean(name, value);            if (tlsInsecure) {                options.checkServerIdentity = () => undefined;                options.rejectUnauthorized = false;            }            else {                options.checkServerIdentity = options.tlsAllowInvalidHostnames                    ? () => undefined                    : undefined;                options.rejectUnauthorized = options.tlsAllowInvalidCertificates ? false : true;            }            return tlsInsecure;        }    },    w: {        target: 'writeConcern',        transform({ values: [value], options }) {            return write_concern_1.WriteConcern.fromOptions({ writeConcern: { ...options.writeConcern, w: value } });        }    },    waitQueueTimeoutMS: {        default: 0,        type: 'uint'    },    writeConcern: {        target: 'writeConcern',        transform({ values: [value], options }) {            if ((0, utils_1.isRecord)(value) || value instanceof write_concern_1.WriteConcern) {                return write_concern_1.WriteConcern.fromOptions({                    writeConcern: {                        ...options.writeConcern,                        ...value                    }                });            }            else if (value === 'majority' || typeof value === 'number') {                return write_concern_1.WriteConcern.fromOptions({                    writeConcern: {                        ...options.writeConcern,                        w: value                    }                });            }            throw new error_1.MongoParseError(`Invalid WriteConcern cannot parse: ${JSON.stringify(value)}`);        }    },    wtimeout: {        deprecated: 'Please use wtimeoutMS instead',        target: 'writeConcern',        transform({ values: [value], options }) {            const wc = write_concern_1.WriteConcern.fromOptions({                writeConcern: {                    ...options.writeConcern,                    wtimeout: getUIntFromOptions('wtimeout', value)                }            });            if (wc)                return wc;            throw new error_1.MongoParseError(`Cannot make WriteConcern from wtimeout`);        }    },    wtimeoutMS: {        target: 'writeConcern',        transform({ values: [value], options }) {            const wc = write_concern_1.WriteConcern.fromOptions({                writeConcern: {                    ...options.writeConcern,                    wtimeoutMS: getUIntFromOptions('wtimeoutMS', value)                }            });            if (wc)                return wc;            throw new error_1.MongoParseError(`Cannot make WriteConcern from wtimeout`);        }    },    zlibCompressionLevel: {        default: 0,        type: 'int'    },    // Custom types for modifying core behavior    connectionType: { type: 'any' },    srvPoller: { type: 'any' },    // Accepted NodeJS Options    minDHSize: { type: 'any' },    pskCallback: { type: 'any' },    secureContext: { type: 'any' },    enableTrace: { type: 'any' },    requestCert: { type: 'any' },    rejectUnauthorized: { type: 'any' },    checkServerIdentity: { type: 'any' },    ALPNProtocols: { type: 'any' },    SNICallback: { type: 'any' },    session: { type: 'any' },    requestOCSP: { type: 'any' },    localAddress: { type: 'any' },    localPort: { type: 'any' },    hints: { type: 'any' },    lookup: { type: 'any' },    ca: { type: 'any' },    cert: { type: 'any' },    ciphers: { type: 'any' },    crl: { type: 'any' },    ecdhCurve: { type: 'any' },    key: { type: 'any' },    passphrase: { type: 'any' },    pfx: { type: 'any' },    secureProtocol: { type: 'any' },    index: { type: 'any' },    // Legacy options from v3 era    useNewUrlParser: {        type: 'boolean',        deprecated: 'useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'    },    useUnifiedTopology: {        type: 'boolean',        deprecated: 'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'    },    // MongoLogger    // TODO(NODE-4849): Tighten the type of mongodbLogPath    mongodbLogPath: { type: 'any' }};exports.DEFAULT_OPTIONS = new CaseInsensitiveMap(Object.entries(exports.OPTIONS)    .filter(([, descriptor]) => descriptor.default != null)    .map(([k, d]) => [k, d.default]));/** * Set of permitted feature flags * @internal */exports.FEATURE_FLAGS = new Set([    Symbol.for('@@mdb.skipPingOnConnect'),    Symbol.for('@@mdb.enableMongoLogger'),    Symbol.for('@@mdb.internalLoggerConfig')]);//# sourceMappingURL=connection_string.js.map
 |