mongodb 6.5.0-dev.20240406.sha.62ea94b → 6.5.0-dev.20240411.sha.ddd1e81
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/lib/bson.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +2 -1
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/commands.js +24 -111
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +1 -1
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +46 -31
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +2 -2
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +88 -0
- package/lib/cmap/wire_protocol/responses.js.map +1 -0
- package/lib/collection.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +1 -0
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/db.js +2 -1
- package/lib/db.js.map +1 -1
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/operations/execute_operation.js +3 -0
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/sessions.js +2 -1
- package/lib/sessions.js.map +1 -1
- package/mongodb.d.ts +19 -3
- package/package.json +1 -1
- package/src/bson.ts +1 -0
- package/src/cmap/auth/gssapi.ts +3 -5
- package/src/cmap/commands.ts +28 -132
- package/src/cmap/connect.ts +1 -1
- package/src/cmap/connection.ts +86 -38
- package/src/cmap/wire_protocol/compression.ts +4 -6
- package/src/cmap/wire_protocol/on_demand/document.ts +1 -0
- package/src/cmap/wire_protocol/responses.ts +109 -0
- package/src/collection.ts +2 -0
- package/src/cursor/abstract_cursor.ts +3 -0
- package/src/db.ts +4 -1
- package/src/gridfs/download.ts +2 -0
- package/src/gridfs/index.ts +2 -0
- package/src/gridfs/upload.ts +2 -0
- package/src/index.ts +3 -2
- package/src/mongo_client.ts +3 -1
- package/src/operations/execute_operation.ts +5 -0
- package/src/operations/operation.ts +3 -0
- package/src/sessions.ts +9 -2
package/src/cmap/connection.ts
CHANGED
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
OpMsgRequest,
|
|
55
55
|
type OpMsgResponse,
|
|
56
56
|
OpQueryRequest,
|
|
57
|
-
type
|
|
57
|
+
type OpReply,
|
|
58
58
|
type WriteProtocolMessageType
|
|
59
59
|
} from './commands';
|
|
60
60
|
import type { Stream } from './connect';
|
|
@@ -62,6 +62,7 @@ import type { ClientMetadata } from './handshake/client_metadata';
|
|
|
62
62
|
import { StreamDescription, type StreamDescriptionOptions } from './stream_description';
|
|
63
63
|
import { type CompressorName, decompressResponse } from './wire_protocol/compression';
|
|
64
64
|
import { onData } from './wire_protocol/on_data';
|
|
65
|
+
import { MongoDBResponse, type MongoDBResponseConstructor } from './wire_protocol/responses';
|
|
65
66
|
import { getReadPreference, isSharded } from './wire_protocol/shared';
|
|
66
67
|
|
|
67
68
|
/** @internal */
|
|
@@ -412,7 +413,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
412
413
|
return message;
|
|
413
414
|
}
|
|
414
415
|
|
|
415
|
-
private async *sendWire(
|
|
416
|
+
private async *sendWire(
|
|
417
|
+
message: WriteProtocolMessageType,
|
|
418
|
+
options: CommandOptions,
|
|
419
|
+
responseType?: MongoDBResponseConstructor
|
|
420
|
+
): AsyncGenerator<MongoDBResponse> {
|
|
416
421
|
this.throwIfAborted();
|
|
417
422
|
|
|
418
423
|
if (typeof options.socketTimeoutMS === 'number') {
|
|
@@ -428,7 +433,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
428
433
|
});
|
|
429
434
|
|
|
430
435
|
if (options.noResponse) {
|
|
431
|
-
yield
|
|
436
|
+
yield MongoDBResponse.empty;
|
|
432
437
|
return;
|
|
433
438
|
}
|
|
434
439
|
|
|
@@ -436,21 +441,9 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
436
441
|
|
|
437
442
|
for await (const response of this.readMany()) {
|
|
438
443
|
this.socket.setTimeout(0);
|
|
439
|
-
response.parse(
|
|
440
|
-
|
|
441
|
-
const [document] = response.documents;
|
|
444
|
+
const bson = response.parse();
|
|
442
445
|
|
|
443
|
-
|
|
444
|
-
const { session } = options;
|
|
445
|
-
if (session) {
|
|
446
|
-
updateSessionFromResponse(session, document);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (document.$clusterTime) {
|
|
450
|
-
this.clusterTime = document.$clusterTime;
|
|
451
|
-
this.emit(Connection.CLUSTER_TIME_RECEIVED, document.$clusterTime);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
446
|
+
const document = new (responseType ?? MongoDBResponse)(bson, 0, false);
|
|
454
447
|
|
|
455
448
|
yield document;
|
|
456
449
|
this.throwIfAborted();
|
|
@@ -469,7 +462,8 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
469
462
|
private async *sendCommand(
|
|
470
463
|
ns: MongoDBNamespace,
|
|
471
464
|
command: Document,
|
|
472
|
-
options: CommandOptions
|
|
465
|
+
options: CommandOptions,
|
|
466
|
+
responseType?: MongoDBResponseConstructor
|
|
473
467
|
) {
|
|
474
468
|
const message = this.prepareCommand(ns.db, command, options);
|
|
475
469
|
|
|
@@ -485,19 +479,41 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
485
479
|
);
|
|
486
480
|
}
|
|
487
481
|
|
|
488
|
-
|
|
482
|
+
// If `documentsReturnedIn` not set or raw is not enabled, use input bson options
|
|
483
|
+
// Otherwise, support raw flag. Raw only works for cursors that hardcode firstBatch/nextBatch fields
|
|
484
|
+
const bsonOptions =
|
|
485
|
+
options.documentsReturnedIn == null || !options.raw
|
|
486
|
+
? options
|
|
487
|
+
: {
|
|
488
|
+
...options,
|
|
489
|
+
raw: false,
|
|
490
|
+
fieldsAsRaw: { [options.documentsReturnedIn]: true }
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
/** MongoDBResponse instance or subclass */
|
|
494
|
+
let document: MongoDBResponse | undefined = undefined;
|
|
495
|
+
/** Cached result of a toObject call */
|
|
496
|
+
let object: Document | undefined = undefined;
|
|
489
497
|
try {
|
|
490
498
|
this.throwIfAborted();
|
|
491
|
-
for await (document of this.sendWire(message, options)) {
|
|
492
|
-
|
|
493
|
-
|
|
499
|
+
for await (document of this.sendWire(message, options, responseType)) {
|
|
500
|
+
object = undefined;
|
|
501
|
+
if (options.session != null) {
|
|
502
|
+
updateSessionFromResponse(options.session, document);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (document.$clusterTime) {
|
|
506
|
+
this.clusterTime = document.$clusterTime;
|
|
507
|
+
this.emit(Connection.CLUSTER_TIME_RECEIVED, document.$clusterTime);
|
|
494
508
|
}
|
|
495
509
|
|
|
496
|
-
if (
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
510
|
+
if (document.has('writeConcernError')) {
|
|
511
|
+
object ??= document.toObject(bsonOptions);
|
|
512
|
+
throw new MongoWriteConcernError(object.writeConcernError, object);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (document.isError) {
|
|
516
|
+
throw new MongoServerError((object ??= document.toObject(bsonOptions)));
|
|
501
517
|
}
|
|
502
518
|
|
|
503
519
|
if (this.shouldEmitAndLogCommand) {
|
|
@@ -509,14 +525,19 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
509
525
|
new CommandSucceededEvent(
|
|
510
526
|
this,
|
|
511
527
|
message,
|
|
512
|
-
options.noResponse ? undefined : document,
|
|
528
|
+
options.noResponse ? undefined : (object ??= document.toObject(bsonOptions)),
|
|
513
529
|
started,
|
|
514
530
|
this.description.serverConnectionId
|
|
515
531
|
)
|
|
516
532
|
);
|
|
517
533
|
}
|
|
518
534
|
|
|
519
|
-
|
|
535
|
+
if (responseType == null) {
|
|
536
|
+
yield (object ??= document.toObject(bsonOptions));
|
|
537
|
+
} else {
|
|
538
|
+
yield document;
|
|
539
|
+
}
|
|
540
|
+
|
|
520
541
|
this.throwIfAborted();
|
|
521
542
|
}
|
|
522
543
|
} catch (error) {
|
|
@@ -530,7 +551,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
530
551
|
new CommandSucceededEvent(
|
|
531
552
|
this,
|
|
532
553
|
message,
|
|
533
|
-
options.noResponse ? undefined : document,
|
|
554
|
+
options.noResponse ? undefined : (object ??= document?.toObject(bsonOptions)),
|
|
534
555
|
started,
|
|
535
556
|
this.description.serverConnectionId
|
|
536
557
|
)
|
|
@@ -555,13 +576,27 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
555
576
|
}
|
|
556
577
|
}
|
|
557
578
|
|
|
579
|
+
public async command<T extends MongoDBResponseConstructor>(
|
|
580
|
+
ns: MongoDBNamespace,
|
|
581
|
+
command: Document,
|
|
582
|
+
options: CommandOptions | undefined,
|
|
583
|
+
responseType: T | undefined
|
|
584
|
+
): Promise<typeof responseType extends undefined ? Document : InstanceType<T>>;
|
|
585
|
+
|
|
586
|
+
public async command(
|
|
587
|
+
ns: MongoDBNamespace,
|
|
588
|
+
command: Document,
|
|
589
|
+
options?: CommandOptions
|
|
590
|
+
): Promise<Document>;
|
|
591
|
+
|
|
558
592
|
public async command(
|
|
559
593
|
ns: MongoDBNamespace,
|
|
560
594
|
command: Document,
|
|
561
|
-
options: CommandOptions = {}
|
|
595
|
+
options: CommandOptions = {},
|
|
596
|
+
responseType?: MongoDBResponseConstructor
|
|
562
597
|
): Promise<Document> {
|
|
563
598
|
this.throwIfAborted();
|
|
564
|
-
for await (const document of this.sendCommand(ns, command, options)) {
|
|
599
|
+
for await (const document of this.sendCommand(ns, command, options, responseType)) {
|
|
565
600
|
return document;
|
|
566
601
|
}
|
|
567
602
|
throw new MongoUnexpectedServerResponseError('Unable to get response from server');
|
|
@@ -622,7 +657,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
622
657
|
*
|
|
623
658
|
* Note that `for-await` loops call `return` automatically when the loop is exited.
|
|
624
659
|
*/
|
|
625
|
-
private async *readMany(): AsyncGenerator<OpMsgResponse |
|
|
660
|
+
private async *readMany(): AsyncGenerator<OpMsgResponse | OpReply> {
|
|
626
661
|
try {
|
|
627
662
|
this.dataEvents = onData(this.messageStream);
|
|
628
663
|
for await (const message of this.dataEvents) {
|
|
@@ -687,11 +722,24 @@ export class CryptoConnection extends Connection {
|
|
|
687
722
|
this.autoEncrypter = options.autoEncrypter;
|
|
688
723
|
}
|
|
689
724
|
|
|
690
|
-
|
|
691
|
-
|
|
725
|
+
public override async command<T extends MongoDBResponseConstructor>(
|
|
726
|
+
ns: MongoDBNamespace,
|
|
727
|
+
command: Document,
|
|
728
|
+
options: CommandOptions | undefined,
|
|
729
|
+
responseType: T
|
|
730
|
+
): Promise<InstanceType<T>>;
|
|
731
|
+
|
|
732
|
+
public override async command(
|
|
733
|
+
ns: MongoDBNamespace,
|
|
734
|
+
command: Document,
|
|
735
|
+
options?: CommandOptions
|
|
736
|
+
): Promise<Document>;
|
|
737
|
+
|
|
738
|
+
override async command<T extends MongoDBResponseConstructor>(
|
|
692
739
|
ns: MongoDBNamespace,
|
|
693
740
|
cmd: Document,
|
|
694
|
-
options
|
|
741
|
+
options?: CommandOptions,
|
|
742
|
+
responseType?: T | undefined
|
|
695
743
|
): Promise<Document> {
|
|
696
744
|
const { autoEncrypter } = this;
|
|
697
745
|
if (!autoEncrypter) {
|
|
@@ -705,7 +753,7 @@ export class CryptoConnection extends Connection {
|
|
|
705
753
|
const serverWireVersion = maxWireVersion(this);
|
|
706
754
|
if (serverWireVersion === 0) {
|
|
707
755
|
// This means the initial handshake hasn't happened yet
|
|
708
|
-
return await super.command(ns, cmd, options);
|
|
756
|
+
return await super.command<T>(ns, cmd, options, responseType);
|
|
709
757
|
}
|
|
710
758
|
|
|
711
759
|
if (serverWireVersion < 8) {
|
|
@@ -739,7 +787,7 @@ export class CryptoConnection extends Connection {
|
|
|
739
787
|
}
|
|
740
788
|
}
|
|
741
789
|
|
|
742
|
-
const response = await super.command(ns, encrypted, options);
|
|
790
|
+
const response = await super.command<T>(ns, encrypted, options, responseType);
|
|
743
791
|
|
|
744
792
|
return await autoEncrypter.decrypt(response, options);
|
|
745
793
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type MessageHeader,
|
|
9
9
|
OpCompressedRequest,
|
|
10
10
|
OpMsgResponse,
|
|
11
|
-
|
|
11
|
+
OpReply,
|
|
12
12
|
type WriteProtocolMessageType
|
|
13
13
|
} from '../commands';
|
|
14
14
|
import { OP_COMPRESSED, OP_MSG } from './constants';
|
|
@@ -163,9 +163,7 @@ export async function compressCommand(
|
|
|
163
163
|
*
|
|
164
164
|
* This method does not parse the response's BSON.
|
|
165
165
|
*/
|
|
166
|
-
export async function decompressResponse(
|
|
167
|
-
message: Buffer
|
|
168
|
-
): Promise<OpMsgResponse | OpQueryResponse> {
|
|
166
|
+
export async function decompressResponse(message: Buffer): Promise<OpMsgResponse | OpReply> {
|
|
169
167
|
const messageHeader: MessageHeader = {
|
|
170
168
|
length: message.readInt32LE(0),
|
|
171
169
|
requestId: message.readInt32LE(4),
|
|
@@ -174,7 +172,7 @@ export async function decompressResponse(
|
|
|
174
172
|
};
|
|
175
173
|
|
|
176
174
|
if (messageHeader.opCode !== OP_COMPRESSED) {
|
|
177
|
-
const ResponseType = messageHeader.opCode === OP_MSG ? OpMsgResponse :
|
|
175
|
+
const ResponseType = messageHeader.opCode === OP_MSG ? OpMsgResponse : OpReply;
|
|
178
176
|
const messageBody = message.subarray(MESSAGE_HEADER_SIZE);
|
|
179
177
|
return new ResponseType(message, messageHeader, messageBody);
|
|
180
178
|
}
|
|
@@ -189,7 +187,7 @@ export async function decompressResponse(
|
|
|
189
187
|
const compressedBuffer = message.slice(MESSAGE_HEADER_SIZE + 9);
|
|
190
188
|
|
|
191
189
|
// recalculate based on wrapped opcode
|
|
192
|
-
const ResponseType = header.opCode === OP_MSG ? OpMsgResponse :
|
|
190
|
+
const ResponseType = header.opCode === OP_MSG ? OpMsgResponse : OpReply;
|
|
193
191
|
const messageBody = await decompress(compressorID, compressedBuffer);
|
|
194
192
|
if (messageBody.length !== header.length) {
|
|
195
193
|
throw new MongoDecompressionError('Message body and message header must be the same length');
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type BSONSerializeOptions, BSONType, type Document, type Timestamp } from '../../bson';
|
|
2
|
+
import { type ClusterTime } from '../../sdam/common';
|
|
3
|
+
import { OnDemandDocument } from './on_demand/document';
|
|
4
|
+
|
|
5
|
+
/** @internal */
|
|
6
|
+
export type MongoDBResponseConstructor = {
|
|
7
|
+
new (bson: Uint8Array, offset?: number, isArray?: boolean): MongoDBResponse;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/** @internal */
|
|
11
|
+
export class MongoDBResponse extends OnDemandDocument {
|
|
12
|
+
// {ok:1}
|
|
13
|
+
static empty = new MongoDBResponse(new Uint8Array([13, 0, 0, 0, 16, 111, 107, 0, 1, 0, 0, 0, 0]));
|
|
14
|
+
|
|
15
|
+
/** Indicates this document is a server error */
|
|
16
|
+
public get isError() {
|
|
17
|
+
let isError = this.ok === 0;
|
|
18
|
+
isError ||= this.has('errmsg');
|
|
19
|
+
isError ||= this.has('code');
|
|
20
|
+
isError ||= this.has('$err'); // The '$err' field is used in OP_REPLY responses
|
|
21
|
+
return isError;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Drivers can safely assume that the `recoveryToken` field is always a BSON document but drivers MUST NOT modify the
|
|
26
|
+
* contents of the document.
|
|
27
|
+
*/
|
|
28
|
+
get recoveryToken(): Document | null {
|
|
29
|
+
return (
|
|
30
|
+
this.get('recoveryToken', BSONType.object)?.toObject({
|
|
31
|
+
promoteValues: false,
|
|
32
|
+
promoteLongs: false,
|
|
33
|
+
promoteBuffers: false
|
|
34
|
+
}) ?? null
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The server creates a cursor in response to a snapshot find/aggregate command and reports atClusterTime within the cursor field in the response.
|
|
40
|
+
* For the distinct command the server adds a top-level atClusterTime field to the response.
|
|
41
|
+
* The atClusterTime field represents the timestamp of the read and is guaranteed to be majority committed.
|
|
42
|
+
*/
|
|
43
|
+
public get atClusterTime(): Timestamp | null {
|
|
44
|
+
return (
|
|
45
|
+
this.get('cursor', BSONType.object)?.get('atClusterTime', BSONType.timestamp) ??
|
|
46
|
+
this.get('atClusterTime', BSONType.timestamp)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public get operationTime(): Timestamp | null {
|
|
51
|
+
return this.get('operationTime', BSONType.timestamp);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public get ok(): 0 | 1 {
|
|
55
|
+
return this.getNumber('ok') ? 1 : 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public get $err(): string | null {
|
|
59
|
+
return this.get('$err', BSONType.string);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public get errmsg(): string | null {
|
|
63
|
+
return this.get('errmsg', BSONType.string);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public get code(): number | null {
|
|
67
|
+
return this.getNumber('code');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private clusterTime?: ClusterTime | null;
|
|
71
|
+
public get $clusterTime(): ClusterTime | null {
|
|
72
|
+
if (!('clusterTime' in this)) {
|
|
73
|
+
const clusterTimeDoc = this.get('$clusterTime', BSONType.object);
|
|
74
|
+
if (clusterTimeDoc == null) {
|
|
75
|
+
this.clusterTime = null;
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const clusterTime = clusterTimeDoc.get('clusterTime', BSONType.timestamp, true);
|
|
79
|
+
const signature = clusterTimeDoc.get('signature', BSONType.object)?.toObject();
|
|
80
|
+
// @ts-expect-error: `signature` is incorrectly typed. It is public API.
|
|
81
|
+
this.clusterTime = { clusterTime, signature };
|
|
82
|
+
}
|
|
83
|
+
return this.clusterTime ?? null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public override toObject(options: BSONSerializeOptions = {}): Record<string, any> {
|
|
87
|
+
const exactBSONOptions = {
|
|
88
|
+
useBigInt64: options.useBigInt64,
|
|
89
|
+
promoteLongs: options.promoteLongs,
|
|
90
|
+
promoteValues: options.promoteValues,
|
|
91
|
+
promoteBuffers: options.promoteBuffers,
|
|
92
|
+
bsonRegExp: options.bsonRegExp,
|
|
93
|
+
raw: options.raw ?? false,
|
|
94
|
+
fieldsAsRaw: options.fieldsAsRaw ?? {},
|
|
95
|
+
validation: this.parseBsonSerializationOptions(options)
|
|
96
|
+
};
|
|
97
|
+
return super.toObject(exactBSONOptions);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private parseBsonSerializationOptions({ enableUtf8Validation }: BSONSerializeOptions): {
|
|
101
|
+
utf8: { writeErrors: false } | false;
|
|
102
|
+
} {
|
|
103
|
+
if (enableUtf8Validation === false) {
|
|
104
|
+
return { utf8: false };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { utf8: { writeErrors: false } };
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/collection.ts
CHANGED
|
@@ -106,6 +106,8 @@ export interface CollectionOptions extends BSONSerializeOptions, WriteConcernOpt
|
|
|
106
106
|
readConcern?: ReadConcernLike;
|
|
107
107
|
/** The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */
|
|
108
108
|
readPreference?: ReadPreferenceLike;
|
|
109
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
110
|
+
timeoutMS?: number;
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
/** @internal */
|
|
@@ -109,6 +109,8 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
|
|
|
109
109
|
*/
|
|
110
110
|
awaitData?: boolean;
|
|
111
111
|
noCursorTimeout?: boolean;
|
|
112
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
113
|
+
timeoutMS?: number;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
/** @internal */
|
|
@@ -184,6 +186,7 @@ export abstract class AbstractCursor<
|
|
|
184
186
|
: ReadPreference.primary,
|
|
185
187
|
...pluckBSONSerializeOptions(options)
|
|
186
188
|
};
|
|
189
|
+
this[kOptions].timeoutMS = options.timeoutMS;
|
|
187
190
|
|
|
188
191
|
const readConcern = ReadConcern.fromOptions(options);
|
|
189
192
|
if (readConcern) {
|
package/src/db.ts
CHANGED
|
@@ -66,7 +66,8 @@ const DB_OPTIONS_ALLOW_LIST = [
|
|
|
66
66
|
'enableUtf8Validation',
|
|
67
67
|
'promoteValues',
|
|
68
68
|
'compression',
|
|
69
|
-
'retryWrites'
|
|
69
|
+
'retryWrites',
|
|
70
|
+
'timeoutMS'
|
|
70
71
|
];
|
|
71
72
|
|
|
72
73
|
/** @internal */
|
|
@@ -94,6 +95,8 @@ export interface DbOptions extends BSONSerializeOptions, WriteConcernOptions {
|
|
|
94
95
|
readConcern?: ReadConcern;
|
|
95
96
|
/** Should retry failed writes */
|
|
96
97
|
retryWrites?: boolean;
|
|
98
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
99
|
+
timeoutMS?: number;
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
/**
|
package/src/gridfs/download.ts
CHANGED
package/src/gridfs/index.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface GridFSBucketOptions extends WriteConcernOptions {
|
|
|
36
36
|
chunkSizeBytes?: number;
|
|
37
37
|
/** Read preference to be passed to read operations */
|
|
38
38
|
readPreference?: ReadPreference;
|
|
39
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
40
|
+
timeoutMS?: number;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/** @internal */
|
package/src/gridfs/upload.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface GridFSBucketWriteStreamOptions extends WriteConcernOptions {
|
|
|
36
36
|
* @deprecated Will be removed in the next major version. Add an aliases field to the metadata document instead.
|
|
37
37
|
*/
|
|
38
38
|
aliases?: string[];
|
|
39
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
40
|
+
timeoutMS?: number;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
package/src/index.ts
CHANGED
|
@@ -264,8 +264,7 @@ export type {
|
|
|
264
264
|
OpMsgResponse,
|
|
265
265
|
OpQueryOptions,
|
|
266
266
|
OpQueryRequest,
|
|
267
|
-
|
|
268
|
-
OpResponseOptions,
|
|
267
|
+
OpReply,
|
|
269
268
|
WriteProtocolMessageType
|
|
270
269
|
} from './cmap/commands';
|
|
271
270
|
export type { HandshakeDocument } from './cmap/connect';
|
|
@@ -290,6 +289,8 @@ export type { ClientMetadata, ClientMetadataOptions } from './cmap/handshake/cli
|
|
|
290
289
|
export type { ConnectionPoolMetrics } from './cmap/metrics';
|
|
291
290
|
export type { StreamDescription, StreamDescriptionOptions } from './cmap/stream_description';
|
|
292
291
|
export type { CompressorName } from './cmap/wire_protocol/compression';
|
|
292
|
+
export type { JSTypeOf, OnDemandDocument } from './cmap/wire_protocol/on_demand/document';
|
|
293
|
+
export type { MongoDBResponse, MongoDBResponseConstructor } from './cmap/wire_protocol/responses';
|
|
293
294
|
export type { CollectionOptions, CollectionPrivate, ModifyResult } from './collection';
|
|
294
295
|
export type {
|
|
295
296
|
COMMAND_FAILED,
|
package/src/mongo_client.ts
CHANGED
|
@@ -120,7 +120,7 @@ export type SupportedNodeConnectionOptions = SupportedTLSConnectionOptions &
|
|
|
120
120
|
export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeConnectionOptions {
|
|
121
121
|
/** Specifies the name of the replica set, if the mongod is a member of a replica set. */
|
|
122
122
|
replicaSet?: string;
|
|
123
|
-
/** @internal This option is in development and currently has no behaviour.
|
|
123
|
+
/** @internal TODO(NODE-5688): This option is in development and currently has no behaviour. */
|
|
124
124
|
timeoutMS?: number;
|
|
125
125
|
/** Enables or disables TLS/SSL for the connection. */
|
|
126
126
|
tls?: boolean;
|
|
@@ -897,4 +897,6 @@ export interface MongoOptions
|
|
|
897
897
|
* TODO: NODE-5671 - remove internal flag
|
|
898
898
|
*/
|
|
899
899
|
mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;
|
|
900
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
901
|
+
timeoutMS?: number;
|
|
900
902
|
}
|
|
@@ -110,6 +110,11 @@ export async function executeOperation<
|
|
|
110
110
|
} else if (session.client !== client) {
|
|
111
111
|
throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');
|
|
112
112
|
}
|
|
113
|
+
if (session.explicit && session?.timeoutMS != null && operation.options.timeoutMS != null) {
|
|
114
|
+
throw new MongoInvalidArgumentError(
|
|
115
|
+
'Do not specify timeoutMS on operation if already specified on an explicit session'
|
|
116
|
+
);
|
|
117
|
+
}
|
|
113
118
|
|
|
114
119
|
const readPreference = operation.readPreference ?? ReadPreference.primary;
|
|
115
120
|
const inTransaction = !!session?.inTransaction();
|
|
@@ -34,6 +34,9 @@ export interface OperationOptions extends BSONSerializeOptions {
|
|
|
34
34
|
/** @internal Hints to `executeOperation` that this operation should not unpin on an ended transaction */
|
|
35
35
|
bypassPinningCheck?: boolean;
|
|
36
36
|
omitReadPreference?: boolean;
|
|
37
|
+
|
|
38
|
+
/** @internal TODO(NODE-5688): make this public */
|
|
39
|
+
timeoutMS?: number;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
/** @internal */
|
package/src/sessions.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { promisify } from 'util';
|
|
|
4
4
|
import { Binary, type Document, Long, type Timestamp } from './bson';
|
|
5
5
|
import type { CommandOptions, Connection } from './cmap/connection';
|
|
6
6
|
import { ConnectionPoolMetrics } from './cmap/metrics';
|
|
7
|
+
import { type MongoDBResponse } from './cmap/wire_protocol/responses';
|
|
7
8
|
import { isSharded } from './cmap/wire_protocol/shared';
|
|
8
9
|
import { PINNED, UNPINNED } from './constants';
|
|
9
10
|
import type { AbstractCursor } from './cursor/abstract_cursor';
|
|
@@ -60,6 +61,9 @@ export interface ClientSessionOptions {
|
|
|
60
61
|
snapshot?: boolean;
|
|
61
62
|
/** The default TransactionOptions to use for transactions started on this session. */
|
|
62
63
|
defaultTransactionOptions?: TransactionOptions;
|
|
64
|
+
/** @internal
|
|
65
|
+
* The value of timeoutMS used for CSOT. Used to override client timeoutMS */
|
|
66
|
+
defaultTimeoutMS?: number;
|
|
63
67
|
|
|
64
68
|
/** @internal */
|
|
65
69
|
owner?: symbol | AbstractCursor;
|
|
@@ -130,6 +134,8 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
130
134
|
[kPinnedConnection]?: Connection;
|
|
131
135
|
/** @internal */
|
|
132
136
|
[kTxnNumberIncrement]: number;
|
|
137
|
+
/** @internal */
|
|
138
|
+
timeoutMS?: number;
|
|
133
139
|
|
|
134
140
|
/**
|
|
135
141
|
* Create a client session.
|
|
@@ -172,6 +178,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
172
178
|
this.sessionPool = sessionPool;
|
|
173
179
|
this.hasEnded = false;
|
|
174
180
|
this.clientOptions = clientOptions;
|
|
181
|
+
this.timeoutMS = options.defaultTimeoutMS ?? client.options?.timeoutMS;
|
|
175
182
|
|
|
176
183
|
this.explicit = !!options.explicit;
|
|
177
184
|
this[kServerSession] = this.explicit ? this.sessionPool.acquire() : null;
|
|
@@ -1040,7 +1047,7 @@ export function applySession(
|
|
|
1040
1047
|
return;
|
|
1041
1048
|
}
|
|
1042
1049
|
|
|
1043
|
-
export function updateSessionFromResponse(session: ClientSession, document:
|
|
1050
|
+
export function updateSessionFromResponse(session: ClientSession, document: MongoDBResponse): void {
|
|
1044
1051
|
if (document.$clusterTime) {
|
|
1045
1052
|
_advanceClusterTime(session, document.$clusterTime);
|
|
1046
1053
|
}
|
|
@@ -1056,7 +1063,7 @@ export function updateSessionFromResponse(session: ClientSession, document: Docu
|
|
|
1056
1063
|
if (session?.[kSnapshotEnabled] && session[kSnapshotTime] == null) {
|
|
1057
1064
|
// find and aggregate commands return atClusterTime on the cursor
|
|
1058
1065
|
// distinct includes it in the response body
|
|
1059
|
-
const atClusterTime = document.
|
|
1066
|
+
const atClusterTime = document.atClusterTime;
|
|
1060
1067
|
if (atClusterTime) {
|
|
1061
1068
|
session[kSnapshotTime] = atClusterTime;
|
|
1062
1069
|
}
|