return Promise.all(bulkGetResponse.results.map(function (bulkGetInfo) { return Promise.all(bulkGetInfo.docs.map(function (doc) { var remoteDoc = doc.ok; if (doc.error) { // when AUTO_COMPACTION is set, docs can be returned which look // like this: {"missing":"1-7c3ac256b693c462af8442f992b83696"} ok = false; } if (!remoteDoc || !remoteDoc._attachments) { return remoteDoc; } return getDocAttachmentsFromTargetOrSource(target, src, remoteDoc).then(function (attachments) { var filenames = Object.keys(remoteDoc._attachments); attachments.forEach(function (attachment, i) { var att = remoteDoc._attachments[filenames[i]]; delete att.stub; delete att.length; att.data = attachment; }); return remoteDoc; }); })); }))
function allDocsKeysQuery(api, opts, callback) { var keys = ('limit' in opts) ? opts.keys.slice(opts.skip, opts.limit + opts.skip) : (opts.skip > 0) ? opts.keys.slice(opts.skip) : opts.keys; if (opts.descending) { keys.reverse(); } if (!keys.length) { return api._allDocs({limit: 0}, callback); } var finalResults = { offset: opts.skip }; return Promise.all(keys.map(function (key) { var subOpts = extend({key: key, deleted: 'ok'}, opts); ['limit', 'skip', 'keys'].forEach(function (optKey) { delete subOpts[optKey]; }); return new Promise(function (resolve, reject) { api._allDocs(subOpts, function (err, res) { /* istanbul ignore if */ if (err) { return reject(err); } finalResults.total_rows = res.total_rows; resolve(res.rows[0] || {key: key, error: 'not_found'}); }); }); })).then(function (results) { finalResults.rows = results; return finalResults; }); }
.then((ddb) => { var ddbAlso = new PouchDB('all-changes', {db: require('memdown')}) return Promise.all([ ddb.allDocs({include_docs: true}), ddbAlso.allDocs({include_docs: true}) ]) })
function fetchAttachments(doc) { var atts = doc._attachments; var filenames = atts && Object.keys(atts); if (!atts || !filenames.length) { return; } // we fetch these manually in separate XHRs, because // Sync Gateway would normally send it back as multipart/mixed, // which we cannot parse. Also, this is more efficient than // receiving attachments as base64-encoded strings. return Promise.all(filenames.map(function (filename) { var att = atts[filename]; var path = encodeDocId(doc._id) + '/' + encodeAttachmentId(filename) + '?rev=' + doc._rev; return ajaxPromise(opts, { method: 'GET', url: genDBUrl(host, path), binary: true }).then(function (blob) { if (opts.binary) { return blob; } return new Promise(function (resolve) { blufferToBase64(blob, resolve); }); }).then(function (data) { delete att.stub; delete att.length; att.data = data; }); })); }
return db.allDocs(opts).then(function (res) { var viewsToStatus = {}; res.rows.forEach(function (row) { var ddocName = row.key.substring(8); Object.keys(docsToViews[row.key]).forEach(function (viewName) { var fullViewName = ddocName + '/' + viewName; /* istanbul ignore if */ if (!metaDoc.views[fullViewName]) { // new format, without slashes, to support PouchDB 2.2.0 // migration test in pouchdb's browser.migration.js verifies this fullViewName = viewName; } var viewDBNames = Object.keys(metaDoc.views[fullViewName]); // design doc deleted, or view function nonexistent var statusIsGood = row.doc && row.doc.views && row.doc.views[viewName]; viewDBNames.forEach(function (viewDBName) { viewsToStatus[viewDBName] = viewsToStatus[viewDBName] || statusIsGood; }); }); }); var dbsToDelete = Object.keys(viewsToStatus).filter( function (viewDBName) { return !viewsToStatus[viewDBName]; }); var destroyPromises = dbsToDelete.map(function (viewDBName) { return sequentialize(getQueue(viewDBName), function () { return new db.constructor(viewDBName, db.__opts).destroy(); })(); }); return Promise.all(destroyPromises).then(function () { return {ok: true}; }); });
return target.get(doc._id).then(function (localDoc) { return Promise.all(filenames.map(function (filename) { if (fileHasChanged(localDoc, doc, filename)) { return src.getAttachment(doc._id, filename); } return target.getAttachment(localDoc._id, filename); })); }).catch(function (error) {
function fetchAllAttachments(docOrDocs) { if (Array.isArray(docOrDocs)) { return Promise.all(docOrDocs.map(function (doc) { if (doc.ok) { return fetchAttachments(doc.ok); } })); } return fetchAttachments(docOrDocs); }
.then(function (lastSeqDoc) { var docIds = Object.keys(docIdsToChangesAndEmits); return Promise.all(docIds.map(function (docId) { return getDocsToPersist(docId, view, docIdsToChangesAndEmits); })).then(function (listOfDocsToPersist) { var docsToPersist = flatten(listOfDocsToPersist); lastSeqDoc.seq = seq; docsToPersist.push(lastSeqDoc); // write all docs in a single operation, update the seq once return view.db.bulkDocs({docs : docsToPersist}); }); });
function onComplete(resp) { var lastSeq = resp.last_seq; Promise.all(promises).then(function () { return upsert(self, '_local/compaction', function deltaFunc(doc) { if (!doc.last_seq || doc.last_seq < lastSeq) { doc.last_seq = lastSeq; return doc; } return false; // somebody else got here first, don't update }); }).then(function () { callback(null, {ok: true}); }).catch(callback); }
module.exports = function buildRequestObject(db, pathEnd, options) { var infoPromise = db.info(); var pathPromise = infoPromise.then(function (info) { pathEnd.unshift(encodeURIComponent(info.db_name)); return normalizePath(pathEnd); }); var userCtxPromise = infoPromise.then(buildUserContextObject); return Promise.all([pathPromise, infoPromise, userCtxPromise]).then(function (args) { args.push(getHost(db)); args.push(uuid()); args.push(options); return actuallyBuildRequestObject.apply(null, args); }); };
function preprocessAttachments(doc) { if (!doc._attachments || !Object.keys(doc._attachments)) { return Promise.resolve(); } return Promise.all(Object.keys(doc._attachments).map(function (key) { var attachment = doc._attachments[key]; if (attachment.data && typeof attachment.data !== 'string') { return new Promise(function (resolve) { blufferToBase64(attachment.data, resolve); }).then(function (b64) { attachment.data = b64; }); } })); }
function fetchAttachments(results, stores, opts) { var atts = []; results.forEach(function (row) { if (!(row.doc && row.doc._attachments)) { return; } var attNames = Object.keys(row.doc._attachments); attNames.forEach(function (attName) { var att = row.doc._attachments[attName]; if (!('data' in att)) { atts.push(att); } }); }); return Promise.all(atts.map(function (att) { return fetchAttachment(att, stores, opts); })); }
self.get('_local/_pouch_dependentDbs', function (err, localDoc) { if (err) { /* istanbul ignore if */ if (err.status !== 404) { return callback(err); } else { // no dependencies return destroyDb(); } } var dependentDbs = localDoc.dependentDbs; var PouchDB = self.constructor; var deletedMap = Object.keys(dependentDbs).map(function (name) { // use_prefix is only false in the browser /* istanbul ignore next */ var trueName = usePrefix ? name.replace(new RegExp('^' + PouchDB.prefix), '') : name; return new PouchDB(trueName, self.__opts).destroy(); }); Promise.all(deletedMap).then(destroyDb, callback); });
var derivator = function (params) { return Promise.all([ upsert(params.db, 'sum', function (doc) { doc.val = doc.val || 0 doc.val -= params.change.doc._rev.slice(0, 2) !== '1-' ? params.change.doc.val : 0 if (!params.change.deleted) { doc.val += params.change.doc.val } return doc }), upsert(params.db, 'count', function (doc) { doc.val = doc.val || 0 if (params.change.deleted) { doc.val -= 1 } else if (params.change.doc._rev.slice(0, 2) === '1-') { doc.val += 1 } return doc }) ]) }
return src.bulkGet(bulkGetOpts).then(function (bulkGetResponse) { /* istanbul ignore if */ if (state.cancelled) { throw new Error('cancelled'); } return Promise.all(bulkGetResponse.results.map(function (bulkGetInfo) { return Promise.all(bulkGetInfo.docs.map(function (doc) { var remoteDoc = doc.ok; if (doc.error) { // when AUTO_COMPACTION is set, docs can be returned which look // like this: {"missing":"1-7c3ac256b693c462af8442f992b83696"} ok = false; } if (!remoteDoc || !remoteDoc._attachments) { return remoteDoc; } return getDocAttachmentsFromTargetOrSource(target, src, remoteDoc).then(function (attachments) { var filenames = Object.keys(remoteDoc._attachments); attachments.forEach(function (attachment, i) { var att = remoteDoc._attachments[filenames[i]]; delete att.stub; delete att.length; att.data = attachment; }); return remoteDoc; }); })); })) .then(function (results) { resultDocs = resultDocs.concat(flatten(results).filter(Boolean)); }); });
function queryViewInQueue(view, opts) { var totalRows; var shouldReduce = view.reduceFun && opts.reduce !== false; var skip = opts.skip || 0; if (typeof opts.keys !== 'undefined' && !opts.keys.length) { // equivalent query opts.limit = 0; delete opts.keys; } function fetchFromView(viewOpts) { viewOpts.include_docs = true; return view.db.allDocs(viewOpts).then(function (res) { totalRows = res.total_rows; return res.rows.map(function (result) { // implicit migration - in older versions of PouchDB, // we explicitly stored the doc as {id: ..., key: ..., value: ...} // this is tested in a migration test /* istanbul ignore next */ if ('value' in result.doc && typeof result.doc.value === 'object' && result.doc.value !== null) { var keys = Object.keys(result.doc.value).sort(); // this detection method is not perfect, but it's unlikely the user // emitted a value which was an object with these 3 exact keys var expectedKeys = ['id', 'key', 'value']; if (!(keys < expectedKeys || keys > expectedKeys)) { return result.doc.value; } } var parsedKeyAndDocId = parseIndexableString(result.doc._id); return { key: parsedKeyAndDocId[0], id: parsedKeyAndDocId[1], value: ('value' in result.doc ? result.doc.value : null) }; }); }); } function onMapResultsReady(rows) { var finalResults; if (shouldReduce) { finalResults = reduceView(view, rows, opts); } else { finalResults = { total_rows: totalRows, offset: skip, rows: rows }; } if (opts.include_docs) { var docIds = uniq(rows.map(rowToDocId)); return view.sourceDB.allDocs({ keys: docIds, include_docs: true, conflicts: opts.conflicts, attachments: opts.attachments, binary: opts.binary }).then(function (allDocsRes) { var docIdsToDocs = {}; allDocsRes.rows.forEach(function (row) { if (row.doc) { docIdsToDocs['$' + row.id] = row.doc; } }); rows.forEach(function (row) { var docId = rowToDocId(row); var doc = docIdsToDocs['$' + docId]; if (doc) { row.doc = doc; } }); return finalResults; }); } else { return finalResults; } } if (typeof opts.keys !== 'undefined') { var keys = opts.keys; var fetchPromises = keys.map(function (key) { var viewOpts = { startkey : toIndexableString([key]), endkey : toIndexableString([key, {}]) }; return fetchFromView(viewOpts); }); return Promise.all(fetchPromises).then(flatten).then(onMapResultsReady); } else { // normal query, no 'keys' var viewOpts = { descending : opts.descending }; if (opts.start_key) { opts.startkey = opts.start_key; } if (opts.end_key) { opts.endkey = opts.end_key; } if (typeof opts.startkey !== 'undefined') { viewOpts.startkey = opts.descending ? toIndexableString([opts.startkey, {}]) : toIndexableString([opts.startkey]); } if (typeof opts.endkey !== 'undefined') { var inclusiveEnd = opts.inclusive_end !== false; if (opts.descending) { inclusiveEnd = !inclusiveEnd; } viewOpts.endkey = toIndexableString( inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]); } if (typeof opts.key !== 'undefined') { var keyStart = toIndexableString([opts.key]); var keyEnd = toIndexableString([opts.key, {}]); if (viewOpts.descending) { viewOpts.endkey = keyStart; viewOpts.startkey = keyEnd; } else { viewOpts.startkey = keyStart; viewOpts.endkey = keyEnd; } } if (!shouldReduce) { if (typeof opts.limit === 'number') { viewOpts.limit = opts.limit; } viewOpts.skip = skip; } return fetchFromView(viewOpts).then(onMapResultsReady); } }
function Sync(src, target, opts, callback) { var self = this; this.canceled = false; var optsPush = opts.push ? extend({}, opts, opts.push) : opts; var optsPull = opts.pull ? extend({}, opts, opts.pull) : opts; this.push = replicate(src, target, optsPush); this.pull = replicate(target, src, optsPull); this.pushPaused = true; this.pullPaused = true; function pullChange(change) { self.emit('change', { direction: 'pull', change: change }); } function pushChange(change) { self.emit('change', { direction: 'push', change: change }); } function pushDenied(doc) { self.emit('denied', { direction: 'push', doc: doc }); } function pullDenied(doc) { self.emit('denied', { direction: 'pull', doc: doc }); } function pushPaused() { self.pushPaused = true; /* istanbul ignore if */ if (self.pullPaused) { self.emit('paused'); } } function pullPaused() { self.pullPaused = true; /* istanbul ignore if */ if (self.pushPaused) { self.emit('paused'); } } function pushActive() { self.pushPaused = false; /* istanbul ignore if */ if (self.pullPaused) { self.emit('active', { direction: 'push' }); } } function pullActive() { self.pullPaused = false; /* istanbul ignore if */ if (self.pushPaused) { self.emit('active', { direction: 'pull' }); } } var removed = {}; function removeAll(type) { // type is 'push' or 'pull' return function (event, func) { var isChange = event === 'change' && (func === pullChange || func === pushChange); var isDenied = event === 'denied' && (func === pullDenied || func === pushDenied); var isPaused = event === 'paused' && (func === pullPaused || func === pushPaused); var isActive = event === 'active' && (func === pullActive || func === pushActive); if (isChange || isDenied || isPaused || isActive) { if (!(event in removed)) { removed[event] = {}; } removed[event][type] = true; if (Object.keys(removed[event]).length === 2) { // both push and pull have asked to be removed self.removeAllListeners(event); } } }; } if (opts.live) { this.push.on('complete', self.pull.cancel.bind(self.pull)); this.pull.on('complete', self.push.cancel.bind(self.push)); } this.on('newListener', function (event) { if (event === 'change') { self.pull.on('change', pullChange); self.push.on('change', pushChange); } else if (event === 'denied') { self.pull.on('denied', pullDenied); self.push.on('denied', pushDenied); } else if (event === 'active') { self.pull.on('active', pullActive); self.push.on('active', pushActive); } else if (event === 'paused') { self.pull.on('paused', pullPaused); self.push.on('paused', pushPaused); } }); this.on('removeListener', function (event) { if (event === 'change') { self.pull.removeListener('change', pullChange); self.push.removeListener('change', pushChange); } else if (event === 'denied') { self.pull.removeListener('denied', pullDenied); self.push.removeListener('denied', pushDenied); } else if (event === 'active') { self.pull.removeListener('active', pullActive); self.push.removeListener('active', pushActive); } else if (event === 'paused') { self.pull.removeListener('paused', pullPaused); self.push.removeListener('paused', pushPaused); } }); this.pull.on('removeListener', removeAll('pull')); this.push.on('removeListener', removeAll('push')); var promise = Promise.all([ this.push, this.pull ]).then(function (resp) { var out = { push: resp[0], pull: resp[1] }; self.emit('complete', out); if (callback) { callback(null, out); } self.removeAllListeners(); return out; }, function (err) { self.cancel(); if (callback) { // if there's a callback, then the callback can receive // the error event callback(err); } else { // if there's no callback, then we're safe to emit an error // event, which would otherwise throw an unhandled error // due to 'error' being a special event in EventEmitters self.emit('error', err); } self.removeAllListeners(); if (callback) { // no sense throwing if we're already emitting an 'error' event throw err; } }); this.then = function (success, err) { return promise.then(success, err); }; this.catch = function (err) { return promise.catch(err); }; }
function getDocAttachments(db, doc) { var filenames = Object.keys(doc._attachments); return Promise.all(filenames.map(function (filename) { return db.getAttachment(doc._id, filename, {rev: doc._rev}); })); }
}).map(function (row) { var obj = fromRawDoc(row.doc); foundObjects.get(type).set(JSON.stringify(obj.id), obj); // fetch all relations var subTasks = []; Object.keys(typeInfo.relations || {}).forEach(function (field) { var relationDef = typeInfo.relations[field]; var relationType = Object.keys(relationDef)[0]; var relatedType = relationDef[relationType]; if (typeof relatedType !== 'string') { var relationOptions = relatedType.options; if (relationOptions && relationOptions.async) { return; } relatedType = relatedType.type; } if (relationType === 'belongsTo') { var relatedId = obj[field]; if (typeof relatedId !== 'undefined') { subTasks.push(Promise.resolve().then(function () { // short-circuit if it's already in the foundObjects // else we could get caught in an infinite loop if (foundObjects.has(relatedType) && foundObjects.get(relatedType).has(JSON.stringify(relatedId))) { return; } // signal that we need to fetch it return { relatedType: relatedType, relatedIds: [relatedId] }; })); } } else { // hasMany var relatedIds = extend(true, [], obj[field]); if (typeof relatedIds !== 'undefined' && relatedIds.length) { subTasks.push(Promise.resolve().then(function () { // filter out all ids that are already in the foundObjects for (var i = relatedIds.length - 1; i >= 0; i--) { var relatedId = relatedIds[i]; if (foundObjects.has(relatedType) && foundObjects.get(relatedType).has(JSON.stringify(relatedId))) { delete relatedIds[i]; } } relatedIds = relatedIds.filter(function (relatedId) { return typeof relatedId !== 'undefined'; }); // just return the ids and the types. We'll find them all // in a single bulk operation in order to minimize HTTP requests if (relatedIds.length) { return { relatedType: relatedType, relatedIds: relatedIds }; } })); } } }); return Promise.all(subTasks); });
setup().then(function () { return Promise.all(req.docs.map(preprocessAttachments)); }).then(function () {