@@ -19,6 +19,7 @@ import {
1919 ServerApi ,
2020 ServerApiVersion
2121} from './mongo_client' ;
22+ import type { OneOrMore } from './mongo_types' ;
2223import { PromiseProvider } from './promise_provider' ;
2324import { ReadConcern , ReadConcernLevel } from './read_concern' ;
2425import { ReadPreference , ReadPreferenceMode } from './read_preference' ;
@@ -28,6 +29,7 @@ import {
2829 Callback ,
2930 DEFAULT_PK_FACTORY ,
3031 emitWarning ,
32+ emitWarningOnce ,
3133 HostAddress ,
3234 isRecord ,
3335 makeClientMetadata ,
@@ -184,8 +186,22 @@ const FALSEHOODS = new Set(['false', 'f', '0', 'n', 'no', '-1']);
184186function getBoolean ( name : string , value : unknown ) : boolean {
185187 if ( typeof value === 'boolean' ) return value ;
186188 const valueString = String ( value ) . toLowerCase ( ) ;
187- if ( TRUTHS . has ( valueString ) ) return true ;
188- if ( FALSEHOODS . has ( valueString ) ) return false ;
189+ if ( TRUTHS . has ( valueString ) ) {
190+ if ( valueString !== 'true' ) {
191+ emitWarningOnce (
192+ `deprecated value for ${ name } : ${ valueString } - please update to ${ name } : true instead`
193+ ) ;
194+ }
195+ return true ;
196+ }
197+ if ( FALSEHOODS . has ( valueString ) ) {
198+ if ( valueString !== 'false' ) {
199+ emitWarningOnce (
200+ `deprecated value for ${ name } : ${ valueString } - please update to ${ name } : false instead`
201+ ) ;
202+ }
203+ return false ;
204+ }
189205 throw new MongoParseError ( `Expected ${ name } to be stringified boolean value, got: ${ value } ` ) ;
190206}
191207
@@ -204,31 +220,24 @@ function getUint(name: string, value: unknown): number {
204220 return parsedValue ;
205221}
206222
207- function toRecord ( value : string ) : Record < string , any > {
208- const record = Object . create ( null ) ;
223+ /** Wrap a single value in an array if the value is not an array */
224+ function toArray < T > ( value : OneOrMore < T > ) : T [ ] {
225+ return Array . isArray ( value ) ? value : [ value ] ;
226+ }
227+
228+ function * entriesFromString ( value : string ) {
209229 const keyValuePairs = value . split ( ',' ) ;
210230 for ( const keyValue of keyValuePairs ) {
211231 const [ key , value ] = keyValue . split ( ':' ) ;
212232 if ( value == null ) {
213233 throw new MongoParseError ( 'Cannot have undefined values in key value pairs' ) ;
214234 }
215- try {
216- // try to get a boolean
217- record [ key ] = getBoolean ( '' , value ) ;
218- } catch {
219- try {
220- // try to get a number
221- record [ key ] = getInt ( '' , value ) ;
222- } catch {
223- // keep value as a string
224- record [ key ] = value ;
225- }
226- }
235+
236+ yield [ key , value ] ;
227237 }
228- return record ;
229238}
230239
231- class CaseInsensitiveMap extends Map < string , any > {
240+ class CaseInsensitiveMap < Value = any > extends Map < string , Value > {
232241 constructor ( entries : Array < [ string , any ] > = [ ] ) {
233242 super ( entries . map ( ( [ k , v ] ) => [ k . toLowerCase ( ) , v ] ) ) ;
234243 }
@@ -262,7 +271,7 @@ export function parseOptions(
262271 const mongoOptions = Object . create ( null ) ;
263272 mongoOptions . hosts = isSRV ? [ ] : hosts . map ( HostAddress . fromString ) ;
264273
265- const urlOptions = new CaseInsensitiveMap ( ) ;
274+ const urlOptions = new CaseInsensitiveMap < any [ ] > ( ) ;
266275
267276 if ( url . pathname !== '/' && url . pathname !== '' ) {
268277 const dbName = decodeURIComponent (
@@ -324,16 +333,11 @@ export function parseOptions(
324333 ] ) ;
325334
326335 for ( const key of allKeys ) {
327- const values = [ ] ;
328- if ( objectOptions . has ( key ) ) {
329- values . push ( objectOptions . get ( key ) ) ;
330- }
331- if ( urlOptions . has ( key ) ) {
332- values . push ( ...urlOptions . get ( key ) ) ;
333- }
334- if ( DEFAULT_OPTIONS . has ( key ) ) {
335- values . push ( DEFAULT_OPTIONS . get ( key ) ) ;
336- }
336+ const values = [ objectOptions , urlOptions , DEFAULT_OPTIONS ] . flatMap ( optionsObject => {
337+ const options = optionsObject . get ( key ) ?? [ ] ;
338+ return toArray ( options ) ;
339+ } ) ;
340+
337341 allOptions . set ( key , values ) ;
338342 }
339343
@@ -478,12 +482,11 @@ export function parseOptions(
478482 throw new MongoParseError ( 'Can only specify both of proxy username/password or neither' ) ;
479483 }
480484
481- if (
482- urlOptions . get ( 'proxyHost' ) ?. length > 1 ||
483- urlOptions . get ( 'proxyPort' ) ?. length > 1 ||
484- urlOptions . get ( 'proxyUsername' ) ?. length > 1 ||
485- urlOptions . get ( 'proxyPassword' ) ?. length > 1
486- ) {
485+ const proxyOptions = [ 'proxyHost' , 'proxyPort' , 'proxyUsername' , 'proxyPassword' ] . map (
486+ key => urlOptions . get ( key ) ?? [ ]
487+ ) ;
488+
489+ if ( proxyOptions . some ( options => options . length > 1 ) ) {
487490 throw new MongoParseError (
488491 'Proxy options cannot be specified multiple times in the connection string'
489492 ) ;
@@ -629,14 +632,26 @@ export const OPTIONS = {
629632 } ,
630633 authMechanismProperties : {
631634 target : 'credentials' ,
632- transform ( { options, values : [ value ] } ) : MongoCredentials {
633- if ( typeof value === 'string' ) {
634- value = toRecord ( value ) ;
635+ transform ( { options, values : [ optionValue ] } ) : MongoCredentials {
636+ if ( typeof optionValue === 'string' ) {
637+ const mechanismProperties = Object . create ( null ) ;
638+
639+ for ( const [ key , value ] of entriesFromString ( optionValue ) ) {
640+ try {
641+ mechanismProperties [ key ] = getBoolean ( key , value ) ;
642+ } catch {
643+ mechanismProperties [ key ] = value ;
644+ }
645+ }
646+
647+ return MongoCredentials . merge ( options . credentials , {
648+ mechanismProperties
649+ } ) ;
635650 }
636- if ( ! isRecord ( value ) ) {
651+ if ( ! isRecord ( optionValue ) ) {
637652 throw new MongoParseError ( 'AuthMechanismProperties must be an object' ) ;
638653 }
639- return MongoCredentials . merge ( options . credentials , { mechanismProperties : value } ) ;
654+ return MongoCredentials . merge ( options . credentials , { mechanismProperties : optionValue } ) ;
640655 }
641656 } ,
642657 authSource : {
@@ -965,7 +980,7 @@ export const OPTIONS = {
965980 for ( const tag of values ) {
966981 const readPreferenceTag : TagSet = Object . create ( null ) ;
967982 if ( typeof tag === 'string' ) {
968- for ( const [ k , v ] of Object . entries ( toRecord ( tag ) ) ) {
983+ for ( const [ k , v ] of entriesFromString ( tag ) ) {
969984 readPreferenceTag [ k ] = v ;
970985 }
971986 }
0 commit comments