UNPKG

cozy-client-js

Version:

Javascript library to interact with a cozy

1,605 lines (1,375 loc) 672 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("client", [], factory); else if(typeof exports === 'object') exports["client"] = factory(); else root["cozy"] = root["cozy"] || {}, root["cozy"]["client"] = factory(); })(typeof self !== 'undefined' ? self : this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 23); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FetchError = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /* global fetch */ exports.cozyFetch = cozyFetch; exports.cozyFetchJSON = cozyFetchJSON; exports.cozyFetchRawJSON = cozyFetchRawJSON; exports.handleInvalidTokenError = handleInvalidTokenError; var _auth_v = __webpack_require__(4); var _utils = __webpack_require__(1); var _jsonapi = __webpack_require__(13); var _jsonapi2 = _interopRequireDefault(_jsonapi); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } function cozyFetch(cozy, path) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return cozy.fullpath(path).then(function (fullpath) { var resp = void 0; if (options.disableAuth) { resp = fetch(fullpath, options); } else if (options.manualAuthCredentials) { resp = cozyFetchWithAuth(cozy, fullpath, options, options.manualAuthCredentials); } else { resp = cozy.authorize().then(function (credentials) { return cozyFetchWithAuth(cozy, fullpath, options, credentials); }); } return resp.then(function (res) { return handleResponse(res, cozy._invalidTokenErrorHandler); }); }); } function cozyFetchWithAuth(cozy, fullpath, options, credentials) { if (credentials) { options.headers = options.headers || {}; options.headers['Authorization'] = credentials.token.toAuthHeader(); } // the option credentials:include tells fetch to include the cookies in the // request even for cross-origin requests options.credentials = 'include'; return Promise.all([cozy.isV2(), fetch(fullpath, options)]).then(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), isV2 = _ref2[0], res = _ref2[1]; if (res.status !== 400 && res.status !== 401 || isV2 || !credentials || options.dontRetry) { return res; } // we try to refresh the token only for OAuth, ie, the client defined // and the token is an instance of AccessToken. var client = credentials.client, token = credentials.token; if (!client || !(token instanceof _auth_v.AccessToken)) { return res; } options.dontRetry = true; return (0, _utils.retry)(function () { return (0, _auth_v.refreshToken)(cozy, client, token); }, 3)().then(function (newToken) { return cozy.saveCredentials(client, newToken); }).then(function (credentials) { return cozyFetchWithAuth(cozy, fullpath, options, credentials); }); }); } function cozyFetchJSON(cozy, method, path, body) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var processJSONAPI = typeof options.processJSONAPI === 'undefined' || options.processJSONAPI; return fetchJSON(cozy, method, path, body, options).then(function (response) { return handleJSONResponse(response, processJSONAPI); }); } function cozyFetchRawJSON(cozy, method, path, body) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; return fetchJSON(cozy, method, path, body, options).then(function (response) { return handleJSONResponse(response, false); }); } function fetchJSON(cozy, method, path, body) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.method = method; var headers = options.headers = options.headers || {}; headers['Accept'] = 'application/json'; if (method !== 'GET' && method !== 'HEAD' && body !== undefined) { if (headers['Content-Type']) { options.body = body; } else { headers['Content-Type'] = 'application/json'; options.body = JSON.stringify(body); } } return cozyFetch(cozy, path, options); } function handleResponse(res, invalidTokenErrorHandler) { if (res.ok) { return res; } var data = void 0; var contentType = res.headers.get('content-type'); if (contentType && contentType.indexOf('json') >= 0) { data = res.json(); } else { data = res.text(); } return data.then(function (err) { var error = new FetchError(res, err); if (FetchError.isInvalidToken(error) && invalidTokenErrorHandler) { invalidTokenErrorHandler(error); } throw error; }); } function handleJSONResponse(res) { var processJSONAPI = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var contentType = res.headers.get('content-type'); if (!contentType || contentType.indexOf('json') < 0) { return res.text(function (data) { throw new FetchError(res, new Error('Response is not JSON: ' + data)); }); } var json = res.json(); if (contentType.indexOf('application/vnd.api+json') === 0 && processJSONAPI) { return json.then(_jsonapi2.default); } else { return json; } } function handleInvalidTokenError(error) { try { var currentOrigin = window.location.origin; var requestUrl = error.url; if (requestUrl.indexOf(currentOrigin.replace(/^(https?:\/\/\w+)-\w+\./, '$1.')) === 0) { var redirectURL = currentOrigin + '?' + (0, _utils.encodeQuery)({ disconnect: 1 }); window.location = redirectURL; } } catch (e) { console.warn('Unable to handle invalid token error', e, error); } } var FetchError = exports.FetchError = function (_Error) { _inherits(FetchError, _Error); function FetchError(res, reason) { _classCallCheck(this, FetchError); var _this = _possibleConstructorReturn(this, (FetchError.__proto__ || Object.getPrototypeOf(FetchError)).call(this)); if (Error.captureStackTrace) { Error.captureStackTrace(_this, _this.constructor); } // XXX We have to hardcode this because babel doesn't play nice when extending Error _this.name = 'FetchError'; _this.response = res; _this.url = res.url; _this.status = res.status; _this.reason = reason; Object.defineProperty(_this, 'message', { value: reason.message || (typeof reason === 'string' ? reason : JSON.stringify(reason)) }); return _this; } return FetchError; }(Error); FetchError.isUnauthorized = function (err) { // XXX We can't use err instanceof FetchError because of the caveats of babel return err.name === 'FetchError' && err.status === 401; }; FetchError.isNotFound = function (err) { // XXX We can't use err instanceof FetchError because of the caveats of babel return err.name === 'FetchError' && err.status === 404; }; FetchError.isInvalidToken = function (err) { // XXX We can't use err instanceof FetchError because of the caveats of babel return err.name === 'FetchError' && (err.status === 400 || err.status === 401) && err.reason && (err.reason.error === 'Invalid JWT token' || err.reason.error === 'Expired token'); }; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.unpromiser = unpromiser; exports.isPromise = isPromise; exports.isOnline = isOnline; exports.isOffline = isOffline; exports.sleep = sleep; exports.retry = retry; exports.getFuzzedDelay = getFuzzedDelay; exports.getBackedoffDelay = getBackedoffDelay; exports.createPath = createPath; exports.encodeQuery = encodeQuery; exports.decodeQuery = decodeQuery; exports.warn = warn; /* global navigator */ var FuzzFactor = 0.3; function unpromiser(fn) { return function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var value = fn.apply(this, args); if (!isPromise(value)) { return value; } var l = args.length; if (l === 0 || typeof args[l - 1] !== 'function') { return; } var cb = args[l - 1]; value.then(function (res) { return cb(null, res); }, function (err) { return cb(err, null); }); }; } function isPromise(value) { return !!value && typeof value.then === 'function'; } function isOnline() { return typeof navigator !== 'undefined' ? navigator.onLine : true; } function isOffline() { return !isOnline(); } function sleep(time, args) { return new Promise(function (resolve) { setTimeout(resolve, time, args); }); } function retry(fn, count) { var delay = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 300; return function doTry() { for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return fn.apply(undefined, args).catch(function (err) { if (--count < 0) { throw err; } return sleep(getBackedoffDelay(delay, count)).then(function () { return doTry.apply(undefined, args); }); }); }; } function getFuzzedDelay(retryDelay) { var fuzzingFactor = (Math.random() * 2 - 1) * FuzzFactor; return retryDelay * (1.0 + fuzzingFactor); } function getBackedoffDelay(retryDelay) { var retryCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; return getFuzzedDelay(retryDelay * Math.pow(2, retryCount - 1)); } function createPath(cozy, isV2, doctype) { var id = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; var query = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; var route = '/data/'; if (!isV2) { route += encodeURIComponent(doctype) + '/'; } if (id !== '') { route += encodeURIComponent(id); } var q = encodeQuery(query); if (q !== '') { route += '?' + q; } return route; } function encodeQuery(query) { if (!query) { return ''; } var q = ''; for (var qname in query) { if (q !== '') { q += '&'; } q += encodeURIComponent(qname) + '=' + encodeURIComponent(query[qname]); } return q; } function decodeQuery(url) { var queryIndex = url.indexOf('?'); if (queryIndex < 0) { queryIndex = url.length; } var queries = {}; var fragIndex = url.indexOf('#'); if (fragIndex < 0) { fragIndex = url.length; } if (fragIndex < queryIndex) { return queries; } var queryStr = url.slice(queryIndex + 1, fragIndex); if (queryStr === '') { return queries; } var parts = queryStr.split('&'); for (var i = 0; i < parts.length; i++) { var pair = parts[i].split('='); if (pair.length === 0 || pair[0] === '') { continue; } var qname = decodeURIComponent(pair[0]); if (queries.hasOwnProperty(qname)) { continue; } if (pair.length === 1) { queries[qname] = true; } else if (pair.length === 2) { queries[qname] = decodeURIComponent(pair[1]); } else { throw new Error('Malformed URL'); } } return queries; } var warned = []; function warn(text) { if (warned.indexOf(text) === -1) { warned.push(text); console.warn('cozy-client-js', text); } } /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DOCTYPE_FILES = undefined; exports.normalizeDoctype = normalizeDoctype; var _utils = __webpack_require__(1); var DOCTYPE_FILES = exports.DOCTYPE_FILES = 'io.cozy.files'; var KNOWN_DOCTYPES = { files: DOCTYPE_FILES, folder: DOCTYPE_FILES, contact: 'io.cozy.contacts', event: 'io.cozy.events', track: 'io.cozy.labs.music.track', playlist: 'io.cozy.labs.music.playlist' }; var REVERSE_KNOWN = {}; Object.keys(KNOWN_DOCTYPES).forEach(function (k) { REVERSE_KNOWN[KNOWN_DOCTYPES[k]] = k; }); function normalizeDoctype(cozy, isV2, doctype) { var isQualified = doctype.indexOf('.') !== -1; if (isV2 && isQualified) { var known = REVERSE_KNOWN[doctype]; if (known) return known; return doctype.replace(/\./g, '-'); } if (!isV2 && !isQualified) { var _known = KNOWN_DOCTYPES[doctype]; if (_known) { (0, _utils.warn)('you are using a non-qualified doctype ' + doctype + ' assumed to be ' + _known); return _known; } throw new Error('Doctype ' + doctype + ' should be qualified.'); } return doctype; } /***/ }), /* 3 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* unused harmony export adapterFun */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return assign$1; }); /* unused harmony export bulkGetShim */ /* unused harmony export changesHandler */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return clone$1; }); /* unused harmony export defaultBackOff */ /* unused harmony export explainError */ /* unused harmony export filterChange */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return flatten; }); /* unused harmony export functionName */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return guardedConsole; }); /* unused harmony export hasLocalStorage */ /* unused harmony export invalidIdError */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "e", function() { return isRemote; }); /* unused harmony export listenerCount */ /* unused harmony export normalizeDdocFunctionName */ /* unused harmony export once */ /* unused harmony export parseDdocFunctionName */ /* unused harmony export parseUri */ /* unused harmony export pick */ /* unused harmony export rev */ /* unused harmony export scopeEval */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "g", function() { return toPromise; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "h", function() { return upsert; }); /* unused harmony export uuid */ /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_argsarray__ = __webpack_require__(8); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_argsarray___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_argsarray__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_pouchdb_collections__ = __webpack_require__(9); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_immediate__ = __webpack_require__(15); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_immediate___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_immediate__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_events__ = __webpack_require__(16); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_events___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3_events__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_inherits__ = __webpack_require__(43); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_inherits___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4_inherits__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__ = __webpack_require__(10); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_uuid__ = __webpack_require__(18); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_uuid___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_uuid__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_7_pouchdb_md5__ = __webpack_require__(11); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_8_pouchdb_utils__ = __webpack_require__(3); /* harmony reexport (default from non-hamory) */ __webpack_require__.d(__webpack_exports__, "f", function() { return __WEBPACK_IMPORTED_MODULE_2_immediate___default.a; }); function isBinaryObject(object) { return (typeof ArrayBuffer !== 'undefined' && object instanceof ArrayBuffer) || (typeof Blob !== 'undefined' && object instanceof Blob); } function cloneArrayBuffer(buff) { if (typeof buff.slice === 'function') { return buff.slice(0); } // IE10-11 slice() polyfill var target = new ArrayBuffer(buff.byteLength); var targetArray = new Uint8Array(target); var sourceArray = new Uint8Array(buff); targetArray.set(sourceArray); return target; } function cloneBinaryObject(object) { if (object instanceof ArrayBuffer) { return cloneArrayBuffer(object); } var size = object.size; var type = object.type; // Blob if (typeof object.slice === 'function') { return object.slice(0, size, type); } // PhantomJS slice() replacement return object.webkitSlice(0, size, type); } // 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); function isPlainObject(value) { var proto = Object.getPrototypeOf(value); /* istanbul ignore if */ if (proto === null) { // not sure when this happens, but I guess it can return true; } var Ctor = proto.constructor; return (typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString); } function clone$1(object) { var newObject; var i; var len; if (!object || typeof object !== 'object') { return object; } if (Array.isArray(object)) { newObject = []; for (i = 0, len = object.length; i < len; i++) { newObject[i] = clone$1(object[i]); } return newObject; } // special case: to avoid inconsistencies between IndexedDB // and other backends, we automatically stringify Dates if (object instanceof Date) { return object.toISOString(); } if (isBinaryObject(object)) { return cloneBinaryObject(object); } if (!isPlainObject(object)) { return object; // don't clone objects like Workers } newObject = {}; for (i in object) { /* istanbul ignore else */ if (Object.prototype.hasOwnProperty.call(object, i)) { var value = clone$1(object[i]); if (typeof value !== 'undefined') { newObject[i] = value; } } } return newObject; } function once(fun) { var called = false; return __WEBPACK_IMPORTED_MODULE_0_argsarray___default()(function (args) { /* istanbul ignore if */ if (called) { // this is a smoke test and should never actually happen throw new Error('once called more than once'); } else { called = true; fun.apply(this, args); } }); } function toPromise(func) { //create the function we will be returning return __WEBPACK_IMPORTED_MODULE_0_argsarray___default()(function (args) { // Clone arguments args = clone$1(args); var self = this; // if the last argument is a function, assume its a callback var usedCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false; var promise = new Promise(function (fulfill, reject) { var resp; try { var callback = once(function (err, mesg) { if (err) { reject(err); } else { fulfill(mesg); } }); // create a callback for this invocation // apply the function in the orig context args.push(callback); resp = func.apply(self, args); if (resp && typeof resp.then === 'function') { fulfill(resp); } } catch (e) { reject(e); } }); // if there is a callback, call it back if (usedCB) { promise.then(function (result) { usedCB(null, result); }, usedCB); } return promise; }); } function logApiCall(self, name, args) { /* istanbul ignore if */ if (self.constructor.listeners('debug').length) { var logArgs = ['api', self.name, name]; for (var i = 0; i < args.length - 1; i++) { logArgs.push(args[i]); } self.constructor.emit('debug', logArgs); // override the callback itself to log the response var origCallback = args[args.length - 1]; args[args.length - 1] = function (err, res) { var responseArgs = ['api', self.name, name]; responseArgs = responseArgs.concat( err ? ['error', err] : ['success', res] ); self.constructor.emit('debug', responseArgs); origCallback(err, res); }; } } function adapterFun(name, callback) { return toPromise(__WEBPACK_IMPORTED_MODULE_0_argsarray___default()(function (args) { if (this._closed) { return Promise.reject(new Error('database is closed')); } if (this._destroyed) { return Promise.reject(new Error('database is destroyed')); } var self = this; logApiCall(self, name, args); if (!this.taskqueue.isReady) { return new Promise(function (fulfill, reject) { self.taskqueue.addTask(function (failed) { if (failed) { reject(failed); } else { fulfill(self[name].apply(self, args)); } }); }); } return callback.apply(this, args); })); } // 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; } // Most browsers throttle concurrent requests at 6, so it's silly // to shim _bulk_get by trying to launch potentially hundreds of requests // and then letting the majority time out. We can handle this ourselves. var MAX_NUM_CONCURRENT_REQUESTS = 6; function identityFunction(x) { return x; } function formatResultForOpenRevsGet(result) { return [{ ok: result }]; } // shim for P/CouchDB adapters that don't directly implement _bulk_get function bulkGet(db, opts, callback) { var requests = opts.docs; // consolidate into one request per doc if possible var requestsById = new __WEBPACK_IMPORTED_MODULE_1_pouchdb_collections__["a" /* Map */](); requests.forEach(function (request) { if (requestsById.has(request.id)) { requestsById.get(request.id).push(request); } else { requestsById.set(request.id, [request]); } }); var numDocs = requestsById.size; var numDone = 0; var perDocResults = new Array(numDocs); function collapseResultsAndFinish() { var results = []; perDocResults.forEach(function (res) { res.docs.forEach(function (info) { results.push({ id: res.id, docs: [info] }); }); }); callback(null, {results: results}); } function checkDone() { if (++numDone === numDocs) { collapseResultsAndFinish(); } } function gotResult(docIndex, id, docs) { perDocResults[docIndex] = {id: id, docs: docs}; checkDone(); } var allRequests = []; requestsById.forEach(function (value, key) { allRequests.push(key); }); var i = 0; function nextBatch() { if (i >= allRequests.length) { return; } var upTo = Math.min(i + MAX_NUM_CONCURRENT_REQUESTS, allRequests.length); var batch = allRequests.slice(i, upTo); processBatch(batch, i); i += batch.length; } function processBatch(batch, offset) { batch.forEach(function (docId, j) { var docIdx = offset + j; var docRequests = requestsById.get(docId); // just use the first request as the "template" // TODO: The _bulk_get API allows for more subtle use cases than this, // but for now it is unlikely that there will be a mix of different // "atts_since" or "attachments" in the same request, since it's just // replicate.js that is using this for the moment. // Also, atts_since is aspirational, since we don't support it yet. var docOpts = pick(docRequests[0], ['atts_since', 'attachments']); docOpts.open_revs = docRequests.map(function (request) { // rev is optional, open_revs disallowed return request.rev; }); // remove falsey / undefined revisions docOpts.open_revs = docOpts.open_revs.filter(identityFunction); var formatResult = identityFunction; if (docOpts.open_revs.length === 0) { delete docOpts.open_revs; // when fetching only the "winning" leaf, // transform the result so it looks like an open_revs // request formatResult = formatResultForOpenRevsGet; } // globally-supplied options ['revs', 'attachments', 'binary', 'ajax', 'latest'].forEach(function (param) { if (param in opts) { docOpts[param] = opts[param]; } }); db.get(docId, docOpts, function (err, res) { var result; /* istanbul ignore if */ if (err) { result = [{error: err}]; } else { result = formatResult(res); } gotResult(docIdx, docId, result); nextBatch(); }); }); } nextBatch(); } var hasLocal; try { localStorage.setItem('_pouch_check_localstorage', 1); hasLocal = !!localStorage.getItem('_pouch_check_localstorage'); } catch (e) { hasLocal = false; } function hasLocalStorage() { return hasLocal; } // Custom nextTick() shim for browsers. In node, this will just be process.nextTick(). We __WEBPACK_IMPORTED_MODULE_4_inherits___default()(Changes, __WEBPACK_IMPORTED_MODULE_3_events__["EventEmitter"]); /* istanbul ignore next */ function attachBrowserEvents(self) { if (hasLocalStorage()) { addEventListener("storage", function (e) { self.emit(e.key); }); } } function Changes() { __WEBPACK_IMPORTED_MODULE_3_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', 'return_docs' ]); /* 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') { __WEBPACK_IMPORTED_MODULE_2_immediate___default()(eventFunction); } 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; } __WEBPACK_IMPORTED_MODULE_3_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 (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 (typeof console !== 'undefined' && typeof console[method] === 'function') { var args = Array.prototype.slice.call(arguments, 1); console[method].apply(console, args); } } function randomNumber(min, max) { var maxTimeout = 600000; // Hard-coded default of 10 minutes min = parseInt(min, 10) || 0; max = parseInt(max, 10); if (max !== max || max <= min) { max = (min || 1) << 1; //doubling } else { max = max + 1; } // In order to not exceed maxTimeout, pick a random value between half of maxTimeout and maxTimeout if (max > maxTimeout) { min = maxTimeout >> 1; // divide by two max = maxTimeout; } var ratio = Math.random(); var range = max - min; return ~~(range * ratio + min); // ~~ coerces to an int, but fast. } function defaultBackOff(min) { var max = 0; if (!min) { max = 2000; } return randomNumber(min, max); } // designed to give info to browser users, who are disturbed // when they see http errors in the console function explainError(status, str) { guardedConsole('info', 'The above ' + status + ' is totally normal. ' + str); } var assign; { if (typeof Object.assign === 'function') { assign = Object.assign; } else { // lite Object.assign polyfill based on // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign assign = function (target) { var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource != null) { // Skip over if undefined or null for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }; } } var assign$1 = assign; function tryFilter(filter, doc, req) { try { return !filter(doc, req); } catch (err) { var msg = 'Filter function threw: ' + err.toString(); return Object(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["e" /* createError */])(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["a" /* BAD_REQUEST */], msg); } } function filterChange(opts) { var req = {}; var hasFilter = opts.filter && typeof opts.filter === 'function'; req.query = opts.query_params; return function filter(change) { if (!change.doc) { // CSG sends events on the changes feed that don't have documents, // this hack makes a whole lot of existing code robust. change.doc = {}; } var filterReturn = hasFilter && tryFilter(opts.filter, change.doc, req); if (typeof filterReturn === 'object') { return filterReturn; } if (filterReturn) { return false; } if (!opts.include_docs) { delete change.doc; } else if (!opts.attachments) { for (var att in change.doc._attachments) { /* istanbul ignore else */ if (change.doc._attachments.hasOwnProperty(att)) { change.doc._attachments[att].stub = true; } } } return true; }; } function flatten(arrs) { var res = []; for (var i = 0, len = arrs.length; i < len; i++) { res = res.concat(arrs[i]); } return res; } // shim for Function.prototype.name, // for browsers that don't support it like IE /* istanbul ignore next */ function f() {} var hasName = f.name; var res; // We dont run coverage in IE /* istanbul ignore else */ if (hasName) { res = function (fun) { return fun.name; }; } else { res = function (fun) { var match = fun.toString().match(/^\s*function\s*(?:(\S+)\s*)?\(/); if (match && match[1]) { return match[1]; } else { return ''; } }; } var res$1 = res; // Determine id an ID is valid // - invalid IDs begin with an underescore that does not begin '_design' or // '_local' // - any other string value is a valid id // Returns the specific error object for each case function invalidIdError(id) { var err; if (!id) { err = Object(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["e" /* createError */])(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["c" /* MISSING_ID */]); } else if (typeof id !== 'string') { err = Object(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["e" /* createError */])(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["b" /* INVALID_ID */]); } else if (/^_/.test(id) && !(/^_(design|local)/).test(id)) { err = Object(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["e" /* createError */])(__WEBPACK_IMPORTED_MODULE_5_pouchdb_errors__["d" /* RESERVED_ID */]); } if (err) { throw err; } } // Checks if a PouchDB object is "remote" or not. This is function isRemote(db) { if (typeof db._remote === 'boolean') { return db._remote; } /* istanbul ignore next */ if (typeof db.type === 'function') { guardedConsole('warn', 'db.type() is deprecated and will be removed in ' + 'a future version of PouchDB'); return db.type() === 'http'; } /* istanbul ignore next */ return false; } function listenerCount(ee, type) { return 'listenerCount' in ee ? ee.listenerCount(type) : __WEBPACK_IMPORTED_MODULE_3_events__["EventEmitter"].listenerCount(ee, type); } function parseDesignDocFunctionName(s) { if (!s) { return null; } var parts = s.split('/'); if (parts.length === 2) { return parts; } if (parts.length === 1) { return [s, s]; } return null; } function normalizeDesignDocFunctionName(s) { var normalized = parseDesignDocFunctionName(s); return normalized ? normalized.join('/') : null; } // originally parseUri 1.2.2, now patched by us // (c) Steven Levithan <stevenlevithan.com> // MIT License var keys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"]; var qName ="queryKey"; var qParser = /(?:^|&)([^&=]*)=?([^&]*)/g; // use the "loose" parser /* eslint maxlen: 0, no-useless-escape: 0 */ var parser = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; function parseUri(str) { var m = parser.exec(str); var uri = {}; var i = 14; while (i--) { var key = keys[i]; var value = m[i] || ""; var encoded = ['user', 'password'].indexOf(key) !== -1; uri[key] = encoded ? decodeURIComponent(value) : value; } uri[qName] = {}; uri[keys[12]].replace(qParser, function ($0, $1, $2) { if ($1) { uri[qName][$1] = $2; } }); return uri; } // Based on https://github.com/alexdavid/scope-eval v0.0.3 // (source: https://unpkg.com/scope-eval@0.0.3/scope_eval.js) // This is basically just a wrapper around new Function() function scopeEval(source, scope) { var keys = []; var values = []; for (var key in scope) { if (scope.hasOwnProperty(key)) { keys.push(key); values.push(scope[key]); } } keys.push(source); return Function.apply(null, keys).apply(null, values); } // 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 Promise(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); }); } function rev(doc, deterministic_revs) { var clonedDoc = Object(__WEBPACK_IMPORTED_MODULE_8_pouchdb_utils__["b" /* clone */])(doc); if (!deterministic_revs) { return __WEBPACK_IMPORTED_MODULE_6_uuid___default.a.v4().replace(/-/g, '').toLowerCase(); } delete clonedDoc._rev_tree; return Object(__WEBPACK_IMPORTED_MODULE_7_pouchdb_md5__["a" /* stringMd5 */])(JSON.stringify(clonedDoc)); } var uuid = __WEBPACK_IMPORTED_MODULE_6_uuid___default.a.v4; /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AppToken = exports.AccessToken = exports.Client = exports.StateKey = exports.CredsKey = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* global btoa */ exports.client = client; exports.registerClient = registerClient; exports.updateClient = updateClient; exports.unregisterClient = unregisterClient; exports.getClient = getClient; exports.getAuthCodeURL = getAuthCodeURL; exports.getAccessToken = getAccessToken; exports.refreshToken = refreshToken; exports.oauthFlow = oauthFlow; var _utils = __webpack_require__(1); var _fetch = __webpack_require__(0); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var StateSize = 16; var CredsKey = exports.CredsKey = 'creds'; var StateKey = exports.StateKey = 'state'; var Client = exports.Client = function () { function Client(opts) { _classCallCheck(this, Client); this.clientID = opts.clientID || opts.client_id || ''; this.clientSecret = opts.clientSecret || opts.client_secret || ''; this.registrationAccessToken = opts.registrationAccessToken || opts.registration_access_token || ''; if (opts.redirect_uris) { this.redirectURI = opts.redirect_uris[0] || ''; } else { this.redirectURI = opts.redirectURI || ''; } this.softwareID = opts.softwareID || opts.software_id || ''; this.softwareVersion = opts.softwareVersion || opts.software_version || ''; this.clientName = opts.clientName || opts.client_name || ''; this.clientKind = opts.clientKind || opts.client_kind || ''; this.clientURI = opts.clientURI || opts.client_uri || ''; this.logoURI = opts.logoURI || opts.logo_uri || ''; this.policyURI = opts.policyURI || opts.policy_uri || ''; this.notificationPlatform = opts.notificationPlatform || opts.notification_platform || ''; this.notificationDeviceToken = opts.notificationDeviceToken || opts.notification_device_token || ''; if (!this.registrationAccessToken) { if (this.redirectURI === '') { throw new Error('Missing redirectURI field'); } if (this.softwareID === '') { throw new Error('Missing softwareID field'); } if (this.clientName === '') { throw new Error('Missing clientName field'); } } } _createClass(Client, [{ key: 'isRegistered', value: function isRegistered() { return this.clientID !== ''; } }, { key: 'toRegisterJSON', value: function toRegisterJSON() { return { redirect_uris: [this.redirectURI], software_id: this.softwareID, software_version: this.softwareVersion, client_name: this.clientName, client_kind: this.clientKind, client_uri: this.clientURI, logo_uri: this.logoURI, policy_uri: this.policyURI, notification_platform: this.notificationPlatform, notification_device_token: this.notificationDeviceToken }; } }, { key: 'toAuthHeader', value: function toAuthHeader() { return 'Bearer ' + this.registrationAccessToken; } }]); return Client; }(); var AccessToken = exports.AccessToken = function () { function AccessToken(opts) { _classCallCheck(this, AccessToken); this.tokenType = opts.tokenType || opts.token_type; this.accessToken = opts.accessToken || opts.access_token; this.refreshToken = opts.refreshToken || opts.refresh_token; this.scope = opts.scope; } _createClass(AccessToken, [{ key: 'toAuthHeader', value: function toAuthHeader() { return 'Bearer ' + this.accessToken; } }, { key: 'toBasicAuth', value: function toBasicAuth() { return 'user:' + this.accessToken + '@'; } }]); return AccessToken; }(); var AppToken = exports.AppToken = function () { function AppToken(opts) { _classCallCheck(this, AppToken); this.token = opts.token || ''; } _createClass(AppToken, [{ key: 'toAuthHeader', value: function toAuthHeader() { return 'Bearer ' + this.token; } }, { key: 'toBasicAuth', value: function toBasicAuth() { return 'user:' + this.token + '@'; } }]); return AppToken; }(); function client(cozy, clientParams) { if (!clientParams) { clientParams = cozy._clientParams; } if (clientParams instanceof Client) { return clientParams; } return new Client(clientParams); } function registerClient(cozy, clientParams) { var cli = client(cozy, clientParams); if (cli.isRegistered()) { return Promise.reject(new Error('Client already registered')); } return (0, _fetch.cozyFetchJSON)(cozy, 'POST', '/auth/register', cli.toRegisterJSON(), { disableAuth: true }).then(function (data) { return new Client(data); }); } function updateClient(cozy, clientParams) { var resetSecret = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var cli = client(cozy, clientParams); if (!cli.isRegistered()) { return Promise.reject(new Error('Client not registered')); } var data = cli.toRegisterJSON(); data.client_id = cli.clientID; if (resetSecret) data.client_secret = cli.clientSecret; return (0, _fetch.cozyFetchJSON)(cozy, 'PUT', '/auth/register/' + cli.clientID, data, { manualAuthCredentials: { token: cli } }).then(function (data) { return createClient(data, cli); }); } function unregisterClient(cozy, clientParams) { var cli = client(cozy, clientParams); if (!cli.isRegistered()) { return Promise.reject(new Error('Client not registered')); } return (0, _fetch.cozyFetchJSON)(cozy, 'DELETE', '/auth/register/' + cli.clientID, null, { manualAuthCredentials: { token: cli } }); } // getClient will retrive the registered client informations from the server. function getClient(cozy, clientParams) { var cli = client(cozy, clientParams); if (!cli.isRegistered()) { return Promise.reject(new Error('Client not registered')); } if ((0, _utils.isOffline)()) { return Promise.resolve(cli); } return (0, _fetch.cozyFetchJSON)(cozy, 'GET', '/auth/register/' + cli.clientID, null, { manualAuthCredentials: { token: cli } }).then(function (data) { return createClient(data, cli); }).catch(function (err) { // If we fall into an error while fetching the client (because of a // bad connectivity for instance), we do not bail the whole process // since the client should be able to continue with the persisted // client and token. // // If it is an explicit Unauthorized error though, we bail, clear th // cache and retry. if (_fetch.FetchError.isUnauthorized(err) || _fetch.FetchError.isNotFound(err)) { throw new Error('Client has been revoked'); } throw err; }); } // createClient returns a new Client instance given on object containing the // data of the client, from the API, and an old instance of the client. function createClient(data, oldClient) { var newClient = new Client(data); // we need to keep track of the registrationAccessToken since it is send // only on registration. The GET /auth/register/:client-id endpoint does // not return this token. var shouldPassRegistration = !!oldClient && oldClient.registrationAccessToken !== '' && newClient.registrationAccessToken === ''; if (shouldPassRegistration) { newClient.registrationAccessToken = oldClient.registrationAccessToken; } return newClient; } // getAuthCodeURL returns a pair {authURL,state} given a registered client. The // state should be stored in order to be checked against on the user validation // phase. function getAuthCodeURL(cozy, client) { var scopes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; if (!(client instanceof Client)) { client = new Client(client); } if (!client.isRegistered()) { throw new Error('Client not