New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

pouchdb-mapreduce

Package Overview
Dependencies
Maintainers
4
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pouchdb-mapreduce - npm Package Compare versions

Comparing version

to
6.0.3

1008

lib/index-browser.js

@@ -5,12 +5,853 @@ 'use strict';

var pouchdbUtils = require('pouchdb-utils');
var pouchdbBinaryUtils = require('pouchdb-binary-utils');
var pouchdbCollate = require('pouchdb-collate');
var Promise = _interopDefault(require('pouchdb-promise'));
var pouchdbMd5 = require('pouchdb-md5');
var lie = _interopDefault(require('lie'));
var getArguments = _interopDefault(require('argsarray'));
var debug = _interopDefault(require('debug'));
var events = require('events');
var inherits = _interopDefault(require('inherits'));
var Md5 = _interopDefault(require('spark-md5'));
var scopedEval = _interopDefault(require('scope-eval'));
var pouchdbMapreduceUtils = require('pouchdb-mapreduce-utils');
var inherits = _interopDefault(require('inherits'));
/* istanbul ignore next */
var PouchPromise = typeof Promise === 'function' ? Promise : lie;
// most of this is borrowed from lodash.isPlainObject:
// https://github.com/fis-components/lodash.isplainobject/
// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
var funcToString = Function.prototype.toString;
var objectCtorString = funcToString.call(Object);
var log$1 = debug('pouchdb:api');
// like underscore/lodash _.pick()
function pick(obj, arr) {
var res = {};
for (var i = 0, len = arr.length; i < len; i++) {
var prop = arr[i];
if (prop in obj) {
res[prop] = obj[prop];
}
}
return res;
}
function isChromeApp() {
return (typeof chrome !== "undefined" &&
typeof chrome.storage !== "undefined" &&
typeof chrome.storage.local !== "undefined");
}
var hasLocal;
if (isChromeApp()) {
hasLocal = false;
} else {
try {
localStorage.setItem('_pouch_check_localstorage', 1);
hasLocal = !!localStorage.getItem('_pouch_check_localstorage');
} catch (e) {
hasLocal = false;
}
}
function hasLocalStorage() {
return hasLocal;
}
inherits(Changes, events.EventEmitter);
/* istanbul ignore next */
function attachBrowserEvents(self) {
if (isChromeApp()) {
chrome.storage.onChanged.addListener(function (e) {
// make sure it's event addressed to us
if (e.db_name != null) {
//object only has oldValue, newValue members
self.emit(e.dbName.newValue);
}
});
} else if (hasLocalStorage()) {
if (typeof addEventListener !== 'undefined') {
addEventListener("storage", function (e) {
self.emit(e.key);
});
} else { // old IE
window.attachEvent("storage", function (e) {
self.emit(e.key);
});
}
}
}
function Changes() {
events.EventEmitter.call(this);
this._listeners = {};
attachBrowserEvents(this);
}
Changes.prototype.addListener = function (dbName, id, db, opts) {
/* istanbul ignore if */
if (this._listeners[id]) {
return;
}
var self = this;
var inprogress = false;
function eventFunction() {
/* istanbul ignore if */
if (!self._listeners[id]) {
return;
}
if (inprogress) {
inprogress = 'waiting';
return;
}
inprogress = true;
var changesOpts = pick(opts, [
'style', 'include_docs', 'attachments', 'conflicts', 'filter',
'doc_ids', 'view', 'since', 'query_params', 'binary'
]);
/* istanbul ignore next */
function onError() {
inprogress = false;
}
db.changes(changesOpts).on('change', function (c) {
if (c.seq > opts.since && !opts.cancelled) {
opts.since = c.seq;
opts.onChange(c);
}
}).on('complete', function () {
if (inprogress === 'waiting') {
setTimeout(function (){
eventFunction();
},0);
}
inprogress = false;
}).on('error', onError);
}
this._listeners[id] = eventFunction;
this.on(dbName, eventFunction);
};
Changes.prototype.removeListener = function (dbName, id) {
/* istanbul ignore if */
if (!(id in this._listeners)) {
return;
}
events.EventEmitter.prototype.removeListener.call(this, dbName,
this._listeners[id]);
delete this._listeners[id];
};
/* istanbul ignore next */
Changes.prototype.notifyLocalWindows = function (dbName) {
//do a useless change on a storage thing
//in order to get other windows's listeners to activate
if (isChromeApp()) {
chrome.storage.local.set({dbName: dbName});
} else if (hasLocalStorage()) {
localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
}
};
Changes.prototype.notify = function (dbName) {
this.emit(dbName);
this.notifyLocalWindows(dbName);
};
function guardedConsole(method) {
/* istanbul ignore else */
if (console !== 'undefined' && method in console) {
var args = Array.prototype.slice.call(arguments, 1);
console[method].apply(console, args);
}
}
inherits(PouchError, Error);
function PouchError(opts) {
Error.call(this, opts.reason);
this.status = opts.status;
this.name = opts.error;
this.message = opts.reason;
this.error = true;
}
PouchError.prototype.toString = function () {
return JSON.stringify({
status: this.status,
name: this.name,
message: this.message,
reason: this.reason
});
};
var UNAUTHORIZED = new PouchError({
status: 401,
error: 'unauthorized',
reason: "Name or password is incorrect."
});
var MISSING_BULK_DOCS = new PouchError({
status: 400,
error: 'bad_request',
reason: "Missing JSON list of 'docs'"
});
var MISSING_DOC = new PouchError({
status: 404,
error: 'not_found',
reason: 'missing'
});
var REV_CONFLICT = new PouchError({
status: 409,
error: 'conflict',
reason: 'Document update conflict'
});
var INVALID_ID = new PouchError({
status: 400,
error: 'bad_request',
reason: '_id field must contain a string'
});
var MISSING_ID = new PouchError({
status: 412,
error: 'missing_id',
reason: '_id is required for puts'
});
var RESERVED_ID = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Only reserved document ids may start with underscore.'
});
var NOT_OPEN = new PouchError({
status: 412,
error: 'precondition_failed',
reason: 'Database not open'
});
var UNKNOWN_ERROR = new PouchError({
status: 500,
error: 'unknown_error',
reason: 'Database encountered an unknown error'
});
var BAD_ARG = new PouchError({
status: 500,
error: 'badarg',
reason: 'Some query argument is invalid'
});
var INVALID_REQUEST = new PouchError({
status: 400,
error: 'invalid_request',
reason: 'Request was invalid'
});
var QUERY_PARSE_ERROR = new PouchError({
status: 400,
error: 'query_parse_error',
reason: 'Some query parameter is invalid'
});
var DOC_VALIDATION = new PouchError({
status: 500,
error: 'doc_validation',
reason: 'Bad special document member'
});
var BAD_REQUEST = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Something wrong with the request'
});
var NOT_AN_OBJECT = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Document must be a JSON object'
});
var DB_MISSING = new PouchError({
status: 404,
error: 'not_found',
reason: 'Database not found'
});
var IDB_ERROR = new PouchError({
status: 500,
error: 'indexed_db_went_bad',
reason: 'unknown'
});
var WSQ_ERROR = new PouchError({
status: 500,
error: 'web_sql_went_bad',
reason: 'unknown'
});
var LDB_ERROR = new PouchError({
status: 500,
error: 'levelDB_went_went_bad',
reason: 'unknown'
});
var FORBIDDEN = new PouchError({
status: 403,
error: 'forbidden',
reason: 'Forbidden by design doc validate_doc_update function'
});
var INVALID_REV = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Invalid rev format'
});
var FILE_EXISTS = new PouchError({
status: 412,
error: 'file_exists',
reason: 'The database could not be created, the file already exists.'
});
var MISSING_STUB = new PouchError({
status: 412,
error: 'missing_stub'
});
var INVALID_URL = new PouchError({
status: 413,
error: 'invalid_url',
reason: 'Provided URL is invalid'
});
function flatten(arrs) {
var res = [];
for (var i = 0, len = arrs.length; i < len; i++) {
res = res.concat(arrs[i]);
}
return res;
}
// this is essentially the "update sugar" function from daleharvey/pouchdb#1388
// the diffFun tells us what delta to apply to the doc. it either returns
// the doc, or false if it doesn't need to do an update after all
function upsert(db, docId, diffFun) {
return new PouchPromise(function (fulfill, reject) {
db.get(docId, function (err, doc) {
if (err) {
/* istanbul ignore next */
if (err.status !== 404) {
return reject(err);
}
doc = {};
}
// the user might change the _rev, so save it for posterity
var docRev = doc._rev;
var newDoc = diffFun(doc);
if (!newDoc) {
// if the diffFun returns falsy, we short-circuit as
// an optimization
return fulfill({updated: false, rev: docRev});
}
// users aren't allowed to modify these values,
// so reset them here
newDoc._id = docId;
newDoc._rev = docRev;
fulfill(tryAndPut(db, newDoc, diffFun));
});
});
}
function tryAndPut(db, doc, diffFun) {
return db.put(doc).then(function (res) {
return {
updated: true,
rev: res.rev
};
}, function (err) {
/* istanbul ignore next */
if (err.status !== 409) {
throw err;
}
return upsert(db, doc._id, diffFun);
});
}
// BEGIN Math.uuid.js
/*!
Math.uuid.js (v1.4)
http://www.broofa.com
mailto:robert@broofa.com
Copyright (c) 2010 Robert Kieffer
Dual licensed under the MIT and GPL licenses.
*/
/*
* Generate a random uuid.
*
* USAGE: Math.uuid(length, radix)
* length - the desired number of characters
* radix - the number of allowable values for each character.
*
* EXAMPLES:
* // No arguments - returns RFC4122, version 4 ID
* >>> Math.uuid()
* "92329D39-6F5C-4520-ABFC-AAB64544E172"
*
* // One argument - returns ID of the specified length
* >>> Math.uuid(15) // 15 character ID (default base=62)
* "VcydxgltxrVZSTV"
*
* // Two arguments - returns ID of the specified length, and radix.
* // (Radix must be <= 62)
* >>> Math.uuid(8, 2) // 8 character ID (base=2)
* "01001010"
* >>> Math.uuid(8, 10) // 8 character ID (base=10)
* "47473046"
* >>> Math.uuid(8, 16) // 8 character ID (base=16)
* "098F4D35"
*/
var chars = (
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz'
).split('');
var atob$1 = function (str) {
return atob(str);
};
// Abstracts constructing a Blob object, so it also works in older
// browsers that don't support the native Blob constructor (e.g.
// old QtWebKit versions, Android < 4.4).
function createBlob(parts, properties) {
/* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
parts = parts || [];
properties = properties || {};
try {
return new Blob(parts, properties);
} catch (e) {
if (e.name !== "TypeError") {
throw e;
}
var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder :
typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder :
typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder :
WebKitBlobBuilder;
var builder = new Builder();
for (var i = 0; i < parts.length; i += 1) {
builder.append(parts[i]);
}
return builder.getBlob(properties.type);
}
}
// From http://stackoverflow.com/questions/14967647/ (continues on next line)
// encode-decode-image-with-base64-breaks-image (2013-04-21)
function binaryStringToArrayBuffer(bin) {
var length = bin.length;
var buf = new ArrayBuffer(length);
var arr = new Uint8Array(buf);
for (var i = 0; i < length; i++) {
arr[i] = bin.charCodeAt(i);
}
return buf;
}
function binStringToBluffer(binString, type) {
return createBlob([binaryStringToArrayBuffer(binString)], {type: type});
}
function b64ToBluffer(b64, type) {
return binStringToBluffer(atob$1(b64), type);
}
function pad(str, padWith, upToLength) {
var padding = '';
var targetLength = upToLength - str.length;
/* istanbul ignore next */
while (padding.length < targetLength) {
padding += padWith;
}
return padding;
}
function padLeft(str, padWith, upToLength) {
var padding = pad(str, padWith, upToLength);
return padding + str;
}
var MIN_MAGNITUDE = -324; // verified by -Number.MIN_VALUE
var MAGNITUDE_DIGITS = 3; // ditto
var SEP = ''; // set to '_' for easier debugging
function collate(a, b) {
if (a === b) {
return 0;
}
a = normalizeKey(a);
b = normalizeKey(b);
var ai = collationIndex(a);
var bi = collationIndex(b);
if ((ai - bi) !== 0) {
return ai - bi;
}
if (a === null) {
return 0;
}
switch (typeof a) {
case 'number':
return a - b;
case 'boolean':
return a === b ? 0 : (a < b ? -1 : 1);
case 'string':
return stringCollate(a, b);
}
return Array.isArray(a) ? arrayCollate(a, b) : objectCollate(a, b);
}
// couch considers null/NaN/Infinity/-Infinity === undefined,
// for the purposes of mapreduce indexes. also, dates get stringified.
function normalizeKey(key) {
switch (typeof key) {
case 'undefined':
return null;
case 'number':
if (key === Infinity || key === -Infinity || isNaN(key)) {
return null;
}
return key;
case 'object':
var origKey = key;
if (Array.isArray(key)) {
var len = key.length;
key = new Array(len);
for (var i = 0; i < len; i++) {
key[i] = normalizeKey(origKey[i]);
}
/* istanbul ignore next */
} else if (key instanceof Date) {
return key.toJSON();
} else if (key !== null) { // generic object
key = {};
for (var k in origKey) {
if (origKey.hasOwnProperty(k)) {
var val = origKey[k];
if (typeof val !== 'undefined') {
key[k] = normalizeKey(val);
}
}
}
}
}
return key;
}
function indexify(key) {
if (key !== null) {
switch (typeof key) {
case 'boolean':
return key ? 1 : 0;
case 'number':
return numToIndexableString(key);
case 'string':
// We've to be sure that key does not contain \u0000
// Do order-preserving replacements:
// 0 -> 1, 1
// 1 -> 1, 2
// 2 -> 2, 2
return key
.replace(/\u0002/g, '\u0002\u0002')
.replace(/\u0001/g, '\u0001\u0002')
.replace(/\u0000/g, '\u0001\u0001');
case 'object':
var isArray = Array.isArray(key);
var arr = isArray ? key : Object.keys(key);
var i = -1;
var len = arr.length;
var result = '';
if (isArray) {
while (++i < len) {
result += toIndexableString(arr[i]);
}
} else {
while (++i < len) {
var objKey = arr[i];
result += toIndexableString(objKey) +
toIndexableString(key[objKey]);
}
}
return result;
}
}
return '';
}
// convert the given key to a string that would be appropriate
// for lexical sorting, e.g. within a database, where the
// sorting is the same given by the collate() function.
function toIndexableString(key) {
var zero = '\u0000';
key = normalizeKey(key);
return collationIndex(key) + SEP + indexify(key) + zero;
}
function parseNumber(str, i) {
var originalIdx = i;
var num;
var zero = str[i] === '1';
if (zero) {
num = 0;
i++;
} else {
var neg = str[i] === '0';
i++;
var numAsString = '';
var magAsString = str.substring(i, i + MAGNITUDE_DIGITS);
var magnitude = parseInt(magAsString, 10) + MIN_MAGNITUDE;
/* istanbul ignore next */
if (neg) {
magnitude = -magnitude;
}
i += MAGNITUDE_DIGITS;
while (true) {
var ch = str[i];
if (ch === '\u0000') {
break;
} else {
numAsString += ch;
}
i++;
}
numAsString = numAsString.split('.');
if (numAsString.length === 1) {
num = parseInt(numAsString, 10);
} else {
/* istanbul ignore next */
num = parseFloat(numAsString[0] + '.' + numAsString[1]);
}
/* istanbul ignore next */
if (neg) {
num = num - 10;
}
/* istanbul ignore next */
if (magnitude !== 0) {
// parseFloat is more reliable than pow due to rounding errors
// e.g. Number.MAX_VALUE would return Infinity if we did
// num * Math.pow(10, magnitude);
num = parseFloat(num + 'e' + magnitude);
}
}
return {num: num, length : i - originalIdx};
}
// move up the stack while parsing
// this function moved outside of parseIndexableString for performance
function pop(stack, metaStack) {
var obj = stack.pop();
if (metaStack.length) {
var lastMetaElement = metaStack[metaStack.length - 1];
if (obj === lastMetaElement.element) {
// popping a meta-element, e.g. an object whose value is another object
metaStack.pop();
lastMetaElement = metaStack[metaStack.length - 1];
}
var element = lastMetaElement.element;
var lastElementIndex = lastMetaElement.index;
if (Array.isArray(element)) {
element.push(obj);
} else if (lastElementIndex === stack.length - 2) { // obj with key+value
var key = stack.pop();
element[key] = obj;
} else {
stack.push(obj); // obj with key only
}
}
}
function parseIndexableString(str) {
var stack = [];
var metaStack = []; // stack for arrays and objects
var i = 0;
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
var collationIndex = str[i++];
if (collationIndex === '\u0000') {
if (stack.length === 1) {
return stack.pop();
} else {
pop(stack, metaStack);
continue;
}
}
switch (collationIndex) {
case '1':
stack.push(null);
break;
case '2':
stack.push(str[i] === '1');
i++;
break;
case '3':
var parsedNum = parseNumber(str, i);
stack.push(parsedNum.num);
i += parsedNum.length;
break;
case '4':
var parsedStr = '';
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
var ch = str[i];
if (ch === '\u0000') {
break;
}
parsedStr += ch;
i++;
}
// perform the reverse of the order-preserving replacement
// algorithm (see above)
parsedStr = parsedStr.replace(/\u0001\u0001/g, '\u0000')
.replace(/\u0001\u0002/g, '\u0001')
.replace(/\u0002\u0002/g, '\u0002');
stack.push(parsedStr);
break;
case '5':
var arrayElement = { element: [], index: stack.length };
stack.push(arrayElement.element);
metaStack.push(arrayElement);
break;
case '6':
var objElement = { element: {}, index: stack.length };
stack.push(objElement.element);
metaStack.push(objElement);
break;
/* istanbul ignore next */
default:
throw new Error(
'bad collationIndex or unexpectedly reached end of input: ' +
collationIndex);
}
}
}
function arrayCollate(a, b) {
var len = Math.min(a.length, b.length);
for (var i = 0; i < len; i++) {
var sort = collate(a[i], b[i]);
if (sort !== 0) {
return sort;
}
}
return (a.length === b.length) ? 0 :
(a.length > b.length) ? 1 : -1;
}
function stringCollate(a, b) {
// See: https://github.com/daleharvey/pouchdb/issues/40
// This is incompatible with the CouchDB implementation, but its the
// best we can do for now
return (a === b) ? 0 : ((a > b) ? 1 : -1);
}
function objectCollate(a, b) {
var ak = Object.keys(a), bk = Object.keys(b);
var len = Math.min(ak.length, bk.length);
for (var i = 0; i < len; i++) {
// First sort the keys
var sort = collate(ak[i], bk[i]);
if (sort !== 0) {
return sort;
}
// if the keys are equal sort the values
sort = collate(a[ak[i]], b[bk[i]]);
if (sort !== 0) {
return sort;
}
}
return (ak.length === bk.length) ? 0 :
(ak.length > bk.length) ? 1 : -1;
}
// The collation is defined by erlangs ordered terms
// the atoms null, true, false come first, then numbers, strings,
// arrays, then objects
// null/undefined/NaN/Infinity/-Infinity are all considered null
function collationIndex(x) {
var id = ['boolean', 'number', 'string', 'object'];
var idx = id.indexOf(typeof x);
//false if -1 otherwise true, but fast!!!!1
if (~idx) {
if (x === null) {
return 1;
}
if (Array.isArray(x)) {
return 5;
}
return idx < 3 ? (idx + 2) : (idx + 3);
}
/* istanbul ignore next */
if (Array.isArray(x)) {
return 5;
}
}
// conversion:
// x yyy zz...zz
// x = 0 for negative, 1 for 0, 2 for positive
// y = exponent (for negative numbers negated) moved so that it's >= 0
// z = mantisse
function numToIndexableString(num) {
if (num === 0) {
return '1';
}
// convert number to exponential format for easier and
// more succinct string sorting
var expFormat = num.toExponential().split(/e\+?/);
var magnitude = parseInt(expFormat[1], 10);
var neg = num < 0;
var result = neg ? '0' : '2';
// first sort by magnitude
// it's easier if all magnitudes are positive
var magForComparison = ((neg ? -magnitude : magnitude) - MIN_MAGNITUDE);
var magString = padLeft((magForComparison).toString(), '0', MAGNITUDE_DIGITS);
result += SEP + magString;
// then sort by the factor
var factor = Math.abs(parseFloat(expFormat[0])); // [1..10)
/* istanbul ignore next */
if (neg) { // for negative reverse ordering
factor = 10 - factor;
}
var factorStr = factor.toFixed(20);
// strip zeros from the end
factorStr = factorStr.replace(/\.?0+$/, '');
result += SEP + factorStr;
return result;
}
/*
* Simple task queue to sequentialize actions. Assumes

@@ -21,3 +862,3 @@ * callbacks will eventually fire (once).

function TaskQueue() {
this.promise = new Promise(function (fulfill) {fulfill(); });
this.promise = new PouchPromise(function (fulfill) {fulfill(); });
}

@@ -36,2 +877,6 @@ TaskQueue.prototype.add = function (promiseFactory) {

function stringMd5(string) {
return Md5.hash(string);
}
function createView(opts) {

@@ -60,3 +905,3 @@ var sourceDB = opts.db;

var depDbName = info.db_name + '-mrview-' +
(temporary ? 'temp' : pouchdbMd5.stringMd5(viewSignature));
(temporary ? 'temp' : stringMd5(viewSignature));

@@ -79,3 +924,3 @@ // save the view name in the source db so it can be cleaned up if necessary

}
return pouchdbUtils.upsert(sourceDB, '_local/mrviews', diffFunction).then(function () {
return upsert(sourceDB, '_local/mrviews', diffFunction).then(function () {
return sourceDB.registerDependentDatabase(depDbName).then(function (res) {

@@ -129,2 +974,69 @@ var db = res.db;

var promisedCallback = function (promise, callback) {
if (callback) {
promise.then(function (res) {
process.nextTick(function () {
callback(null, res);
});
}, function (reason) {
process.nextTick(function () {
callback(reason);
});
});
}
return promise;
};
var callbackify = function (fun) {
return getArguments(function (args) {
var cb = args.pop();
var promise = fun.apply(this, args);
if (typeof cb === 'function') {
promisedCallback(promise, cb);
}
return promise;
});
};
// Promise finally util similar to Q.finally
var fin = function (promise, finalPromiseFactory) {
return promise.then(function (res) {
return finalPromiseFactory().then(function () {
return res;
});
}, function (reason) {
return finalPromiseFactory().then(function () {
throw reason;
});
});
};
var sequentialize = function (queue, promiseFactory) {
return function () {
var args = arguments;
var that = this;
return queue.add(function () {
return promiseFactory.apply(that, args);
});
};
};
// uniq an array of strings, order not guaranteed
// similar to underscore/lodash _.uniq
var uniq = function (arr) {
var map = {};
for (var i = 0, len = arr.length; i < len; i++) {
map['$' + arr[i]] = true;
}
var keys = Object.keys(map);
var output = new Array(keys.length);
for (i = 0, len = keys.length; i < len; i++) {
output[i] = keys[i].substring(1);
}
return output;
};
var persistentQueues = {};

@@ -134,3 +1046,3 @@ var tempViewQueue = new TaskQueue();

var log = pouchdbUtils.guardedConsole.bind(null, 'log');
var log = guardedConsole.bind(null, 'log');

@@ -153,3 +1065,3 @@ function parseViewName(name) {

} catch (err) {
pouchdbUtils.guardedConsole('error',
guardedConsole('error',
'The user\'s map/reduce function threw an uncaught error.\n' +

@@ -159,3 +1071,3 @@ 'You can debug this error by doing:\n' +

'Please double-check your map/reduce function.');
pouchdbUtils.guardedConsole('error', e);
guardedConsole('error', e);
}

@@ -178,4 +1090,4 @@ }

function sortByKeyThenValue(x, y) {
var keyCompare = pouchdbCollate.collate(x.key, y.key);
return keyCompare !== 0 ? keyCompare : pouchdbCollate.collate(x.value, y.value);
var keyCompare = collate(x.key, y.key);
return keyCompare !== 0 ? keyCompare : collate(x.value, y.value);
}

@@ -209,3 +1121,3 @@

var att = atts[filename];
atts[filename].data = pouchdbBinaryUtils.base64StringToBlobOrBuffer(att.data, att.content_type);
atts[filename].data = b64ToBluffer(att.data, att.content_type);
});

@@ -340,3 +1252,3 @@ });

typeof options[endkeyName] !== 'undefined' &&
pouchdbCollate.collate(options[startkeyName], options[endkeyName]) > 0) {
collate(options[startkeyName], options[endkeyName]) > 0) {
throw new QueryParseError('No rows can match your key range, ' +

@@ -445,3 +1357,3 @@ 'reverse your start_key and end_key or set {descending : true}');

function customQuery(db, fun, opts) {
return new Promise(function (resolve, reject) {
return new PouchPromise(function (resolve, reject) {
db._query(fun, opts, function (err, res) {

@@ -460,3 +1372,3 @@ if (err) {

function customViewCleanup(db) {
return new Promise(function (resolve, reject) {
return new PouchPromise(function (resolve, reject) {
db._viewCleanup(function (err, res) {

@@ -496,3 +1408,3 @@ if (err) {

// for performance reasons (avoids unnecessary GETs)
return Promise.resolve(defaultMetaDoc);
return PouchPromise.resolve(defaultMetaDoc);
}

@@ -505,3 +1417,3 @@ return view.db.get(metaDocId).catch(defaultsTo(defaultMetaDoc));

// no keys, no need for a lookup
return Promise.resolve({rows: []});
return PouchPromise.resolve({rows: []});
}

@@ -549,3 +1461,3 @@ return view.db.allDocs({

});
metaDoc.keys = pouchdbMapreduceUtils.uniq(newKeys.concat(metaDoc.keys));
metaDoc.keys = uniq(newKeys.concat(metaDoc.keys));
kvDocs.push(metaDoc);

@@ -571,6 +1483,6 @@

var docIds = Object.keys(docIdsToChangesAndEmits);
return Promise.all(docIds.map(function (docId) {
return PouchPromise.all(docIds.map(function (docId) {
return getDocsToPersist(docId, view, docIdsToChangesAndEmits);
})).then(function (listOfDocsToPersist) {
var docsToPersist = pouchdbUtils.flatten(listOfDocsToPersist);
var docsToPersist = flatten(listOfDocsToPersist);
lastSeqDoc.seq = seq;

@@ -594,3 +1506,3 @@ docsToPersist.push(lastSeqDoc);

function updateView(view) {
return pouchdbMapreduceUtils.sequentialize(getQueue(view), function () {
return sequentialize(getQueue(view), function () {
return updateViewInQueue(view);

@@ -606,7 +1518,7 @@ })();

function emit(key, value) {
var output = {id: doc._id, key: pouchdbCollate.normalizeKey(key)};
var output = {id: doc._id, key: normalizeKey(key)};
// Don't explicitly store the value unless it's defined and non-null.
// This saves on storage space, because often people don't use it.
if (typeof value !== 'undefined' && value !== null) {
output.value = pouchdbCollate.normalizeKey(value);
output.value = normalizeKey(value);
}

@@ -639,3 +1551,3 @@ mapResults.push(output);

return new Promise(function (resolve, reject) {
return new PouchPromise(function (resolve, reject) {

@@ -678,6 +1590,6 @@ function complete() {

var complexKey = [obj.key, obj.id];
if (pouchdbCollate.collate(obj.key, lastKey) === 0) {
if (collate(obj.key, lastKey) === 0) {
complexKey.push(j); // dup key+id, so make it unique
}
var indexableKey = pouchdbCollate.toIndexableString(complexKey);
var indexableKey = toIndexableString(complexKey);
indexableKeysToKeyValues[indexableKey] = obj;

@@ -736,3 +1648,3 @@ lastKey = obj.key;

if (last && pouchdbCollate.collate(last.groupKey, groupKey) === 0) {
if (last && collate(last.groupKey, groupKey) === 0) {
last.keys.push([e.key, e.id]);

@@ -768,3 +1680,3 @@ last.values.push(e.value);

function queryView(view, opts) {
return pouchdbMapreduceUtils.sequentialize(getQueue(view), function () {
return sequentialize(getQueue(view), function () {
return queryViewInQueue(view, opts);

@@ -805,3 +1717,3 @@ })();

var parsedKeyAndDocId = pouchdbCollate.parseIndexableString(result.doc._id);
var parsedKeyAndDocId = parseIndexableString(result.doc._id);
return {

@@ -828,3 +1740,3 @@ key: parsedKeyAndDocId[0],

if (opts.include_docs) {
var docIds = pouchdbMapreduceUtils.uniq(rows.map(rowToDocId));
var docIds = uniq(rows.map(rowToDocId));

@@ -862,8 +1774,8 @@ return view.sourceDB.allDocs({

var viewOpts = {
startkey : pouchdbCollate.toIndexableString([key]),
endkey : pouchdbCollate.toIndexableString([key, {}])
startkey : toIndexableString([key]),
endkey : toIndexableString([key, {}])
};
return fetchFromView(viewOpts);
});
return Promise.all(fetchPromises).then(pouchdbUtils.flatten).then(onMapResultsReady);
return PouchPromise.all(fetchPromises).then(flatten).then(onMapResultsReady);
} else { // normal query, no 'keys'

@@ -881,4 +1793,4 @@ var viewOpts = {

viewOpts.startkey = opts.descending ?
pouchdbCollate.toIndexableString([opts.startkey, {}]) :
pouchdbCollate.toIndexableString([opts.startkey]);
toIndexableString([opts.startkey, {}]) :
toIndexableString([opts.startkey]);
}

@@ -891,8 +1803,8 @@ if (typeof opts.endkey !== 'undefined') {

viewOpts.endkey = pouchdbCollate.toIndexableString(
viewOpts.endkey = toIndexableString(
inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]);
}
if (typeof opts.key !== 'undefined') {
var keyStart = pouchdbCollate.toIndexableString([opts.key]);
var keyEnd = pouchdbCollate.toIndexableString([opts.key, {}]);
var keyStart = toIndexableString([opts.key]);
var keyEnd = toIndexableString([opts.key, {}]);
if (viewOpts.descending) {

@@ -962,7 +1874,7 @@ viewOpts.endkey = keyStart;

var destroyPromises = dbsToDelete.map(function (viewDBName) {
return pouchdbMapreduceUtils.sequentialize(getQueue(viewDBName), function () {
return sequentialize(getQueue(viewDBName), function () {
return new db.constructor(viewDBName, db.__opts).destroy();
})();
});
return Promise.all(destroyPromises).then(function () {
return PouchPromise.all(destroyPromises).then(function () {
return {ok: true};

@@ -974,3 +1886,3 @@ });

var viewCleanup = pouchdbMapreduceUtils.callbackify(function () {
var viewCleanup = callbackify(function () {
var db = this;

@@ -1013,3 +1925,3 @@ if (db.type() === 'http') {

}
return pouchdbMapreduceUtils.fin(updateView(view).then(function () {
return fin(updateView(view).then(function () {
return queryView(view, opts);

@@ -1071,6 +1983,6 @@ }), cleanup);

var db = this;
var promise = Promise.resolve().then(function () {
var promise = PouchPromise.resolve().then(function () {
return queryPromised(db, fun, opts);
});
pouchdbMapreduceUtils.promisedCallback(promise, callback);
promisedCallback(promise, callback);
return promise;

@@ -1077,0 +1989,0 @@ };

@@ -5,12 +5,802 @@ 'use strict';

var pouchdbUtils = require('pouchdb-utils');
var pouchdbBinaryUtils = require('pouchdb-binary-utils');
var pouchdbCollate = require('pouchdb-collate');
var Promise = _interopDefault(require('pouchdb-promise'));
var pouchdbMd5 = require('pouchdb-md5');
var lie = _interopDefault(require('lie'));
var getArguments = _interopDefault(require('argsarray'));
var debug = _interopDefault(require('debug'));
var events = require('events');
var inherits = _interopDefault(require('inherits'));
var crypto = _interopDefault(require('crypto'));
var scopedEval = _interopDefault(require('scope-eval'));
var pouchdbMapreduceUtils = require('pouchdb-mapreduce-utils');
var inherits = _interopDefault(require('inherits'));
/* istanbul ignore next */
var PouchPromise = typeof Promise === 'function' ? Promise : lie;
// most of this is borrowed from lodash.isPlainObject:
// https://github.com/fis-components/lodash.isplainobject/
// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
var funcToString = Function.prototype.toString;
var objectCtorString = funcToString.call(Object);
var log$1 = debug('pouchdb:api');
// like underscore/lodash _.pick()
function pick(obj, arr) {
var res = {};
for (var i = 0, len = arr.length; i < len; i++) {
var prop = arr[i];
if (prop in obj) {
res[prop] = obj[prop];
}
}
return res;
}
// in Node of course this is false
function isChromeApp() {
return false;
}
// in Node of course this is false
function hasLocalStorage() {
return false;
}
inherits(Changes, events.EventEmitter);
/* istanbul ignore next */
function attachBrowserEvents(self) {
if (isChromeApp()) {
chrome.storage.onChanged.addListener(function (e) {
// make sure it's event addressed to us
if (e.db_name != null) {
//object only has oldValue, newValue members
self.emit(e.dbName.newValue);
}
});
} else if (hasLocalStorage()) {
if (typeof addEventListener !== 'undefined') {
addEventListener("storage", function (e) {
self.emit(e.key);
});
} else { // old IE
window.attachEvent("storage", function (e) {
self.emit(e.key);
});
}
}
}
function Changes() {
events.EventEmitter.call(this);
this._listeners = {};
attachBrowserEvents(this);
}
Changes.prototype.addListener = function (dbName, id, db, opts) {
/* istanbul ignore if */
if (this._listeners[id]) {
return;
}
var self = this;
var inprogress = false;
function eventFunction() {
/* istanbul ignore if */
if (!self._listeners[id]) {
return;
}
if (inprogress) {
inprogress = 'waiting';
return;
}
inprogress = true;
var changesOpts = pick(opts, [
'style', 'include_docs', 'attachments', 'conflicts', 'filter',
'doc_ids', 'view', 'since', 'query_params', 'binary'
]);
/* istanbul ignore next */
function onError() {
inprogress = false;
}
db.changes(changesOpts).on('change', function (c) {
if (c.seq > opts.since && !opts.cancelled) {
opts.since = c.seq;
opts.onChange(c);
}
}).on('complete', function () {
if (inprogress === 'waiting') {
setTimeout(function (){
eventFunction();
},0);
}
inprogress = false;
}).on('error', onError);
}
this._listeners[id] = eventFunction;
this.on(dbName, eventFunction);
};
Changes.prototype.removeListener = function (dbName, id) {
/* istanbul ignore if */
if (!(id in this._listeners)) {
return;
}
events.EventEmitter.prototype.removeListener.call(this, dbName,
this._listeners[id]);
delete this._listeners[id];
};
/* istanbul ignore next */
Changes.prototype.notifyLocalWindows = function (dbName) {
//do a useless change on a storage thing
//in order to get other windows's listeners to activate
if (isChromeApp()) {
chrome.storage.local.set({dbName: dbName});
} else if (hasLocalStorage()) {
localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
}
};
Changes.prototype.notify = function (dbName) {
this.emit(dbName);
this.notifyLocalWindows(dbName);
};
function guardedConsole(method) {
/* istanbul ignore else */
if (console !== 'undefined' && method in console) {
var args = Array.prototype.slice.call(arguments, 1);
console[method].apply(console, args);
}
}
inherits(PouchError, Error);
function PouchError(opts) {
Error.call(this, opts.reason);
this.status = opts.status;
this.name = opts.error;
this.message = opts.reason;
this.error = true;
}
PouchError.prototype.toString = function () {
return JSON.stringify({
status: this.status,
name: this.name,
message: this.message,
reason: this.reason
});
};
var UNAUTHORIZED = new PouchError({
status: 401,
error: 'unauthorized',
reason: "Name or password is incorrect."
});
var MISSING_BULK_DOCS = new PouchError({
status: 400,
error: 'bad_request',
reason: "Missing JSON list of 'docs'"
});
var MISSING_DOC = new PouchError({
status: 404,
error: 'not_found',
reason: 'missing'
});
var REV_CONFLICT = new PouchError({
status: 409,
error: 'conflict',
reason: 'Document update conflict'
});
var INVALID_ID = new PouchError({
status: 400,
error: 'bad_request',
reason: '_id field must contain a string'
});
var MISSING_ID = new PouchError({
status: 412,
error: 'missing_id',
reason: '_id is required for puts'
});
var RESERVED_ID = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Only reserved document ids may start with underscore.'
});
var NOT_OPEN = new PouchError({
status: 412,
error: 'precondition_failed',
reason: 'Database not open'
});
var UNKNOWN_ERROR = new PouchError({
status: 500,
error: 'unknown_error',
reason: 'Database encountered an unknown error'
});
var BAD_ARG = new PouchError({
status: 500,
error: 'badarg',
reason: 'Some query argument is invalid'
});
var INVALID_REQUEST = new PouchError({
status: 400,
error: 'invalid_request',
reason: 'Request was invalid'
});
var QUERY_PARSE_ERROR = new PouchError({
status: 400,
error: 'query_parse_error',
reason: 'Some query parameter is invalid'
});
var DOC_VALIDATION = new PouchError({
status: 500,
error: 'doc_validation',
reason: 'Bad special document member'
});
var BAD_REQUEST = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Something wrong with the request'
});
var NOT_AN_OBJECT = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Document must be a JSON object'
});
var DB_MISSING = new PouchError({
status: 404,
error: 'not_found',
reason: 'Database not found'
});
var IDB_ERROR = new PouchError({
status: 500,
error: 'indexed_db_went_bad',
reason: 'unknown'
});
var WSQ_ERROR = new PouchError({
status: 500,
error: 'web_sql_went_bad',
reason: 'unknown'
});
var LDB_ERROR = new PouchError({
status: 500,
error: 'levelDB_went_went_bad',
reason: 'unknown'
});
var FORBIDDEN = new PouchError({
status: 403,
error: 'forbidden',
reason: 'Forbidden by design doc validate_doc_update function'
});
var INVALID_REV = new PouchError({
status: 400,
error: 'bad_request',
reason: 'Invalid rev format'
});
var FILE_EXISTS = new PouchError({
status: 412,
error: 'file_exists',
reason: 'The database could not be created, the file already exists.'
});
var MISSING_STUB = new PouchError({
status: 412,
error: 'missing_stub'
});
var INVALID_URL = new PouchError({
status: 413,
error: 'invalid_url',
reason: 'Provided URL is invalid'
});
function flatten(arrs) {
var res = [];
for (var i = 0, len = arrs.length; i < len; i++) {
res = res.concat(arrs[i]);
}
return res;
}
// this is essentially the "update sugar" function from daleharvey/pouchdb#1388
// the diffFun tells us what delta to apply to the doc. it either returns
// the doc, or false if it doesn't need to do an update after all
function upsert(db, docId, diffFun) {
return new PouchPromise(function (fulfill, reject) {
db.get(docId, function (err, doc) {
if (err) {
/* istanbul ignore next */
if (err.status !== 404) {
return reject(err);
}
doc = {};
}
// the user might change the _rev, so save it for posterity
var docRev = doc._rev;
var newDoc = diffFun(doc);
if (!newDoc) {
// if the diffFun returns falsy, we short-circuit as
// an optimization
return fulfill({updated: false, rev: docRev});
}
// users aren't allowed to modify these values,
// so reset them here
newDoc._id = docId;
newDoc._rev = docRev;
fulfill(tryAndPut(db, newDoc, diffFun));
});
});
}
function tryAndPut(db, doc, diffFun) {
return db.put(doc).then(function (res) {
return {
updated: true,
rev: res.rev
};
}, function (err) {
/* istanbul ignore next */
if (err.status !== 409) {
throw err;
}
return upsert(db, doc._id, diffFun);
});
}
// BEGIN Math.uuid.js
/*!
Math.uuid.js (v1.4)
http://www.broofa.com
mailto:robert@broofa.com
Copyright (c) 2010 Robert Kieffer
Dual licensed under the MIT and GPL licenses.
*/
/*
* Generate a random uuid.
*
* USAGE: Math.uuid(length, radix)
* length - the desired number of characters
* radix - the number of allowable values for each character.
*
* EXAMPLES:
* // No arguments - returns RFC4122, version 4 ID
* >>> Math.uuid()
* "92329D39-6F5C-4520-ABFC-AAB64544E172"
*
* // One argument - returns ID of the specified length
* >>> Math.uuid(15) // 15 character ID (default base=62)
* "VcydxgltxrVZSTV"
*
* // Two arguments - returns ID of the specified length, and radix.
* // (Radix must be <= 62)
* >>> Math.uuid(8, 2) // 8 character ID (base=2)
* "01001010"
* >>> Math.uuid(8, 10) // 8 character ID (base=10)
* "47473046"
* >>> Math.uuid(8, 16) // 8 character ID (base=16)
* "098F4D35"
*/
var chars = (
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz'
).split('');
function typedBuffer(binString, buffType, type) {
// buffType is either 'binary' or 'base64'
var buff = new Buffer(binString, buffType);
buff.type = type; // non-standard, but used for consistency with the browser
return buff;
}
function b64ToBluffer(b64, type) {
return typedBuffer(b64, 'base64', type);
}
function pad(str, padWith, upToLength) {
var padding = '';
var targetLength = upToLength - str.length;
/* istanbul ignore next */
while (padding.length < targetLength) {
padding += padWith;
}
return padding;
}
function padLeft(str, padWith, upToLength) {
var padding = pad(str, padWith, upToLength);
return padding + str;
}
var MIN_MAGNITUDE = -324; // verified by -Number.MIN_VALUE
var MAGNITUDE_DIGITS = 3; // ditto
var SEP = ''; // set to '_' for easier debugging
function collate(a, b) {
if (a === b) {
return 0;
}
a = normalizeKey(a);
b = normalizeKey(b);
var ai = collationIndex(a);
var bi = collationIndex(b);
if ((ai - bi) !== 0) {
return ai - bi;
}
if (a === null) {
return 0;
}
switch (typeof a) {
case 'number':
return a - b;
case 'boolean':
return a === b ? 0 : (a < b ? -1 : 1);
case 'string':
return stringCollate(a, b);
}
return Array.isArray(a) ? arrayCollate(a, b) : objectCollate(a, b);
}
// couch considers null/NaN/Infinity/-Infinity === undefined,
// for the purposes of mapreduce indexes. also, dates get stringified.
function normalizeKey(key) {
switch (typeof key) {
case 'undefined':
return null;
case 'number':
if (key === Infinity || key === -Infinity || isNaN(key)) {
return null;
}
return key;
case 'object':
var origKey = key;
if (Array.isArray(key)) {
var len = key.length;
key = new Array(len);
for (var i = 0; i < len; i++) {
key[i] = normalizeKey(origKey[i]);
}
/* istanbul ignore next */
} else if (key instanceof Date) {
return key.toJSON();
} else if (key !== null) { // generic object
key = {};
for (var k in origKey) {
if (origKey.hasOwnProperty(k)) {
var val = origKey[k];
if (typeof val !== 'undefined') {
key[k] = normalizeKey(val);
}
}
}
}
}
return key;
}
function indexify(key) {
if (key !== null) {
switch (typeof key) {
case 'boolean':
return key ? 1 : 0;
case 'number':
return numToIndexableString(key);
case 'string':
// We've to be sure that key does not contain \u0000
// Do order-preserving replacements:
// 0 -> 1, 1
// 1 -> 1, 2
// 2 -> 2, 2
return key
.replace(/\u0002/g, '\u0002\u0002')
.replace(/\u0001/g, '\u0001\u0002')
.replace(/\u0000/g, '\u0001\u0001');
case 'object':
var isArray = Array.isArray(key);
var arr = isArray ? key : Object.keys(key);
var i = -1;
var len = arr.length;
var result = '';
if (isArray) {
while (++i < len) {
result += toIndexableString(arr[i]);
}
} else {
while (++i < len) {
var objKey = arr[i];
result += toIndexableString(objKey) +
toIndexableString(key[objKey]);
}
}
return result;
}
}
return '';
}
// convert the given key to a string that would be appropriate
// for lexical sorting, e.g. within a database, where the
// sorting is the same given by the collate() function.
function toIndexableString(key) {
var zero = '\u0000';
key = normalizeKey(key);
return collationIndex(key) + SEP + indexify(key) + zero;
}
function parseNumber(str, i) {
var originalIdx = i;
var num;
var zero = str[i] === '1';
if (zero) {
num = 0;
i++;
} else {
var neg = str[i] === '0';
i++;
var numAsString = '';
var magAsString = str.substring(i, i + MAGNITUDE_DIGITS);
var magnitude = parseInt(magAsString, 10) + MIN_MAGNITUDE;
/* istanbul ignore next */
if (neg) {
magnitude = -magnitude;
}
i += MAGNITUDE_DIGITS;
while (true) {
var ch = str[i];
if (ch === '\u0000') {
break;
} else {
numAsString += ch;
}
i++;
}
numAsString = numAsString.split('.');
if (numAsString.length === 1) {
num = parseInt(numAsString, 10);
} else {
/* istanbul ignore next */
num = parseFloat(numAsString[0] + '.' + numAsString[1]);
}
/* istanbul ignore next */
if (neg) {
num = num - 10;
}
/* istanbul ignore next */
if (magnitude !== 0) {
// parseFloat is more reliable than pow due to rounding errors
// e.g. Number.MAX_VALUE would return Infinity if we did
// num * Math.pow(10, magnitude);
num = parseFloat(num + 'e' + magnitude);
}
}
return {num: num, length : i - originalIdx};
}
// move up the stack while parsing
// this function moved outside of parseIndexableString for performance
function pop(stack, metaStack) {
var obj = stack.pop();
if (metaStack.length) {
var lastMetaElement = metaStack[metaStack.length - 1];
if (obj === lastMetaElement.element) {
// popping a meta-element, e.g. an object whose value is another object
metaStack.pop();
lastMetaElement = metaStack[metaStack.length - 1];
}
var element = lastMetaElement.element;
var lastElementIndex = lastMetaElement.index;
if (Array.isArray(element)) {
element.push(obj);
} else if (lastElementIndex === stack.length - 2) { // obj with key+value
var key = stack.pop();
element[key] = obj;
} else {
stack.push(obj); // obj with key only
}
}
}
function parseIndexableString(str) {
var stack = [];
var metaStack = []; // stack for arrays and objects
var i = 0;
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
var collationIndex = str[i++];
if (collationIndex === '\u0000') {
if (stack.length === 1) {
return stack.pop();
} else {
pop(stack, metaStack);
continue;
}
}
switch (collationIndex) {
case '1':
stack.push(null);
break;
case '2':
stack.push(str[i] === '1');
i++;
break;
case '3':
var parsedNum = parseNumber(str, i);
stack.push(parsedNum.num);
i += parsedNum.length;
break;
case '4':
var parsedStr = '';
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
var ch = str[i];
if (ch === '\u0000') {
break;
}
parsedStr += ch;
i++;
}
// perform the reverse of the order-preserving replacement
// algorithm (see above)
parsedStr = parsedStr.replace(/\u0001\u0001/g, '\u0000')
.replace(/\u0001\u0002/g, '\u0001')
.replace(/\u0002\u0002/g, '\u0002');
stack.push(parsedStr);
break;
case '5':
var arrayElement = { element: [], index: stack.length };
stack.push(arrayElement.element);
metaStack.push(arrayElement);
break;
case '6':
var objElement = { element: {}, index: stack.length };
stack.push(objElement.element);
metaStack.push(objElement);
break;
/* istanbul ignore next */
default:
throw new Error(
'bad collationIndex or unexpectedly reached end of input: ' +
collationIndex);
}
}
}
function arrayCollate(a, b) {
var len = Math.min(a.length, b.length);
for (var i = 0; i < len; i++) {
var sort = collate(a[i], b[i]);
if (sort !== 0) {
return sort;
}
}
return (a.length === b.length) ? 0 :
(a.length > b.length) ? 1 : -1;
}
function stringCollate(a, b) {
// See: https://github.com/daleharvey/pouchdb/issues/40
// This is incompatible with the CouchDB implementation, but its the
// best we can do for now
return (a === b) ? 0 : ((a > b) ? 1 : -1);
}
function objectCollate(a, b) {
var ak = Object.keys(a), bk = Object.keys(b);
var len = Math.min(ak.length, bk.length);
for (var i = 0; i < len; i++) {
// First sort the keys
var sort = collate(ak[i], bk[i]);
if (sort !== 0) {
return sort;
}
// if the keys are equal sort the values
sort = collate(a[ak[i]], b[bk[i]]);
if (sort !== 0) {
return sort;
}
}
return (ak.length === bk.length) ? 0 :
(ak.length > bk.length) ? 1 : -1;
}
// The collation is defined by erlangs ordered terms
// the atoms null, true, false come first, then numbers, strings,
// arrays, then objects
// null/undefined/NaN/Infinity/-Infinity are all considered null
function collationIndex(x) {
var id = ['boolean', 'number', 'string', 'object'];
var idx = id.indexOf(typeof x);
//false if -1 otherwise true, but fast!!!!1
if (~idx) {
if (x === null) {
return 1;
}
if (Array.isArray(x)) {
return 5;
}
return idx < 3 ? (idx + 2) : (idx + 3);
}
/* istanbul ignore next */
if (Array.isArray(x)) {
return 5;
}
}
// conversion:
// x yyy zz...zz
// x = 0 for negative, 1 for 0, 2 for positive
// y = exponent (for negative numbers negated) moved so that it's >= 0
// z = mantisse
function numToIndexableString(num) {
if (num === 0) {
return '1';
}
// convert number to exponential format for easier and
// more succinct string sorting
var expFormat = num.toExponential().split(/e\+?/);
var magnitude = parseInt(expFormat[1], 10);
var neg = num < 0;
var result = neg ? '0' : '2';
// first sort by magnitude
// it's easier if all magnitudes are positive
var magForComparison = ((neg ? -magnitude : magnitude) - MIN_MAGNITUDE);
var magString = padLeft((magForComparison).toString(), '0', MAGNITUDE_DIGITS);
result += SEP + magString;
// then sort by the factor
var factor = Math.abs(parseFloat(expFormat[0])); // [1..10)
/* istanbul ignore next */
if (neg) { // for negative reverse ordering
factor = 10 - factor;
}
var factorStr = factor.toFixed(20);
// strip zeros from the end
factorStr = factorStr.replace(/\.?0+$/, '');
result += SEP + factorStr;
return result;
}
/*
* Simple task queue to sequentialize actions. Assumes

@@ -21,3 +811,3 @@ * callbacks will eventually fire (once).

function TaskQueue() {
this.promise = new Promise(function (fulfill) {fulfill(); });
this.promise = new PouchPromise(function (fulfill) {fulfill(); });
}

@@ -36,2 +826,6 @@ TaskQueue.prototype.add = function (promiseFactory) {

function stringMd5(string) {
return crypto.createHash('md5').update(string, 'binary').digest('hex');
}
function createView(opts) {

@@ -60,3 +854,3 @@ var sourceDB = opts.db;

var depDbName = info.db_name + '-mrview-' +
(temporary ? 'temp' : pouchdbMd5.stringMd5(viewSignature));
(temporary ? 'temp' : stringMd5(viewSignature));

@@ -79,3 +873,3 @@ // save the view name in the source db so it can be cleaned up if necessary

}
return pouchdbUtils.upsert(sourceDB, '_local/mrviews', diffFunction).then(function () {
return upsert(sourceDB, '_local/mrviews', diffFunction).then(function () {
return sourceDB.registerDependentDatabase(depDbName).then(function (res) {

@@ -129,2 +923,69 @@ var db = res.db;

var promisedCallback = function (promise, callback) {
if (callback) {
promise.then(function (res) {
process.nextTick(function () {
callback(null, res);
});
}, function (reason) {
process.nextTick(function () {
callback(reason);
});
});
}
return promise;
};
var callbackify = function (fun) {
return getArguments(function (args) {
var cb = args.pop();
var promise = fun.apply(this, args);
if (typeof cb === 'function') {
promisedCallback(promise, cb);
}
return promise;
});
};
// Promise finally util similar to Q.finally
var fin = function (promise, finalPromiseFactory) {
return promise.then(function (res) {
return finalPromiseFactory().then(function () {
return res;
});
}, function (reason) {
return finalPromiseFactory().then(function () {
throw reason;
});
});
};
var sequentialize = function (queue, promiseFactory) {
return function () {
var args = arguments;
var that = this;
return queue.add(function () {
return promiseFactory.apply(that, args);
});
};
};
// uniq an array of strings, order not guaranteed
// similar to underscore/lodash _.uniq
var uniq = function (arr) {
var map = {};
for (var i = 0, len = arr.length; i < len; i++) {
map['$' + arr[i]] = true;
}
var keys = Object.keys(map);
var output = new Array(keys.length);
for (i = 0, len = keys.length; i < len; i++) {
output[i] = keys[i].substring(1);
}
return output;
};
var persistentQueues = {};

@@ -134,3 +995,3 @@ var tempViewQueue = new TaskQueue();

var log = pouchdbUtils.guardedConsole.bind(null, 'log');
var log = guardedConsole.bind(null, 'log');

@@ -153,3 +1014,3 @@ function parseViewName(name) {

} catch (err) {
pouchdbUtils.guardedConsole('error',
guardedConsole('error',
'The user\'s map/reduce function threw an uncaught error.\n' +

@@ -159,3 +1020,3 @@ 'You can debug this error by doing:\n' +

'Please double-check your map/reduce function.');
pouchdbUtils.guardedConsole('error', e);
guardedConsole('error', e);
}

@@ -178,4 +1039,4 @@ }

function sortByKeyThenValue(x, y) {
var keyCompare = pouchdbCollate.collate(x.key, y.key);
return keyCompare !== 0 ? keyCompare : pouchdbCollate.collate(x.value, y.value);
var keyCompare = collate(x.key, y.key);
return keyCompare !== 0 ? keyCompare : collate(x.value, y.value);
}

@@ -209,3 +1070,3 @@

var att = atts[filename];
atts[filename].data = pouchdbBinaryUtils.base64StringToBlobOrBuffer(att.data, att.content_type);
atts[filename].data = b64ToBluffer(att.data, att.content_type);
});

@@ -340,3 +1201,3 @@ });

typeof options[endkeyName] !== 'undefined' &&
pouchdbCollate.collate(options[startkeyName], options[endkeyName]) > 0) {
collate(options[startkeyName], options[endkeyName]) > 0) {
throw new QueryParseError('No rows can match your key range, ' +

@@ -445,3 +1306,3 @@ 'reverse your start_key and end_key or set {descending : true}');

function customQuery(db, fun, opts) {
return new Promise(function (resolve, reject) {
return new PouchPromise(function (resolve, reject) {
db._query(fun, opts, function (err, res) {

@@ -460,3 +1321,3 @@ if (err) {

function customViewCleanup(db) {
return new Promise(function (resolve, reject) {
return new PouchPromise(function (resolve, reject) {
db._viewCleanup(function (err, res) {

@@ -496,3 +1357,3 @@ if (err) {

// for performance reasons (avoids unnecessary GETs)
return Promise.resolve(defaultMetaDoc);
return PouchPromise.resolve(defaultMetaDoc);
}

@@ -505,3 +1366,3 @@ return view.db.get(metaDocId).catch(defaultsTo(defaultMetaDoc));

// no keys, no need for a lookup
return Promise.resolve({rows: []});
return PouchPromise.resolve({rows: []});
}

@@ -549,3 +1410,3 @@ return view.db.allDocs({

});
metaDoc.keys = pouchdbMapreduceUtils.uniq(newKeys.concat(metaDoc.keys));
metaDoc.keys = uniq(newKeys.concat(metaDoc.keys));
kvDocs.push(metaDoc);

@@ -571,6 +1432,6 @@

var docIds = Object.keys(docIdsToChangesAndEmits);
return Promise.all(docIds.map(function (docId) {
return PouchPromise.all(docIds.map(function (docId) {
return getDocsToPersist(docId, view, docIdsToChangesAndEmits);
})).then(function (listOfDocsToPersist) {
var docsToPersist = pouchdbUtils.flatten(listOfDocsToPersist);
var docsToPersist = flatten(listOfDocsToPersist);
lastSeqDoc.seq = seq;

@@ -594,3 +1455,3 @@ docsToPersist.push(lastSeqDoc);

function updateView(view) {
return pouchdbMapreduceUtils.sequentialize(getQueue(view), function () {
return sequentialize(getQueue(view), function () {
return updateViewInQueue(view);

@@ -606,7 +1467,7 @@ })();

function emit(key, value) {
var output = {id: doc._id, key: pouchdbCollate.normalizeKey(key)};
var output = {id: doc._id, key: normalizeKey(key)};
// Don't explicitly store the value unless it's defined and non-null.
// This saves on storage space, because often people don't use it.
if (typeof value !== 'undefined' && value !== null) {
output.value = pouchdbCollate.normalizeKey(value);
output.value = normalizeKey(value);
}

@@ -639,3 +1500,3 @@ mapResults.push(output);

return new Promise(function (resolve, reject) {
return new PouchPromise(function (resolve, reject) {

@@ -678,6 +1539,6 @@ function complete() {

var complexKey = [obj.key, obj.id];
if (pouchdbCollate.collate(obj.key, lastKey) === 0) {
if (collate(obj.key, lastKey) === 0) {
complexKey.push(j); // dup key+id, so make it unique
}
var indexableKey = pouchdbCollate.toIndexableString(complexKey);
var indexableKey = toIndexableString(complexKey);
indexableKeysToKeyValues[indexableKey] = obj;

@@ -736,3 +1597,3 @@ lastKey = obj.key;

if (last && pouchdbCollate.collate(last.groupKey, groupKey) === 0) {
if (last && collate(last.groupKey, groupKey) === 0) {
last.keys.push([e.key, e.id]);

@@ -768,3 +1629,3 @@ last.values.push(e.value);

function queryView(view, opts) {
return pouchdbMapreduceUtils.sequentialize(getQueue(view), function () {
return sequentialize(getQueue(view), function () {
return queryViewInQueue(view, opts);

@@ -805,3 +1666,3 @@ })();

var parsedKeyAndDocId = pouchdbCollate.parseIndexableString(result.doc._id);
var parsedKeyAndDocId = parseIndexableString(result.doc._id);
return {

@@ -828,3 +1689,3 @@ key: parsedKeyAndDocId[0],

if (opts.include_docs) {
var docIds = pouchdbMapreduceUtils.uniq(rows.map(rowToDocId));
var docIds = uniq(rows.map(rowToDocId));

@@ -862,8 +1723,8 @@ return view.sourceDB.allDocs({

var viewOpts = {
startkey : pouchdbCollate.toIndexableString([key]),
endkey : pouchdbCollate.toIndexableString([key, {}])
startkey : toIndexableString([key]),
endkey : toIndexableString([key, {}])
};
return fetchFromView(viewOpts);
});
return Promise.all(fetchPromises).then(pouchdbUtils.flatten).then(onMapResultsReady);
return PouchPromise.all(fetchPromises).then(flatten).then(onMapResultsReady);
} else { // normal query, no 'keys'

@@ -881,4 +1742,4 @@ var viewOpts = {

viewOpts.startkey = opts.descending ?
pouchdbCollate.toIndexableString([opts.startkey, {}]) :
pouchdbCollate.toIndexableString([opts.startkey]);
toIndexableString([opts.startkey, {}]) :
toIndexableString([opts.startkey]);
}

@@ -891,8 +1752,8 @@ if (typeof opts.endkey !== 'undefined') {

viewOpts.endkey = pouchdbCollate.toIndexableString(
viewOpts.endkey = toIndexableString(
inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]);
}
if (typeof opts.key !== 'undefined') {
var keyStart = pouchdbCollate.toIndexableString([opts.key]);
var keyEnd = pouchdbCollate.toIndexableString([opts.key, {}]);
var keyStart = toIndexableString([opts.key]);
var keyEnd = toIndexableString([opts.key, {}]);
if (viewOpts.descending) {

@@ -962,7 +1823,7 @@ viewOpts.endkey = keyStart;

var destroyPromises = dbsToDelete.map(function (viewDBName) {
return pouchdbMapreduceUtils.sequentialize(getQueue(viewDBName), function () {
return sequentialize(getQueue(viewDBName), function () {
return new db.constructor(viewDBName, db.__opts).destroy();
})();
});
return Promise.all(destroyPromises).then(function () {
return PouchPromise.all(destroyPromises).then(function () {
return {ok: true};

@@ -974,3 +1835,3 @@ });

var viewCleanup = pouchdbMapreduceUtils.callbackify(function () {
var viewCleanup = callbackify(function () {
var db = this;

@@ -1013,3 +1874,3 @@ if (db.type() === 'http') {

}
return pouchdbMapreduceUtils.fin(updateView(view).then(function () {
return fin(updateView(view).then(function () {
return queryView(view, opts);

@@ -1071,6 +1932,6 @@ }), cleanup);

var db = this;
var promise = Promise.resolve().then(function () {
var promise = PouchPromise.resolve().then(function () {
return queryPromised(db, fun, opts);
});
pouchdbMapreduceUtils.promisedCallback(promise, callback);
promisedCallback(promise, callback);
return promise;

@@ -1077,0 +1938,0 @@ };

18

package.json
{
"name": "pouchdb-mapreduce",
"version": "6.0.2",
"version": "6.0.3",
"description": "PouchDB's map/reduce query API as a plugin.",

@@ -17,9 +17,9 @@ "main": "./lib/index.js",

"inherits": "2.0.1",
"pouchdb-binary-utils": "6.0.2",
"pouchdb-collate": "6.0.2",
"pouchdb-errors": "6.0.2",
"pouchdb-mapreduce-utils": "6.0.2",
"pouchdb-md5": "6.0.2",
"pouchdb-promise": "6.0.2",
"pouchdb-utils": "6.0.2",
"pouchdb-binary-utils": "6.0.3",
"pouchdb-collate": "6.0.3",
"pouchdb-errors": "6.0.3",
"pouchdb-mapreduce-utils": "6.0.3",
"pouchdb-md5": "6.0.3",
"pouchdb-promise": "6.0.3",
"pouchdb-utils": "6.0.3",
"scope-eval": "0.0.3",

@@ -32,4 +32,4 @@ "spark-md5": "2.0.2"

"peerDependencies": {
"pouchdb-core": "6.0.2"
"pouchdb-core": "6.0.3"
}
}