Socket
Socket
Sign inDemoInstall

mongo-cursor-pagination

Package Overview
Dependencies
9
Maintainers
24
Versions
33
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 8.0.1 to 8.1.0

7

CHANGELOG.md

@@ -0,1 +1,8 @@

## [8.1.0](https://github.com/mixmaxhq/mongo-cursor-pagination/compare/v8.0.1...v8.1.0) (2022-08-25)
### Features
* update babel to v7 ([6a86084](https://github.com/mixmaxhq/mongo-cursor-pagination/commit/6a86084253a5b950e2549df3cb537e8c5eaef7c5))
### [8.0.1](https://github.com/mixmaxhq/mongo-cursor-pagination/compare/v8.0.0...v8.0.1) (2022-08-24)

@@ -2,0 +9,0 @@

101

dist/node/aggregate.js

@@ -1,8 +0,14 @@

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
"use strict";
const _ = require('underscore');
const sanitizeParams = require('./utils/sanitizeParams');
const { prepareResponse, generateSort, generateCursorQuery } = require('./utils/query');
const {
prepareResponse,
generateSort,
generateCursorQuery
} = require('./utils/query');
const config = require('./config');
/**

@@ -45,45 +51,60 @@ * Performs an aggregate() query on a passed-in Mongo collection, using criteria you specify.

*/
module.exports = (() => {
var _ref = _asyncToGenerator(function* (collection, params) {
params = _.defaults((yield sanitizeParams(collection, params)), { aggregation: [] });
const $match = generateCursorQuery(params);
const $sort = generateSort(params);
const $limit = params.limit + 1;
let aggregation;
if (params.sortCaseInsensitive) {
aggregation = params.aggregation.concat([{ $addFields: { __lc: { $toLower: '$' + params.paginatedField } } }, { $match }, { $sort }, { $limit }, { $project: { __lc: 0 } }]);
} else {
aggregation = params.aggregation.concat([{ $match }, { $sort }, { $limit }]);
}
module.exports = async function aggregate(collection, params) {
params = _.defaults(await sanitizeParams(collection, params), {
aggregation: []
});
const $match = generateCursorQuery(params);
const $sort = generateSort(params);
const $limit = params.limit + 1;
let aggregation;
// Aggregation options:
// https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#aggregate
// https://mongodb.github.io/node-mongodb-native/4.0/interfaces/aggregateoptions.html
const options = Object.assign({}, params.options);
/**
* IMPORTANT
*
* If using collation, check the README:
* https://github.com/mixmaxhq/mongo-cursor-pagination#important-note-regarding-collation
*/
const isCollationNull = params.collation === null;
const collation = params.collation || config.COLLATION;
if (collation && !isCollationNull) options.collation = collation;
if (params.sortCaseInsensitive) {
aggregation = params.aggregation.concat([{
$addFields: {
__lc: {
$toLower: '$' + params.paginatedField
}
}
}, {
$match
}, {
$sort
}, {
$limit
}, {
$project: {
__lc: 0
}
}]);
} else {
aggregation = params.aggregation.concat([{
$match
}, {
$sort
}, {
$limit
}]);
} // Aggregation options:
// https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#aggregate
// https://mongodb.github.io/node-mongodb-native/4.0/interfaces/aggregateoptions.html
// Support both the native 'mongodb' driver and 'mongoist'. See:
// https://www.npmjs.com/package/mongoist#cursor-operations
const aggregateMethod = collection.aggregateAsCursor ? 'aggregateAsCursor' : 'aggregate';
const results = yield collection[aggregateMethod](aggregation, options).toArray();
const options = Object.assign({}, params.options);
/**
* IMPORTANT
*
* If using collation, check the README:
* https://github.com/mixmaxhq/mongo-cursor-pagination#important-note-regarding-collation
*/
return prepareResponse(results, params);
});
const isCollationNull = params.collation === null;
const collation = params.collation || config.COLLATION;
if (collation && !isCollationNull) options.collation = collation; // Support both the native 'mongodb' driver and 'mongoist'. See:
// https://www.npmjs.com/package/mongoist#cursor-operations
function aggregate(_x, _x2) {
return _ref.apply(this, arguments);
}
return aggregate;
})();
const aggregateMethod = collection.aggregateAsCursor ? 'aggregateAsCursor' : 'aggregate';
const results = await collection[aggregateMethod](aggregation, options).toArray();
return prepareResponse(results, params);
};

@@ -0,1 +1,3 @@

"use strict";
module.exports = {

@@ -2,0 +4,0 @@ /**

@@ -1,9 +0,16 @@

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
"use strict";
const _ = require('underscore');
const sanitizeParams = require('./utils/sanitizeParams');
const { prepareResponse, generateSort, generateCursorQuery } = require('./utils/query');
const {
prepareResponse,
generateSort,
generateCursorQuery
} = require('./utils/query');
const aggregate = require('./aggregate');
const config = require('./config');
/**

@@ -37,55 +44,51 @@ * Performs a find() query on a passed-in Mongo collection, using criteria you specify. The results

*/
module.exports = (() => {
var _ref = _asyncToGenerator(function* (collection, params) {
const removePaginatedFieldInResponse = params.fields && !params.fields[params.paginatedField || '_id'];
let response;
if (params.sortCaseInsensitive) {
// For case-insensitive sorting, we need to work with an aggregation:
response = aggregate(collection, Object.assign({}, params, {
aggregation: params.query ? [{ $match: params.query }] : []
}));
} else {
// Need to repeat `params.paginatedField` default value ('_id') since it's set in 'sanitizeParams()'
params = _.defaults((yield sanitizeParams(collection, params)), { query: {} });
const cursorQuery = generateCursorQuery(params);
const $sort = generateSort(params);
module.exports = async function (collection, params) {
const removePaginatedFieldInResponse = params.fields && !params.fields[params.paginatedField || '_id'];
let response;
// Support both the native 'mongodb' driver and 'mongoist'. See:
// https://www.npmjs.com/package/mongoist#cursor-operations
const findMethod = collection.findAsCursor ? 'findAsCursor' : 'find';
if (params.sortCaseInsensitive) {
// For case-insensitive sorting, we need to work with an aggregation:
response = aggregate(collection, Object.assign({}, params, {
aggregation: params.query ? [{
$match: params.query
}] : []
}));
} else {
// Need to repeat `params.paginatedField` default value ('_id') since it's set in 'sanitizeParams()'
params = _.defaults(await sanitizeParams(collection, params), {
query: {}
});
const cursorQuery = generateCursorQuery(params);
const $sort = generateSort(params); // Support both the native 'mongodb' driver and 'mongoist'. See:
// https://www.npmjs.com/package/mongoist#cursor-operations
const query = collection[findMethod]({ $and: [cursorQuery, params.query] }, params.fields);
const findMethod = collection.findAsCursor ? 'findAsCursor' : 'find';
const query = collection[findMethod]({
$and: [cursorQuery, params.query]
}, params.fields);
/**
* IMPORTANT
*
* If using collation, check the README:
* https://github.com/mixmaxhq/mongo-cursor-pagination#important-note-regarding-collation
*/
/**
* IMPORTANT
*
* If using collation, check the README:
* https://github.com/mixmaxhq/mongo-cursor-pagination#important-note-regarding-collation
*/
const isCollationNull = params.collation === null;
const collation = params.collation || config.COLLATION;
const collatedQuery = collation && !isCollationNull ? query.collation(collation) : query;
// Query one more element to see if there's another page.
const cursor = collatedQuery.sort($sort).limit(params.limit + 1);
if (params.hint) cursor.hint(params.hint);
const results = yield cursor.toArray();
const isCollationNull = params.collation === null;
const collation = params.collation || config.COLLATION;
const collatedQuery = collation && !isCollationNull ? query.collation(collation) : query; // Query one more element to see if there's another page.
response = prepareResponse(results, params);
}
const cursor = collatedQuery.sort($sort).limit(params.limit + 1);
if (params.hint) cursor.hint(params.hint);
const results = await cursor.toArray();
response = prepareResponse(results, params);
} // Remove fields that we added to the query (such as paginatedField and _id) that the user didn't ask for.
// Remove fields that we added to the query (such as paginatedField and _id) that the user didn't ask for.
if (removePaginatedFieldInResponse) {
response.results = _.map(response.results, function (result) {
return _.omit(result, params.paginatedField);
});
}
return response;
});
if (removePaginatedFieldInResponse) {
response.results = _.map(response.results, result => _.omit(result, params.paginatedField));
}
return function (_x, _x2) {
return _ref.apply(this, arguments);
};
})();
return response;
};

@@ -1,6 +0,6 @@

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
"use strict";
const find = require('./find');
const sanitizeQuery = require('./utils/sanitizeQuery');
/**

@@ -26,14 +26,7 @@ * A wrapper around `find()` that make it easy to implement a basic HTTP API using Express. So your

*/
module.exports = (() => {
var _ref = _asyncToGenerator(function* (req, collection, params) {
params = sanitizeQuery(req.query, params);
return find(collection, params);
});
function findWithReq(_x, _x2, _x3) {
return _ref.apply(this, arguments);
}
return findWithReq;
})();
module.exports = async function findWithReq(req, collection, params) {
params = sanitizeQuery(req.query, params);
return find(collection, params);
};

@@ -0,8 +1,19 @@

"use strict";
const config = require('./config');
const aggregate = require('./aggregate');
const find = require('./find');
const findWithReq = require('./findWithReq');
const search = require('./search');
const sanitizeQuery = require('./utils/sanitizeQuery');
const { encodePaginationTokens } = require('./utils/query');
const {
encodePaginationTokens
} = require('./utils/query');
const mongoosePlugin = require('./mongoose.plugin');

@@ -9,0 +20,0 @@

@@ -0,5 +1,8 @@

"use strict";
const find = require('./find');
const search = require('./search');
const _ = require('underscore');
/**

@@ -13,2 +16,3 @@ * Mongoose plugin

module.exports = function (schema, options) {

@@ -25,6 +29,4 @@ /**

params = _.extend({}, params);
return find(this.collection, params);
};
/**

@@ -35,2 +37,4 @@ * search function

*/
const searchFn = function (searchString, params) {

@@ -42,3 +46,2 @@ if (!this.collection) {

params = _.extend({}, params);
return search(this.collection, searchString, params);

@@ -45,0 +48,0 @@ };

@@ -1,7 +0,8 @@

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
"use strict";
const _ = require('underscore');
const config = require('./config');
const bsonUrlEncoding = require('./utils/bsonUrlEncoding');
/**

@@ -26,87 +27,77 @@ * Performs a search query on a Mongo collection and pages the results. This is different from

*/
module.exports = (() => {
var _ref = _asyncToGenerator(function* (collection, searchString, params) {
if (_.isString(params.limit)) params.limit = parseInt(params.limit, 10);
if (params.next) params.next = bsonUrlEncoding.decode(params.next);
params = _.defaults(params, {
query: {},
limit: config.MAX_LIMIT
});
if (params.limit < 1) params.limit = 1;
if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT;
module.exports = async function (collection, searchString, params) {
if (_.isString(params.limit)) params.limit = parseInt(params.limit, 10);
if (params.next) params.next = bsonUrlEncoding.decode(params.next);
params = _.defaults(params, {
query: {},
limit: config.MAX_LIMIT
});
if (params.limit < 1) params.limit = 1;
if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT; // We must perform an aggregate query since Mongo can't query a range when using $text search.
// We must perform an aggregate query since Mongo can't query a range when using $text search.
const aggregate = [{
$match: _.extend({}, params.query, {
$text: {
$search: searchString
}
})
}, {
$project: _.extend({}, params.fields, {
_id: 1,
score: {
$meta: 'textScore'
}
})
}, {
$sort: {
score: {
$meta: 'textScore'
},
_id: -1
const aggregate = [{
$match: _.extend({}, params.query, {
$text: {
$search: searchString
}
}];
if (params.next) {
aggregate.push({
$match: {
$or: [{
score: {
$lt: params.next[0]
}
}, {
score: {
$eq: params.next[0]
},
_id: {
$lt: params.next[1]
}
}]
}
});
})
}, {
$project: _.extend({}, params.fields, {
_id: 1,
score: {
$meta: 'textScore'
}
})
}, {
$sort: {
score: {
$meta: 'textScore'
},
_id: -1
}
}];
if (params.next) {
aggregate.push({
$limit: params.limit
$match: {
$or: [{
score: {
$lt: params.next[0]
}
}, {
score: {
$eq: params.next[0]
},
_id: {
$lt: params.next[1]
}
}]
}
});
}
let response;
aggregate.push({
$limit: params.limit
});
let response; // Support both the native 'mongodb' driver and 'mongoist'. See:
// https://www.npmjs.com/package/mongoist#cursor-operations
// Support both the native 'mongodb' driver and 'mongoist'. See:
// https://www.npmjs.com/package/mongoist#cursor-operations
const aggregateMethod = collection.aggregateAsCursor ? 'aggregateAsCursor' : 'aggregate';
const aggregateMethod = collection.aggregateAsCursor ? 'aggregateAsCursor' : 'aggregate';
const results = await collection[aggregateMethod](aggregate).toArray();
const fullPageOfResults = results.length === params.limit;
const results = yield collection[aggregateMethod](aggregate).toArray();
if (fullPageOfResults) {
response = {
results,
next: bsonUrlEncoding.encode([_.last(results).score, _.last(results)._id])
};
} else {
response = {
results
};
}
const fullPageOfResults = results.length === params.limit;
if (fullPageOfResults) {
response = {
results,
next: bsonUrlEncoding.encode([_.last(results).score, _.last(results)._id])
};
} else {
response = {
results
};
}
return response;
});
return function (_x, _x2, _x3) {
return _ref.apply(this, arguments);
};
})();
return response;
};

@@ -1,4 +0,8 @@

const { EJSON } = require('bson');
"use strict";
const {
EJSON
} = require('bson');
const base64url = require('base64-url');
/**

@@ -9,2 +13,3 @@ * These will take a BSON object (an database result returned by the MongoDB library) and

module.exports.encode = function (obj) {

@@ -11,0 +16,0 @@ return base64url.encode(EJSON.stringify(obj));

@@ -0,9 +1,12 @@

"use strict";
module.exports = function getPropertyViaDotNotation(propertyName, object) {
const parts = propertyName.split('.');
let prop = object;
let prop = object;
for (let i = 0; i < parts.length; i++) {
prop = prop[parts[i]];
}
return prop;
};

@@ -0,4 +1,6 @@

"use strict";
const bsonUrlEncoding = require('./bsonUrlEncoding');
const objectPath = require('object-path');
/**

@@ -19,2 +21,4 @@ * Helper function to encode pagination tokens.

*/
function encodePaginationTokens(params, response) {

@@ -26,2 +30,3 @@ const shouldSecondarySortOnId = params.paginatedField !== '_id';

if (params.sortCaseInsensitive) previousPaginatedField = previousPaginatedField.toLowerCase();
if (shouldSecondarySortOnId) {

@@ -33,5 +38,7 @@ response.previous = bsonUrlEncoding.encode([previousPaginatedField, response.previous._id]);

}
if (response.next) {
let nextPaginatedField = objectPath.get(response.next, params.paginatedField);
if (params.sortCaseInsensitive) nextPaginatedField = nextPaginatedField.toLowerCase();
if (shouldSecondarySortOnId) {

@@ -56,12 +63,9 @@ response.next = bsonUrlEncoding.encode([nextPaginatedField, response.next._id]);

prepareResponse(results, params) {
const hasMore = results.length > params.limit;
// Remove the extra element that we added to 'peek' to see if there were more entries.
const hasMore = results.length > params.limit; // Remove the extra element that we added to 'peek' to see if there were more entries.
if (hasMore) results.pop();
const hasPrevious = !!params.next || !!(params.previous && hasMore);
const hasNext = !!params.previous || hasMore;
const hasNext = !!params.previous || hasMore; // If we sorted reverse to get the previous page, correct the sort order.
// If we sorted reverse to get the previous page, correct the sort order.
if (params.previous) results = results.reverse();
const response = {

@@ -74,5 +78,3 @@ results,

};
encodePaginationTokens(params, response);
return response;

@@ -116,7 +118,5 @@ },

if (!params.next && !params.previous) return {};
const sortAsc = !params.sortAscending && params.previous || params.sortAscending && !params.previous;
const comparisonOp = sortAsc ? '$gt' : '$lt';
const comparisonOp = sortAsc ? '$gt' : '$lt'; // a `next` cursor will have precedence over a `previous` cursor.
// a `next` cursor will have precedence over a `previous` cursor.
const op = params.next || params.previous;

@@ -148,2 +148,3 @@

}
};

@@ -0,4 +1,8 @@

"use strict";
const _ = require('underscore');
const { ProjectionFieldSet } = require('projection-utils');
const {
ProjectionFieldSet
} = require('projection-utils');
/**

@@ -12,2 +16,4 @@ * Produce a ProjectionFieldSet from the given mongo projection, after validating it to ensure it

*/
function fieldsFromMongo(projection = {}, includeIdDefault = false) {

@@ -18,2 +24,3 @@ const fields = _.reduce(projection, (memo, value, key) => {

}
if (value || key === '_id' && value === undefined && includeIdDefault) {

@@ -28,3 +35,2 @@ memo.push(key);

}
/**

@@ -41,2 +47,4 @@ * Resolve the fields object, given potentially untrusted fields the user has provided, permitted

*/
function resolveFields(desiredFields, allowedFields, overrideFields) {

@@ -53,13 +61,11 @@ if (desiredFields != null && !Array.isArray(desiredFields)) {

throw new TypeError('expected optional plain object for overrideFields');
}
} // If no desired fields are specified, we treat that as wanting the default set of fields.
// If no desired fields are specified, we treat that as wanting the default set of fields.
const desiredFieldset = _.isEmpty(desiredFields) ? new ProjectionFieldSet([[]]) : ProjectionFieldSet.fromDotted(desiredFields);
// If allowedFields isn't provided, we treat that as not having restrictions. However, if it's an
const desiredFieldset = _.isEmpty(desiredFields) ? new ProjectionFieldSet([[]]) : ProjectionFieldSet.fromDotted(desiredFields); // If allowedFields isn't provided, we treat that as not having restrictions. However, if it's an
// empty array, we treat that as have no valid fields.
const allowedFieldset = allowedFields ? fieldsFromMongo(allowedFields) : new ProjectionFieldSet([[]]);
// Don't trust fields passed in the querystring, so whitelist them against the
const allowedFieldset = allowedFields ? fieldsFromMongo(allowedFields) : new ProjectionFieldSet([[]]); // Don't trust fields passed in the querystring, so whitelist them against the
// fields defined in parameters. Add override fields from parameters.
const fields = desiredFieldset.intersect(allowedFieldset).union(fieldsFromMongo(overrideFields));

@@ -71,12 +77,10 @@

return null;
}
} // Generate the mongo projection.
// Generate the mongo projection.
const projection = fields.toMongo();
// Whether overrideFields explicitly removes _id.
const disableIdOverride = overrideFields && overrideFields._id !== undefined && !overrideFields._id;
const projection = fields.toMongo(); // Whether overrideFields explicitly removes _id.
// Explicitly exclude the _id field (which mongo includes by default) if we don't allow it, or
const disableIdOverride = overrideFields && overrideFields._id !== undefined && !overrideFields._id; // Explicitly exclude the _id field (which mongo includes by default) if we don't allow it, or
// if we've disabled it in the override.
if (!fields.contains(['_id']) || disableIdOverride) {

@@ -88,2 +92,3 @@ // If the override excludes _id, then enforce that here. All other fields will be included by

}
return projection;

@@ -90,0 +95,0 @@ }

@@ -1,89 +0,93 @@

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
"use strict";
const _ = require('underscore');
const bsonUrlEncoding = require('./bsonUrlEncoding');
const getPropertyViaDotNotation = require('./getPropertyViaDotNotation');
const config = require('../config');
module.exports = (() => {
var _ref = _asyncToGenerator(function* (collection, params) {
if (params.previous) params.previous = bsonUrlEncoding.decode(params.previous);
if (params.next) params.next = bsonUrlEncoding.decode(params.next);
module.exports = async function sanitizeParams(collection, params) {
if (params.previous) params.previous = bsonUrlEncoding.decode(params.previous);
if (params.next) params.next = bsonUrlEncoding.decode(params.next);
params = _.defaults(params, {
limit: config.DEFAULT_LIMIT,
paginatedField: '_id'
});
if (params.limit < 1) params.limit = 1;
if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT; // If the paginated field is not _id, then it might have duplicate values in it. This is bad
// because then we can't exclusively use it for our range queries (that use $lt and $gt). So
// to fix this, we secondarily sort on _id, which is always unique.
params = _.defaults(params, {
limit: config.DEFAULT_LIMIT,
paginatedField: '_id'
});
const shouldSecondarySortOnId = params.paginatedField !== '_id'; //
// params.after - overides params.next
//
// The 'after' param sets the start position for the next page. This is similar to the
// 'next' param, with the difference that 'after' takes a plain _id instead of an encoded
// string of both _id and paginatedField values.
if (params.limit < 1) params.limit = 1;
if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT;
if (params.after) {
if (shouldSecondarySortOnId) {
// Since the primary sort field is not provided by the 'after' pagination cursor we
// have to look it up when the paginated field is not _id.
const doc = await collection.findOne({
_id: params.after
}, {
[params.paginatedField]: true,
_id: false
});
// If the paginated field is not _id, then it might have duplicate values in it. This is bad
// because then we can't exclusively use it for our range queries (that use $lt and $gt). So
// to fix this, we secondarily sort on _id, which is always unique.
const shouldSecondarySortOnId = params.paginatedField !== '_id';
//
// params.after - overides params.next
//
// The 'after' param sets the start position for the next page. This is similar to the
// 'next' param, with the difference that 'after' takes a plain _id instead of an encoded
// string of both _id and paginatedField values.
if (params.after) {
if (shouldSecondarySortOnId) {
// Since the primary sort field is not provided by the 'after' pagination cursor we
// have to look it up when the paginated field is not _id.
const doc = yield collection.findOne({ _id: params.after }, { [params.paginatedField]: true, _id: false });
if (doc) {
// Handle usage of dot notation in paginatedField
let prop = getPropertyViaDotNotation(params.paginatedField, doc);
if (params.sortCaseInsensitive) prop = prop.toLowerCase();
params.next = [prop, params.after];
}
} else {
params.next = params.after;
if (doc) {
// Handle usage of dot notation in paginatedField
let prop = getPropertyViaDotNotation(params.paginatedField, doc);
if (params.sortCaseInsensitive) prop = prop.toLowerCase();
params.next = [prop, params.after];
}
} else {
params.next = params.after;
}
} //
// params.before - overides params.previous
//
// The 'before' param sets the start position for the previous page. This is similar to the
// 'previous' param, with the difference that 'before' takes a plain _id instead of an encoded
// string of both _id and paginatedField values.
//
// params.before - overides params.previous
//
// The 'before' param sets the start position for the previous page. This is similar to the
// 'previous' param, with the difference that 'before' takes a plain _id instead of an encoded
// string of both _id and paginatedField values.
if (params.before) {
if (shouldSecondarySortOnId) {
// Since the primary sort field is not provided by the 'before' pagination cursor we
// have to look it up when the paginated field is not _id.
const doc = yield collection.findOne({ _id: params.before }, { [params.paginatedField]: true, _id: false });
if (doc) {
// Handle usage of dot notation in paginatedField
let prop = getPropertyViaDotNotation(params.paginatedField, doc);
if (params.sortCaseInsensitive) prop = prop.toLowerCase();
params.previous = [prop, params.before];
}
} else {
params.previous = params.before;
}
}
// The query must always include the paginatedField so we can construct the cursor.
if (params.fields) {
params.fields = _.extend({
_id: 0 // Mongo includes this field by default, so don't request it unless the user wants it.
}, params.fields);
if (params.before) {
if (shouldSecondarySortOnId) {
// Since the primary sort field is not provided by the 'before' pagination cursor we
// have to look it up when the paginated field is not _id.
const doc = await collection.findOne({
_id: params.before
}, {
[params.paginatedField]: true,
_id: false
});
if (!params.fields[params.paginatedField]) {
params.fields[params.paginatedField] = 1;
if (doc) {
// Handle usage of dot notation in paginatedField
let prop = getPropertyViaDotNotation(params.paginatedField, doc);
if (params.sortCaseInsensitive) prop = prop.toLowerCase();
params.previous = [prop, params.before];
}
} else {
params.previous = params.before;
}
} // The query must always include the paginatedField so we can construct the cursor.
return params;
});
function sanitizeParams(_x, _x2) {
return _ref.apply(this, arguments);
if (params.fields) {
params.fields = _.extend({
_id: 0 // Mongo includes this field by default, so don't request it unless the user wants it.
}, params.fields);
if (!params.fields[params.paginatedField]) {
params.fields[params.paginatedField] = 1;
}
}
return sanitizeParams;
})();
return params;
};

@@ -0,4 +1,6 @@

"use strict";
const _ = require('underscore');
const resolveFields = require('./resolveFields');
/**

@@ -14,4 +16,7 @@ * Normalize the given query parameter to an array, so we support both param=a,b and

*/
function normalizeQueryArray(query, param) {
const value = query[param];
if (Array.isArray(value)) {

@@ -23,15 +28,18 @@ for (let i = 0; i < value.length; ++i) {

}
return value;
}
// This goes before _.isString so we don't split an empty string into ['']. The array option just
} // This goes before _.isString so we don't split an empty string into ['']. The array option just
// uses whatever the user provides.
if (_.isEmpty(value)) {
return [];
}
if (_.isString(value)) {
return value.split(',');
}
throw new TypeError('expected string array or comma-separated string for ' + param);
}
/**

@@ -53,2 +61,4 @@ * Sanitizes a `query` object received and merges it's changes to an optional `params` object

*/
module.exports = function sanitizeQuery(query, params) {

@@ -58,4 +68,4 @@ params = params || {};

if (!_.isEmpty(query.limit)) {
const limit = parseInt(query.limit, 10);
// Don't let the user specify a higher limit than params.limit, if defined.
const limit = parseInt(query.limit, 10); // Don't let the user specify a higher limit than params.limit, if defined.
if (!isNaN(limit) && (!params.limit || params.limit > limit)) {

@@ -72,15 +82,15 @@ params.limit = limit;

params.previous = query.previous;
}
} // Don't trust fields passed in the querystring, so whitelist them against the fields defined in
// parameters.
// Don't trust fields passed in the querystring, so whitelist them against the fields defined in
// parameters.
const fields = resolveFields(normalizeQueryArray(query, 'fields'), params.fields, params.overrideFields);
if (fields === null) {
throw new TypeError('no valid fields provided');
}
} // Set fields to undefined if it's empty to avoid adding _id: 0 in find.
// Set fields to undefined if it's empty to avoid adding _id: 0 in find.
params.fields = _.isEmpty(fields) ? undefined : fields;
return params;
};
{
"name": "mongo-cursor-pagination",
"version": "8.0.1",
"version": "8.1.0",
"description": "Make it easy to return cursor-paginated results from a Mongo collection",

@@ -49,2 +49,5 @@ "main": "index.js",

"devDependencies": {
"@babel/cli": "^7.18.10",
"@babel/core": "^7.18.13",
"@babel/preset-env": "^7.18.10",
"@commitlint/config-conventional": "^8.3.4",

@@ -54,5 +57,3 @@ "@mixmaxhq/commitlint-jenkins": "^1.4.4",

"@mixmaxhq/semantic-release-config": "^2.0.0",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-jest": "^29.0.0",
"cz-conventional-changelog": "^3.2.0",

@@ -59,0 +60,0 @@ "eslint": "^6.8.0",

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