pouchdb-checkpointer
Advanced tools
Comparing version 6.0.2 to 6.0.3
485
lib/index.js
@@ -5,6 +5,477 @@ 'use strict'; | ||
var Promise = _interopDefault(require('pouchdb-promise')); | ||
var pouchdbUtils = require('pouchdb-utils'); | ||
var pouchdbCollate = require('pouchdb-collate'); | ||
var lie = _interopDefault(require('lie')); | ||
var getArguments = _interopDefault(require('argsarray')); | ||
var debug = _interopDefault(require('debug')); | ||
var events = require('events'); | ||
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 = 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); | ||
}; | ||
// We assume Node users don't need to see this warning | ||
var res = function () {}; | ||
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' | ||
}); | ||
// 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(''); | ||
// 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 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; | ||
} | ||
} | ||
var CHECKPOINT_VERSION = 1; | ||
@@ -26,3 +497,3 @@ var REPLICATOR = "pouchdb"; | ||
if (db.type() === 'http') { | ||
pouchdbUtils.explainError( | ||
res( | ||
404, 'PouchDB is just checking if a remote checkpoint exists.' | ||
@@ -104,3 +575,3 @@ ); | ||
if (this.readOnlySource) { | ||
return Promise.resolve(true); | ||
return PouchPromise.resolve(true); | ||
} | ||
@@ -121,3 +592,3 @@ return updateCheckpoint(this.src, this.id, checkpoint, | ||
// This is the previous comparison function | ||
if (pouchdbCollate.collate(targetDoc.last_seq, sourceDoc.last_seq) === 0) { | ||
if (collate(targetDoc.last_seq, sourceDoc.last_seq) === 0) { | ||
return sourceDoc.last_seq; | ||
@@ -138,3 +609,3 @@ } | ||
if (self.readOnlySource) { | ||
return Promise.resolve(targetDoc.last_seq); | ||
return PouchPromise.resolve(targetDoc.last_seq); | ||
} | ||
@@ -141,0 +612,0 @@ |
{ | ||
"name": "pouchdb-checkpointer", | ||
"version": "6.0.2", | ||
"version": "6.0.3", | ||
"description": "PouchDB tool to write a checkpoint, e.g. during replication.", | ||
@@ -16,6 +16,6 @@ "main": "./lib/index.js", | ||
"dependencies": { | ||
"pouchdb-collate": "6.0.2", | ||
"pouchdb-promise": "6.0.2", | ||
"pouchdb-utils": "6.0.2" | ||
"pouchdb-collate": "6.0.3", | ||
"pouchdb-promise": "6.0.3", | ||
"pouchdb-utils": "6.0.3" | ||
} | ||
} |
37322
847
+ Addedpouchdb-binary-utils@6.0.3(transitive)
+ Addedpouchdb-collate@6.0.3(transitive)
+ Addedpouchdb-collections@6.0.3(transitive)
+ Addedpouchdb-errors@6.0.3(transitive)
+ Addedpouchdb-md5@6.0.3(transitive)
+ Addedpouchdb-promise@6.0.3(transitive)
+ Addedpouchdb-utils@6.0.3(transitive)
- Removedpouchdb-binary-utils@6.0.2(transitive)
- Removedpouchdb-collate@6.0.2(transitive)
- Removedpouchdb-collections@6.0.2(transitive)
- Removedpouchdb-errors@6.0.2(transitive)
- Removedpouchdb-md5@6.0.2(transitive)
- Removedpouchdb-promise@6.0.2(transitive)
- Removedpouchdb-utils@6.0.2(transitive)
Updatedpouchdb-collate@6.0.3
Updatedpouchdb-promise@6.0.3
Updatedpouchdb-utils@6.0.3