mongodb 7.7.0 → 7.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/bulk/unordered.js +1 -1
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/change_stream.js +3 -11
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +2 -0
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/collection.js +18 -119
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +110 -15
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +9 -62
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +0 -9
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +0 -9
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/list_collections_cursor.js +1 -1
- package/lib/cursor/list_collections_cursor.js.map +1 -1
- package/lib/cursor/list_indexes_cursor.js +1 -1
- package/lib/cursor/list_indexes_cursor.js.map +1 -1
- package/lib/cursor/run_command_cursor.js.map +1 -1
- package/lib/db.js +5 -58
- package/lib/db.js.map +1 -1
- package/lib/deps.js +35 -1
- package/lib/deps.js.map +1 -1
- package/lib/explain.js +0 -8
- package/lib/explain.js.map +1 -1
- package/lib/gridfs/download.js +3 -11
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +4 -4
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +3 -18
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/mongo_client.js +2 -60
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/command.js +0 -2
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/create_collection.js +1 -1
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/indexes.js +45 -16
- package/lib/operations/indexes.js.map +1 -1
- package/lib/sdam/srv_polling.js +7 -2
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +3 -1
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +10 -47
- package/lib/sessions.js.map +1 -1
- package/lib/sort.js +1 -1
- package/lib/sort.js.map +1 -1
- package/lib/timeout.js +2 -11
- package/lib/timeout.js.map +1 -1
- package/lib/utils.js +84 -23
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +249 -314
- package/package.json +1 -1
- package/src/bulk/unordered.ts +1 -1
- package/src/change_stream.ts +4 -12
- package/src/client-side-encryption/client_encryption.ts +5 -32
- package/src/client-side-encryption/kms_options.ts +1 -0
- package/src/cmap/wire_protocol/compression.ts +3 -1
- package/src/collection.ts +83 -71
- package/src/connection_string.ts +135 -19
- package/src/cursor/abstract_cursor.ts +20 -97
- package/src/cursor/aggregation_cursor.ts +1 -16
- package/src/cursor/find_cursor.ts +2 -17
- package/src/cursor/list_collections_cursor.ts +1 -1
- package/src/cursor/list_indexes_cursor.ts +1 -1
- package/src/cursor/run_command_cursor.ts +4 -31
- package/src/db.ts +13 -59
- package/src/deps.ts +38 -1
- package/src/explain.ts +0 -8
- package/src/gridfs/download.ts +7 -14
- package/src/gridfs/index.ts +27 -8
- package/src/gridfs/upload.ts +6 -21
- package/src/index.ts +2 -0
- package/src/mongo_client.ts +46 -61
- package/src/operations/command.ts +1 -7
- package/src/operations/create_collection.ts +4 -2
- package/src/operations/drop.ts +1 -1
- package/src/operations/indexes.ts +213 -17
- package/src/operations/operation.ts +1 -1
- package/src/operations/run_command.ts +1 -1
- package/src/sdam/srv_polling.ts +12 -3
- package/src/sdam/topology.ts +5 -1
- package/src/sessions.ts +21 -63
- package/src/sort.ts +1 -1
- package/src/timeout.ts +2 -13
- package/src/utils.ts +96 -30
package/package.json
CHANGED
package/src/bulk/unordered.ts
CHANGED
|
@@ -112,7 +112,7 @@ export class UnorderedBulkOperation extends BulkOperationBase {
|
|
|
112
112
|
if (batchType === BatchType.INSERT) {
|
|
113
113
|
this.s.currentInsertBatch = this.s.currentBatch;
|
|
114
114
|
this.s.bulkResult.insertedIds.push({
|
|
115
|
-
index: this.s.
|
|
115
|
+
index: this.s.currentIndex - 1,
|
|
116
116
|
_id: (document as Document)._id
|
|
117
117
|
});
|
|
118
118
|
} else if (batchType === BatchType.UPDATE) {
|
package/src/change_stream.ts
CHANGED
|
@@ -20,7 +20,7 @@ import type { AggregateOptions } from './operations/aggregate';
|
|
|
20
20
|
import type { OperationParent } from './operations/command';
|
|
21
21
|
import { DeprioritizedServers } from './sdam/server_selection';
|
|
22
22
|
import type { ServerSessionId } from './sessions';
|
|
23
|
-
import {
|
|
23
|
+
import { type TimeoutContext } from './timeout';
|
|
24
24
|
import { type AnyOptions, getTopology, type MongoDBNamespace, squashError } from './utils';
|
|
25
25
|
|
|
26
26
|
const CHANGE_DOMAIN_TYPES = {
|
|
@@ -647,18 +647,17 @@ export class ChangeStream<
|
|
|
647
647
|
|
|
648
648
|
this.pipeline = pipeline;
|
|
649
649
|
this.options = { ...options };
|
|
650
|
-
|
|
650
|
+
// timeoutMS and timeoutMode are no-ops, so they must never reach the cursor options
|
|
651
|
+
this.options.timeoutMS = undefined;
|
|
652
|
+
this.options.timeoutMode = undefined;
|
|
651
653
|
delete this.options.writeConcern;
|
|
652
654
|
|
|
653
655
|
if (parent instanceof Collection) {
|
|
654
656
|
this.type = CHANGE_DOMAIN_TYPES.COLLECTION;
|
|
655
|
-
serverSelectionTimeoutMS = parent.s.db.client.options.serverSelectionTimeoutMS;
|
|
656
657
|
} else if (parent instanceof Db) {
|
|
657
658
|
this.type = CHANGE_DOMAIN_TYPES.DATABASE;
|
|
658
|
-
serverSelectionTimeoutMS = parent.client.options.serverSelectionTimeoutMS;
|
|
659
659
|
} else if (parent instanceof MongoClient) {
|
|
660
660
|
this.type = CHANGE_DOMAIN_TYPES.CLUSTER;
|
|
661
|
-
serverSelectionTimeoutMS = parent.options.serverSelectionTimeoutMS;
|
|
662
661
|
} else {
|
|
663
662
|
throw new MongoChangeStreamError(
|
|
664
663
|
'Parent provided to ChangeStream constructor must be an instance of Collection, Db, or MongoClient'
|
|
@@ -690,13 +689,6 @@ export class ChangeStream<
|
|
|
690
689
|
this.cursorStream?.removeAllListeners('data');
|
|
691
690
|
}
|
|
692
691
|
});
|
|
693
|
-
|
|
694
|
-
if (this.options.timeoutMS != null) {
|
|
695
|
-
this.timeoutContext = new CSOTTimeoutContext({
|
|
696
|
-
timeoutMS: this.options.timeoutMS,
|
|
697
|
-
serverSelectionTimeoutMS
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
692
|
}
|
|
701
693
|
|
|
702
694
|
/** The cached resume token that is used to resume after the most recently returned change. */
|
|
@@ -294,7 +294,7 @@ export class ClientEncryption {
|
|
|
294
294
|
async rewrapManyDataKey(
|
|
295
295
|
filter: Filter<DataKey>,
|
|
296
296
|
options?: ClientEncryptionRewrapManyDataKeyProviderOptions
|
|
297
|
-
): Promise<
|
|
297
|
+
): Promise<ClientEncryptionRewrapManyDataKeyResult> {
|
|
298
298
|
let keyEncryptionKeyBson = undefined;
|
|
299
299
|
if (options) {
|
|
300
300
|
const keyEncryptionKey = Object.assign({ provider: options.provider }, options.masterKey);
|
|
@@ -927,7 +927,7 @@ export type TextQueryOptions = StringQueryOptions;
|
|
|
927
927
|
|
|
928
928
|
/**
|
|
929
929
|
* @public
|
|
930
|
-
*
|
|
930
|
+
* Options interface for `rewrapManyDataKey`; selects the new KMS provider/masterKey for bulk key rotation.
|
|
931
931
|
*/
|
|
932
932
|
export interface ClientEncryptionRewrapManyDataKeyProviderOptions {
|
|
933
933
|
provider: ClientEncryptionDataKeyProvider;
|
|
@@ -987,35 +987,8 @@ export interface ClientEncryptionOptions {
|
|
|
987
987
|
keyExpirationMS?: number;
|
|
988
988
|
|
|
989
989
|
/**
|
|
990
|
-
* @experimental
|
|
991
|
-
*
|
|
992
990
|
* The timeout setting to be used for all the operations on ClientEncryption.
|
|
993
|
-
*
|
|
994
|
-
* When provided, `timeoutMS` is used as the timeout for each operation executed on
|
|
995
|
-
* the ClientEncryption object. For example:
|
|
996
|
-
*
|
|
997
|
-
* ```typescript
|
|
998
|
-
* const clientEncryption = new ClientEncryption(client, {
|
|
999
|
-
* timeoutMS: 1_000
|
|
1000
|
-
* kmsProviders: { local: { key: '<KEY>' } }
|
|
1001
|
-
* });
|
|
1002
|
-
*
|
|
1003
|
-
* // `1_000` is used as the timeout for createDataKey call
|
|
1004
|
-
* await clientEncryption.createDataKey('local');
|
|
1005
|
-
* ```
|
|
1006
|
-
*
|
|
1007
|
-
* If `timeoutMS` is configured on the provided client, the client's `timeoutMS` value
|
|
1008
|
-
* will be used unless `timeoutMS` is also provided as a client encryption option.
|
|
1009
|
-
*
|
|
1010
|
-
* ```typescript
|
|
1011
|
-
* const client = new MongoClient('<uri>', { timeoutMS: 2_000 });
|
|
1012
|
-
*
|
|
1013
|
-
* // timeoutMS is set to 1_000 on clientEncryption
|
|
1014
|
-
* const clientEncryption = new ClientEncryption(client, {
|
|
1015
|
-
* timeoutMS: 1_000
|
|
1016
|
-
* kmsProviders: { local: { key: '<KEY>' } }
|
|
1017
|
-
* });
|
|
1018
|
-
* ```
|
|
991
|
+
* @deprecated `timeoutMS` has no effect and will be removed in a future release.
|
|
1019
992
|
*/
|
|
1020
993
|
timeoutMS?: number;
|
|
1021
994
|
}
|
|
@@ -1144,7 +1117,7 @@ export interface ClientEncryptionCreateDataKeyProviderOptions {
|
|
|
1144
1117
|
*/
|
|
1145
1118
|
keyAltNames?: string[] | undefined;
|
|
1146
1119
|
|
|
1147
|
-
/**
|
|
1120
|
+
/** Optional 96-byte custom key material used when creating a data key instead of libmongocrypt’s random material. **/
|
|
1148
1121
|
keyMaterial?: Buffer | Binary;
|
|
1149
1122
|
|
|
1150
1123
|
/** @internal */
|
|
@@ -1153,7 +1126,7 @@ export interface ClientEncryptionCreateDataKeyProviderOptions {
|
|
|
1153
1126
|
|
|
1154
1127
|
/**
|
|
1155
1128
|
* @public
|
|
1156
|
-
*
|
|
1129
|
+
* Public result shape describing the `bulkWriteResult` produced by `rewrapManyDataKey`.
|
|
1157
1130
|
*/
|
|
1158
1131
|
export interface ClientEncryptionRewrapManyDataKeyResult {
|
|
1159
1132
|
/** The result of rewrapping data keys. If unset, no keys matched the filter. */
|
|
@@ -88,6 +88,7 @@ export type ClientEncryptionSocketOptions = Pick<
|
|
|
88
88
|
export type KMSConnectCallback = (options: {
|
|
89
89
|
host: string;
|
|
90
90
|
port: number;
|
|
91
|
+
/** @deprecated `timeoutMS` has no effect and will be removed in a future release. */
|
|
91
92
|
timeoutMS?: number;
|
|
92
93
|
signal: AbortSignal;
|
|
93
94
|
}) => Promise<Duplex>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as zlib from 'zlib';
|
|
2
2
|
|
|
3
3
|
import { ByteUtils, readInt32LE } from '../../bson';
|
|
4
|
-
import { LEGACY_HELLO_COMMAND } from '../../constants';
|
|
4
|
+
import { LEGACY_HELLO_COMMAND, LEGACY_HELLO_COMMAND_CAMEL_CASE } from '../../constants';
|
|
5
5
|
import { getSnappy, getZstdLibrary, type SnappyLib, type ZStandard } from '../../deps';
|
|
6
6
|
import { MongoDecompressionError, MongoInvalidArgumentError } from '../../error';
|
|
7
7
|
import {
|
|
@@ -29,7 +29,9 @@ export type Compressor = (typeof Compressor)[CompressorName];
|
|
|
29
29
|
export type CompressorName = keyof typeof Compressor;
|
|
30
30
|
|
|
31
31
|
export const uncompressibleCommands = new Set([
|
|
32
|
+
'hello',
|
|
32
33
|
LEGACY_HELLO_COMMAND,
|
|
34
|
+
LEGACY_HELLO_COMMAND_CAMEL_CASE,
|
|
33
35
|
'saslStart',
|
|
34
36
|
'saslContinue',
|
|
35
37
|
'getnonce',
|
package/src/collection.ts
CHANGED
|
@@ -56,12 +56,14 @@ import {
|
|
|
56
56
|
import {
|
|
57
57
|
CreateIndexesOperation,
|
|
58
58
|
type CreateIndexesOptions,
|
|
59
|
+
type CreateIndexOptions,
|
|
59
60
|
type DropIndexesOptions,
|
|
60
61
|
DropIndexOperation,
|
|
61
62
|
type IndexDescription,
|
|
62
63
|
type IndexDescriptionCompact,
|
|
63
64
|
type IndexDescriptionInfo,
|
|
64
65
|
type IndexInformationOptions,
|
|
66
|
+
type IndexOptions,
|
|
65
67
|
type IndexSpecification,
|
|
66
68
|
type ListIndexesOptions
|
|
67
69
|
} from './operations/indexes';
|
|
@@ -120,8 +122,8 @@ export interface CollectionOptions extends BSONSerializeOptions, WriteConcernOpt
|
|
|
120
122
|
/** The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */
|
|
121
123
|
readPreference?: ReadPreferenceLike;
|
|
122
124
|
/**
|
|
123
|
-
* @experimental
|
|
124
125
|
* Specifies the time an operation will run until it throws a timeout error
|
|
126
|
+
* @deprecated `timeoutMS` has no effect and will be removed in a future release.
|
|
125
127
|
*/
|
|
126
128
|
timeoutMS?: number;
|
|
127
129
|
}
|
|
@@ -275,8 +277,9 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
275
277
|
this.s.collectionHint = normalizeHintField(v);
|
|
276
278
|
}
|
|
277
279
|
|
|
280
|
+
/** @deprecated `timeoutMS` has no effect and will be removed in a future release. */
|
|
278
281
|
public get timeoutMS(): number | undefined {
|
|
279
|
-
return
|
|
282
|
+
return undefined;
|
|
280
283
|
}
|
|
281
284
|
|
|
282
285
|
/**
|
|
@@ -609,7 +612,7 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
609
612
|
* Creates an index on the db and collection collection.
|
|
610
613
|
*
|
|
611
614
|
* @param indexSpec - The field name or index specification to create an index for
|
|
612
|
-
* @param
|
|
615
|
+
* @param indexOptions - Optional settings for the command
|
|
613
616
|
*
|
|
614
617
|
* @example
|
|
615
618
|
* ```ts
|
|
@@ -632,20 +635,55 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
632
635
|
* // Equivalent to { j: 1, k: -1, l: 2d }
|
|
633
636
|
* await collection.createIndex(['j', ['k', -1], { l: '2d' }])
|
|
634
637
|
* ```
|
|
638
|
+
* @deprecated Use the three parameter overload, which separates index options from
|
|
639
|
+
* command options. This overload will be removed in a future major release.
|
|
640
|
+
*/
|
|
641
|
+
createIndex(indexSpec: IndexSpecification, options?: CreateIndexesOptions): Promise<string>;
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Creates an index on the db and collection collection.
|
|
645
|
+
*
|
|
646
|
+
* Options for the index itself are given in `indexOptions`, and options for the
|
|
647
|
+
* `createIndexes` command are given separately in `commandOptions`. Unknown fields in
|
|
648
|
+
* `indexOptions` are passed through to the server for validation rather than being dropped.
|
|
649
|
+
*
|
|
650
|
+
* @param keys - The field name or index specification to create an index for
|
|
651
|
+
* @param indexOptions - Settings for the index, or `undefined` if there are none
|
|
652
|
+
* @param commandOptions - Settings for the `createIndexes` command, or `undefined` if there are none
|
|
635
653
|
*/
|
|
654
|
+
createIndex(
|
|
655
|
+
keys: IndexSpecification,
|
|
656
|
+
indexOptions: IndexOptions | undefined,
|
|
657
|
+
commandOptions: CreateIndexOptions | undefined
|
|
658
|
+
): Promise<string>;
|
|
659
|
+
|
|
636
660
|
async createIndex(
|
|
637
661
|
indexSpec: IndexSpecification,
|
|
638
|
-
|
|
662
|
+
indexOptions?: CreateIndexesOptions | IndexOptions,
|
|
663
|
+
commandOptions?: CreateIndexOptions
|
|
639
664
|
): Promise<string> {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
665
|
+
// When commandOptions is provided the caller has separated the two kinds of options, so the
|
|
666
|
+
// index options are forwarded as-is and only the command options inherit from the parent.
|
|
667
|
+
// Otherwise indexOptions is both, and it inherits from the parent as it always has.
|
|
668
|
+
const operation =
|
|
669
|
+
commandOptions == null
|
|
670
|
+
? CreateIndexesOperation.fromIndexSpecification(
|
|
671
|
+
this,
|
|
672
|
+
this.collectionName,
|
|
673
|
+
indexSpec,
|
|
674
|
+
false,
|
|
675
|
+
resolveOptions(this, indexOptions), // at this point indexOptions is the combined index and command options
|
|
676
|
+
undefined
|
|
677
|
+
)
|
|
678
|
+
: CreateIndexesOperation.fromIndexSpecification(
|
|
679
|
+
this,
|
|
680
|
+
this.collectionName,
|
|
681
|
+
indexSpec,
|
|
682
|
+
true,
|
|
683
|
+
indexOptions,
|
|
684
|
+
resolveOptions(this, commandOptions)
|
|
685
|
+
);
|
|
686
|
+
const indexes = await executeOperation(this.client, operation);
|
|
649
687
|
|
|
650
688
|
return indexes[0];
|
|
651
689
|
}
|
|
@@ -659,7 +697,7 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
659
697
|
* Index specifications are defined {@link https://www.mongodb.com/docs/manual/reference/command/createIndexes/| here}.
|
|
660
698
|
*
|
|
661
699
|
* @param indexSpecs - An array of index specifications to be created
|
|
662
|
-
* @param
|
|
700
|
+
* @param commandOptions - Optional settings for the `createIndexes` command
|
|
663
701
|
*
|
|
664
702
|
* @example
|
|
665
703
|
* ```ts
|
|
@@ -681,9 +719,35 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
681
719
|
* ]);
|
|
682
720
|
* ```
|
|
683
721
|
*/
|
|
722
|
+
createIndexes(
|
|
723
|
+
indexSpecs: IndexDescription[],
|
|
724
|
+
commandOptions?: CreateIndexesOptions
|
|
725
|
+
): Promise<string[]>;
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Creates multiple indexes in the collection, opting into "pass through" behavior for index
|
|
729
|
+
* options the driver does not recognise.
|
|
730
|
+
*
|
|
731
|
+
* @param indexSpecs - An array of index specifications to be created
|
|
732
|
+
* @param commandOptions - Optional settings for the `createIndexes` command
|
|
733
|
+
* @param allowUnknownIndexOptions - When `true`, index options the driver does not recognise are
|
|
734
|
+
* sent to the server instead of being dropped, for the server to validate.
|
|
735
|
+
*
|
|
736
|
+
* @deprecated Used to opt into "pass through" behavior, where options will be validated by the
|
|
737
|
+
* server rather than the driver. In a future release this overload will be removed and the
|
|
738
|
+
* default behavior will change from `false` to `true`.
|
|
739
|
+
*/
|
|
740
|
+
createIndexes(
|
|
741
|
+
indexSpecs: IndexDescription[],
|
|
742
|
+
commandOptions: CreateIndexesOptions | undefined,
|
|
743
|
+
allowUnknownIndexOptions: boolean
|
|
744
|
+
): Promise<string[]>;
|
|
745
|
+
|
|
684
746
|
async createIndexes(
|
|
685
747
|
indexSpecs: IndexDescription[],
|
|
686
|
-
|
|
748
|
+
commandOptions?: CreateIndexesOptions,
|
|
749
|
+
// TODO(NODE-7868): Remove allowUnknownIndexOptions with a default behavior of true in a future major version release
|
|
750
|
+
allowUnknownIndexOptions = false
|
|
687
751
|
): Promise<string[]> {
|
|
688
752
|
return await executeOperation(
|
|
689
753
|
this.client,
|
|
@@ -691,7 +755,9 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
691
755
|
this,
|
|
692
756
|
this.collectionName,
|
|
693
757
|
indexSpecs,
|
|
694
|
-
|
|
758
|
+
// TODO(NODE-7868): Remove allowUnknownIndexOptions with a default behavior of true in a future major version release
|
|
759
|
+
allowUnknownIndexOptions,
|
|
760
|
+
resolveOptions(this, { ...commandOptions, maxTimeMS: undefined })
|
|
695
761
|
)
|
|
696
762
|
);
|
|
697
763
|
}
|
|
@@ -1105,60 +1171,6 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
1105
1171
|
* expectType<Schema>(change.fullDocument);
|
|
1106
1172
|
* });
|
|
1107
1173
|
* ```
|
|
1108
|
-
*
|
|
1109
|
-
* @remarks
|
|
1110
|
-
* When `timeoutMS` is configured for a change stream, it will have different behaviour depending
|
|
1111
|
-
* on whether the change stream is in iterator mode or emitter mode. In both cases, a change
|
|
1112
|
-
* stream will time out if it does not receive a change event within `timeoutMS` of the last change
|
|
1113
|
-
* event.
|
|
1114
|
-
*
|
|
1115
|
-
* Note that if a change stream is consistently timing out when watching a collection, database or
|
|
1116
|
-
* client that is being changed, then this may be due to the server timing out before it can finish
|
|
1117
|
-
* processing the existing oplog. To address this, restart the change stream with a higher
|
|
1118
|
-
* `timeoutMS`.
|
|
1119
|
-
*
|
|
1120
|
-
* If the change stream times out the initial aggregate operation to establish the change stream on
|
|
1121
|
-
* the server, then the client will close the change stream. If the getMore calls to the server
|
|
1122
|
-
* time out, then the change stream will be left open, but will throw a MongoOperationTimeoutError
|
|
1123
|
-
* when in iterator mode and emit an error event that returns a MongoOperationTimeoutError in
|
|
1124
|
-
* emitter mode.
|
|
1125
|
-
*
|
|
1126
|
-
* To determine whether or not the change stream is still open following a timeout, check the
|
|
1127
|
-
* {@link ChangeStream.closed} getter.
|
|
1128
|
-
*
|
|
1129
|
-
* @example
|
|
1130
|
-
* In iterator mode, if a next() call throws a timeout error, it will attempt to resume the change stream.
|
|
1131
|
-
* The next call can just be retried after this succeeds.
|
|
1132
|
-
* ```ts
|
|
1133
|
-
* const changeStream = collection.watch([], { timeoutMS: 100 });
|
|
1134
|
-
* try {
|
|
1135
|
-
* await changeStream.next();
|
|
1136
|
-
* } catch (e) {
|
|
1137
|
-
* if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
|
|
1138
|
-
* await changeStream.next();
|
|
1139
|
-
* }
|
|
1140
|
-
* throw e;
|
|
1141
|
-
* }
|
|
1142
|
-
* ```
|
|
1143
|
-
*
|
|
1144
|
-
* @example
|
|
1145
|
-
* In emitter mode, if the change stream goes `timeoutMS` without emitting a change event, it will
|
|
1146
|
-
* emit an error event that returns a MongoOperationTimeoutError, but will not close the change
|
|
1147
|
-
* stream unless the resume attempt fails. There is no need to re-establish change listeners as
|
|
1148
|
-
* this will automatically continue emitting change events once the resume attempt completes.
|
|
1149
|
-
*
|
|
1150
|
-
* ```ts
|
|
1151
|
-
* const changeStream = collection.watch([], { timeoutMS: 100 });
|
|
1152
|
-
* changeStream.on('change', console.log);
|
|
1153
|
-
* changeStream.on('error', e => {
|
|
1154
|
-
* if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
|
|
1155
|
-
* // do nothing
|
|
1156
|
-
* } else {
|
|
1157
|
-
* changeStream.close();
|
|
1158
|
-
* }
|
|
1159
|
-
* });
|
|
1160
|
-
* ```
|
|
1161
|
-
*
|
|
1162
1174
|
* @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.
|
|
1163
1175
|
* @param options - Optional settings for the command
|
|
1164
1176
|
* @typeParam TLocal - Type of the data being detected by the change stream
|
|
@@ -1251,7 +1263,7 @@ export class Collection<TSchema extends Document = Document> {
|
|
|
1251
1263
|
? null
|
|
1252
1264
|
: indexNameOrOptions;
|
|
1253
1265
|
|
|
1254
|
-
return new ListSearchIndexesCursor(this as TODO_NODE_3286, indexName, options);
|
|
1266
|
+
return new ListSearchIndexesCursor(this as TODO_NODE_3286, indexName, { ...options });
|
|
1255
1267
|
}
|
|
1256
1268
|
|
|
1257
1269
|
/**
|
package/src/connection_string.ts
CHANGED
|
@@ -24,19 +24,46 @@ import { resolveRuntimeAdapters } from './runtime_adapters';
|
|
|
24
24
|
import { ServerMonitoringMode } from './sdam/monitor';
|
|
25
25
|
import type { TagSet } from './sdam/server_description';
|
|
26
26
|
import {
|
|
27
|
-
checkParentDomainMatch,
|
|
28
27
|
DEFAULT_PK_FACTORY,
|
|
28
|
+
dnsNameToASCII,
|
|
29
29
|
emitWarning,
|
|
30
30
|
HostAddress,
|
|
31
31
|
isRecord,
|
|
32
|
+
normalizeDnsName,
|
|
32
33
|
parseInteger,
|
|
33
34
|
setDifference,
|
|
34
|
-
squashError
|
|
35
|
+
squashError,
|
|
36
|
+
verifySrvHost
|
|
35
37
|
} from './utils';
|
|
36
38
|
import { type W, WriteConcern } from './write_concern';
|
|
37
39
|
|
|
38
40
|
const VALID_TXT_RECORDS = ['authSource', 'replicaSet', 'loadBalanced'];
|
|
39
41
|
|
|
42
|
+
/** Options that only apply to SRV resolution, and so are invalid with a non-SRV connection string */
|
|
43
|
+
const SRV_ONLY_OPTIONS = [
|
|
44
|
+
'srvMaxHosts',
|
|
45
|
+
'srvServiceName',
|
|
46
|
+
'srvAllowedHostsSuffix',
|
|
47
|
+
'srvHostValidator'
|
|
48
|
+
] as const;
|
|
49
|
+
|
|
50
|
+
/** Single labels that srvAllowedHostsSuffix may be, as they are reserved for private or special use */
|
|
51
|
+
const VALID_SINGLE_LABEL_SUFFIXES = new Set([
|
|
52
|
+
// RFC 6761 special use names
|
|
53
|
+
'test',
|
|
54
|
+
'localhost',
|
|
55
|
+
'invalid',
|
|
56
|
+
'example',
|
|
57
|
+
// RFC 6762 multicast DNS
|
|
58
|
+
'local',
|
|
59
|
+
// Reserved by ICANN for private use
|
|
60
|
+
'internal',
|
|
61
|
+
// Not officially reserved by ICANN, but commonly used privately
|
|
62
|
+
'corp',
|
|
63
|
+
'home',
|
|
64
|
+
'mail'
|
|
65
|
+
]);
|
|
66
|
+
|
|
40
67
|
const LB_SINGLE_HOST_ERROR = 'loadBalanced option only supported with a single host in the URI';
|
|
41
68
|
const LB_REPLICA_SET_ERROR = 'loadBalanced option not supported with a replicaSet option';
|
|
42
69
|
const LB_DIRECT_CONNECTION_ERROR =
|
|
@@ -73,6 +100,9 @@ const resolveTxt = retryDNSTimeoutFor('TXT');
|
|
|
73
100
|
*
|
|
74
101
|
* @param uri - The connection string to parse
|
|
75
102
|
* @param options - Optional user provided connection string options
|
|
103
|
+
* @throws MongoAPIError if the SRV lookup returns no hosts, or a returned host fails verification
|
|
104
|
+
* @throws MongoParseError if the TXT record is invalid, or conflicts with the provided options
|
|
105
|
+
* @throws Error the error from the DNS lookup, if it fails
|
|
76
106
|
*/
|
|
77
107
|
export async function resolveSRVRecord(options: MongoOptions): Promise<HostAddress[]> {
|
|
78
108
|
if (typeof options.srvHost !== 'string') {
|
|
@@ -94,11 +124,11 @@ export async function resolveSRVRecord(options: MongoOptions): Promise<HostAddre
|
|
|
94
124
|
throw new MongoAPIError('No addresses found at host');
|
|
95
125
|
}
|
|
96
126
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
const hostAddresses = addresses.map(({ name, port }) => {
|
|
128
|
+
const host = normalizeDnsName(name);
|
|
129
|
+
verifySrvHost(host, lookupAddress, options);
|
|
130
|
+
return HostAddress.fromString(`${host}:${port ?? 27017}`);
|
|
131
|
+
});
|
|
102
132
|
|
|
103
133
|
validateLoadBalancedOptions(hostAddresses, options, true);
|
|
104
134
|
|
|
@@ -294,7 +324,7 @@ export function parseOptions(
|
|
|
294
324
|
);
|
|
295
325
|
}
|
|
296
326
|
|
|
297
|
-
if (!isReadPreferenceTags && values.includes('')) {
|
|
327
|
+
if (!isReadPreferenceTags && values.includes('') && !isIgnoredOption(key)) {
|
|
298
328
|
throw new MongoAPIError(`URI option "${key}" cannot be specified with no value`);
|
|
299
329
|
}
|
|
300
330
|
|
|
@@ -315,6 +345,12 @@ export function parseOptions(
|
|
|
315
345
|
);
|
|
316
346
|
}
|
|
317
347
|
|
|
348
|
+
if (urlOptions.has('srvHostValidator')) {
|
|
349
|
+
throw new MongoParseError(
|
|
350
|
+
'URI cannot contain `srvHostValidator`, it can only be passed to the client'
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
318
354
|
const uriMechanismProperties = urlOptions.get('authMechanismProperties');
|
|
319
355
|
if (uriMechanismProperties) {
|
|
320
356
|
for (const property of uriMechanismProperties) {
|
|
@@ -380,12 +416,14 @@ export function parseOptions(
|
|
|
380
416
|
setOption(mongoOptions, key, descriptor, [DEFAULT_OPTIONS.get(key)]);
|
|
381
417
|
}
|
|
382
418
|
} else {
|
|
383
|
-
const { deprecated } = descriptor;
|
|
419
|
+
const { deprecated, ignored } = descriptor;
|
|
384
420
|
if (deprecated) {
|
|
385
421
|
const deprecatedMsg = typeof deprecated === 'string' ? `: ${deprecated}` : '';
|
|
386
422
|
emitWarning(`${key} is a deprecated option${deprecatedMsg}`);
|
|
387
423
|
}
|
|
388
424
|
|
|
425
|
+
if (ignored) continue;
|
|
426
|
+
|
|
389
427
|
setOption(mongoOptions, key, descriptor, values);
|
|
390
428
|
}
|
|
391
429
|
}
|
|
@@ -477,6 +515,10 @@ export function parseOptions(
|
|
|
477
515
|
throw new MongoParseError('Cannot use srvMaxHosts option with replicaSet');
|
|
478
516
|
}
|
|
479
517
|
|
|
518
|
+
if (mongoOptions.srvAllowedHostsSuffix != null && mongoOptions.srvHostValidator != null) {
|
|
519
|
+
throw new MongoParseError('Cannot use srvAllowedHostsSuffix together with srvHostValidator');
|
|
520
|
+
}
|
|
521
|
+
|
|
480
522
|
// SRV turns on TLS by default, but users can override and turn it off
|
|
481
523
|
const noUserSpecifiedTLS = !objectOptions.has('tls') && !urlOptions.has('tls');
|
|
482
524
|
const noUserSpecifiedSSL = !objectOptions.has('ssl') && !urlOptions.has('ssl');
|
|
@@ -484,15 +526,13 @@ export function parseOptions(
|
|
|
484
526
|
mongoOptions.tls = true;
|
|
485
527
|
}
|
|
486
528
|
} else {
|
|
487
|
-
const userSpecifiedSrvOptions =
|
|
488
|
-
urlOptions.has(
|
|
489
|
-
|
|
490
|
-
urlOptions.has('srvServiceName') ||
|
|
491
|
-
objectOptions.has('srvServiceName');
|
|
529
|
+
const userSpecifiedSrvOptions = SRV_ONLY_OPTIONS.filter(
|
|
530
|
+
option => urlOptions.has(option) || objectOptions.has(option)
|
|
531
|
+
);
|
|
492
532
|
|
|
493
|
-
if (userSpecifiedSrvOptions) {
|
|
533
|
+
if (userSpecifiedSrvOptions.length > 0) {
|
|
494
534
|
throw new MongoParseError(
|
|
495
|
-
|
|
535
|
+
`Cannot use ${userSpecifiedSrvOptions.join(', ')} with a non-srv connection string`
|
|
496
536
|
);
|
|
497
537
|
}
|
|
498
538
|
}
|
|
@@ -584,6 +624,50 @@ function validateLoadBalancedOptions(
|
|
|
584
624
|
return;
|
|
585
625
|
}
|
|
586
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Validates and normalizes an `srvAllowedHostsSuffix` value, following the steps in the Initial DNS
|
|
629
|
+
* Seedlist Discovery specification.
|
|
630
|
+
*
|
|
631
|
+
* @returns the suffix in lowercase A-label (Punycode) form, beginning with `.`
|
|
632
|
+
* @throws MongoParseError if the value is not a valid suffix
|
|
633
|
+
*/
|
|
634
|
+
function normalizeSrvAllowedHostsSuffix(value: string): string {
|
|
635
|
+
// 1. Strip leading and trailing dots
|
|
636
|
+
const stripped = value.replace(/^\.+|\.+$/g, '');
|
|
637
|
+
if (stripped === '') {
|
|
638
|
+
throw new MongoParseError('srvAllowedHostsSuffix must contain at least one domain label');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// 2 and 3. Convert to lowercase A-label form, using the WHATWG URL Standard's domain parser, the
|
|
642
|
+
// same conversion applied to the host names returned by the SRV lookup
|
|
643
|
+
const suffix = dnsNameToASCII(stripped);
|
|
644
|
+
if (suffix == null) {
|
|
645
|
+
throw new MongoParseError(`srvAllowedHostsSuffix "${value}" is not a valid domain name`);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// 4. Require at least two labels, unless the value is a reserved single label
|
|
649
|
+
if (!suffix.includes('.') && !VALID_SINGLE_LABEL_SUFFIXES.has(suffix)) {
|
|
650
|
+
throw new MongoParseError(
|
|
651
|
+
`srvAllowedHostsSuffix "${value}" must contain at least two domain labels, or be one of: ${[
|
|
652
|
+
...VALID_SINGLE_LABEL_SUFFIXES
|
|
653
|
+
].join(', ')}`
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// 5. Rejecting public suffixes via the Public Suffix List is a SHOULD that is intentionally not
|
|
658
|
+
// implemented by the Node Driver. Skipping.
|
|
659
|
+
|
|
660
|
+
// 6. Prepend a dot so the suffix only matches whole labels
|
|
661
|
+
return `.${suffix}`;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** Ignored options are never validated, since their values have no effect. */
|
|
665
|
+
function isIgnoredOption(key: string): boolean {
|
|
666
|
+
return Object.entries(OPTIONS).some(
|
|
667
|
+
([name, descriptor]) => descriptor.ignored && name.toLowerCase() === key.toLowerCase()
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
|
|
587
671
|
function setOption(
|
|
588
672
|
mongoOptions: any,
|
|
589
673
|
key: string,
|
|
@@ -638,6 +722,8 @@ interface OptionDescriptor {
|
|
|
638
722
|
default?: any;
|
|
639
723
|
|
|
640
724
|
deprecated?: boolean | string;
|
|
725
|
+
/** The option is accepted but has no effect, so it is never stored on the resolved options. */
|
|
726
|
+
ignored?: boolean;
|
|
641
727
|
/**
|
|
642
728
|
* @param name - the original option name
|
|
643
729
|
* @param options - the options so far for resolution
|
|
@@ -1105,10 +1191,40 @@ export const OPTIONS = {
|
|
|
1105
1191
|
type: 'string'
|
|
1106
1192
|
},
|
|
1107
1193
|
socketTimeoutMS: {
|
|
1108
|
-
// TODO(NODE-6491): deprecated: 'Please use timeoutMS instead',
|
|
1109
1194
|
default: 0,
|
|
1110
1195
|
type: 'uint'
|
|
1111
1196
|
},
|
|
1197
|
+
srvAllowedHostsSuffix: {
|
|
1198
|
+
transform({ values: [value] }): string {
|
|
1199
|
+
if (typeof value !== 'string') {
|
|
1200
|
+
throw new MongoParseError('srvAllowedHostsSuffix must be a string');
|
|
1201
|
+
}
|
|
1202
|
+
return normalizeSrvAllowedHostsSuffix(value);
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
srvHostValidator: {
|
|
1206
|
+
transform({ values: [value] }): (host: string) => boolean {
|
|
1207
|
+
if (typeof value !== 'function') {
|
|
1208
|
+
throw new MongoParseError('srvHostValidator must be a function');
|
|
1209
|
+
}
|
|
1210
|
+
// Wrap the validator so that every caller gets a boolean or a MongoAPIError
|
|
1211
|
+
return (host: string): boolean => {
|
|
1212
|
+
let isValid: unknown;
|
|
1213
|
+
try {
|
|
1214
|
+
isValid = value(host);
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
throw new MongoAPIError('srvHostValidator threw an error', { cause: error });
|
|
1217
|
+
}
|
|
1218
|
+
// A Promise (e.g. from an async validator) is truthy, so accepting it would silently allow every host
|
|
1219
|
+
if (typeof isValid !== 'boolean') {
|
|
1220
|
+
throw new MongoAPIError(
|
|
1221
|
+
`srvHostValidator must return a boolean, received ${typeof isValid}`
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
return isValid;
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1112
1228
|
srvMaxHosts: {
|
|
1113
1229
|
type: 'uint',
|
|
1114
1230
|
default: 0
|
|
@@ -1122,7 +1238,8 @@ export const OPTIONS = {
|
|
|
1122
1238
|
type: 'boolean'
|
|
1123
1239
|
},
|
|
1124
1240
|
timeoutMS: {
|
|
1125
|
-
|
|
1241
|
+
deprecated: 'timeoutMS has no effect and will be removed in a future release',
|
|
1242
|
+
ignored: true
|
|
1126
1243
|
},
|
|
1127
1244
|
tls: {
|
|
1128
1245
|
type: 'boolean'
|
|
@@ -1176,7 +1293,6 @@ export const OPTIONS = {
|
|
|
1176
1293
|
}
|
|
1177
1294
|
},
|
|
1178
1295
|
waitQueueTimeoutMS: {
|
|
1179
|
-
// TODO(NODE-6491): deprecated: 'Please use timeoutMS instead',
|
|
1180
1296
|
default: 0,
|
|
1181
1297
|
type: 'uint'
|
|
1182
1298
|
},
|