Socket
Socket
Sign inDemoInstall

mongodb

Package Overview
Dependencies
216
Maintainers
8
Versions
511
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 6.7.0-dev.20240607.sha.aa429f8c to 6.7.0-dev.20240608.sha.0655c730

502

lib/cursor/abstract_cursor.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assertUninitialized = exports.AbstractCursor = exports.CURSOR_FLAGS = void 0;
exports.AbstractCursor = exports.CURSOR_FLAGS = void 0;
const stream_1 = require("stream");

@@ -16,26 +16,2 @@ const bson_1 = require("../bson");

const utils_1 = require("../utils");
/** @internal */
const kId = Symbol('id');
/** @internal */
const kDocuments = Symbol('documents');
/** @internal */
const kServer = Symbol('server');
/** @internal */
const kNamespace = Symbol('namespace');
/** @internal */
const kClient = Symbol('client');
/** @internal */
const kSession = Symbol('session');
/** @internal */
const kOptions = Symbol('options');
/** @internal */
const kTransform = Symbol('transform');
/** @internal */
const kInitialized = Symbol('initialized');
/** @internal */
const kClosed = Symbol('closed');
/** @internal */
const kKilled = Symbol('killed');
/** @internal */
const kInit = Symbol('kInit');
/** @public */

@@ -55,13 +31,15 @@ exports.CURSOR_FLAGS = [

super();
/** @internal */
this.hasEmittedClose = false;
if (!client.s.isMongoClient) {
throw new error_1.MongoRuntimeError('Cursor must be constructed with MongoClient');
}
this[kClient] = client;
this[kNamespace] = namespace;
this[kId] = null;
this[kDocuments] = new utils_1.List();
this[kInitialized] = false;
this[kClosed] = false;
this[kKilled] = false;
this[kOptions] = {
this.cursorClient = client;
this.cursorNamespace = namespace;
this.cursorId = null;
this.documents = new utils_1.List();
this.initialized = false;
this.isClosed = false;
this.isKilled = false;
this.cursorOptions = {
readPreference: options.readPreference && options.readPreference instanceof read_preference_1.ReadPreference

@@ -72,9 +50,9 @@ ? options.readPreference

};
this[kOptions].timeoutMS = options.timeoutMS;
this.cursorOptions.timeoutMS = options.timeoutMS;
const readConcern = read_concern_1.ReadConcern.fromOptions(options);
if (readConcern) {
this[kOptions].readConcern = readConcern;
this.cursorOptions.readConcern = readConcern;
}
if (typeof options.batchSize === 'number') {
this[kOptions].batchSize = options.batchSize;
this.cursorOptions.batchSize = options.batchSize;
}

@@ -84,64 +62,60 @@ // we check for undefined specifically here to allow falsy values

if (options.comment !== undefined) {
this[kOptions].comment = options.comment;
this.cursorOptions.comment = options.comment;
}
if (typeof options.maxTimeMS === 'number') {
this[kOptions].maxTimeMS = options.maxTimeMS;
this.cursorOptions.maxTimeMS = options.maxTimeMS;
}
if (typeof options.maxAwaitTimeMS === 'number') {
this[kOptions].maxAwaitTimeMS = options.maxAwaitTimeMS;
this.cursorOptions.maxAwaitTimeMS = options.maxAwaitTimeMS;
}
if (options.session instanceof sessions_1.ClientSession) {
this[kSession] = options.session;
this.cursorSession = options.session;
}
else {
this[kSession] = this[kClient].startSession({ owner: this, explicit: false });
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
}
}
get id() {
return this[kId] ?? undefined;
return this.cursorId ?? undefined;
}
/** @internal */
get isDead() {
return (this[kId]?.isZero() ?? false) || this[kClosed] || this[kKilled];
return (this.cursorId?.isZero() ?? false) || this.isClosed || this.isKilled;
}
/** @internal */
get client() {
return this[kClient];
return this.cursorClient;
}
/** @internal */
get server() {
return this[kServer];
return this.selectedServer;
}
get namespace() {
return this[kNamespace];
return this.cursorNamespace;
}
get readPreference() {
return this[kOptions].readPreference;
return this.cursorOptions.readPreference;
}
get readConcern() {
return this[kOptions].readConcern;
return this.cursorOptions.readConcern;
}
/** @internal */
get session() {
return this[kSession];
return this.cursorSession;
}
set session(clientSession) {
this[kSession] = clientSession;
this.cursorSession = clientSession;
}
/** @internal */
get cursorOptions() {
return this[kOptions];
}
get closed() {
return this[kClosed];
return this.isClosed;
}
get killed() {
return this[kKilled];
return this.isKilled;
}
get loadBalanced() {
return !!this[kClient].topology?.loadBalanced;
return !!this.cursorClient.topology?.loadBalanced;
}
/** Returns current buffered documents length */
bufferedCount() {
return this[kDocuments].length;
return this.documents.length;
}

@@ -151,5 +125,5 @@ /** Returns current buffered documents */

const bufferedDocs = [];
const documentsToRead = Math.min(number ?? this[kDocuments].length, this[kDocuments].length);
const documentsToRead = Math.min(number ?? this.documents.length, this.documents.length);
for (let count = 0; count < documentsToRead; count++) {
const document = this[kDocuments].shift(this[kOptions]);
const document = this.documents.shift(this.cursorOptions);
if (document != null) {

@@ -162,3 +136,3 @@ bufferedDocs.push(document);

async *[Symbol.asyncIterator]() {
if (this.closed) {
if (this.isClosed) {
return;

@@ -168,24 +142,17 @@ }

while (true) {
if (this.isKilled) {
return;
}
if (this.isClosed && this.documents.length === 0) {
return;
}
if (this.cursorId != null && this.isDead && this.documents.length === 0) {
return;
}
const document = await this.next();
// Intentional strict null check, because users can map cursors to falsey values.
// We allow mapping to all values except for null.
// eslint-disable-next-line no-restricted-syntax
if (document === null) {
if (!this.closed) {
const message = 'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
try {
await cleanupCursor(this, { needsToEmitClosed: true });
}
catch (error) {
(0, utils_1.squashError)(error);
}
throw new error_1.MongoAPIError(message);
}
break;
return;
}
yield document;
if (this[kId] === bson_1.Long.ZERO) {
// Cursor exhausted
break;
}
}

@@ -196,3 +163,3 @@ }

// the case when a user would break out of a for await of loop early.
if (!this.closed) {
if (!this.isClosed) {
try {

@@ -232,16 +199,28 @@ await this.close();

async hasNext() {
if (this[kId] === bson_1.Long.ZERO) {
if (this.cursorId === bson_1.Long.ZERO) {
return false;
}
if (this[kDocuments].length !== 0) {
return true;
}
return await next(this, { blocking: true, transform: false, shift: false });
do {
if (this.documents.length !== 0) {
return true;
}
await this.fetchBatch();
} while (!this.isDead || this.documents.length !== 0);
return false;
}
/** Get the next available document from the cursor, returns null if no more documents are available. */
async next() {
if (this[kId] === bson_1.Long.ZERO) {
if (this.cursorId === bson_1.Long.ZERO) {
throw new error_1.MongoCursorExhaustedError();
}
return await next(this, { blocking: true, transform: true, shift: true });
do {
const doc = this.documents.shift();
if (doc != null) {
if (this.transform != null)
return await this.transformDocument(doc);
return doc;
}
await this.fetchBatch();
} while (!this.isDead || this.documents.length !== 0);
return null;
}

@@ -252,6 +231,19 @@ /**

async tryNext() {
if (this[kId] === bson_1.Long.ZERO) {
if (this.cursorId === bson_1.Long.ZERO) {
throw new error_1.MongoCursorExhaustedError();
}
return await next(this, { blocking: false, transform: true, shift: true });
let doc = this.documents.shift();
if (doc != null) {
if (this.transform != null)
return await this.transformDocument(doc);
return doc;
}
await this.fetchBatch();
doc = this.documents.shift();
if (doc != null) {
if (this.transform != null)
return await this.transformDocument(doc);
return doc;
}
return null;
}

@@ -278,5 +270,3 @@ /**

async close() {
const needsToEmitClosed = !this[kClosed];
this[kClosed] = true;
await cleanupCursor(this, { needsToEmitClosed });
await this.cleanup();
}

@@ -303,3 +293,3 @@ /**

addCursorFlag(flag, value) {
assertUninitialized(this);
this.throwIfInitialized();
if (!exports.CURSOR_FLAGS.includes(flag)) {

@@ -311,3 +301,3 @@ throw new error_1.MongoInvalidArgumentError(`Flag ${flag} is not one of ${exports.CURSOR_FLAGS}`);

}
this[kOptions][flag] = value;
this.cursorOptions[flag] = value;
return this;

@@ -358,6 +348,6 @@ }

map(transform) {
assertUninitialized(this);
const oldTransform = this[kTransform]; // TODO(NODE-3283): Improve transform typing
this.throwIfInitialized();
const oldTransform = this.transform;
if (oldTransform) {
this[kTransform] = doc => {
this.transform = doc => {
return transform(oldTransform(doc));

@@ -367,3 +357,3 @@ };

else {
this[kTransform] = transform;
this.transform = transform;
}

@@ -378,8 +368,8 @@ return this;

withReadPreference(readPreference) {
assertUninitialized(this);
this.throwIfInitialized();
if (readPreference instanceof read_preference_1.ReadPreference) {
this[kOptions].readPreference = readPreference;
this.cursorOptions.readPreference = readPreference;
}
else if (typeof readPreference === 'string') {
this[kOptions].readPreference = read_preference_1.ReadPreference.fromString(readPreference);
this.cursorOptions.readPreference = read_preference_1.ReadPreference.fromString(readPreference);
}

@@ -397,6 +387,6 @@ else {

withReadConcern(readConcern) {
assertUninitialized(this);
this.throwIfInitialized();
const resolvedReadConcern = read_concern_1.ReadConcern.fromOptions({ readConcern });
if (resolvedReadConcern) {
this[kOptions].readConcern = resolvedReadConcern;
this.cursorOptions.readConcern = resolvedReadConcern;
}

@@ -411,7 +401,7 @@ return this;

maxTimeMS(value) {
assertUninitialized(this);
this.throwIfInitialized();
if (typeof value !== 'number') {
throw new error_1.MongoInvalidArgumentError('Argument for maxTimeMS must be a number');
}
this[kOptions].maxTimeMS = value;
this.cursorOptions.maxTimeMS = value;
return this;

@@ -425,4 +415,4 @@ }

batchSize(value) {
assertUninitialized(this);
if (this[kOptions].tailable) {
this.throwIfInitialized();
if (this.cursorOptions.tailable) {
throw new error_1.MongoTailableCursorError('Tailable cursor does not support batchSize');

@@ -433,3 +423,3 @@ }

}
this[kOptions].batchSize = value;
this.cursorOptions.batchSize = value;
return this;

@@ -443,11 +433,11 @@ }

rewind() {
if (!this[kInitialized]) {
if (!this.initialized) {
return;
}
this[kId] = null;
this[kDocuments].clear();
this[kClosed] = false;
this[kKilled] = false;
this[kInitialized] = false;
const session = this[kSession];
this.cursorId = null;
this.documents.clear();
this.isClosed = false;
this.isKilled = false;
this.initialized = false;
const session = this.cursorSession;
if (session) {

@@ -460,3 +450,3 @@ // We only want to end this session if we created it, and it hasn't ended yet

}
this[kSession] = this.client.startSession({ owner: this, explicit: false });
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
}

@@ -467,10 +457,15 @@ }

async getMore(batchSize, useCursorResponse = false) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const getMoreOperation = new get_more_1.GetMoreOperation(this[kNamespace], this[kId], this[kServer], {
...this[kOptions],
session: this[kSession],
if (this.cursorId == null) {
throw new error_1.MongoRuntimeError('Unexpected null cursor id. A cursor creating command should have set this');
}
if (this.selectedServer == null) {
throw new error_1.MongoRuntimeError('Unexpected null selectedServer. A cursor creating command should have set this');
}
const getMoreOperation = new get_more_1.GetMoreOperation(this.cursorNamespace, this.cursorId, this.selectedServer, {
...this.cursorOptions,
session: this.cursorSession,
batchSize,
useCursorResponse
});
return await (0, execute_operation_1.executeOperation)(this[kClient], getMoreOperation);
return await (0, execute_operation_1.executeOperation)(this.cursorClient, getMoreOperation);
}

@@ -484,109 +479,74 @@ /**

*/
async [kInit]() {
async cursorInit() {
try {
const state = await this._initialize(this[kSession]);
const state = await this._initialize(this.cursorSession);
const response = state.response;
this[kServer] = state.server;
this.selectedServer = state.server;
if (responses_1.CursorResponse.is(response)) {
this[kId] = response.id;
this.cursorId = response.id;
if (response.ns)
this[kNamespace] = response.ns;
this[kDocuments] = response;
this.cursorNamespace = response.ns;
this.documents = response;
}
else if (response.cursor) {
// TODO(NODE-2674): Preserve int64 sent from MongoDB
this[kId] =
typeof response.cursor.id === 'number'
? bson_1.Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? bson_1.Long.fromBigInt(response.cursor.id)
: response.cursor.id;
if (response.cursor.ns) {
this[kNamespace] = (0, utils_1.ns)(response.cursor.ns);
}
this[kDocuments].pushMany(response.cursor.firstBatch);
this.cursorId = getCursorId(response);
if (response.cursor.ns)
this.cursorNamespace = (0, utils_1.ns)(response.cursor.ns);
this.documents.pushMany(response.cursor.firstBatch);
}
// When server responses return without a cursor document, we close this cursor
// and return the raw server response. This is often the case for explain commands
// for example
if (this[kId] == null) {
this[kId] = bson_1.Long.ZERO;
if (this.cursorId == null) {
// When server responses return without a cursor document, we close this cursor
// and return the raw server response. This is the case for explain commands
this.cursorId = bson_1.Long.ZERO;
// TODO(NODE-3286): ExecutionResult needs to accept a generic parameter
this[kDocuments].push(state.response);
this.documents.push(state.response);
}
// the cursor is now initialized, even if it is dead
this[kInitialized] = true;
this.initialized = true;
}
catch (error) {
// the cursor is now initialized, even if an error occurred
this[kInitialized] = true;
await cleanupCursor(this, { error });
this.initialized = true;
await this.cleanup(error);
throw error;
}
if (this.isDead) {
await cleanupCursor(this, undefined);
await this.cleanup();
}
return;
}
}
/** @event */
AbstractCursor.CLOSE = 'close';
exports.AbstractCursor = AbstractCursor;
async function next(cursor, { blocking, transform, shift }) {
if (cursor.closed) {
if (!shift)
return false;
return null;
}
do {
if (cursor[kId] == null) {
// All cursors must operate within a session, one must be made implicitly if not explicitly provided
await cursor[kInit]();
/** @internal Attempt to obtain more documents */
async fetchBatch() {
if (this.isClosed) {
return;
}
if (cursor[kDocuments].length !== 0) {
if (!shift)
return true;
const doc = cursor[kDocuments].shift(cursor[kOptions]);
if (doc != null && transform && cursor[kTransform]) {
try {
return cursor[kTransform](doc);
}
catch (error) {
try {
await cleanupCursor(cursor, { error, needsToEmitClosed: true });
}
catch (error) {
// `cleanupCursor` should never throw, squash and throw the original error
(0, utils_1.squashError)(error);
}
throw error;
}
}
return doc;
}
if (cursor.isDead) {
if (this.isDead) {
// if the cursor is dead, we clean it up
// cleanupCursor should never throw, but if it does it indicates a bug in the driver
// and we should surface the error
await cleanupCursor(cursor, {});
if (!shift)
return false;
return null;
await this.cleanup();
return;
}
if (this.cursorId == null) {
await this.cursorInit();
// If the cursor died or returned documents, return
if (this.documents.length !== 0 || this.isDead)
return;
// Otherwise, run a getMore
}
// otherwise need to call getMore
const batchSize = cursor[kOptions].batchSize || 1000;
const batchSize = this.cursorOptions.batchSize || 1000;
try {
const response = await cursor.getMore(batchSize);
const response = await this.getMore(batchSize);
// CursorResponse is disabled in this PR
// however the special `emptyGetMore` can be returned from find cursors
if (responses_1.CursorResponse.is(response)) {
cursor[kId] = response.id;
cursor[kDocuments] = response;
this.cursorId = response.id;
this.documents = response;
}
else if (response) {
const cursorId = typeof response.cursor.id === 'number'
? bson_1.Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? bson_1.Long.fromBigInt(response.cursor.id)
: response.cursor.id;
cursor[kDocuments].pushMany(response.cursor.nextBatch);
cursor[kId] = cursorId;
else if (response?.cursor) {
const cursorId = getCursorId(response);
this.documents.pushMany(response.cursor.nextBatch);
this.cursorId = cursorId;
}

@@ -596,3 +556,3 @@ }

try {
await cleanupCursor(cursor, { error, needsToEmitClosed: true });
await this.cleanup(error);
}

@@ -605,3 +565,3 @@ catch (error) {

}
if (cursor.isDead) {
if (this.isDead) {
// If we successfully received a response from a cursor BUT the cursor indicates that it is exhausted,

@@ -614,85 +574,87 @@ // we intentionally clean up the cursor to release its session back into the pool before the cursor

// and we should surface the error
await cleanupCursor(cursor, {});
await this.cleanup();
}
if (cursor[kDocuments].length === 0 && blocking === false) {
if (!shift)
return false;
return null;
}
/** @internal */
async cleanup(error) {
this.isClosed = true;
const session = this.cursorSession;
try {
if (!this.isKilled &&
this.cursorId &&
!this.cursorId.isZero() &&
this.cursorNamespace &&
this.selectedServer &&
!session.hasEnded) {
this.isKilled = true;
await (0, execute_operation_1.executeOperation)(this.cursorClient, new kill_cursors_1.KillCursorsOperation(this.cursorId, this.cursorNamespace, this.selectedServer, {
session
}));
}
}
} while (!cursor.isDead || cursor[kDocuments].length !== 0);
if (!shift)
return false;
return null;
}
async function cleanupCursor(cursor, options) {
const cursorId = cursor[kId];
const cursorNs = cursor[kNamespace];
const server = cursor[kServer];
const session = cursor[kSession];
const error = options?.error;
// Cursors only emit closed events once the client-side cursor has been exhausted fully or there
// was an error. Notably, when the server returns a cursor id of 0 and a non-empty batch, we
// cleanup the cursor but don't emit a `close` event.
const needsToEmitClosed = options?.needsToEmitClosed ?? cursor[kDocuments].length === 0;
if (error) {
if (cursor.loadBalanced && error instanceof error_1.MongoNetworkError) {
return await completeCleanup();
catch (error) {
(0, utils_1.squashError)(error);
}
}
if (cursorId == null || server == null || cursorId.isZero() || cursorNs == null) {
if (needsToEmitClosed) {
cursor[kClosed] = true;
cursor[kId] = bson_1.Long.ZERO;
cursor.emit(AbstractCursor.CLOSE);
}
if (session) {
if (session.owner === cursor) {
finally {
if (session?.owner === this) {
await session.endSession({ error });
return;
}
if (!session.inTransaction()) {
if (!session?.inTransaction()) {
(0, sessions_1.maybeClearPinnedConnection)(session, { error });
}
this.emitClose();
}
return;
}
async function completeCleanup() {
if (session) {
if (session.owner === cursor) {
try {
await session.endSession({ error });
}
finally {
cursor.emit(AbstractCursor.CLOSE);
}
return;
/** @internal */
emitClose() {
try {
if (!this.hasEmittedClose && (this.documents.length === 0 || this.isClosed)) {
// @ts-expect-error: CursorEvents is generic so Parameters<CursorEvents["close"]> may not be assignable to `[]`. Not sure how to require extenders do not add parameters.
this.emit('close');
}
if (!session.inTransaction()) {
(0, sessions_1.maybeClearPinnedConnection)(session, { error });
}
finally {
this.hasEmittedClose = true;
}
}
/** @internal */
async transformDocument(document) {
if (this.transform == null)
return document;
try {
const transformedDocument = this.transform(document);
// eslint-disable-next-line no-restricted-syntax
if (transformedDocument === null) {
const TRANSFORM_TO_NULL_ERROR = 'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
throw new error_1.MongoAPIError(TRANSFORM_TO_NULL_ERROR);
}
return transformedDocument;
}
cursor.emit(AbstractCursor.CLOSE);
return;
catch (transformError) {
try {
await this.close();
}
catch (closeError) {
(0, utils_1.squashError)(closeError);
}
throw transformError;
}
}
cursor[kKilled] = true;
if (session.hasEnded) {
return await completeCleanup();
/** @internal */
throwIfInitialized() {
if (this.initialized)
throw new error_1.MongoCursorInUseError();
}
try {
await (0, execute_operation_1.executeOperation)(cursor[kClient], new kill_cursors_1.KillCursorsOperation(cursorId, cursorNs, server, { session }));
}
catch (error) {
(0, utils_1.squashError)(error);
}
finally {
await completeCleanup();
}
}
/** @internal */
function assertUninitialized(cursor) {
if (cursor[kInitialized]) {
throw new error_1.MongoCursorInUseError();
}
/** @event */
AbstractCursor.CLOSE = 'close';
exports.AbstractCursor = AbstractCursor;
/** A temporary helper to box up the many possible type issue of cursor ids */
function getCursorId(response) {
return typeof response.cursor.id === 'number'
? bson_1.Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? bson_1.Long.fromBigInt(response.cursor.id)
: response.cursor.id;
}
exports.assertUninitialized = assertUninitialized;
class ReadableCursorStream extends stream_1.Readable {

@@ -720,4 +682,8 @@ constructor(cursor) {

_readNext() {
if (this._cursor.id === bson_1.Long.ZERO) {
this.push(null);
return;
}
// eslint-disable-next-line github/no-then
next(this._cursor, { blocking: true, transform: true, shift: true }).then(result => {
this._cursor.next().then(result => {
if (result == null) {

@@ -724,0 +690,0 @@ this.push(null);

@@ -8,6 +8,2 @@ "use strict";

const abstract_cursor_1 = require("./abstract_cursor");
/** @internal */
const kPipeline = Symbol('pipeline');
/** @internal */
const kOptions = Symbol('options');
/**

@@ -24,12 +20,9 @@ * The **AggregationCursor** class is an internal class that embodies an aggregation cursor on MongoDB

super(client, namespace, options);
this[kPipeline] = pipeline;
this[kOptions] = options;
this.pipeline = pipeline;
this.aggregateOptions = options;
}
get pipeline() {
return this[kPipeline];
}
clone() {
const clonedOptions = (0, utils_1.mergeOptions)({}, this[kOptions]);
const clonedOptions = (0, utils_1.mergeOptions)({}, this.aggregateOptions);
delete clonedOptions.session;
return new AggregationCursor(this.client, this.namespace, this[kPipeline], {
return new AggregationCursor(this.client, this.namespace, this.pipeline, {
...clonedOptions

@@ -43,4 +36,4 @@ });

async _initialize(session) {
const aggregateOperation = new aggregate_1.AggregateOperation(this.namespace, this[kPipeline], {
...this[kOptions],
const aggregateOperation = new aggregate_1.AggregateOperation(this.namespace, this.pipeline, {
...this.aggregateOptions,
...this.cursorOptions,

@@ -55,4 +48,4 @@ session

async explain(verbosity) {
return await (0, execute_operation_1.executeOperation)(this.client, new aggregate_1.AggregateOperation(this.namespace, this[kPipeline], {
...this[kOptions],
return await (0, execute_operation_1.executeOperation)(this.client, new aggregate_1.AggregateOperation(this.namespace, this.pipeline, {
...this.aggregateOptions,
...this.cursorOptions,

@@ -63,4 +56,4 @@ explain: verbosity ?? true

addStage(stage) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kPipeline].push(stage);
this.throwIfInitialized();
this.pipeline.push(stage);
return this;

@@ -67,0 +60,0 @@ }

@@ -15,3 +15,3 @@ "use strict";

this.pipeline = pipeline;
this.options = options;
this.changeStreamCursorOptions = options;
this._resumeToken = null;

@@ -35,3 +35,3 @@ this.startAtOperationTime = options.startAtOperationTime;

const options = {
...this.options
...this.changeStreamCursorOptions
};

@@ -42,3 +42,3 @@ for (const key of ['resumeAfter', 'startAfter', 'startAtOperationTime']) {

if (this.resumeToken != null) {
if (this.options.startAfter && !this.hasReceived) {
if (this.changeStreamCursorOptions.startAfter && !this.hasReceived) {
options.startAfter = this.resumeToken;

@@ -82,3 +82,3 @@ }

...this.cursorOptions,
...this.options,
...this.changeStreamCursorOptions,
session

@@ -90,4 +90,4 @@ });

if (this.startAtOperationTime == null &&
this.resumeAfter == null &&
this.startAfter == null &&
this.changeStreamCursorOptions.resumeAfter == null &&
this.changeStreamCursorOptions.startAfter == null &&
this.maxWireVersion >= 7) {

@@ -94,0 +94,0 @@ this.startAtOperationTime = response.operationTime;

"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });

@@ -13,8 +12,2 @@ exports.FindCursor = exports.FLAGS = void 0;

const abstract_cursor_1 = require("./abstract_cursor");
/** @internal */
const kFilter = Symbol('filter');
/** @internal */
const kNumReturned = Symbol('numReturned');
/** @internal */
const kBuiltOptions = Symbol('builtOptions');
/** @public Flags allowed for cursor */

@@ -35,13 +28,13 @@ exports.FLAGS = [

/** @internal */
this[_a] = 0;
this[kFilter] = filter;
this[kBuiltOptions] = options;
this.numReturned = 0;
this.cursorFilter = filter;
this.findOptions = options;
if (options.sort != null) {
this[kBuiltOptions].sort = (0, sort_1.formatSort)(options.sort);
this.findOptions.sort = (0, sort_1.formatSort)(options.sort);
}
}
clone() {
const clonedOptions = (0, utils_1.mergeOptions)({}, this[kBuiltOptions]);
const clonedOptions = (0, utils_1.mergeOptions)({}, this.findOptions);
delete clonedOptions.session;
return new FindCursor(this.client, this.namespace, this[kFilter], {
return new FindCursor(this.client, this.namespace, this.cursorFilter, {
...clonedOptions

@@ -55,4 +48,4 @@ });

async _initialize(session) {
const findOperation = new find_1.FindOperation(this.namespace, this[kFilter], {
...this[kBuiltOptions],
const findOperation = new find_1.FindOperation(this.namespace, this.cursorFilter, {
...this.findOptions,
...this.cursorOptions,

@@ -64,7 +57,7 @@ session

if (responses_1.CursorResponse.is(response)) {
this[kNumReturned] = response.batchSize;
this.numReturned = response.batchSize;
}
else {
// Can be an explain response, hence the ?. on everything
this[kNumReturned] = this[kNumReturned] + (response?.cursor?.firstBatch?.length ?? 0);
this.numReturned = this.numReturned + (response?.cursor?.firstBatch?.length ?? 0);
}

@@ -76,6 +69,6 @@ // TODO: NODE-2882

async getMore(batchSize) {
const numReturned = this[kNumReturned];
const numReturned = this.numReturned;
if (numReturned) {
// TODO(DRIVERS-1448): Remove logic to enforce `limit` in the driver
const limit = this[kBuiltOptions].limit;
const limit = this.findOptions.limit;
batchSize =

@@ -104,6 +97,6 @@ limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize;

if (responses_1.CursorResponse.is(response)) {
this[kNumReturned] = this[kNumReturned] + response.batchSize;
this.numReturned = this.numReturned + response.batchSize;
}
else {
this[kNumReturned] = this[kNumReturned] + (response?.cursor?.nextBatch?.length ?? 0);
this.numReturned = this.numReturned + (response?.cursor?.nextBatch?.length ?? 0);
}

@@ -121,4 +114,4 @@ return response;

}
return await (0, execute_operation_1.executeOperation)(this.client, new count_1.CountOperation(this.namespace, this[kFilter], {
...this[kBuiltOptions],
return await (0, execute_operation_1.executeOperation)(this.client, new count_1.CountOperation(this.namespace, this.cursorFilter, {
...this.findOptions,
...this.cursorOptions,

@@ -130,4 +123,4 @@ ...options

async explain(verbosity) {
return await (0, execute_operation_1.executeOperation)(this.client, new find_1.FindOperation(this.namespace, this[kFilter], {
...this[kBuiltOptions],
return await (0, execute_operation_1.executeOperation)(this.client, new find_1.FindOperation(this.namespace, this.cursorFilter, {
...this.findOptions,
...this.cursorOptions,

@@ -139,4 +132,4 @@ explain: verbosity ?? true

filter(filter) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kFilter] = filter;
this.throwIfInitialized();
this.cursorFilter = filter;
return this;

@@ -150,4 +143,4 @@ }

hint(hint) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].hint = hint;
this.throwIfInitialized();
this.findOptions.hint = hint;
return this;

@@ -161,4 +154,4 @@ }

min(min) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].min = min;
this.throwIfInitialized();
this.findOptions.min = min;
return this;

@@ -172,4 +165,4 @@ }

max(max) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].max = max;
this.throwIfInitialized();
this.findOptions.max = max;
return this;

@@ -185,4 +178,4 @@ }

returnKey(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].returnKey = value;
this.throwIfInitialized();
this.findOptions.returnKey = value;
return this;

@@ -196,4 +189,4 @@ }

showRecordId(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].showRecordId = value;
this.throwIfInitialized();
this.findOptions.showRecordId = value;
return this;

@@ -208,3 +201,3 @@ }

addQueryModifier(name, value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this.throwIfInitialized();
if (name[0] !== '$') {

@@ -218,30 +211,30 @@ throw new error_1.MongoInvalidArgumentError(`${name} is not a valid query modifier`);

case 'comment':
this[kBuiltOptions].comment = value;
this.findOptions.comment = value;
break;
case 'explain':
this[kBuiltOptions].explain = value;
this.findOptions.explain = value;
break;
case 'hint':
this[kBuiltOptions].hint = value;
this.findOptions.hint = value;
break;
case 'max':
this[kBuiltOptions].max = value;
this.findOptions.max = value;
break;
case 'maxTimeMS':
this[kBuiltOptions].maxTimeMS = value;
this.findOptions.maxTimeMS = value;
break;
case 'min':
this[kBuiltOptions].min = value;
this.findOptions.min = value;
break;
case 'orderby':
this[kBuiltOptions].sort = (0, sort_1.formatSort)(value);
this.findOptions.sort = (0, sort_1.formatSort)(value);
break;
case 'query':
this[kFilter] = value;
this.cursorFilter = value;
break;
case 'returnKey':
this[kBuiltOptions].returnKey = value;
this.findOptions.returnKey = value;
break;
case 'showDiskLoc':
this[kBuiltOptions].showRecordId = value;
this.findOptions.showRecordId = value;
break;

@@ -259,4 +252,4 @@ default:

comment(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].comment = value;
this.throwIfInitialized();
this.findOptions.comment = value;
return this;

@@ -270,7 +263,7 @@ }

maxAwaitTimeMS(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this.throwIfInitialized();
if (typeof value !== 'number') {
throw new error_1.MongoInvalidArgumentError('Argument for maxAwaitTimeMS must be a number');
}
this[kBuiltOptions].maxAwaitTimeMS = value;
this.findOptions.maxAwaitTimeMS = value;
return this;

@@ -284,7 +277,7 @@ }

maxTimeMS(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this.throwIfInitialized();
if (typeof value !== 'number') {
throw new error_1.MongoInvalidArgumentError('Argument for maxTimeMS must be a number');
}
this[kBuiltOptions].maxTimeMS = value;
this.findOptions.maxTimeMS = value;
return this;

@@ -333,4 +326,4 @@ }

project(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].projection = value;
this.throwIfInitialized();
this.findOptions.projection = value;
return this;

@@ -345,7 +338,7 @@ }

sort(sort, direction) {
(0, abstract_cursor_1.assertUninitialized)(this);
if (this[kBuiltOptions].tailable) {
this.throwIfInitialized();
if (this.findOptions.tailable) {
throw new error_1.MongoTailableCursorError('Tailable cursor does not support sorting');
}
this[kBuiltOptions].sort = (0, sort_1.formatSort)(sort, direction);
this.findOptions.sort = (0, sort_1.formatSort)(sort, direction);
return this;

@@ -360,4 +353,4 @@ }

allowDiskUse(allow = true) {
(0, abstract_cursor_1.assertUninitialized)(this);
if (!this[kBuiltOptions].sort) {
this.throwIfInitialized();
if (!this.findOptions.sort) {
throw new error_1.MongoInvalidArgumentError('Option "allowDiskUse" requires a sort specification');

@@ -367,6 +360,6 @@ }

if (!allow) {
this[kBuiltOptions].allowDiskUse = false;
this.findOptions.allowDiskUse = false;
return this;
}
this[kBuiltOptions].allowDiskUse = true;
this.findOptions.allowDiskUse = true;
return this;

@@ -380,4 +373,4 @@ }

collation(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
this[kBuiltOptions].collation = value;
this.throwIfInitialized();
this.findOptions.collation = value;
return this;

@@ -391,4 +384,4 @@ }

limit(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
if (this[kBuiltOptions].tailable) {
this.throwIfInitialized();
if (this.findOptions.tailable) {
throw new error_1.MongoTailableCursorError('Tailable cursor does not support limit');

@@ -399,3 +392,3 @@ }

}
this[kBuiltOptions].limit = value;
this.findOptions.limit = value;
return this;

@@ -409,4 +402,4 @@ }

skip(value) {
(0, abstract_cursor_1.assertUninitialized)(this);
if (this[kBuiltOptions].tailable) {
this.throwIfInitialized();
if (this.findOptions.tailable) {
throw new error_1.MongoTailableCursorError('Tailable cursor does not support skip');

@@ -417,3 +410,3 @@ }

}
this[kBuiltOptions].skip = value;
this.findOptions.skip = value;
return this;

@@ -423,3 +416,2 @@ }

exports.FindCursor = FindCursor;
_a = kNumReturned;
//# sourceMappingURL=find_cursor.js.map
{
"name": "mongodb",
"version": "6.7.0-dev.20240607.sha.aa429f8c",
"version": "6.7.0-dev.20240608.sha.0655c730",
"description": "The official MongoDB driver for Node.js",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -6,3 +6,2 @@ import { Readable, Transform } from 'stream';

import {
type AnyError,
MongoAPIError,

@@ -12,3 +11,2 @@ MongoCursorExhaustedError,

MongoInvalidArgumentError,
MongoNetworkError,
MongoRuntimeError,

@@ -28,27 +26,2 @@ MongoTailableCursorError

/** @internal */
const kId = Symbol('id');
/** @internal */
const kDocuments = Symbol('documents');
/** @internal */
const kServer = Symbol('server');
/** @internal */
const kNamespace = Symbol('namespace');
/** @internal */
const kClient = Symbol('client');
/** @internal */
const kSession = Symbol('session');
/** @internal */
const kOptions = Symbol('options');
/** @internal */
const kTransform = Symbol('transform');
/** @internal */
const kInitialized = Symbol('initialized');
/** @internal */
const kClosed = Symbol('closed');
/** @internal */
const kKilled = Symbol('killed');
/** @internal */
const kInit = Symbol('kInit');
/** @public */

@@ -143,11 +116,11 @@ export const CURSOR_FLAGS = [

/** @internal */
[kId]: Long | null;
private cursorId: Long | null;
/** @internal */
[kSession]: ClientSession;
private cursorSession: ClientSession;
/** @internal */
[kServer]?: Server;
private selectedServer?: Server;
/** @internal */
[kNamespace]: MongoDBNamespace;
private cursorNamespace: MongoDBNamespace;
/** @internal */
[kDocuments]: {
private documents: {
length: number;

@@ -160,13 +133,13 @@ shift(bsonOptions?: any): TSchema | null;

/** @internal */
[kClient]: MongoClient;
private cursorClient: MongoClient;
/** @internal */
[kTransform]?: (doc: TSchema) => any;
private transform?: (doc: TSchema) => any;
/** @internal */
[kInitialized]: boolean;
private initialized: boolean;
/** @internal */
[kClosed]: boolean;
private isClosed: boolean;
/** @internal */
[kKilled]: boolean;
private isKilled: boolean;
/** @internal */
[kOptions]: InternalAbstractCursorOptions;
protected readonly cursorOptions: InternalAbstractCursorOptions;

@@ -177,3 +150,3 @@ /** @event */

/** @internal */
constructor(
protected constructor(
client: MongoClient,

@@ -188,10 +161,10 @@ namespace: MongoDBNamespace,

}
this[kClient] = client;
this[kNamespace] = namespace;
this[kId] = null;
this[kDocuments] = new List();
this[kInitialized] = false;
this[kClosed] = false;
this[kKilled] = false;
this[kOptions] = {
this.cursorClient = client;
this.cursorNamespace = namespace;
this.cursorId = null;
this.documents = new List();
this.initialized = false;
this.isClosed = false;
this.isKilled = false;
this.cursorOptions = {
readPreference:

@@ -203,11 +176,11 @@ options.readPreference && options.readPreference instanceof ReadPreference

};
this[kOptions].timeoutMS = options.timeoutMS;
this.cursorOptions.timeoutMS = options.timeoutMS;
const readConcern = ReadConcern.fromOptions(options);
if (readConcern) {
this[kOptions].readConcern = readConcern;
this.cursorOptions.readConcern = readConcern;
}
if (typeof options.batchSize === 'number') {
this[kOptions].batchSize = options.batchSize;
this.cursorOptions.batchSize = options.batchSize;
}

@@ -218,17 +191,17 @@

if (options.comment !== undefined) {
this[kOptions].comment = options.comment;
this.cursorOptions.comment = options.comment;
}
if (typeof options.maxTimeMS === 'number') {
this[kOptions].maxTimeMS = options.maxTimeMS;
this.cursorOptions.maxTimeMS = options.maxTimeMS;
}
if (typeof options.maxAwaitTimeMS === 'number') {
this[kOptions].maxAwaitTimeMS = options.maxAwaitTimeMS;
this.cursorOptions.maxAwaitTimeMS = options.maxAwaitTimeMS;
}
if (options.session instanceof ClientSession) {
this[kSession] = options.session;
this.cursorSession = options.session;
} else {
this[kSession] = this[kClient].startSession({ owner: this, explicit: false });
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
}

@@ -238,3 +211,3 @@ }

get id(): Long | undefined {
return this[kId] ?? undefined;
return this.cursorId ?? undefined;
}

@@ -244,3 +217,3 @@

get isDead() {
return (this[kId]?.isZero() ?? false) || this[kClosed] || this[kKilled];
return (this.cursorId?.isZero() ?? false) || this.isClosed || this.isKilled;
}

@@ -250,3 +223,3 @@

get client(): MongoClient {
return this[kClient];
return this.cursorClient;
}

@@ -256,15 +229,15 @@

get server(): Server | undefined {
return this[kServer];
return this.selectedServer;
}
get namespace(): MongoDBNamespace {
return this[kNamespace];
return this.cursorNamespace;
}
get readPreference(): ReadPreference {
return this[kOptions].readPreference;
return this.cursorOptions.readPreference;
}
get readConcern(): ReadConcern | undefined {
return this[kOptions].readConcern;
return this.cursorOptions.readConcern;
}

@@ -274,24 +247,19 @@

get session(): ClientSession {
return this[kSession];
return this.cursorSession;
}
set session(clientSession: ClientSession) {
this[kSession] = clientSession;
this.cursorSession = clientSession;
}
/** @internal */
get cursorOptions(): InternalAbstractCursorOptions {
return this[kOptions];
}
get closed(): boolean {
return this[kClosed];
return this.isClosed;
}
get killed(): boolean {
return this[kKilled];
return this.isKilled;
}
get loadBalanced(): boolean {
return !!this[kClient].topology?.loadBalanced;
return !!this.cursorClient.topology?.loadBalanced;
}

@@ -301,3 +269,3 @@

bufferedCount(): number {
return this[kDocuments].length;
return this.documents.length;
}

@@ -308,6 +276,6 @@

const bufferedDocs: TSchema[] = [];
const documentsToRead = Math.min(number ?? this[kDocuments].length, this[kDocuments].length);
const documentsToRead = Math.min(number ?? this.documents.length, this.documents.length);
for (let count = 0; count < documentsToRead; count++) {
const document = this[kDocuments].shift(this[kOptions]);
const document = this.documents.shift(this.cursorOptions);
if (document != null) {

@@ -322,3 +290,3 @@ bufferedDocs.push(document);

async *[Symbol.asyncIterator](): AsyncGenerator<TSchema, void, void> {
if (this.closed) {
if (this.isClosed) {
return;

@@ -329,29 +297,22 @@ }

while (true) {
if (this.isKilled) {
return;
}
if (this.isClosed && this.documents.length === 0) {
return;
}
if (this.cursorId != null && this.isDead && this.documents.length === 0) {
return;
}
const document = await this.next();
// Intentional strict null check, because users can map cursors to falsey values.
// We allow mapping to all values except for null.
// eslint-disable-next-line no-restricted-syntax
if (document === null) {
if (!this.closed) {
const message =
'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
try {
await cleanupCursor(this, { needsToEmitClosed: true });
} catch (error) {
squashError(error);
}
throw new MongoAPIError(message);
}
break;
return;
}
yield document;
if (this[kId] === Long.ZERO) {
// Cursor exhausted
break;
}
}

@@ -361,3 +322,3 @@ } finally {

// the case when a user would break out of a for await of loop early.
if (!this.closed) {
if (!this.isClosed) {
try {

@@ -403,11 +364,14 @@ await this.close();

async hasNext(): Promise<boolean> {
if (this[kId] === Long.ZERO) {
if (this.cursorId === Long.ZERO) {
return false;
}
if (this[kDocuments].length !== 0) {
return true;
}
do {
if (this.documents.length !== 0) {
return true;
}
await this.fetchBatch();
} while (!this.isDead || this.documents.length !== 0);
return await next(this, { blocking: true, transform: false, shift: false });
return false;
}

@@ -417,7 +381,16 @@

async next(): Promise<TSchema | null> {
if (this[kId] === Long.ZERO) {
if (this.cursorId === Long.ZERO) {
throw new MongoCursorExhaustedError();
}
return await next(this, { blocking: true, transform: true, shift: true });
do {
const doc = this.documents.shift();
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
}
await this.fetchBatch();
} while (!this.isDead || this.documents.length !== 0);
return null;
}

@@ -429,7 +402,21 @@

async tryNext(): Promise<TSchema | null> {
if (this[kId] === Long.ZERO) {
if (this.cursorId === Long.ZERO) {
throw new MongoCursorExhaustedError();
}
return await next(this, { blocking: false, transform: true, shift: true });
let doc = this.documents.shift();
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
}
await this.fetchBatch();
doc = this.documents.shift();
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
}
return null;
}

@@ -458,5 +445,3 @@

async close(): Promise<void> {
const needsToEmitClosed = !this[kClosed];
this[kClosed] = true;
await cleanupCursor(this, { needsToEmitClosed });
await this.cleanup();
}

@@ -485,3 +470,3 @@

addCursorFlag(flag: CursorFlag, value: boolean): this {
assertUninitialized(this);
this.throwIfInitialized();
if (!CURSOR_FLAGS.includes(flag)) {

@@ -495,3 +480,3 @@ throw new MongoInvalidArgumentError(`Flag ${flag} is not one of ${CURSOR_FLAGS}`);

this[kOptions][flag] = value;
this.cursorOptions[flag] = value;
return this;

@@ -543,10 +528,10 @@ }

map<T = any>(transform: (doc: TSchema) => T): AbstractCursor<T> {
assertUninitialized(this);
const oldTransform = this[kTransform] as (doc: TSchema) => TSchema; // TODO(NODE-3283): Improve transform typing
this.throwIfInitialized();
const oldTransform = this.transform;
if (oldTransform) {
this[kTransform] = doc => {
this.transform = doc => {
return transform(oldTransform(doc));
};
} else {
this[kTransform] = transform;
this.transform = transform;
}

@@ -563,7 +548,7 @@

withReadPreference(readPreference: ReadPreferenceLike): this {
assertUninitialized(this);
this.throwIfInitialized();
if (readPreference instanceof ReadPreference) {
this[kOptions].readPreference = readPreference;
this.cursorOptions.readPreference = readPreference;
} else if (typeof readPreference === 'string') {
this[kOptions].readPreference = ReadPreference.fromString(readPreference);
this.cursorOptions.readPreference = ReadPreference.fromString(readPreference);
} else {

@@ -582,6 +567,6 @@ throw new MongoInvalidArgumentError(`Invalid read preference: ${readPreference}`);

withReadConcern(readConcern: ReadConcernLike): this {
assertUninitialized(this);
this.throwIfInitialized();
const resolvedReadConcern = ReadConcern.fromOptions({ readConcern });
if (resolvedReadConcern) {
this[kOptions].readConcern = resolvedReadConcern;
this.cursorOptions.readConcern = resolvedReadConcern;
}

@@ -598,3 +583,3 @@

maxTimeMS(value: number): this {
assertUninitialized(this);
this.throwIfInitialized();
if (typeof value !== 'number') {

@@ -604,3 +589,3 @@ throw new MongoInvalidArgumentError('Argument for maxTimeMS must be a number');

this[kOptions].maxTimeMS = value;
this.cursorOptions.maxTimeMS = value;
return this;

@@ -615,4 +600,4 @@ }

batchSize(value: number): this {
assertUninitialized(this);
if (this[kOptions].tailable) {
this.throwIfInitialized();
if (this.cursorOptions.tailable) {
throw new MongoTailableCursorError('Tailable cursor does not support batchSize');

@@ -625,3 +610,3 @@ }

this[kOptions].batchSize = value;
this.cursorOptions.batchSize = value;
return this;

@@ -636,13 +621,13 @@ }

rewind(): void {
if (!this[kInitialized]) {
if (!this.initialized) {
return;
}
this[kId] = null;
this[kDocuments].clear();
this[kClosed] = false;
this[kKilled] = false;
this[kInitialized] = false;
this.cursorId = null;
this.documents.clear();
this.isClosed = false;
this.isKilled = false;
this.initialized = false;
const session = this[kSession];
const session = this.cursorSession;
if (session) {

@@ -655,3 +640,3 @@ // We only want to end this session if we created it, and it hasn't ended yet

}
this[kSession] = this.client.startSession({ owner: this, explicit: false });
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
}

@@ -671,11 +656,25 @@ }

async getMore(batchSize: number, useCursorResponse = false): Promise<Document | null> {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const getMoreOperation = new GetMoreOperation(this[kNamespace], this[kId]!, this[kServer]!, {
...this[kOptions],
session: this[kSession],
batchSize,
useCursorResponse
});
if (this.cursorId == null) {
throw new MongoRuntimeError(
'Unexpected null cursor id. A cursor creating command should have set this'
);
}
if (this.selectedServer == null) {
throw new MongoRuntimeError(
'Unexpected null selectedServer. A cursor creating command should have set this'
);
}
const getMoreOperation = new GetMoreOperation(
this.cursorNamespace,
this.cursorId,
this.selectedServer,
{
...this.cursorOptions,
session: this.cursorSession,
batchSize,
useCursorResponse
}
);
return await executeOperation(this[kClient], getMoreOperation);
return await executeOperation(this.cursorClient, getMoreOperation);
}

@@ -690,42 +689,32 @@

*/
async [kInit](): Promise<void> {
private async cursorInit(): Promise<void> {
try {
const state = await this._initialize(this[kSession]);
const state = await this._initialize(this.cursorSession);
const response = state.response;
this[kServer] = state.server;
this.selectedServer = state.server;
if (CursorResponse.is(response)) {
this[kId] = response.id;
if (response.ns) this[kNamespace] = response.ns;
this[kDocuments] = response;
this.cursorId = response.id;
if (response.ns) this.cursorNamespace = response.ns;
this.documents = response;
} else if (response.cursor) {
// TODO(NODE-2674): Preserve int64 sent from MongoDB
this[kId] =
typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? Long.fromBigInt(response.cursor.id)
: response.cursor.id;
if (response.cursor.ns) {
this[kNamespace] = ns(response.cursor.ns);
}
this[kDocuments].pushMany(response.cursor.firstBatch);
this.cursorId = getCursorId(response);
if (response.cursor.ns) this.cursorNamespace = ns(response.cursor.ns);
this.documents.pushMany(response.cursor.firstBatch);
}
// When server responses return without a cursor document, we close this cursor
// and return the raw server response. This is often the case for explain commands
// for example
if (this[kId] == null) {
this[kId] = Long.ZERO;
if (this.cursorId == null) {
// When server responses return without a cursor document, we close this cursor
// and return the raw server response. This is the case for explain commands
this.cursorId = Long.ZERO;
// TODO(NODE-3286): ExecutionResult needs to accept a generic parameter
this[kDocuments].push(state.response as TODO_NODE_3286);
this.documents.push(state.response as TODO_NODE_3286);
}
// the cursor is now initialized, even if it is dead
this[kInitialized] = true;
this.initialized = true;
} catch (error) {
// the cursor is now initialized, even if an error occurred
this[kInitialized] = true;
await cleanupCursor(this, { error });
this.initialized = true;
await this.cleanup(error);
throw error;

@@ -735,3 +724,3 @@ }

if (this.isDead) {
await cleanupCursor(this, undefined);
await this.cleanup();
}

@@ -741,116 +730,42 @@

}
}
/**
* @param cursor - the cursor on which to call `next`
* @param blocking - a boolean indicating whether or not the cursor should `block` until data
* is available. Generally, this flag is set to `false` because if the getMore returns no documents,
* the cursor has been exhausted. In certain scenarios (ChangeStreams, tailable await cursors and
* `tryNext`, for example) blocking is necessary because a getMore returning no documents does
* not indicate the end of the cursor.
* @param transform - if true, the cursor's transform function is applied to the result document (if the transform exists)
* @returns the next document in the cursor, or `null`. When `blocking` is `true`, a `null` document means
* the cursor has been exhausted. Otherwise, it means that there is no document available in the cursor's buffer.
*/
async function next<T>(
cursor: AbstractCursor<T>,
{
blocking,
transform,
shift
}: {
blocking: boolean;
transform: boolean;
shift: false;
}
): Promise<boolean>;
async function next<T>(
cursor: AbstractCursor<T>,
{
blocking,
transform,
shift
}: {
blocking: boolean;
transform: boolean;
shift: true;
}
): Promise<T | null>;
async function next<T>(
cursor: AbstractCursor<T>,
{
blocking,
transform,
shift
}: {
blocking: boolean;
transform: boolean;
shift: boolean;
}
): Promise<boolean | T | null> {
if (cursor.closed) {
if (!shift) return false;
return null;
}
do {
if (cursor[kId] == null) {
// All cursors must operate within a session, one must be made implicitly if not explicitly provided
await cursor[kInit]();
/** @internal Attempt to obtain more documents */
private async fetchBatch(): Promise<void> {
if (this.isClosed) {
return;
}
if (cursor[kDocuments].length !== 0) {
if (!shift) return true;
const doc = cursor[kDocuments].shift(cursor[kOptions]);
if (doc != null && transform && cursor[kTransform]) {
try {
return cursor[kTransform](doc);
} catch (error) {
try {
await cleanupCursor(cursor, { error, needsToEmitClosed: true });
} catch (error) {
// `cleanupCursor` should never throw, squash and throw the original error
squashError(error);
}
throw error;
}
}
return doc;
}
if (cursor.isDead) {
if (this.isDead) {
// if the cursor is dead, we clean it up
// cleanupCursor should never throw, but if it does it indicates a bug in the driver
// and we should surface the error
await cleanupCursor(cursor, {});
if (!shift) return false;
return null;
await this.cleanup();
return;
}
if (this.cursorId == null) {
await this.cursorInit();
// If the cursor died or returned documents, return
if (this.documents.length !== 0 || this.isDead) return;
// Otherwise, run a getMore
}
// otherwise need to call getMore
const batchSize = cursor[kOptions].batchSize || 1000;
const batchSize = this.cursorOptions.batchSize || 1000;
try {
const response = await cursor.getMore(batchSize);
const response = await this.getMore(batchSize);
// CursorResponse is disabled in this PR
// however the special `emptyGetMore` can be returned from find cursors
if (CursorResponse.is(response)) {
cursor[kId] = response.id;
cursor[kDocuments] = response;
} else if (response) {
const cursorId =
typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? Long.fromBigInt(response.cursor.id)
: response.cursor.id;
cursor[kDocuments].pushMany(response.cursor.nextBatch);
cursor[kId] = cursorId;
this.cursorId = response.id;
this.documents = response;
} else if (response?.cursor) {
const cursorId = getCursorId(response);
this.documents.pushMany(response.cursor.nextBatch);
this.cursorId = cursorId;
}
} catch (error) {
try {
await cleanupCursor(cursor, { error, needsToEmitClosed: true });
await this.cleanup(error);
} catch (error) {

@@ -863,3 +778,3 @@ // `cleanupCursor` should never throw, squash and throw the original error

if (cursor.isDead) {
if (this.isDead) {
// If we successfully received a response from a cursor BUT the cursor indicates that it is exhausted,

@@ -872,100 +787,91 @@ // we intentionally clean up the cursor to release its session back into the pool before the cursor

// and we should surface the error
await cleanupCursor(cursor, {});
await this.cleanup();
}
if (cursor[kDocuments].length === 0 && blocking === false) {
if (!shift) return false;
return null;
}
} while (!cursor.isDead || cursor[kDocuments].length !== 0);
if (!shift) return false;
return null;
}
async function cleanupCursor(
cursor: AbstractCursor,
options: { error?: AnyError | undefined; needsToEmitClosed?: boolean } | undefined
): Promise<void> {
const cursorId = cursor[kId];
const cursorNs = cursor[kNamespace];
const server = cursor[kServer];
const session = cursor[kSession];
const error = options?.error;
// Cursors only emit closed events once the client-side cursor has been exhausted fully or there
// was an error. Notably, when the server returns a cursor id of 0 and a non-empty batch, we
// cleanup the cursor but don't emit a `close` event.
const needsToEmitClosed = options?.needsToEmitClosed ?? cursor[kDocuments].length === 0;
if (error) {
if (cursor.loadBalanced && error instanceof MongoNetworkError) {
return await completeCleanup();
}
}
if (cursorId == null || server == null || cursorId.isZero() || cursorNs == null) {
if (needsToEmitClosed) {
cursor[kClosed] = true;
cursor[kId] = Long.ZERO;
cursor.emit(AbstractCursor.CLOSE);
}
if (session) {
if (session.owner === cursor) {
/** @internal */
private async cleanup(error?: Error) {
this.isClosed = true;
const session = this.cursorSession;
try {
if (
!this.isKilled &&
this.cursorId &&
!this.cursorId.isZero() &&
this.cursorNamespace &&
this.selectedServer &&
!session.hasEnded
) {
this.isKilled = true;
await executeOperation(
this.cursorClient,
new KillCursorsOperation(this.cursorId, this.cursorNamespace, this.selectedServer, {
session
})
);
}
} catch (error) {
squashError(error);
} finally {
if (session?.owner === this) {
await session.endSession({ error });
return;
}
if (!session.inTransaction()) {
if (!session?.inTransaction()) {
maybeClearPinnedConnection(session, { error });
}
this.emitClose();
}
return;
}
async function completeCleanup() {
if (session) {
if (session.owner === cursor) {
try {
await session.endSession({ error });
} finally {
cursor.emit(AbstractCursor.CLOSE);
}
return;
/** @internal */
private hasEmittedClose = false;
/** @internal */
private emitClose() {
try {
if (!this.hasEmittedClose && (this.documents.length === 0 || this.isClosed)) {
// @ts-expect-error: CursorEvents is generic so Parameters<CursorEvents["close"]> may not be assignable to `[]`. Not sure how to require extenders do not add parameters.
this.emit('close');
}
if (!session.inTransaction()) {
maybeClearPinnedConnection(session, { error });
}
} finally {
this.hasEmittedClose = true;
}
cursor.emit(AbstractCursor.CLOSE);
return;
}
cursor[kKilled] = true;
/** @internal */
private async transformDocument(document: NonNullable<TSchema>): Promise<TSchema> {
if (this.transform == null) return document;
if (session.hasEnded) {
return await completeCleanup();
try {
const transformedDocument = this.transform(document);
// eslint-disable-next-line no-restricted-syntax
if (transformedDocument === null) {
const TRANSFORM_TO_NULL_ERROR =
'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
throw new MongoAPIError(TRANSFORM_TO_NULL_ERROR);
}
return transformedDocument;
} catch (transformError) {
try {
await this.close();
} catch (closeError) {
squashError(closeError);
}
throw transformError;
}
}
try {
await executeOperation(
cursor[kClient],
new KillCursorsOperation(cursorId, cursorNs, server, { session })
);
} catch (error) {
squashError(error);
} finally {
await completeCleanup();
/** @internal */
protected throwIfInitialized() {
if (this.initialized) throw new MongoCursorInUseError();
}
}
/** @internal */
export function assertUninitialized(cursor: AbstractCursor): void {
if (cursor[kInitialized]) {
throw new MongoCursorInUseError();
}
/** A temporary helper to box up the many possible type issue of cursor ids */
function getCursorId(response: Document) {
return typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? Long.fromBigInt(response.cursor.id)
: response.cursor.id;
}

@@ -1003,4 +909,9 @@

private _readNext() {
if (this._cursor.id === Long.ZERO) {
this.push(null);
return;
}
// eslint-disable-next-line github/no-then
next(this._cursor, { blocking: true, transform: true, shift: true }).then(
this._cursor.next().then(
result => {

@@ -1007,0 +918,0 @@ if (result == null) {

@@ -11,3 +11,3 @@ import type { Document } from '../bson';

import type { AbstractCursorOptions } from './abstract_cursor';
import { AbstractCursor, assertUninitialized } from './abstract_cursor';
import { AbstractCursor } from './abstract_cursor';

@@ -17,7 +17,2 @@ /** @public */

/** @internal */
const kPipeline = Symbol('pipeline');
/** @internal */
const kOptions = Symbol('options');
/**

@@ -31,6 +26,5 @@ * The **AggregationCursor** class is an internal class that embodies an aggregation cursor on MongoDB

export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
public readonly pipeline: Document[];
/** @internal */
[kPipeline]: Document[];
/** @internal */
[kOptions]: AggregateOptions;
private aggregateOptions: AggregateOptions;

@@ -46,14 +40,10 @@ /** @internal */

this[kPipeline] = pipeline;
this[kOptions] = options;
this.pipeline = pipeline;
this.aggregateOptions = options;
}
get pipeline(): Document[] {
return this[kPipeline];
}
clone(): AggregationCursor<TSchema> {
const clonedOptions = mergeOptions({}, this[kOptions]);
const clonedOptions = mergeOptions({}, this.aggregateOptions);
delete clonedOptions.session;
return new AggregationCursor(this.client, this.namespace, this[kPipeline], {
return new AggregationCursor(this.client, this.namespace, this.pipeline, {
...clonedOptions

@@ -69,4 +59,4 @@ });

async _initialize(session: ClientSession): Promise<ExecutionResult> {
const aggregateOperation = new AggregateOperation(this.namespace, this[kPipeline], {
...this[kOptions],
const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, {
...this.aggregateOptions,
...this.cursorOptions,

@@ -86,4 +76,4 @@ session

this.client,
new AggregateOperation(this.namespace, this[kPipeline], {
...this[kOptions], // NOTE: order matters here, we may need to refine this
new AggregateOperation(this.namespace, this.pipeline, {
...this.aggregateOptions, // NOTE: order matters here, we may need to refine this
...this.cursorOptions,

@@ -110,4 +100,4 @@ explain: verbosity ?? true

addStage<T = Document>(stage: Document): AggregationCursor<T> {
assertUninitialized(this);
this[kPipeline].push(stage);
this.throwIfInitialized();
this.pipeline.push(stage);
return this as unknown as AggregationCursor<T>;

@@ -114,0 +104,0 @@ }

@@ -46,12 +46,9 @@ import type { Document, Long, Timestamp } from '../bson';

> extends AbstractCursor<TChange, ChangeStreamEvents> {
_resumeToken: ResumeToken;
startAtOperationTime?: OperationTime;
hasReceived?: boolean;
resumeAfter: ResumeToken;
startAfter: ResumeToken;
options: ChangeStreamCursorOptions;
private _resumeToken: ResumeToken;
private startAtOperationTime?: OperationTime;
private hasReceived?: boolean;
private readonly changeStreamCursorOptions: ChangeStreamCursorOptions;
private postBatchResumeToken?: ResumeToken;
private readonly pipeline: Document[];
postBatchResumeToken?: ResumeToken;
pipeline: Document[];
/**

@@ -73,3 +70,3 @@ * @internal

this.pipeline = pipeline;
this.options = options;
this.changeStreamCursorOptions = options;
this._resumeToken = null;

@@ -96,3 +93,3 @@ this.startAtOperationTime = options.startAtOperationTime;

const options: ChangeStreamCursorOptions = {
...this.options
...this.changeStreamCursorOptions
};

@@ -105,3 +102,3 @@

if (this.resumeToken != null) {
if (this.options.startAfter && !this.hasReceived) {
if (this.changeStreamCursorOptions.startAfter && !this.hasReceived) {
options.startAfter = this.resumeToken;

@@ -149,3 +146,3 @@ } else {

...this.cursorOptions,
...this.options,
...this.changeStreamCursorOptions,
session

@@ -164,4 +161,4 @@ });

this.startAtOperationTime == null &&
this.resumeAfter == null &&
this.startAfter == null &&
this.changeStreamCursorOptions.resumeAfter == null &&
this.changeStreamCursorOptions.startAfter == null &&
this.maxWireVersion >= 7

@@ -168,0 +165,0 @@ ) {

@@ -14,11 +14,4 @@ import { type Document } from '../bson';

import { emitWarningOnce, mergeOptions, type MongoDBNamespace, squashError } from '../utils';
import { AbstractCursor, assertUninitialized } from './abstract_cursor';
import { AbstractCursor } from './abstract_cursor';
/** @internal */
const kFilter = Symbol('filter');
/** @internal */
const kNumReturned = Symbol('numReturned');
/** @internal */
const kBuiltOptions = Symbol('builtOptions');
/** @public Flags allowed for cursor */

@@ -37,7 +30,7 @@ export const FLAGS = [

/** @internal */
[kFilter]: Document;
private cursorFilter: Document;
/** @internal */
[kNumReturned] = 0;
private numReturned = 0;
/** @internal */
[kBuiltOptions]: FindOptions;
private readonly findOptions: FindOptions;

@@ -53,7 +46,7 @@ /** @internal */

this[kFilter] = filter;
this[kBuiltOptions] = options;
this.cursorFilter = filter;
this.findOptions = options;
if (options.sort != null) {
this[kBuiltOptions].sort = formatSort(options.sort);
this.findOptions.sort = formatSort(options.sort);
}

@@ -63,5 +56,5 @@ }

clone(): FindCursor<TSchema> {
const clonedOptions = mergeOptions({}, this[kBuiltOptions]);
const clonedOptions = mergeOptions({}, this.findOptions);
delete clonedOptions.session;
return new FindCursor(this.client, this.namespace, this[kFilter], {
return new FindCursor(this.client, this.namespace, this.cursorFilter, {
...clonedOptions

@@ -77,4 +70,4 @@ });

async _initialize(session: ClientSession): Promise<ExecutionResult> {
const findOperation = new FindOperation(this.namespace, this[kFilter], {
...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this
const findOperation = new FindOperation(this.namespace, this.cursorFilter, {
...this.findOptions, // NOTE: order matters here, we may need to refine this
...this.cursorOptions,

@@ -88,6 +81,6 @@ session

if (CursorResponse.is(response)) {
this[kNumReturned] = response.batchSize;
this.numReturned = response.batchSize;
} else {
// Can be an explain response, hence the ?. on everything
this[kNumReturned] = this[kNumReturned] + (response?.cursor?.firstBatch?.length ?? 0);
this.numReturned = this.numReturned + (response?.cursor?.firstBatch?.length ?? 0);
}

@@ -101,6 +94,6 @@

override async getMore(batchSize: number): Promise<Document | null> {
const numReturned = this[kNumReturned];
const numReturned = this.numReturned;
if (numReturned) {
// TODO(DRIVERS-1448): Remove logic to enforce `limit` in the driver
const limit = this[kBuiltOptions].limit;
const limit = this.findOptions.limit;
batchSize =

@@ -130,5 +123,5 @@ limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize;

if (CursorResponse.is(response)) {
this[kNumReturned] = this[kNumReturned] + response.batchSize;
this.numReturned = this.numReturned + response.batchSize;
} else {
this[kNumReturned] = this[kNumReturned] + (response?.cursor?.nextBatch?.length ?? 0);
this.numReturned = this.numReturned + (response?.cursor?.nextBatch?.length ?? 0);
}

@@ -152,4 +145,4 @@

this.client,
new CountOperation(this.namespace, this[kFilter], {
...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this
new CountOperation(this.namespace, this.cursorFilter, {
...this.findOptions, // NOTE: order matters here, we may need to refine this
...this.cursorOptions,

@@ -165,4 +158,4 @@ ...options

this.client,
new FindOperation(this.namespace, this[kFilter], {
...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this
new FindOperation(this.namespace, this.cursorFilter, {
...this.findOptions, // NOTE: order matters here, we may need to refine this
...this.cursorOptions,

@@ -176,4 +169,4 @@ explain: verbosity ?? true

filter(filter: Document): this {
assertUninitialized(this);
this[kFilter] = filter;
this.throwIfInitialized();
this.cursorFilter = filter;
return this;

@@ -188,4 +181,4 @@ }

hint(hint: Hint): this {
assertUninitialized(this);
this[kBuiltOptions].hint = hint;
this.throwIfInitialized();
this.findOptions.hint = hint;
return this;

@@ -200,4 +193,4 @@ }

min(min: Document): this {
assertUninitialized(this);
this[kBuiltOptions].min = min;
this.throwIfInitialized();
this.findOptions.min = min;
return this;

@@ -212,4 +205,4 @@ }

max(max: Document): this {
assertUninitialized(this);
this[kBuiltOptions].max = max;
this.throwIfInitialized();
this.findOptions.max = max;
return this;

@@ -226,4 +219,4 @@ }

returnKey(value: boolean): this {
assertUninitialized(this);
this[kBuiltOptions].returnKey = value;
this.throwIfInitialized();
this.findOptions.returnKey = value;
return this;

@@ -238,4 +231,4 @@ }

showRecordId(value: boolean): this {
assertUninitialized(this);
this[kBuiltOptions].showRecordId = value;
this.throwIfInitialized();
this.findOptions.showRecordId = value;
return this;

@@ -251,3 +244,3 @@ }

addQueryModifier(name: string, value: string | boolean | number | Document): this {
assertUninitialized(this);
this.throwIfInitialized();
if (name[0] !== '$') {

@@ -263,39 +256,39 @@ throw new MongoInvalidArgumentError(`${name} is not a valid query modifier`);

case 'comment':
this[kBuiltOptions].comment = value as string | Document;
this.findOptions.comment = value as string | Document;
break;
case 'explain':
this[kBuiltOptions].explain = value as boolean;
this.findOptions.explain = value as boolean;
break;
case 'hint':
this[kBuiltOptions].hint = value as string | Document;
this.findOptions.hint = value as string | Document;
break;
case 'max':
this[kBuiltOptions].max = value as Document;
this.findOptions.max = value as Document;
break;
case 'maxTimeMS':
this[kBuiltOptions].maxTimeMS = value as number;
this.findOptions.maxTimeMS = value as number;
break;
case 'min':
this[kBuiltOptions].min = value as Document;
this.findOptions.min = value as Document;
break;
case 'orderby':
this[kBuiltOptions].sort = formatSort(value as string | Document);
this.findOptions.sort = formatSort(value as string | Document);
break;
case 'query':
this[kFilter] = value as Document;
this.cursorFilter = value as Document;
break;
case 'returnKey':
this[kBuiltOptions].returnKey = value as boolean;
this.findOptions.returnKey = value as boolean;
break;
case 'showDiskLoc':
this[kBuiltOptions].showRecordId = value as boolean;
this.findOptions.showRecordId = value as boolean;
break;

@@ -316,4 +309,4 @@

comment(value: string): this {
assertUninitialized(this);
this[kBuiltOptions].comment = value;
this.throwIfInitialized();
this.findOptions.comment = value;
return this;

@@ -328,3 +321,3 @@ }

maxAwaitTimeMS(value: number): this {
assertUninitialized(this);
this.throwIfInitialized();
if (typeof value !== 'number') {

@@ -334,3 +327,3 @@ throw new MongoInvalidArgumentError('Argument for maxAwaitTimeMS must be a number');

this[kBuiltOptions].maxAwaitTimeMS = value;
this.findOptions.maxAwaitTimeMS = value;
return this;

@@ -345,3 +338,3 @@ }

override maxTimeMS(value: number): this {
assertUninitialized(this);
this.throwIfInitialized();
if (typeof value !== 'number') {

@@ -351,3 +344,3 @@ throw new MongoInvalidArgumentError('Argument for maxTimeMS must be a number');

this[kBuiltOptions].maxTimeMS = value;
this.findOptions.maxTimeMS = value;
return this;

@@ -397,4 +390,4 @@ }

project<T extends Document = Document>(value: Document): FindCursor<T> {
assertUninitialized(this);
this[kBuiltOptions].projection = value;
this.throwIfInitialized();
this.findOptions.projection = value;
return this as unknown as FindCursor<T>;

@@ -410,8 +403,8 @@ }

sort(sort: Sort | string, direction?: SortDirection): this {
assertUninitialized(this);
if (this[kBuiltOptions].tailable) {
this.throwIfInitialized();
if (this.findOptions.tailable) {
throw new MongoTailableCursorError('Tailable cursor does not support sorting');
}
this[kBuiltOptions].sort = formatSort(sort, direction);
this.findOptions.sort = formatSort(sort, direction);
return this;

@@ -427,5 +420,5 @@ }

allowDiskUse(allow = true): this {
assertUninitialized(this);
this.throwIfInitialized();
if (!this[kBuiltOptions].sort) {
if (!this.findOptions.sort) {
throw new MongoInvalidArgumentError('Option "allowDiskUse" requires a sort specification');

@@ -436,7 +429,7 @@ }

if (!allow) {
this[kBuiltOptions].allowDiskUse = false;
this.findOptions.allowDiskUse = false;
return this;
}
this[kBuiltOptions].allowDiskUse = true;
this.findOptions.allowDiskUse = true;
return this;

@@ -451,4 +444,4 @@ }

collation(value: CollationOptions): this {
assertUninitialized(this);
this[kBuiltOptions].collation = value;
this.throwIfInitialized();
this.findOptions.collation = value;
return this;

@@ -463,4 +456,4 @@ }

limit(value: number): this {
assertUninitialized(this);
if (this[kBuiltOptions].tailable) {
this.throwIfInitialized();
if (this.findOptions.tailable) {
throw new MongoTailableCursorError('Tailable cursor does not support limit');

@@ -473,3 +466,3 @@ }

this[kBuiltOptions].limit = value;
this.findOptions.limit = value;
return this;

@@ -484,4 +477,4 @@ }

skip(value: number): this {
assertUninitialized(this);
if (this[kBuiltOptions].tailable) {
this.throwIfInitialized();
if (this.findOptions.tailable) {
throw new MongoTailableCursorError('Tailable cursor does not support skip');

@@ -494,5 +487,5 @@ }

this[kBuiltOptions].skip = value;
this.findOptions.skip = value;
return this;
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc