UNPKG

27.8 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _typeof2 = require("babel-runtime/helpers/typeof");
8
9var _typeof3 = _interopRequireDefault(_typeof2);
10
11var _keys = require("babel-runtime/core-js/object/keys");
12
13var _keys2 = _interopRequireDefault(_keys);
14
15var _regenerator = require("babel-runtime/regenerator");
16
17var _regenerator2 = _interopRequireDefault(_regenerator);
18
19var _asyncToGenerator2 = require("babel-runtime/helpers/asyncToGenerator");
20
21var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
22
23exports.default = setupCache;
24
25var _pouchdb = require("pouchdb");
26
27var _pouchdb2 = _interopRequireDefault(_pouchdb);
28
29function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
30
31var pretty = require('prettyjson');
32var Promise = require("bluebird");
33
34var url = require("url");
35var _ = require("lodash");
36var pointer = require("json-pointer");
37var OFFLINE = false;
38
39function setupCache(_ref) {
40 var getResFromServer = function () {
41 var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(req) {
42 var res;
43 return _regenerator2.default.wrap(function _callee$(_context) {
44 while (1) {
45 switch (_context.prev = _context.next) {
46 case 0:
47 _context.next = 2;
48 return request({
49 method: 'GET',
50 url: req.url,
51 headers: req.headers
52 });
53
54 case 2:
55 res = _context.sent;
56
57 res.cached = false;
58 req.data = res.data;
59 _context.next = 7;
60 return dbUpsert(req);
61
62 case 7:
63 return _context.abrupt("return", res);
64
65 case 8:
66 case "end":
67 return _context.stop();
68 }
69 }
70 }, _callee, this);
71 }));
72
73 return function getResFromServer(_x) {
74 return _ref2.apply(this, arguments);
75 };
76 }();
77
78 // Perform lookup from bookmarks to resource id (and path leftover) mapping.
79 // If the lookup fails, use a HEAD request to get it from the server and put
80 // it in the cache. An optional _id can be passed into req to force creation
81 // of a particular lookup in the event that the resource doesn't yet exist but will.
82 // This is primarily for when links are created before the resource itself has been.
83
84
85 // Accepts an axios-style request. Returns:
86 // {
87 //
88 // data: the data requested,
89 //
90 // _rev: the rev of the parent resource requested
91 //
92 // location: e.g.: /resources/abc123/some/path/leftover
93 //
94 // }
95 var get = function () {
96 var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(req) {
97 var urlObj, newReq, lookup;
98 return _regenerator2.default.wrap(function _callee3$(_context3) {
99 while (1) {
100 switch (_context3.prev = _context3.next) {
101 case 0:
102 urlObj = url.parse(req.url);
103 newReq = _.cloneDeep(req);
104
105 if (/^\/resources/.test(urlObj.path)) {
106 _context3.next = 7;
107 break;
108 }
109
110 _context3.next = 5;
111 return getLookup(req);
112
113 case 5:
114 lookup = _context3.sent;
115
116 newReq.url = urlObj.protocol + '//' + urlObj.host + '/' + lookup.resourceId + lookup.pathLeftover;
117
118 case 7:
119 return _context3.abrupt("return", getResFromDb(newReq));
120
121 case 8:
122 case "end":
123 return _context3.stop();
124 }
125 }
126 }, _callee3, this);
127 }));
128
129 return function get(_x2) {
130 return _ref4.apply(this, arguments);
131 };
132 }();
133
134 // TODO: Need to update the cache for both the parent resource and child new
135 // resource if one is created
136
137
138 var put = function () {
139 var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(req, offline) {
140 var urlObj, lookup, response;
141 return _regenerator2.default.wrap(function _callee4$(_context4) {
142 while (1) {
143 switch (_context4.prev = _context4.next) {
144 case 0:
145 urlObj = url.parse(req.url);
146
147 if (!offline) {
148 _context4.next = 7;
149 break;
150 }
151
152 _context4.next = 4;
153 return getLookup({
154 url: urlObj.protocol + '//' + urlObj.host + reqPieces.slice(0, reqPieces.length - 1).join('/'),
155 headers: req.headers
156 });
157
158 case 4:
159 lookup = _context4.sent;
160 _context4.next = 13;
161 break;
162
163 case 7:
164 _context4.next = 9;
165 return request(req);
166
167 case 9:
168 response = _context4.sent;
169 _context4.next = 12;
170 return dbUpsert({
171 url: response.headers['content-location'],
172 data: req.data,
173 _rev: response.headers['x-oada-rev'],
174 // TODO: should it be invalidated until pulled from server?
175 valid: false
176 });
177
178 case 12:
179 return _context4.abrupt("return", response);
180
181 case 13:
182 case "end":
183 return _context4.stop();
184 }
185 }
186 }, _callee4, this);
187 }));
188
189 return function put(_x3, _x4) {
190 return _ref5.apply(this, arguments);
191 };
192 }();
193
194 // Remove the deleted key from the parent resource optimistically using
195 // put(). Also mark the parent invalid as the _rev update will affect it
196
197
198 var updateParent = function () {
199 var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(req) {
200 var urlObj, reqPieces, lookup;
201 return _regenerator2.default.wrap(function _callee5$(_context5) {
202 while (1) {
203 switch (_context5.prev = _context5.next) {
204 case 0:
205 urlObj = url.parse(req.url);
206 // Try to get the parent document
207
208 reqPieces = urlObj.path.split('/');
209 _context5.next = 4;
210 return getLookup({
211 url: urlObj.protocol + '//' + urlObj.host + reqPieces.slice(0, reqPieces.length - 1).join('/'),
212 headers: req.headers
213 });
214
215 case 4:
216 lookup = _context5.sent;
217 _context5.next = 7;
218 return dbUpsert({
219 url: '/' + lookup.resourceId + lookup.pathLeftover,
220 method: 'delete',
221 valid: false
222 });
223
224 case 7:
225 return _context5.abrupt("return");
226
227 case 8:
228 case "end":
229 return _context5.stop();
230 }
231 }
232 }, _callee5, this);
233 }));
234
235 return function updateParent(_x5) {
236 return _ref6.apply(this, arguments);
237 };
238 }();
239
240 // Issue DELETE to server then update the db
241
242
243 var del = function () {
244 var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(req, offline) {
245 var urlObj, res, lookup, response;
246 return _regenerator2.default.wrap(function _callee6$(_context6) {
247 while (1) {
248 switch (_context6.prev = _context6.next) {
249 case 0:
250 urlObj = url.parse(req.url);
251 // Handle resource deletion
252
253 if (!/^\/resources/.test(urlObj.path)) {
254 _context6.next = 7;
255 break;
256 }
257
258 _context6.next = 4;
259 return dbUpsert({
260 url: req.url,
261 method: req.method,
262 _valid: false
263 });
264
265 case 4:
266 res = _context6.sent;
267 _context6.next = 20;
268 break;
269
270 case 7:
271 _context6.prev = 7;
272 _context6.next = 10;
273 return getLookup({
274 url: req.url,
275 headers: req.headers
276 });
277
278 case 10:
279 lookup = _context6.sent;
280 _context6.next = 13;
281 return db.remove(lookup);
282
283 case 13:
284 if (lookup.pathLeftover) {
285 _context6.next = 16;
286 break;
287 }
288
289 _context6.next = 16;
290 return updateParent(req);
291
292 case 16:
293 _context6.next = 20;
294 break;
295
296 case 18:
297 _context6.prev = 18;
298 _context6.t0 = _context6["catch"](7);
299
300 case 20:
301 if (offline) {
302 _context6.next = 26;
303 break;
304 }
305
306 _context6.next = 23;
307 return request(req);
308
309 case 23:
310 response = _context6.sent;
311 _context6.next = 26;
312 break;
313
314 case 26:
315 return _context6.abrupt("return", response || res);
316
317 case 27:
318 case "end":
319 return _context6.stop();
320 }
321 }
322 }, _callee6, this, [[7, 18]]);
323 }));
324
325 return function del(_x6, _x7) {
326 return _ref7.apply(this, arguments);
327 };
328 }();
329
330 var _recursiveUpsert = function () {
331 var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8(req, body) {
332 var urlObj, lookup, newBody;
333 return _regenerator2.default.wrap(function _callee8$(_context8) {
334 while (1) {
335 switch (_context8.prev = _context8.next) {
336 case 0:
337 urlObj = url.parse(req.url);
338
339 if (!body._rev) {
340 _context8.next = 8;
341 break;
342 }
343
344 _context8.next = 4;
345 return getLookup({
346 url: req.url,
347 headers: req.headers,
348 _id: body._id
349 });
350
351 case 4:
352 lookup = _context8.sent;
353 newBody = replaceLinks(body, {
354 url: req.url,
355 headers: req.headers
356 });
357 _context8.next = 8;
358 return dbUpsert({
359 url: '/' + (body._id || lookup.resourceId),
360 data: newBody
361 });
362
363 case 8:
364 if (!((typeof body === "undefined" ? "undefined" : (0, _typeof3.default)(body)) === 'object')) {
365 _context8.next = 12;
366 break;
367 }
368
369 return _context8.abrupt("return", Promise.map((0, _keys2.default)(body || {}), function (key) {
370 if (key.charAt(0) === '_') return;
371 if (!body[key]) return;
372 return _recursiveUpsert({
373 url: req.url + '/' + key,
374 headers: req.headers
375 }, body[key]);
376 }));
377
378 case 12:
379 return _context8.abrupt("return");
380
381 case 13:
382 case "end":
383 return _context8.stop();
384 }
385 }
386 }, _callee8, this);
387 }));
388
389 return function _recursiveUpsert(_x10, _x11) {
390 return _ref9.apply(this, arguments);
391 };
392 }();
393
394 var resetCache = function () {
395 var _ref11 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10() {
396 return _regenerator2.default.wrap(function _callee10$(_context10) {
397 while (1) {
398 switch (_context10.prev = _context10.next) {
399 case 0:
400 _context10.prev = 0;
401
402 if (!db) {
403 _context10.next = 4;
404 break;
405 }
406
407 _context10.next = 4;
408 return db.destroy();
409
410 case 4:
411 _context10.next = 10;
412 break;
413
414 case 6:
415 _context10.prev = 6;
416 _context10.t0 = _context10["catch"](0);
417
418 console.log('Reset cache errored. db.destroy threw an error. Assuming the cache was already destroyed.', _context10.t0);
419 return _context10.abrupt("return");
420
421 case 10:
422 case "end":
423 return _context10.stop();
424 }
425 }
426 }, _callee10, this, [[0, 6]]);
427 }));
428
429 return function resetCache() {
430 return _ref11.apply(this, arguments);
431 };
432 }();
433
434 var name = _ref.name,
435 req = _ref.req,
436 expires = _ref.expires;
437
438 // name should be made unique across domains and users
439 var db = db || new _pouchdb2.default(name);
440 // This fixes concurrent accesses
441 _pouchdb2.default.on('destroyed', function (dbName) {
442 if (dbName === name) {
443 db = new _pouchdb2.default(name);
444 }
445 });
446 var request = req;
447 var expiration = expires || 1000 * 60 * 60 * 24 * 2; //(ms/s)*(s/min)*(min/hr)*(hr/days)*days
448
449 // Get the resource and merge data if its already in the db.
450 function dbUpsert(req, waitTime) {
451 var urlObj = url.parse(req.url);
452 var pieces = urlObj.path.split('/');
453 var resourceId = pieces.slice(1, 3).join('/'); //returns resources/abc
454 var pathLeftover = pieces.length > 3 ? '/' + pieces.slice(3, pieces.length).join('/') : '';
455
456 // Create the content to put in the cache
457 var dbPut = {
458 _id: resourceId,
459 valid: req.valid === undefined ? true : req.valid,
460 // TODO: This current resets this access date for e.g., every put
461 // while offline. This doesn't seem right. I think this should only
462 // get updated when we know we're upserting a new value from the server
463 accessed: Date.now()
464
465 // ALL updates to existing docs (upserts) need to supply the current _rev.
466 };return db.get(resourceId).then(function (result) {
467 //If theres a path leftover, create an empty object, add a key to warn users
468 //that the data is incomplete, and put the data at that path Leftover
469 if (result.INCOMPLETE_RESOURCE) dbPut.INCOMPLETE_RESOURCE = true;
470 dbPut._rev = result._rev;
471 if (req.method && req.method.toLowerCase() === 'delete') {
472 if (!pathLeftover) {
473 return db.remove(result);
474 } else if (pointer.has(dbPut.doc, pathLeftover)) {
475 dbPut.doc = result.doc;
476 pointer.remove(dbPut.doc, pathLeftover);
477 return db.put(dbPut).then(function (rrr) {
478 return rrr;
479 }).catch(function (err) {
480 if (err.status === 409) {
481 waitTime = waitTime || 1000;
482 return Promise.delay(waitTime).then(function () {
483 if (waitTime > 16000) throw err;
484 return dbUpsert(req, waitTime * 2);
485 });
486 }
487 throw err;
488 });
489 }
490 } else {
491 if (pathLeftover) {
492 // merge the new data into the old at the path leftover, then return old
493 var curData = {};
494 if (pointer.has(result.doc, pathLeftover)) curData = pointer.get(result.doc, pathLeftover);
495 var newData = _.merge(curData, req.data || {});
496 pointer.set(result.doc, pathLeftover, newData);
497 dbPut.doc = result.doc;
498 } else {
499 dbPut.doc = _.merge(result.doc, req.data);
500 }
501 }
502
503 if (req._rev) dbPut.doc._rev = req._rev;
504
505 return db.put(dbPut).then(function (rrr) {
506 return rrr;
507 }).catch(function (err) {
508 // retry 409s
509 if (err.status === 409) {
510 waitTime = waitTime || 1000;
511 return Promise.delay(waitTime).then(function () {
512 if (waitTime > 16000) throw err;
513 return dbUpsert(req, waitTime * 2);
514 });
515 }
516 throw err;
517 });
518
519 // Else, the resource was not in the cache.
520 }).catch(function (e) {
521 if (req.method && req.method.toLowerCase() === 'delete') {
522 // Deleting a resource that doesn't exist: do nothing.
523 } else {
524 if (pathLeftover) {
525 //Execute the PUT and Warn users that the data is incomplete
526 dbPut.doc = {};
527 dbPut.INCOMPLETE_RESOURCE = true;
528 dbPut.valid = false;
529 pointer.set(dbPut.doc, pathLeftover, _.clone(req.data));
530 } else dbPut.doc = req.data;
531 }
532
533 if (req._rev) dbPut.doc._rev = req._rev;
534
535 return db.put(dbPut).then(function (rrr) {
536 return rrr;
537 }).catch(function (err) {
538 // retry 409s
539 if (err.status === 409) {
540 waitTime = waitTime || 1000;
541 return Promise.delay(waitTime).then(function () {
542 if (waitTime > 16000) throw err;
543 return dbUpsert(req, waitTime * 2);
544 });
545 }
546 throw err;
547 });
548 });
549 }
550
551 function getLookup(req) {
552 var urlObj = url.parse(req.url);
553 return db.get(urlObj.path).catch((0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2() {
554 var resourceId, pathLeftover, response, pieces;
555 return _regenerator2.default.wrap(function _callee2$(_context2) {
556 while (1) {
557 switch (_context2.prev = _context2.next) {
558 case 0:
559 if (!req._id) {
560 _context2.next = 5;
561 break;
562 }
563
564 resourceId = req._id;
565 pathLeftover = '';
566 _context2.next = 11;
567 break;
568
569 case 5:
570 _context2.next = 7;
571 return request({
572 method: 'HEAD',
573 url: req.url,
574 headers: req.headers
575 });
576
577 case 7:
578 response = _context2.sent;
579
580 //Save the url lookup for future use
581 pieces = response.headers['content-location'].split('/');
582
583 resourceId = pieces.slice(1, 3).join('/'); //returns resources/abc
584 pathLeftover = pieces.length > 3 ? '/' + pieces.slice(3, pieces.length).join('/') : '';
585
586 case 11:
587 return _context2.abrupt("return", db.put({
588 _id: urlObj.path,
589 resourceId: resourceId,
590 pathLeftover: pathLeftover
591 }).then(function () {
592 return getLookup(req);
593 }).catch(function (err) {
594 //TODO: avoid an infinite loop
595 if (err.name === 'conflict') {
596 // If there is a conflict in the lookup, repeat the lookup (the HEAD
597 // request likely took too long and the lookup was already created by
598 // another simultaneous request
599 return getLookup(req);
600 }
601 }));
602
603 case 12:
604 case "end":
605 return _context2.stop();
606 }
607 }
608 }, _callee2, this);
609 })));
610 }
611
612 // Create a queue of actual PUTs to make when online.
613 // Resource breaks are known via setupTree.
614 // Do the puts, save out the resource IDs, and return to client
615 // Create an index on the data to find those that need synced
616
617 // Create a service that other apps can run which starts up and periodically
618 // checks if a connections has yet been made. A periodic service may also
619 // concievably check for updates to cached things.
620 //
621 // The cache should go "stale" after some period of time; However, if it cannot
622 // establish a connection, it should remain valid, usable data.
623
624 function getResFromDb(req, offline) {
625 var urlObj = url.parse(req.url);
626 var pieces = urlObj.path.split('/');
627 var resourceId = pieces.slice(1, 3).join('/'); //returns resources/abc
628 var pathLeftover = pieces.length > 3 ? '/' + pieces.slice(3, pieces.length).join('/') : '';
629 return db.get(resourceId).then(function (resource) {
630 if (!offline && (resource.accessed + expiration <= Date.now() || !resource.valid)) {
631 return getResFromServer(req);
632 }
633 //If no pathLeftover, it'll just return resource!
634 if (pointer.has(resource.doc, pathLeftover)) {
635 var data = pointer.get(resource.doc, pathLeftover);
636 return {
637 data: data,
638 headers: {
639 'x-oada-rev': data._rev,
640 'content-location': resourceId + pathLeftover
641 },
642 status: 200,
643 cached: true
644 };
645 } else {
646 return getResFromServer(req);
647 }
648 }).catch(function (err) {
649 if (!offline) return getResFromServer(req);
650 return;
651 });
652 }
653
654 function replaceLinks(obj, req) {
655 var ret = Array.isArray(obj) ? [] : {};
656 if (!obj) return obj;
657 (0, _keys2.default)(obj || {}).forEach(function () {
658 var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7(key, i) {
659 var val, lookup;
660 return _regenerator2.default.wrap(function _callee7$(_context7) {
661 while (1) {
662 switch (_context7.prev = _context7.next) {
663 case 0:
664 val = obj[key];
665
666 if (!((typeof val === "undefined" ? "undefined" : (0, _typeof3.default)(val)) !== 'object' || !val)) {
667 _context7.next = 4;
668 break;
669 }
670
671 ret[key] = val; // keep it asntType: 'application/vnd.oada.harvest.1+json'
672 return _context7.abrupt("return");
673
674 case 4:
675 if (!val._meta) {
676 _context7.next = 11;
677 break;
678 }
679
680 _context7.next = 7;
681 return getLookup({
682 url: req.url + '/' + key,
683 headers: req.headers
684 });
685
686 case 7:
687 lookup = _context7.sent;
688
689 ret[key] = { _id: lookup.resourceId };
690 if (obj[key]._rev) ret[key]._rev = obj[key]._rev;
691 return _context7.abrupt("return");
692
693 case 11:
694 ret[key] = replaceLinks(obj[key], {
695 url: req.url + '/' + key,
696 headers: req.headers
697 }); // otherwise, recurse into the object
698
699 case 12:
700 case "end":
701 return _context7.stop();
702 }
703 }
704 }, _callee7, this);
705 }));
706
707 return function (_x8, _x9) {
708 return _ref8.apply(this, arguments);
709 };
710 }());
711 return ret;
712 }
713
714 function findNullValue(obj, path, nullPath) {
715 if ((typeof obj === "undefined" ? "undefined" : (0, _typeof3.default)(obj)) === "object") {
716 return Promise.map((0, _keys2.default)(obj || {}), function (key) {
717 if (obj[key] === null) {
718 nullPath = path + "/" + key;
719 return nullPath;
720 }
721 return findNullValue(obj[key], path + "/" + key, nullPath).then(function (res) {
722 nullPath = res || nullPath;
723 return res || nullPath;
724 });
725 }).then(function () {
726 return nullPath;
727 });
728 } else return Promise.resolve(undefined);
729 }
730
731 /*
732 async function _upsertChangeArray(payload) {
733 let urlObj = url.parse(payload.request.url);
734 return Promise.map(payload.response.changes || [], async (change) => {
735 if (change.type === 'merge') {
736 return dbUpsert({
737 url: urlObj.protocol+'//'+urlObj.host+'/'+change._id,
738 data: change.body,
739 })
740 } else if (change.type === 'delete') {
741 var nullPath = await findNullValue(change.body, '', '')
742 return dbUpsert({
743 url: urlObj.protocol+'//'+urlObj.host+'/'+change._id+nullPath,
744 data: change.body,
745 })
746 }
747 })
748 }
749 */
750
751 // Will this handle watches put on keys of a resource? i.e., no _id to be found
752 function findDeepestResource(obj, path, deepestResource) {
753 if ((typeof obj === "undefined" ? "undefined" : (0, _typeof3.default)(obj)) === 'object') {
754 return Promise.map((0, _keys2.default)(obj || {}), function (key) {
755 // _rev updates guaranteed to be present in change docs for affected resources
756 if (key === '_rev') {
757 deepestResource.path = path;
758 deepestResource.data = obj;
759 } else if (key.charAt(0) === '_') return deepestResource;
760 return findDeepestResource(obj[key], path + '/' + key, deepestResource).then(function () {
761 return deepestResource;
762 });
763 }).then(function () {
764 return deepestResource;
765 });
766 }
767 return Promise.resolve(deepestResource);
768 }
769
770 function handleWatchChange(payload) {
771 var _this = this;
772
773 var urlObj = url.parse(payload.request.url);
774 // Give the change body an _id so the deepest resource can be found
775 payload.response.change.body._id = payload.response.resourceId;
776 //TODO: This should be unnecessary. The payload ought to specify the root
777 //of the watch as a resource.
778 return findDeepestResource(payload.response.change.body, '', {
779 path: '',
780 data: payload.response.change.body
781 }).then(function () {
782 var _ref10 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9(deepestResource) {
783 var nullPath, deletedPath, lookup;
784 return _regenerator2.default.wrap(function _callee9$(_context9) {
785 while (1) {
786 switch (_context9.prev = _context9.next) {
787 case 0:
788 _context9.t0 = payload.response.change.type.toLowerCase();
789 _context9.next = _context9.t0 === 'delete' ? 3 : _context9.t0 === 'merge' ? 13 : 15;
790 break;
791
792 case 3:
793 _context9.next = 5;
794 return findNullValue(deepestResource.data, '', '');
795
796 case 5:
797 nullPath = _context9.sent;
798 deletedPath = deepestResource.path + nullPath;
799
800 payload.nullPath = deletedPath;
801 _context9.next = 10;
802 return getLookup({
803 url: payload.request.url + deepestResource.path + nullPath,
804 header: payload.request.headers
805 });
806
807 case 10:
808 lookup = _context9.sent;
809 return _context9.abrupt("return", dbDelete({
810 url: payload.request.url + deepestResource.path + nullPath,
811 headers: payload.request.headers,
812 method: payload.response.change.type
813 }, {
814 headers: {
815 'x-oada-rev': deepestResource.data._rev,
816 'content-location': '/' + lookup.resourceId
817 }
818 }).then(function () {
819 // Update revs on all parents all the way down to (BUT OMITTING) the
820 // resource on which was the delete was called.
821 pointer.remove(payload.response.change.body, deepestResource.path || '/');
822 return _recursiveUpsert(payload.request, payload.response.change.body);
823 }));
824
825 case 13:
826 return _context9.abrupt("return", _recursiveUpsert(payload.request, payload.response.change.body));
827
828 case 15:
829 return _context9.abrupt("return");
830
831 case 16:
832 case "end":
833 return _context9.stop();
834 }
835 }
836 }, _callee9, _this);
837 }));
838
839 return function (_x12) {
840 return _ref10.apply(this, arguments);
841 };
842 }()).catch(function (err) {
843 return;
844 });
845 }
846
847 var api = function handleRequest(req) {
848 switch (req.method) {
849 case 'get':
850 return get(req);
851 case 'delete':
852 return del(req);
853 case 'put':
854 return put(req);
855 }
856 };
857
858 return {
859 api: api,
860 db: db,
861 resetCache: resetCache,
862 handleWatchChange: handleWatchChange
863 //handleWatchChange: _upsertChangeArray,
864 };
865}
\No newline at end of file