cozy-client-js
Version:
Javascript library to interact with a cozy
1,605 lines (1,375 loc) • 672 kB
JavaScript
(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