diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 09:21:33 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 09:21:33 -0500 |
commit | 9627f18cebab38cdfe45592d83371ee7bbc62cfa (patch) | |
tree | 9ac98ca9a764666bd0edd4cfd59ae970705b98a3 /services/common/kinto-offline-client.js | |
parent | c28c5b704fb3f3af6e7846abd73f63da1e35921f (diff) | |
download | UXP-9627f18cebab38cdfe45592d83371ee7bbc62cfa.tar UXP-9627f18cebab38cdfe45592d83371ee7bbc62cfa.tar.gz UXP-9627f18cebab38cdfe45592d83371ee7bbc62cfa.tar.lz UXP-9627f18cebab38cdfe45592d83371ee7bbc62cfa.tar.xz UXP-9627f18cebab38cdfe45592d83371ee7bbc62cfa.zip |
Remove kinto client, Firefox kinto storage adapter, blocklist update client and integration with sync, OneCRL and the custom time check for derives system time.
Diffstat (limited to 'services/common/kinto-offline-client.js')
-rw-r--r-- | services/common/kinto-offline-client.js | 4286 |
1 files changed, 0 insertions, 4286 deletions
diff --git a/services/common/kinto-offline-client.js b/services/common/kinto-offline-client.js deleted file mode 100644 index 4d0dbd0f3..000000000 --- a/services/common/kinto-offline-client.js +++ /dev/null @@ -1,4286 +0,0 @@ -/* - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This file is generated from kinto.js - do not modify directly. - */ - -this.EXPORTED_SYMBOLS = ["loadKinto"]; - -/* - * Version 5.1.0 - 8beb61d - */ - -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.loadKinto = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _extends2 = require("babel-runtime/helpers/extends"); - -var _extends3 = _interopRequireDefault(_extends2); - -var _stringify = require("babel-runtime/core-js/json/stringify"); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _promise = require("babel-runtime/core-js/promise"); - -var _promise2 = _interopRequireDefault(_promise); - -exports.reduceRecords = reduceRecords; - -var _base = require("../src/adapters/base"); - -var _base2 = _interopRequireDefault(_base); - -var _utils = require("../src/utils"); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -Components.utils.import("resource://gre/modules/Sqlite.jsm"); -Components.utils.import("resource://gre/modules/Task.jsm"); - -const SQLITE_PATH = "kinto.sqlite"; - -const statements = { - "createCollectionData": ` - CREATE TABLE collection_data ( - collection_name TEXT, - record_id TEXT, - record TEXT - );`, - - "createCollectionMetadata": ` - CREATE TABLE collection_metadata ( - collection_name TEXT PRIMARY KEY, - last_modified INTEGER - ) WITHOUT ROWID;`, - - "createCollectionDataRecordIdIndex": ` - CREATE UNIQUE INDEX unique_collection_record - ON collection_data(collection_name, record_id);`, - - "clearData": ` - DELETE FROM collection_data - WHERE collection_name = :collection_name;`, - - "createData": ` - INSERT INTO collection_data (collection_name, record_id, record) - VALUES (:collection_name, :record_id, :record);`, - - "updateData": ` - INSERT OR REPLACE INTO collection_data (collection_name, record_id, record) - VALUES (:collection_name, :record_id, :record);`, - - "deleteData": ` - DELETE FROM collection_data - WHERE collection_name = :collection_name - AND record_id = :record_id;`, - - "saveLastModified": ` - REPLACE INTO collection_metadata (collection_name, last_modified) - VALUES (:collection_name, :last_modified);`, - - "getLastModified": ` - SELECT last_modified - FROM collection_metadata - WHERE collection_name = :collection_name;`, - - "getRecord": ` - SELECT record - FROM collection_data - WHERE collection_name = :collection_name - AND record_id = :record_id;`, - - "listRecords": ` - SELECT record - FROM collection_data - WHERE collection_name = :collection_name;`, - - // N.B. we have to have a dynamic number of placeholders, which you - // can't do without building your own statement. See `execute` for details - "listRecordsById": ` - SELECT record_id, record - FROM collection_data - WHERE collection_name = ? - AND record_id IN `, - - "importData": ` - REPLACE INTO collection_data (collection_name, record_id, record) - VALUES (:collection_name, :record_id, :record);`, - - "scanAllRecords": `SELECT * FROM collection_data;`, - - "clearCollectionMetadata": `DELETE FROM collection_metadata;` -}; - -const createStatements = ["createCollectionData", "createCollectionMetadata", "createCollectionDataRecordIdIndex"]; - -const currentSchemaVersion = 1; - -/** - * Firefox adapter. - * - * Uses Sqlite as a backing store. - * - * Options: - * - path: the filename/path for the Sqlite database. If absent, use SQLITE_PATH. - */ -class FirefoxAdapter extends _base2.default { - constructor(collection, options = {}) { - super(); - const { sqliteHandle = null } = options; - this.collection = collection; - this._connection = sqliteHandle; - this._options = options; - } - - // We need to be capable of calling this from "outside" the adapter - // so that someone can initialize a connection and pass it to us in - // adapterOptions. - static _init(connection) { - return Task.spawn(function* () { - yield connection.executeTransaction(function* doSetup() { - const schema = yield connection.getSchemaVersion(); - - if (schema == 0) { - - for (let statementName of createStatements) { - yield connection.execute(statements[statementName]); - } - - yield connection.setSchemaVersion(currentSchemaVersion); - } else if (schema != 1) { - throw new Error("Unknown database schema: " + schema); - } - }); - return connection; - }); - } - - _executeStatement(statement, params) { - if (!this._connection) { - throw new Error("The storage adapter is not open"); - } - return this._connection.executeCached(statement, params); - } - - open() { - const self = this; - return Task.spawn(function* () { - if (!self._connection) { - const path = self._options.path || SQLITE_PATH; - const opts = { path, sharedMemoryCache: false }; - self._connection = yield Sqlite.openConnection(opts).then(FirefoxAdapter._init); - } - }); - } - - close() { - if (this._connection) { - const promise = this._connection.close(); - this._connection = null; - return promise; - } - return _promise2.default.resolve(); - } - - clear() { - const params = { collection_name: this.collection }; - return this._executeStatement(statements.clearData, params); - } - - execute(callback, options = { preload: [] }) { - if (!this._connection) { - throw new Error("The storage adapter is not open"); - } - - let result; - const conn = this._connection; - const collection = this.collection; - - return conn.executeTransaction(function* doExecuteTransaction() { - // Preload specified records from DB, within transaction. - const parameters = [collection, ...options.preload]; - const placeholders = options.preload.map(_ => "?"); - const stmt = statements.listRecordsById + "(" + placeholders.join(",") + ");"; - const rows = yield conn.execute(stmt, parameters); - - const preloaded = rows.reduce((acc, row) => { - const record = JSON.parse(row.getResultByName("record")); - acc[row.getResultByName("record_id")] = record; - return acc; - }, {}); - - const proxy = transactionProxy(collection, preloaded); - result = callback(proxy); - - for (let { statement, params } of proxy.operations) { - yield conn.executeCached(statement, params); - } - }, conn.TRANSACTION_EXCLUSIVE).then(_ => result); - } - - get(id) { - const params = { - collection_name: this.collection, - record_id: id - }; - return this._executeStatement(statements.getRecord, params).then(result => { - if (result.length == 0) { - return; - } - return JSON.parse(result[0].getResultByName("record")); - }); - } - - list(params = { filters: {}, order: "" }) { - const parameters = { - collection_name: this.collection - }; - return this._executeStatement(statements.listRecords, parameters).then(result => { - const records = []; - for (let k = 0; k < result.length; k++) { - const row = result[k]; - records.push(JSON.parse(row.getResultByName("record"))); - } - return records; - }).then(results => { - // The resulting list of records is filtered and sorted. - // XXX: with some efforts, this could be implemented using SQL. - return reduceRecords(params.filters, params.order, results); - }); - } - - /** - * Load a list of records into the local database. - * - * Note: The adapter is not in charge of filtering the already imported - * records. This is done in `Collection#loadDump()`, as a common behaviour - * between every adapters. - * - * @param {Array} records. - * @return {Array} imported records. - */ - loadDump(records) { - const connection = this._connection; - const collection_name = this.collection; - return Task.spawn(function* () { - yield connection.executeTransaction(function* doImport() { - for (let record of records) { - const params = { - collection_name: collection_name, - record_id: record.id, - record: (0, _stringify2.default)(record) - }; - yield connection.execute(statements.importData, params); - } - const lastModified = Math.max(...records.map(record => record.last_modified)); - const params = { - collection_name: collection_name - }; - const previousLastModified = yield connection.execute(statements.getLastModified, params).then(result => { - return result.length > 0 ? result[0].getResultByName("last_modified") : -1; - }); - if (lastModified > previousLastModified) { - const params = { - collection_name: collection_name, - last_modified: lastModified - }; - yield connection.execute(statements.saveLastModified, params); - } - }); - return records; - }); - } - - saveLastModified(lastModified) { - const parsedLastModified = parseInt(lastModified, 10) || null; - const params = { - collection_name: this.collection, - last_modified: parsedLastModified - }; - return this._executeStatement(statements.saveLastModified, params).then(() => parsedLastModified); - } - - getLastModified() { - const params = { - collection_name: this.collection - }; - return this._executeStatement(statements.getLastModified, params).then(result => { - if (result.length == 0) { - return 0; - } - return result[0].getResultByName("last_modified"); - }); - } - - /** - * Reset the sync status of every record and collection we have - * access to. - */ - resetSyncStatus() { - // We're going to use execute instead of executeCached, so build - // in our own sanity check - if (!this._connection) { - throw new Error("The storage adapter is not open"); - } - - return this._connection.executeTransaction(function* (conn) { - const promises = []; - yield conn.execute(statements.scanAllRecords, null, function (row) { - const record = JSON.parse(row.getResultByName("record")); - const record_id = row.getResultByName("record_id"); - const collection_name = row.getResultByName("collection_name"); - if (record._status === "deleted") { - // Garbage collect deleted records. - promises.push(conn.execute(statements.deleteData, { collection_name, record_id })); - } else { - const newRecord = (0, _extends3.default)({}, record, { - _status: "created", - last_modified: undefined - }); - promises.push(conn.execute(statements.updateData, { record: (0, _stringify2.default)(newRecord), record_id, collection_name })); - } - }); - yield _promise2.default.all(promises); - yield conn.execute(statements.clearCollectionMetadata); - }); - } -} - -exports.default = FirefoxAdapter; -function transactionProxy(collection, preloaded) { - const _operations = []; - - return { - get operations() { - return _operations; - }, - - create(record) { - _operations.push({ - statement: statements.createData, - params: { - collection_name: collection, - record_id: record.id, - record: (0, _stringify2.default)(record) - } - }); - }, - - update(record) { - _operations.push({ - statement: statements.updateData, - params: { - collection_name: collection, - record_id: record.id, - record: (0, _stringify2.default)(record) - } - }); - }, - - delete(id) { - _operations.push({ - statement: statements.deleteData, - params: { - collection_name: collection, - record_id: id - } - }); - }, - - get(id) { - // Gecko JS engine outputs undesired warnings if id is not in preloaded. - return id in preloaded ? preloaded[id] : undefined; - } - }; -} - -/** - * Filter and sort list against provided filters and order. - * - * @param {Object} filters The filters to apply. - * @param {String} order The order to apply. - * @param {Array} list The list to reduce. - * @return {Array} - */ -function reduceRecords(filters, order, list) { - const filtered = filters ? (0, _utils.filterObjects)(filters, list) : list; - return order ? (0, _utils.sortObjects)(order, filtered) : filtered; -} - -},{"../src/adapters/base":85,"../src/utils":87,"babel-runtime/core-js/json/stringify":3,"babel-runtime/core-js/promise":6,"babel-runtime/helpers/extends":8}],2:[function(require,module,exports){ -/* - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _extends2 = require("babel-runtime/helpers/extends"); - -var _extends3 = _interopRequireDefault(_extends2); - -exports.default = loadKinto; - -var _base = require("../src/adapters/base"); - -var _base2 = _interopRequireDefault(_base); - -var _KintoBase = require("../src/KintoBase"); - -var _KintoBase2 = _interopRequireDefault(_KintoBase); - -var _FirefoxStorage = require("./FirefoxStorage"); - -var _FirefoxStorage2 = _interopRequireDefault(_FirefoxStorage); - -var _utils = require("../src/utils"); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -function loadKinto() { - const { EventEmitter } = Cu.import("resource://devtools/shared/event-emitter.js", {}); - const { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); - - // Use standalone kinto-http module landed in FFx. - const { KintoHttpClient } = Cu.import("resource://services-common/kinto-http-client.js"); - - Cu.import("resource://gre/modules/Timer.jsm"); - Cu.importGlobalProperties(['fetch']); - - // Leverage Gecko service to generate UUIDs. - function makeIDSchema() { - return { - validate: _utils.RE_UUID.test.bind(_utils.RE_UUID), - generate: function () { - return generateUUID().toString().replace(/[{}]/g, ""); - } - }; - } - - class KintoFX extends _KintoBase2.default { - static get adapters() { - return { - BaseAdapter: _base2.default, - FirefoxAdapter: _FirefoxStorage2.default - }; - } - - constructor(options = {}) { - const emitter = {}; - EventEmitter.decorate(emitter); - - const defaults = { - events: emitter, - ApiClass: KintoHttpClient, - adapter: _FirefoxStorage2.default - }; - - const expandedOptions = (0, _extends3.default)({}, defaults, options); - super(expandedOptions); - } - - collection(collName, options = {}) { - const idSchema = makeIDSchema(); - const expandedOptions = (0, _extends3.default)({ idSchema }, options); - return super.collection(collName, expandedOptions); - } - } - - return KintoFX; -} - -// This fixes compatibility with CommonJS required by browserify. -// See http://stackoverflow.com/questions/33505992/babel-6-changes-how-it-exports-default/33683495#33683495 -if (typeof module === "object") { - module.exports = loadKinto; -} - -},{"../src/KintoBase":83,"../src/adapters/base":85,"../src/utils":87,"./FirefoxStorage":1,"babel-runtime/helpers/extends":8}],3:[function(require,module,exports){ -module.exports = { "default": require("core-js/library/fn/json/stringify"), __esModule: true }; -},{"core-js/library/fn/json/stringify":10}],4:[function(require,module,exports){ -module.exports = { "default": require("core-js/library/fn/object/assign"), __esModule: true }; -},{"core-js/library/fn/object/assign":11}],5:[function(require,module,exports){ -module.exports = { "default": require("core-js/library/fn/object/keys"), __esModule: true }; -},{"core-js/library/fn/object/keys":12}],6:[function(require,module,exports){ -module.exports = { "default": require("core-js/library/fn/promise"), __esModule: true }; -},{"core-js/library/fn/promise":13}],7:[function(require,module,exports){ -"use strict"; - -exports.__esModule = true; - -var _promise = require("../core-js/promise"); - -var _promise2 = _interopRequireDefault(_promise); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -exports.default = function (fn) { - return function () { - var gen = fn.apply(this, arguments); - return new _promise2.default(function (resolve, reject) { - function step(key, arg) { - try { - var info = gen[key](arg); - var value = info.value; - } catch (error) { - reject(error); - return; - } - - if (info.done) { - resolve(value); - } else { - return _promise2.default.resolve(value).then(function (value) { - return step("next", value); - }, function (err) { - return step("throw", err); - }); - } - } - - return step("next"); - }); - }; -}; -},{"../core-js/promise":6}],8:[function(require,module,exports){ -"use strict"; - -exports.__esModule = true; - -var _assign = require("../core-js/object/assign"); - -var _assign2 = _interopRequireDefault(_assign); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -exports.default = _assign2.default || function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - - return target; -}; -},{"../core-js/object/assign":4}],9:[function(require,module,exports){ - -},{}],10:[function(require,module,exports){ -var core = require('../../modules/_core') - , $JSON = core.JSON || (core.JSON = {stringify: JSON.stringify}); -module.exports = function stringify(it){ // eslint-disable-line no-unused-vars - return $JSON.stringify.apply($JSON, arguments); -}; -},{"../../modules/_core":21}],11:[function(require,module,exports){ -require('../../modules/es6.object.assign'); -module.exports = require('../../modules/_core').Object.assign; -},{"../../modules/_core":21,"../../modules/es6.object.assign":77}],12:[function(require,module,exports){ -require('../../modules/es6.object.keys'); -module.exports = require('../../modules/_core').Object.keys; -},{"../../modules/_core":21,"../../modules/es6.object.keys":78}],13:[function(require,module,exports){ -require('../modules/es6.object.to-string'); -require('../modules/es6.string.iterator'); -require('../modules/web.dom.iterable'); -require('../modules/es6.promise'); -module.exports = require('../modules/_core').Promise; -},{"../modules/_core":21,"../modules/es6.object.to-string":79,"../modules/es6.promise":80,"../modules/es6.string.iterator":81,"../modules/web.dom.iterable":82}],14:[function(require,module,exports){ -module.exports = function(it){ - if(typeof it != 'function')throw TypeError(it + ' is not a function!'); - return it; -}; -},{}],15:[function(require,module,exports){ -module.exports = function(){ /* empty */ }; -},{}],16:[function(require,module,exports){ -module.exports = function(it, Constructor, name, forbiddenField){ - if(!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)){ - throw TypeError(name + ': incorrect invocation!'); - } return it; -}; -},{}],17:[function(require,module,exports){ -var isObject = require('./_is-object'); -module.exports = function(it){ - if(!isObject(it))throw TypeError(it + ' is not an object!'); - return it; -}; -},{"./_is-object":38}],18:[function(require,module,exports){ -// false -> Array#indexOf -// true -> Array#includes -var toIObject = require('./_to-iobject') - , toLength = require('./_to-length') - , toIndex = require('./_to-index'); -module.exports = function(IS_INCLUDES){ - return function($this, el, fromIndex){ - var O = toIObject($this) - , length = toLength(O.length) - , index = toIndex(fromIndex, length) - , value; - // Array#includes uses SameValueZero equality algorithm - if(IS_INCLUDES && el != el)while(length > index){ - value = O[index++]; - if(value != value)return true; - // Array#toIndex ignores holes, Array#includes - not - } else for(;length > index; index++)if(IS_INCLUDES || index in O){ - if(O[index] === el)return IS_INCLUDES || index || 0; - } return !IS_INCLUDES && -1; - }; -}; -},{"./_to-index":67,"./_to-iobject":69,"./_to-length":70}],19:[function(require,module,exports){ -// getting tag from 19.1.3.6 Object.prototype.toString() -var cof = require('./_cof') - , TAG = require('./_wks')('toStringTag') - // ES3 wrong here - , ARG = cof(function(){ return arguments; }()) == 'Arguments'; - -// fallback for IE11 Script Access Denied error -var tryGet = function(it, key){ - try { - return it[key]; - } catch(e){ /* empty */ } -}; - -module.exports = function(it){ - var O, T, B; - return it === undefined ? 'Undefined' : it === null ? 'Null' - // @@toStringTag case - : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T - // builtinTag case - : ARG ? cof(O) - // ES3 arguments fallback - : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; -}; -},{"./_cof":20,"./_wks":74}],20:[function(require,module,exports){ -var toString = {}.toString; - -module.exports = function(it){ - return toString.call(it).slice(8, -1); -}; -},{}],21:[function(require,module,exports){ -var core = module.exports = {version: '2.4.0'}; -if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef -},{}],22:[function(require,module,exports){ -// optional / simple context binding -var aFunction = require('./_a-function'); -module.exports = function(fn, that, length){ - aFunction(fn); - if(that === undefined)return fn; - switch(length){ - case 1: return function(a){ - return fn.call(that, a); - }; - case 2: return function(a, b){ - return fn.call(that, a, b); - }; - case 3: return function(a, b, c){ - return fn.call(that, a, b, c); - }; - } - return function(/* ...args */){ - return fn.apply(that, arguments); - }; -}; -},{"./_a-function":14}],23:[function(require,module,exports){ -// 7.2.1 RequireObjectCoercible(argument) -module.exports = function(it){ - if(it == undefined)throw TypeError("Can't call method on " + it); - return it; -}; -},{}],24:[function(require,module,exports){ -// Thank's IE8 for his funny defineProperty -module.exports = !require('./_fails')(function(){ - return Object.defineProperty({}, 'a', {get: function(){ return 7; }}).a != 7; -}); -},{"./_fails":28}],25:[function(require,module,exports){ -var isObject = require('./_is-object') - , document = require('./_global').document - // in old IE typeof document.createElement is 'object' - , is = isObject(document) && isObject(document.createElement); -module.exports = function(it){ - return is ? document.createElement(it) : {}; -}; -},{"./_global":30,"./_is-object":38}],26:[function(require,module,exports){ -// IE 8- don't enum bug keys -module.exports = ( - 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' -).split(','); -},{}],27:[function(require,module,exports){ -var global = require('./_global') - , core = require('./_core') - , ctx = require('./_ctx') - , hide = require('./_hide') - , PROTOTYPE = 'prototype'; - -var $export = function(type, name, source){ - var IS_FORCED = type & $export.F - , IS_GLOBAL = type & $export.G - , IS_STATIC = type & $export.S - , IS_PROTO = type & $export.P - , IS_BIND = type & $export.B - , IS_WRAP = type & $export.W - , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) - , expProto = exports[PROTOTYPE] - , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] - , key, own, out; - if(IS_GLOBAL)source = name; - for(key in source){ - // contains in native - own = !IS_FORCED && target && target[key] !== undefined; - if(own && key in exports)continue; - // export native or passed - out = own ? target[key] : source[key]; - // prevent global pollution for namespaces - exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] - // bind timers to global for call from export context - : IS_BIND && own ? ctx(out, global) - // wrap global constructors for prevent change them in library - : IS_WRAP && target[key] == out ? (function(C){ - var F = function(a, b, c){ - if(this instanceof C){ - switch(arguments.length){ - case 0: return new C; - case 1: return new C(a); - case 2: return new C(a, b); - } return new C(a, b, c); - } return C.apply(this, arguments); - }; - F[PROTOTYPE] = C[PROTOTYPE]; - return F; - // make static versions for prototype methods - })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; - // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% - if(IS_PROTO){ - (exports.virtual || (exports.virtual = {}))[key] = out; - // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% - if(type & $export.R && expProto && !expProto[key])hide(expProto, key, out); - } - } -}; -// type bitmap -$export.F = 1; // forced -$export.G = 2; // global -$export.S = 4; // static -$export.P = 8; // proto -$export.B = 16; // bind -$export.W = 32; // wrap -$export.U = 64; // safe -$export.R = 128; // real proto method for `library` -module.exports = $export; -},{"./_core":21,"./_ctx":22,"./_global":30,"./_hide":32}],28:[function(require,module,exports){ -module.exports = function(exec){ - try { - return !!exec(); - } catch(e){ - return true; - } -}; -},{}],29:[function(require,module,exports){ -var ctx = require('./_ctx') - , call = require('./_iter-call') - , isArrayIter = require('./_is-array-iter') - , anObject = require('./_an-object') - , toLength = require('./_to-length') - , getIterFn = require('./core.get-iterator-method') - , BREAK = {} - , RETURN = {}; -var exports = module.exports = function(iterable, entries, fn, that, ITERATOR){ - var iterFn = ITERATOR ? function(){ return iterable; } : getIterFn(iterable) - , f = ctx(fn, that, entries ? 2 : 1) - , index = 0 - , length, step, iterator, result; - if(typeof iterFn != 'function')throw TypeError(iterable + ' is not iterable!'); - // fast case for arrays with default iterator - if(isArrayIter(iterFn))for(length = toLength(iterable.length); length > index; index++){ - result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); - if(result === BREAK || result === RETURN)return result; - } else for(iterator = iterFn.call(iterable); !(step = iterator.next()).done; ){ - result = call(iterator, f, step.value, entries); - if(result === BREAK || result === RETURN)return result; - } -}; -exports.BREAK = BREAK; -exports.RETURN = RETURN; -},{"./_an-object":17,"./_ctx":22,"./_is-array-iter":37,"./_iter-call":39,"./_to-length":70,"./core.get-iterator-method":75}],30:[function(require,module,exports){ -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -var global = module.exports = typeof window != 'undefined' && window.Math == Math - ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); -if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef -},{}],31:[function(require,module,exports){ -var hasOwnProperty = {}.hasOwnProperty; -module.exports = function(it, key){ - return hasOwnProperty.call(it, key); -}; -},{}],32:[function(require,module,exports){ -var dP = require('./_object-dp') - , createDesc = require('./_property-desc'); -module.exports = require('./_descriptors') ? function(object, key, value){ - return dP.f(object, key, createDesc(1, value)); -} : function(object, key, value){ - object[key] = value; - return object; -}; -},{"./_descriptors":24,"./_object-dp":49,"./_property-desc":57}],33:[function(require,module,exports){ -module.exports = require('./_global').document && document.documentElement; -},{"./_global":30}],34:[function(require,module,exports){ -module.exports = !require('./_descriptors') && !require('./_fails')(function(){ - return Object.defineProperty(require('./_dom-create')('div'), 'a', {get: function(){ return 7; }}).a != 7; -}); -},{"./_descriptors":24,"./_dom-create":25,"./_fails":28}],35:[function(require,module,exports){ -// fast apply, http://jsperf.lnkit.com/fast-apply/5 -module.exports = function(fn, args, that){ - var un = that === undefined; - switch(args.length){ - case 0: return un ? fn() - : fn.call(that); - case 1: return un ? fn(args[0]) - : fn.call(that, args[0]); - case 2: return un ? fn(args[0], args[1]) - : fn.call(that, args[0], args[1]); - case 3: return un ? fn(args[0], args[1], args[2]) - : fn.call(that, args[0], args[1], args[2]); - case 4: return un ? fn(args[0], args[1], args[2], args[3]) - : fn.call(that, args[0], args[1], args[2], args[3]); - } return fn.apply(that, args); -}; -},{}],36:[function(require,module,exports){ -// fallback for non-array-like ES3 and non-enumerable old V8 strings -var cof = require('./_cof'); -module.exports = Object('z').propertyIsEnumerable(0) ? Object : function(it){ - return cof(it) == 'String' ? it.split('') : Object(it); -}; -},{"./_cof":20}],37:[function(require,module,exports){ -// check on default Array iterator -var Iterators = require('./_iterators') - , ITERATOR = require('./_wks')('iterator') - , ArrayProto = Array.prototype; - -module.exports = function(it){ - return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); -}; -},{"./_iterators":44,"./_wks":74}],38:[function(require,module,exports){ -module.exports = function(it){ - return typeof it === 'object' ? it !== null : typeof it === 'function'; -}; -},{}],39:[function(require,module,exports){ -// call something on iterator step with safe closing on error -var anObject = require('./_an-object'); -module.exports = function(iterator, fn, value, entries){ - try { - return entries ? fn(anObject(value)[0], value[1]) : fn(value); - // 7.4.6 IteratorClose(iterator, completion) - } catch(e){ - var ret = iterator['return']; - if(ret !== undefined)anObject(ret.call(iterator)); - throw e; - } -}; -},{"./_an-object":17}],40:[function(require,module,exports){ -'use strict'; -var create = require('./_object-create') - , descriptor = require('./_property-desc') - , setToStringTag = require('./_set-to-string-tag') - , IteratorPrototype = {}; - -// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() -require('./_hide')(IteratorPrototype, require('./_wks')('iterator'), function(){ return this; }); - -module.exports = function(Constructor, NAME, next){ - Constructor.prototype = create(IteratorPrototype, {next: descriptor(1, next)}); - setToStringTag(Constructor, NAME + ' Iterator'); -}; -},{"./_hide":32,"./_object-create":48,"./_property-desc":57,"./_set-to-string-tag":61,"./_wks":74}],41:[function(require,module,exports){ -'use strict'; -var LIBRARY = require('./_library') - , $export = require('./_export') - , redefine = require('./_redefine') - , hide = require('./_hide') - , has = require('./_has') - , Iterators = require('./_iterators') - , $iterCreate = require('./_iter-create') - , setToStringTag = require('./_set-to-string-tag') - , getPrototypeOf = require('./_object-gpo') - , ITERATOR = require('./_wks')('iterator') - , BUGGY = !([].keys && 'next' in [].keys()) // Safari has buggy iterators w/o `next` - , FF_ITERATOR = '@@iterator' - , KEYS = 'keys' - , VALUES = 'values'; - -var returnThis = function(){ return this; }; - -module.exports = function(Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED){ - $iterCreate(Constructor, NAME, next); - var getMethod = function(kind){ - if(!BUGGY && kind in proto)return proto[kind]; - switch(kind){ - case KEYS: return function keys(){ return new Constructor(this, kind); }; - case VALUES: return function values(){ return new Constructor(this, kind); }; - } return function entries(){ return new Constructor(this, kind); }; - }; - var TAG = NAME + ' Iterator' - , DEF_VALUES = DEFAULT == VALUES - , VALUES_BUG = false - , proto = Base.prototype - , $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT] - , $default = $native || getMethod(DEFAULT) - , $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined - , $anyNative = NAME == 'Array' ? proto.entries || $native : $native - , methods, key, IteratorPrototype; - // Fix native - if($anyNative){ - IteratorPrototype = getPrototypeOf($anyNative.call(new Base)); - if(IteratorPrototype !== Object.prototype){ - // Set @@toStringTag to native iterators - setToStringTag(IteratorPrototype, TAG, true); - // fix for some old engines - if(!LIBRARY && !has(IteratorPrototype, ITERATOR))hide(IteratorPrototype, ITERATOR, returnThis); - } - } - // fix Array#{values, @@iterator}.name in V8 / FF - if(DEF_VALUES && $native && $native.name !== VALUES){ - VALUES_BUG = true; - $default = function values(){ return $native.call(this); }; - } - // Define iterator - if((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])){ - hide(proto, ITERATOR, $default); - } - // Plug for library - Iterators[NAME] = $default; - Iterators[TAG] = returnThis; - if(DEFAULT){ - methods = { - values: DEF_VALUES ? $default : getMethod(VALUES), - keys: IS_SET ? $default : getMethod(KEYS), - entries: $entries - }; - if(FORCED)for(key in methods){ - if(!(key in proto))redefine(proto, key, methods[key]); - } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); - } - return methods; -}; -},{"./_export":27,"./_has":31,"./_hide":32,"./_iter-create":40,"./_iterators":44,"./_library":45,"./_object-gpo":52,"./_redefine":59,"./_set-to-string-tag":61,"./_wks":74}],42:[function(require,module,exports){ -var ITERATOR = require('./_wks')('iterator') - , SAFE_CLOSING = false; - -try { - var riter = [7][ITERATOR](); - riter['return'] = function(){ SAFE_CLOSING = true; }; - Array.from(riter, function(){ throw 2; }); -} catch(e){ /* empty */ } - -module.exports = function(exec, skipClosing){ - if(!skipClosing && !SAFE_CLOSING)return false; - var safe = false; - try { - var arr = [7] - , iter = arr[ITERATOR](); - iter.next = function(){ return {done: safe = true}; }; - arr[ITERATOR] = function(){ return iter; }; - exec(arr); - } catch(e){ /* empty */ } - return safe; -}; -},{"./_wks":74}],43:[function(require,module,exports){ -module.exports = function(done, value){ - return {value: value, done: !!done}; -}; -},{}],44:[function(require,module,exports){ -module.exports = {}; -},{}],45:[function(require,module,exports){ -module.exports = true; -},{}],46:[function(require,module,exports){ -var global = require('./_global') - , macrotask = require('./_task').set - , Observer = global.MutationObserver || global.WebKitMutationObserver - , process = global.process - , Promise = global.Promise - , isNode = require('./_cof')(process) == 'process'; - -module.exports = function(){ - var head, last, notify; - - var flush = function(){ - var parent, fn; - if(isNode && (parent = process.domain))parent.exit(); - while(head){ - fn = head.fn; - head = head.next; - try { - fn(); - } catch(e){ - if(head)notify(); - else last = undefined; - throw e; - } - } last = undefined; - if(parent)parent.enter(); - }; - - // Node.js - if(isNode){ - notify = function(){ - process.nextTick(flush); - }; - // browsers with MutationObserver - } else if(Observer){ - var toggle = true - , node = document.createTextNode(''); - new Observer(flush).observe(node, {characterData: true}); // eslint-disable-line no-new - notify = function(){ - node.data = toggle = !toggle; - }; - // environments with maybe non-completely correct, but existent Promise - } else if(Promise && Promise.resolve){ - var promise = Promise.resolve(); - notify = function(){ - promise.then(flush); - }; - // for other environments - macrotask based on: - // - setImmediate - // - MessageChannel - // - window.postMessag - // - onreadystatechange - // - setTimeout - } else { - notify = function(){ - // strange IE + webpack dev server bug - use .call(global) - macrotask.call(global, flush); - }; - } - - return function(fn){ - var task = {fn: fn, next: undefined}; - if(last)last.next = task; - if(!head){ - head = task; - notify(); - } last = task; - }; -}; -},{"./_cof":20,"./_global":30,"./_task":66}],47:[function(require,module,exports){ -'use strict'; -// 19.1.2.1 Object.assign(target, source, ...) -var getKeys = require('./_object-keys') - , gOPS = require('./_object-gops') - , pIE = require('./_object-pie') - , toObject = require('./_to-object') - , IObject = require('./_iobject') - , $assign = Object.assign; - -// should work with symbols and should have deterministic property order (V8 bug) -module.exports = !$assign || require('./_fails')(function(){ - var A = {} - , B = {} - , S = Symbol() - , K = 'abcdefghijklmnopqrst'; - A[S] = 7; - K.split('').forEach(function(k){ B[k] = k; }); - return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K; -}) ? function assign(target, source){ // eslint-disable-line no-unused-vars - var T = toObject(target) - , aLen = arguments.length - , index = 1 - , getSymbols = gOPS.f - , isEnum = pIE.f; - while(aLen > index){ - var S = IObject(arguments[index++]) - , keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S) - , length = keys.length - , j = 0 - , key; - while(length > j)if(isEnum.call(S, key = keys[j++]))T[key] = S[key]; - } return T; -} : $assign; -},{"./_fails":28,"./_iobject":36,"./_object-gops":51,"./_object-keys":54,"./_object-pie":55,"./_to-object":71}],48:[function(require,module,exports){ -// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) -var anObject = require('./_an-object') - , dPs = require('./_object-dps') - , enumBugKeys = require('./_enum-bug-keys') - , IE_PROTO = require('./_shared-key')('IE_PROTO') - , Empty = function(){ /* empty */ } - , PROTOTYPE = 'prototype'; - -// Create object with fake `null` prototype: use iframe Object with cleared prototype -var createDict = function(){ - // Thrash, waste and sodomy: IE GC bug - var iframe = require('./_dom-create')('iframe') - , i = enumBugKeys.length - , lt = '<' - , gt = '>' - , iframeDocument; - iframe.style.display = 'none'; - require('./_html').appendChild(iframe); - iframe.src = 'javascript:'; // eslint-disable-line no-script-url - // createDict = iframe.contentWindow.Object; - // html.removeChild(iframe); - iframeDocument = iframe.contentWindow.document; - iframeDocument.open(); - iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); - iframeDocument.close(); - createDict = iframeDocument.F; - while(i--)delete createDict[PROTOTYPE][enumBugKeys[i]]; - return createDict(); -}; - -module.exports = Object.create || function create(O, Properties){ - var result; - if(O !== null){ - Empty[PROTOTYPE] = anObject(O); - result = new Empty; - Empty[PROTOTYPE] = null; - // add "__proto__" for Object.getPrototypeOf polyfill - result[IE_PROTO] = O; - } else result = createDict(); - return Properties === undefined ? result : dPs(result, Properties); -}; - -},{"./_an-object":17,"./_dom-create":25,"./_enum-bug-keys":26,"./_html":33,"./_object-dps":50,"./_shared-key":62}],49:[function(require,module,exports){ -var anObject = require('./_an-object') - , IE8_DOM_DEFINE = require('./_ie8-dom-define') - , toPrimitive = require('./_to-primitive') - , dP = Object.defineProperty; - -exports.f = require('./_descriptors') ? Object.defineProperty : function defineProperty(O, P, Attributes){ - anObject(O); - P = toPrimitive(P, true); - anObject(Attributes); - if(IE8_DOM_DEFINE)try { - return dP(O, P, Attributes); - } catch(e){ /* empty */ } - if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!'); - if('value' in Attributes)O[P] = Attributes.value; - return O; -}; -},{"./_an-object":17,"./_descriptors":24,"./_ie8-dom-define":34,"./_to-primitive":72}],50:[function(require,module,exports){ -var dP = require('./_object-dp') - , anObject = require('./_an-object') - , getKeys = require('./_object-keys'); - -module.exports = require('./_descriptors') ? Object.defineProperties : function defineProperties(O, Properties){ - anObject(O); - var keys = getKeys(Properties) - , length = keys.length - , i = 0 - , P; - while(length > i)dP.f(O, P = keys[i++], Properties[P]); - return O; -}; -},{"./_an-object":17,"./_descriptors":24,"./_object-dp":49,"./_object-keys":54}],51:[function(require,module,exports){ -exports.f = Object.getOwnPropertySymbols; -},{}],52:[function(require,module,exports){ -// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) -var has = require('./_has') - , toObject = require('./_to-object') - , IE_PROTO = require('./_shared-key')('IE_PROTO') - , ObjectProto = Object.prototype; - -module.exports = Object.getPrototypeOf || function(O){ - O = toObject(O); - if(has(O, IE_PROTO))return O[IE_PROTO]; - if(typeof O.constructor == 'function' && O instanceof O.constructor){ - return O.constructor.prototype; - } return O instanceof Object ? ObjectProto : null; -}; -},{"./_has":31,"./_shared-key":62,"./_to-object":71}],53:[function(require,module,exports){ -var has = require('./_has') - , toIObject = require('./_to-iobject') - , arrayIndexOf = require('./_array-includes')(false) - , IE_PROTO = require('./_shared-key')('IE_PROTO'); - -module.exports = function(object, names){ - var O = toIObject(object) - , i = 0 - , result = [] - , key; - for(key in O)if(key != IE_PROTO)has(O, key) && result.push(key); - // Don't enum bug & hidden keys - while(names.length > i)if(has(O, key = names[i++])){ - ~arrayIndexOf(result, key) || result.push(key); - } - return result; -}; -},{"./_array-includes":18,"./_has":31,"./_shared-key":62,"./_to-iobject":69}],54:[function(require,module,exports){ -// 19.1.2.14 / 15.2.3.14 Object.keys(O) -var $keys = require('./_object-keys-internal') - , enumBugKeys = require('./_enum-bug-keys'); - -module.exports = Object.keys || function keys(O){ - return $keys(O, enumBugKeys); -}; -},{"./_enum-bug-keys":26,"./_object-keys-internal":53}],55:[function(require,module,exports){ -exports.f = {}.propertyIsEnumerable; -},{}],56:[function(require,module,exports){ -// most Object methods by ES6 should accept primitives -var $export = require('./_export') - , core = require('./_core') - , fails = require('./_fails'); -module.exports = function(KEY, exec){ - var fn = (core.Object || {})[KEY] || Object[KEY] - , exp = {}; - exp[KEY] = exec(fn); - $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp); -}; -},{"./_core":21,"./_export":27,"./_fails":28}],57:[function(require,module,exports){ -module.exports = function(bitmap, value){ - return { - enumerable : !(bitmap & 1), - configurable: !(bitmap & 2), - writable : !(bitmap & 4), - value : value - }; -}; -},{}],58:[function(require,module,exports){ -var hide = require('./_hide'); -module.exports = function(target, src, safe){ - for(var key in src){ - if(safe && target[key])target[key] = src[key]; - else hide(target, key, src[key]); - } return target; -}; -},{"./_hide":32}],59:[function(require,module,exports){ -module.exports = require('./_hide'); -},{"./_hide":32}],60:[function(require,module,exports){ -'use strict'; -var global = require('./_global') - , core = require('./_core') - , dP = require('./_object-dp') - , DESCRIPTORS = require('./_descriptors') - , SPECIES = require('./_wks')('species'); - -module.exports = function(KEY){ - var C = typeof core[KEY] == 'function' ? core[KEY] : global[KEY]; - if(DESCRIPTORS && C && !C[SPECIES])dP.f(C, SPECIES, { - configurable: true, - get: function(){ return this; } - }); -}; -},{"./_core":21,"./_descriptors":24,"./_global":30,"./_object-dp":49,"./_wks":74}],61:[function(require,module,exports){ -var def = require('./_object-dp').f - , has = require('./_has') - , TAG = require('./_wks')('toStringTag'); - -module.exports = function(it, tag, stat){ - if(it && !has(it = stat ? it : it.prototype, TAG))def(it, TAG, {configurable: true, value: tag}); -}; -},{"./_has":31,"./_object-dp":49,"./_wks":74}],62:[function(require,module,exports){ -var shared = require('./_shared')('keys') - , uid = require('./_uid'); -module.exports = function(key){ - return shared[key] || (shared[key] = uid(key)); -}; -},{"./_shared":63,"./_uid":73}],63:[function(require,module,exports){ -var global = require('./_global') - , SHARED = '__core-js_shared__' - , store = global[SHARED] || (global[SHARED] = {}); -module.exports = function(key){ - return store[key] || (store[key] = {}); -}; -},{"./_global":30}],64:[function(require,module,exports){ -// 7.3.20 SpeciesConstructor(O, defaultConstructor) -var anObject = require('./_an-object') - , aFunction = require('./_a-function') - , SPECIES = require('./_wks')('species'); -module.exports = function(O, D){ - var C = anObject(O).constructor, S; - return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); -}; -},{"./_a-function":14,"./_an-object":17,"./_wks":74}],65:[function(require,module,exports){ -var toInteger = require('./_to-integer') - , defined = require('./_defined'); -// true -> String#at -// false -> String#codePointAt -module.exports = function(TO_STRING){ - return function(that, pos){ - var s = String(defined(that)) - , i = toInteger(pos) - , l = s.length - , a, b; - if(i < 0 || i >= l)return TO_STRING ? '' : undefined; - a = s.charCodeAt(i); - return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff - ? TO_STRING ? s.charAt(i) : a - : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; - }; -}; -},{"./_defined":23,"./_to-integer":68}],66:[function(require,module,exports){ -var ctx = require('./_ctx') - , invoke = require('./_invoke') - , html = require('./_html') - , cel = require('./_dom-create') - , global = require('./_global') - , process = global.process - , setTask = global.setImmediate - , clearTask = global.clearImmediate - , MessageChannel = global.MessageChannel - , counter = 0 - , queue = {} - , ONREADYSTATECHANGE = 'onreadystatechange' - , defer, channel, port; -var run = function(){ - var id = +this; - if(queue.hasOwnProperty(id)){ - var fn = queue[id]; - delete queue[id]; - fn(); - } -}; -var listener = function(event){ - run.call(event.data); -}; -// Node.js 0.9+ & IE10+ has setImmediate, otherwise: -if(!setTask || !clearTask){ - setTask = function setImmediate(fn){ - var args = [], i = 1; - while(arguments.length > i)args.push(arguments[i++]); - queue[++counter] = function(){ - invoke(typeof fn == 'function' ? fn : Function(fn), args); - }; - defer(counter); - return counter; - }; - clearTask = function clearImmediate(id){ - delete queue[id]; - }; - // Node.js 0.8- - if(require('./_cof')(process) == 'process'){ - defer = function(id){ - process.nextTick(ctx(run, id, 1)); - }; - // Browsers with MessageChannel, includes WebWorkers - } else if(MessageChannel){ - channel = new MessageChannel; - port = channel.port2; - channel.port1.onmessage = listener; - defer = ctx(port.postMessage, port, 1); - // Browsers with postMessage, skip WebWorkers - // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' - } else if(global.addEventListener && typeof postMessage == 'function' && !global.importScripts){ - defer = function(id){ - global.postMessage(id + '', '*'); - }; - global.addEventListener('message', listener, false); - // IE8- - } else if(ONREADYSTATECHANGE in cel('script')){ - defer = function(id){ - html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function(){ - html.removeChild(this); - run.call(id); - }; - }; - // Rest old browsers - } else { - defer = function(id){ - setTimeout(ctx(run, id, 1), 0); - }; - } -} -module.exports = { - set: setTask, - clear: clearTask -}; -},{"./_cof":20,"./_ctx":22,"./_dom-create":25,"./_global":30,"./_html":33,"./_invoke":35}],67:[function(require,module,exports){ -var toInteger = require('./_to-integer') - , max = Math.max - , min = Math.min; -module.exports = function(index, length){ - index = toInteger(index); - return index < 0 ? max(index + length, 0) : min(index, length); -}; -},{"./_to-integer":68}],68:[function(require,module,exports){ -// 7.1.4 ToInteger -var ceil = Math.ceil - , floor = Math.floor; -module.exports = function(it){ - return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); -}; -},{}],69:[function(require,module,exports){ -// to indexed object, toObject with fallback for non-array-like ES3 strings -var IObject = require('./_iobject') - , defined = require('./_defined'); -module.exports = function(it){ - return IObject(defined(it)); -}; -},{"./_defined":23,"./_iobject":36}],70:[function(require,module,exports){ -// 7.1.15 ToLength -var toInteger = require('./_to-integer') - , min = Math.min; -module.exports = function(it){ - return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 -}; -},{"./_to-integer":68}],71:[function(require,module,exports){ -// 7.1.13 ToObject(argument) -var defined = require('./_defined'); -module.exports = function(it){ - return Object(defined(it)); -}; -},{"./_defined":23}],72:[function(require,module,exports){ -// 7.1.1 ToPrimitive(input [, PreferredType]) -var isObject = require('./_is-object'); -// instead of the ES6 spec version, we didn't implement @@toPrimitive case -// and the second argument - flag - preferred type is a string -module.exports = function(it, S){ - if(!isObject(it))return it; - var fn, val; - if(S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; - if(typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it)))return val; - if(!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; - throw TypeError("Can't convert object to primitive value"); -}; -},{"./_is-object":38}],73:[function(require,module,exports){ -var id = 0 - , px = Math.random(); -module.exports = function(key){ - return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); -}; -},{}],74:[function(require,module,exports){ -var store = require('./_shared')('wks') - , uid = require('./_uid') - , Symbol = require('./_global').Symbol - , USE_SYMBOL = typeof Symbol == 'function'; - -var $exports = module.exports = function(name){ - return store[name] || (store[name] = - USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); -}; - -$exports.store = store; -},{"./_global":30,"./_shared":63,"./_uid":73}],75:[function(require,module,exports){ -var classof = require('./_classof') - , ITERATOR = require('./_wks')('iterator') - , Iterators = require('./_iterators'); -module.exports = require('./_core').getIteratorMethod = function(it){ - if(it != undefined)return it[ITERATOR] - || it['@@iterator'] - || Iterators[classof(it)]; -}; -},{"./_classof":19,"./_core":21,"./_iterators":44,"./_wks":74}],76:[function(require,module,exports){ -'use strict'; -var addToUnscopables = require('./_add-to-unscopables') - , step = require('./_iter-step') - , Iterators = require('./_iterators') - , toIObject = require('./_to-iobject'); - -// 22.1.3.4 Array.prototype.entries() -// 22.1.3.13 Array.prototype.keys() -// 22.1.3.29 Array.prototype.values() -// 22.1.3.30 Array.prototype[@@iterator]() -module.exports = require('./_iter-define')(Array, 'Array', function(iterated, kind){ - this._t = toIObject(iterated); // target - this._i = 0; // next index - this._k = kind; // kind -// 22.1.5.2.1 %ArrayIteratorPrototype%.next() -}, function(){ - var O = this._t - , kind = this._k - , index = this._i++; - if(!O || index >= O.length){ - this._t = undefined; - return step(1); - } - if(kind == 'keys' )return step(0, index); - if(kind == 'values')return step(0, O[index]); - return step(0, [index, O[index]]); -}, 'values'); - -// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) -Iterators.Arguments = Iterators.Array; - -addToUnscopables('keys'); -addToUnscopables('values'); -addToUnscopables('entries'); -},{"./_add-to-unscopables":15,"./_iter-define":41,"./_iter-step":43,"./_iterators":44,"./_to-iobject":69}],77:[function(require,module,exports){ -// 19.1.3.1 Object.assign(target, source) -var $export = require('./_export'); - -$export($export.S + $export.F, 'Object', {assign: require('./_object-assign')}); -},{"./_export":27,"./_object-assign":47}],78:[function(require,module,exports){ -// 19.1.2.14 Object.keys(O) -var toObject = require('./_to-object') - , $keys = require('./_object-keys'); - -require('./_object-sap')('keys', function(){ - return function keys(it){ - return $keys(toObject(it)); - }; -}); -},{"./_object-keys":54,"./_object-sap":56,"./_to-object":71}],79:[function(require,module,exports){ -arguments[4][9][0].apply(exports,arguments) -},{"dup":9}],80:[function(require,module,exports){ -'use strict'; -var LIBRARY = require('./_library') - , global = require('./_global') - , ctx = require('./_ctx') - , classof = require('./_classof') - , $export = require('./_export') - , isObject = require('./_is-object') - , aFunction = require('./_a-function') - , anInstance = require('./_an-instance') - , forOf = require('./_for-of') - , speciesConstructor = require('./_species-constructor') - , task = require('./_task').set - , microtask = require('./_microtask')() - , PROMISE = 'Promise' - , TypeError = global.TypeError - , process = global.process - , $Promise = global[PROMISE] - , process = global.process - , isNode = classof(process) == 'process' - , empty = function(){ /* empty */ } - , Internal, GenericPromiseCapability, Wrapper; - -var USE_NATIVE = !!function(){ - try { - // correct subclassing with @@species support - var promise = $Promise.resolve(1) - , FakePromise = (promise.constructor = {})[require('./_wks')('species')] = function(exec){ exec(empty, empty); }; - // unhandled rejections tracking support, NodeJS Promise without it fails @@species test - return (isNode || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise; - } catch(e){ /* empty */ } -}(); - -// helpers -var sameConstructor = function(a, b){ - // with library wrapper special case - return a === b || a === $Promise && b === Wrapper; -}; -var isThenable = function(it){ - var then; - return isObject(it) && typeof (then = it.then) == 'function' ? then : false; -}; -var newPromiseCapability = function(C){ - return sameConstructor($Promise, C) - ? new PromiseCapability(C) - : new GenericPromiseCapability(C); -}; -var PromiseCapability = GenericPromiseCapability = function(C){ - var resolve, reject; - this.promise = new C(function($$resolve, $$reject){ - if(resolve !== undefined || reject !== undefined)throw TypeError('Bad Promise constructor'); - resolve = $$resolve; - reject = $$reject; - }); - this.resolve = aFunction(resolve); - this.reject = aFunction(reject); -}; -var perform = function(exec){ - try { - exec(); - } catch(e){ - return {error: e}; - } -}; -var notify = function(promise, isReject){ - if(promise._n)return; - promise._n = true; - var chain = promise._c; - microtask(function(){ - var value = promise._v - , ok = promise._s == 1 - , i = 0; - var run = function(reaction){ - var handler = ok ? reaction.ok : reaction.fail - , resolve = reaction.resolve - , reject = reaction.reject - , domain = reaction.domain - , result, then; - try { - if(handler){ - if(!ok){ - if(promise._h == 2)onHandleUnhandled(promise); - promise._h = 1; - } - if(handler === true)result = value; - else { - if(domain)domain.enter(); - result = handler(value); - if(domain)domain.exit(); - } - if(result === reaction.promise){ - reject(TypeError('Promise-chain cycle')); - } else if(then = isThenable(result)){ - then.call(result, resolve, reject); - } else resolve(result); - } else reject(value); - } catch(e){ - reject(e); - } - }; - while(chain.length > i)run(chain[i++]); // variable length - can't use forEach - promise._c = []; - promise._n = false; - if(isReject && !promise._h)onUnhandled(promise); - }); -}; -var onUnhandled = function(promise){ - task.call(global, function(){ - var value = promise._v - , abrupt, handler, console; - if(isUnhandled(promise)){ - abrupt = perform(function(){ - if(isNode){ - process.emit('unhandledRejection', value, promise); - } else if(handler = global.onunhandledrejection){ - handler({promise: promise, reason: value}); - } else if((console = global.console) && console.error){ - console.error('Unhandled promise rejection', value); - } - }); - // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should - promise._h = isNode || isUnhandled(promise) ? 2 : 1; - } promise._a = undefined; - if(abrupt)throw abrupt.error; - }); -}; -var isUnhandled = function(promise){ - if(promise._h == 1)return false; - var chain = promise._a || promise._c - , i = 0 - , reaction; - while(chain.length > i){ - reaction = chain[i++]; - if(reaction.fail || !isUnhandled(reaction.promise))return false; - } return true; -}; -var onHandleUnhandled = function(promise){ - task.call(global, function(){ - var handler; - if(isNode){ - process.emit('rejectionHandled', promise); - } else if(handler = global.onrejectionhandled){ - handler({promise: promise, reason: promise._v}); - } - }); -}; -var $reject = function(value){ - var promise = this; - if(promise._d)return; - promise._d = true; - promise = promise._w || promise; // unwrap - promise._v = value; - promise._s = 2; - if(!promise._a)promise._a = promise._c.slice(); - notify(promise, true); -}; -var $resolve = function(value){ - var promise = this - , then; - if(promise._d)return; - promise._d = true; - promise = promise._w || promise; // unwrap - try { - if(promise === value)throw TypeError("Promise can't be resolved itself"); - if(then = isThenable(value)){ - microtask(function(){ - var wrapper = {_w: promise, _d: false}; // wrap - try { - then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); - } catch(e){ - $reject.call(wrapper, e); - } - }); - } else { - promise._v = value; - promise._s = 1; - notify(promise, false); - } - } catch(e){ - $reject.call({_w: promise, _d: false}, e); // wrap - } -}; - -// constructor polyfill -if(!USE_NATIVE){ - // 25.4.3.1 Promise(executor) - $Promise = function Promise(executor){ - anInstance(this, $Promise, PROMISE, '_h'); - aFunction(executor); - Internal.call(this); - try { - executor(ctx($resolve, this, 1), ctx($reject, this, 1)); - } catch(err){ - $reject.call(this, err); - } - }; - Internal = function Promise(executor){ - this._c = []; // <- awaiting reactions - this._a = undefined; // <- checked in isUnhandled reactions - this._s = 0; // <- state - this._d = false; // <- done - this._v = undefined; // <- value - this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled - this._n = false; // <- notify - }; - Internal.prototype = require('./_redefine-all')($Promise.prototype, { - // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) - then: function then(onFulfilled, onRejected){ - var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); - reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; - reaction.fail = typeof onRejected == 'function' && onRejected; - reaction.domain = isNode ? process.domain : undefined; - this._c.push(reaction); - if(this._a)this._a.push(reaction); - if(this._s)notify(this, false); - return reaction.promise; - }, - // 25.4.5.1 Promise.prototype.catch(onRejected) - 'catch': function(onRejected){ - return this.then(undefined, onRejected); - } - }); - PromiseCapability = function(){ - var promise = new Internal; - this.promise = promise; - this.resolve = ctx($resolve, promise, 1); - this.reject = ctx($reject, promise, 1); - }; -} - -$export($export.G + $export.W + $export.F * !USE_NATIVE, {Promise: $Promise}); -require('./_set-to-string-tag')($Promise, PROMISE); -require('./_set-species')(PROMISE); -Wrapper = require('./_core')[PROMISE]; - -// statics -$export($export.S + $export.F * !USE_NATIVE, PROMISE, { - // 25.4.4.5 Promise.reject(r) - reject: function reject(r){ - var capability = newPromiseCapability(this) - , $$reject = capability.reject; - $$reject(r); - return capability.promise; - } -}); -$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { - // 25.4.4.6 Promise.resolve(x) - resolve: function resolve(x){ - // instanceof instead of internal slot check because we should fix it without replacement native Promise core - if(x instanceof $Promise && sameConstructor(x.constructor, this))return x; - var capability = newPromiseCapability(this) - , $$resolve = capability.resolve; - $$resolve(x); - return capability.promise; - } -}); -$export($export.S + $export.F * !(USE_NATIVE && require('./_iter-detect')(function(iter){ - $Promise.all(iter)['catch'](empty); -})), PROMISE, { - // 25.4.4.1 Promise.all(iterable) - all: function all(iterable){ - var C = this - , capability = newPromiseCapability(C) - , resolve = capability.resolve - , reject = capability.reject; - var abrupt = perform(function(){ - var values = [] - , index = 0 - , remaining = 1; - forOf(iterable, false, function(promise){ - var $index = index++ - , alreadyCalled = false; - values.push(undefined); - remaining++; - C.resolve(promise).then(function(value){ - if(alreadyCalled)return; - alreadyCalled = true; - values[$index] = value; - --remaining || resolve(values); - }, reject); - }); - --remaining || resolve(values); - }); - if(abrupt)reject(abrupt.error); - return capability.promise; - }, - // 25.4.4.4 Promise.race(iterable) - race: function race(iterable){ - var C = this - , capability = newPromiseCapability(C) - , reject = capability.reject; - var abrupt = perform(function(){ - forOf(iterable, false, function(promise){ - C.resolve(promise).then(capability.resolve, reject); - }); - }); - if(abrupt)reject(abrupt.error); - return capability.promise; - } -}); -},{"./_a-function":14,"./_an-instance":16,"./_classof":19,"./_core":21,"./_ctx":22,"./_export":27,"./_for-of":29,"./_global":30,"./_is-object":38,"./_iter-detect":42,"./_library":45,"./_microtask":46,"./_redefine-all":58,"./_set-species":60,"./_set-to-string-tag":61,"./_species-constructor":64,"./_task":66,"./_wks":74}],81:[function(require,module,exports){ -'use strict'; -var $at = require('./_string-at')(true); - -// 21.1.3.27 String.prototype[@@iterator]() -require('./_iter-define')(String, 'String', function(iterated){ - this._t = String(iterated); // target - this._i = 0; // next index -// 21.1.5.2.1 %StringIteratorPrototype%.next() -}, function(){ - var O = this._t - , index = this._i - , point; - if(index >= O.length)return {value: undefined, done: true}; - point = $at(O, index); - this._i += point.length; - return {value: point, done: false}; -}); -},{"./_iter-define":41,"./_string-at":65}],82:[function(require,module,exports){ -require('./es6.array.iterator'); -var global = require('./_global') - , hide = require('./_hide') - , Iterators = require('./_iterators') - , TO_STRING_TAG = require('./_wks')('toStringTag'); - -for(var collections = ['NodeList', 'DOMTokenList', 'MediaList', 'StyleSheetList', 'CSSRuleList'], i = 0; i < 5; i++){ - var NAME = collections[i] - , Collection = global[NAME] - , proto = Collection && Collection.prototype; - if(proto && !proto[TO_STRING_TAG])hide(proto, TO_STRING_TAG, NAME); - Iterators[NAME] = Iterators.Array; -} -},{"./_global":30,"./_hide":32,"./_iterators":44,"./_wks":74,"./es6.array.iterator":76}],83:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _extends2 = require("babel-runtime/helpers/extends"); - -var _extends3 = _interopRequireDefault(_extends2); - -var _collection = require("./collection"); - -var _collection2 = _interopRequireDefault(_collection); - -var _base = require("./adapters/base"); - -var _base2 = _interopRequireDefault(_base); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const DEFAULT_BUCKET_NAME = "default"; -const DEFAULT_REMOTE = "http://localhost:8888/v1"; - -/** - * KintoBase class. - */ -class KintoBase { - /** - * Provides a public access to the base adapter class. Users can create a - * custom DB adapter by extending {@link BaseAdapter}. - * - * @type {Object} - */ - static get adapters() { - return { - BaseAdapter: _base2.default - }; - } - - /** - * Synchronization strategies. Available strategies are: - * - * - `MANUAL`: Conflicts will be reported in a dedicated array. - * - `SERVER_WINS`: Conflicts are resolved using remote data. - * - `CLIENT_WINS`: Conflicts are resolved using local data. - * - * @type {Object} - */ - static get syncStrategy() { - return _collection2.default.strategy; - } - - /** - * Constructor. - * - * Options: - * - `{String}` `remote` The server URL to use. - * - `{String}` `bucket` The collection bucket name. - * - `{EventEmitter}` `events` Events handler. - * - `{BaseAdapter}` `adapter` The base DB adapter class. - * - `{Object}` `adapterOptions` Options given to the adapter. - * - `{String}` `dbPrefix` The DB name prefix. - * - `{Object}` `headers` The HTTP headers to use. - * - `{String}` `requestMode` The HTTP CORS mode to use. - * - `{Number}` `timeout` The requests timeout in ms (default: `5000`). - * - * @param {Object} options The options object. - */ - constructor(options = {}) { - const defaults = { - bucket: DEFAULT_BUCKET_NAME, - remote: DEFAULT_REMOTE - }; - this._options = (0, _extends3.default)({}, defaults, options); - if (!this._options.adapter) { - throw new Error("No adapter provided"); - } - - const { remote, events, headers, requestMode, timeout, ApiClass } = this._options; - - // public properties - - /** - * The kinto HTTP client instance. - * @type {KintoClient} - */ - this.api = new ApiClass(remote, { events, headers, requestMode, timeout }); - /** - * The event emitter instance. - * @type {EventEmitter} - */ - this.events = this._options.events; - } - - /** - * Creates a {@link Collection} instance. The second (optional) parameter - * will set collection-level options like e.g. `remoteTransformers`. - * - * @param {String} collName The collection name. - * @param {Object} options May contain the following fields: - * remoteTransformers: Array<RemoteTransformer> - * @return {Collection} - */ - collection(collName, options = {}) { - if (!collName) { - throw new Error("missing collection name"); - } - - const bucket = this._options.bucket; - return new _collection2.default(bucket, collName, this.api, { - events: this._options.events, - adapter: this._options.adapter, - adapterOptions: this._options.adapterOptions, - dbPrefix: this._options.dbPrefix, - idSchema: options.idSchema, - remoteTransformers: options.remoteTransformers, - hooks: options.hooks - }); - } -} -exports.default = KintoBase; - -},{"./adapters/base":85,"./collection":86,"babel-runtime/helpers/extends":8}],84:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _asyncToGenerator2 = require("babel-runtime/helpers/asyncToGenerator"); - -var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); - -var _promise = require("babel-runtime/core-js/promise"); - -var _promise2 = _interopRequireDefault(_promise); - -var _keys = require("babel-runtime/core-js/object/keys"); - -var _keys2 = _interopRequireDefault(_keys); - -var _base = require("./base.js"); - -var _base2 = _interopRequireDefault(_base); - -var _utils = require("../utils"); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const INDEXED_FIELDS = ["id", "_status", "last_modified"]; - -/** - * IDB cursor handlers. - * @type {Object} - */ -const cursorHandlers = { - all(filters, done) { - const results = []; - return function (event) { - const cursor = event.target.result; - if (cursor) { - if ((0, _utils.filterObject)(filters, cursor.value)) { - results.push(cursor.value); - } - cursor.continue(); - } else { - done(results); - } - }; - }, - - in(values, done) { - if (values.length === 0) { - return done([]); - } - const sortedValues = [].slice.call(values).sort(); - const results = []; - return function (event) { - const cursor = event.target.result; - if (!cursor) { - done(results); - return; - } - const { key, value } = cursor; - let i = 0; - while (key > sortedValues[i]) { - // The cursor has passed beyond this key. Check next. - ++i; - if (i === sortedValues.length) { - done(results); // There is no next. Stop searching. - return; - } - } - if (key === sortedValues[i]) { - results.push(value); - cursor.continue(); - } else { - cursor.continue(sortedValues[i]); - } - }; - } -}; - -/** - * Extract from filters definition the first indexed field. Since indexes were - * created on single-columns, extracting a single one makes sense. - * - * @param {Object} filters The filters object. - * @return {String|undefined} - */ -function findIndexedField(filters) { - const filteredFields = (0, _keys2.default)(filters); - const indexedFields = filteredFields.filter(field => { - return INDEXED_FIELDS.indexOf(field) !== -1; - }); - return indexedFields[0]; -} - -/** - * Creates an IDB request and attach it the appropriate cursor event handler to - * perform a list query. - * - * Multiple matching values are handled by passing an array. - * - * @param {IDBStore} store The IDB store. - * @param {String|undefined} indexField The indexed field to query, if any. - * @param {Any} value The value to filter, if any. - * @param {Object} filters More filters. - * @param {Function} done The operation completion handler. - * @return {IDBRequest} - */ -function createListRequest(store, indexField, value, filters, done) { - if (!indexField) { - // Get all records. - const request = store.openCursor(); - request.onsuccess = cursorHandlers.all(filters, done); - return request; - } - - // WHERE IN equivalent clause - if (Array.isArray(value)) { - const request = store.index(indexField).openCursor(); - request.onsuccess = cursorHandlers.in(value, done); - return request; - } - - // WHERE field = value clause - const request = store.index(indexField).openCursor(IDBKeyRange.only(value)); - request.onsuccess = cursorHandlers.all(filters, done); - return request; -} - -/** - * IndexedDB adapter. - * - * This adapter doesn't support any options. - */ -class IDB extends _base2.default { - /** - * Constructor. - * - * @param {String} dbname The database nale. - */ - constructor(dbname) { - super(); - this._db = null; - // public properties - /** - * The database name. - * @type {String} - */ - this.dbname = dbname; - } - - _handleError(method, err) { - const error = new Error(method + "() " + err.message); - error.stack = err.stack; - throw error; - } - - /** - * Ensures a connection to the IndexedDB database has been opened. - * - * @override - * @return {Promise} - */ - open() { - if (this._db) { - return _promise2.default.resolve(this); - } - return new _promise2.default((resolve, reject) => { - const request = indexedDB.open(this.dbname, 1); - request.onupgradeneeded = event => { - // DB object - const db = event.target.result; - // Main collection store - const collStore = db.createObjectStore(this.dbname, { - keyPath: "id" - }); - // Primary key (generated by IdSchema, UUID by default) - collStore.createIndex("id", "id", { unique: true }); - // Local record status ("synced", "created", "updated", "deleted") - collStore.createIndex("_status", "_status"); - // Last modified field - collStore.createIndex("last_modified", "last_modified"); - - // Metadata store - const metaStore = db.createObjectStore("__meta__", { - keyPath: "name" - }); - metaStore.createIndex("name", "name", { unique: true }); - }; - request.onerror = event => reject(event.target.error); - request.onsuccess = event => { - this._db = event.target.result; - resolve(this); - }; - }); - } - - /** - * Closes current connection to the database. - * - * @override - * @return {Promise} - */ - close() { - if (this._db) { - this._db.close(); // indexedDB.close is synchronous - this._db = null; - } - return super.close(); - } - - /** - * Returns a transaction and a store objects for this collection. - * - * To determine if a transaction has completed successfully, we should rather - * listen to the transaction’s complete event rather than the IDBObjectStore - * request’s success event, because the transaction may still fail after the - * success event fires. - * - * @param {String} mode Transaction mode ("readwrite" or undefined) - * @param {String|null} name Store name (defaults to coll name) - * @return {Object} - */ - prepare(mode = undefined, name = null) { - const storeName = name || this.dbname; - // On Safari, calling IDBDatabase.transaction with mode == undefined raises - // a TypeError. - const transaction = mode ? this._db.transaction([storeName], mode) : this._db.transaction([storeName]); - const store = transaction.objectStore(storeName); - return { transaction, store }; - } - - /** - * Deletes every records in the current collection. - * - * @override - * @return {Promise} - */ - clear() { - var _this = this; - - return (0, _asyncToGenerator3.default)(function* () { - try { - yield _this.open(); - return new _promise2.default(function (resolve, reject) { - const { transaction, store } = _this.prepare("readwrite"); - store.clear(); - transaction.onerror = function (event) { - return reject(new Error(event.target.error)); - }; - transaction.oncomplete = function () { - return resolve(); - }; - }); - } catch (e) { - _this._handleError("clear", e); - } - })(); - } - - /** - * Executes the set of synchronous CRUD operations described in the provided - * callback within an IndexedDB transaction, for current db store. - * - * The callback will be provided an object exposing the following synchronous - * CRUD operation methods: get, create, update, delete. - * - * Important note: because limitations in IndexedDB implementations, no - * asynchronous code should be performed within the provided callback; the - * promise will therefore be rejected if the callback returns a Promise. - * - * Options: - * - {Array} preload: The list of record IDs to fetch and make available to - * the transaction object get() method (default: []) - * - * @example - * const db = new IDB("example"); - * db.execute(transaction => { - * transaction.create({id: 1, title: "foo"}); - * transaction.update({id: 2, title: "bar"}); - * transaction.delete(3); - * return "foo"; - * }) - * .catch(console.error.bind(console)); - * .then(console.log.bind(console)); // => "foo" - * - * @param {Function} callback The operation description callback. - * @param {Object} options The options object. - * @return {Promise} - */ - execute(callback, options = { preload: [] }) { - var _this2 = this; - - return (0, _asyncToGenerator3.default)(function* () { - // Transactions in IndexedDB are autocommited when a callback does not - // perform any additional operation. - // The way Promises are implemented in Firefox (see https://bugzilla.mozilla.org/show_bug.cgi?id=1193394) - // prevents using within an opened transaction. - // To avoid managing asynchronocity in the specified `callback`, we preload - // a list of record in order to execute the `callback` synchronously. - // See also: - // - http://stackoverflow.com/a/28388805/330911 - // - http://stackoverflow.com/a/10405196 - // - https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ - yield _this2.open(); - return new _promise2.default(function (resolve, reject) { - // Start transaction. - const { transaction, store } = _this2.prepare("readwrite"); - // Preload specified records using index. - const ids = options.preload; - store.index("id").openCursor().onsuccess = cursorHandlers.in(ids, function (records) { - // Store obtained records by id. - const preloaded = records.reduce(function (acc, record) { - acc[record.id] = record; - return acc; - }, {}); - // Expose a consistent API for every adapter instead of raw store methods. - const proxy = transactionProxy(store, preloaded); - // The callback is executed synchronously within the same transaction. - let result; - try { - result = callback(proxy); - } catch (e) { - transaction.abort(); - reject(e); - } - if (result instanceof _promise2.default) { - // XXX: investigate how to provide documentation details in error. - reject(new Error("execute() callback should not return a Promise.")); - } - // XXX unsure if we should manually abort the transaction on error - transaction.onerror = function (event) { - return reject(new Error(event.target.error)); - }; - transaction.oncomplete = function (event) { - return resolve(result); - }; - }); - }); - })(); - } - - /** - * Retrieve a record by its primary key from the IndexedDB database. - * - * @override - * @param {String} id The record id. - * @return {Promise} - */ - get(id) { - var _this3 = this; - - return (0, _asyncToGenerator3.default)(function* () { - try { - yield _this3.open(); - return new _promise2.default(function (resolve, reject) { - const { transaction, store } = _this3.prepare(); - const request = store.get(id); - transaction.onerror = function (event) { - return reject(new Error(event.target.error)); - }; - transaction.oncomplete = function () { - return resolve(request.result); - }; - }); - } catch (e) { - _this3._handleError("get", e); - } - })(); - } - - /** - * Lists all records from the IndexedDB database. - * - * @override - * @return {Promise} - */ - list(params = { filters: {} }) { - var _this4 = this; - - return (0, _asyncToGenerator3.default)(function* () { - const { filters } = params; - const indexField = findIndexedField(filters); - const value = filters[indexField]; - try { - yield _this4.open(); - const results = yield new _promise2.default(function (resolve, reject) { - let results = []; - // If `indexField` was used already, don't filter again. - const remainingFilters = (0, _utils.omitKeys)(filters, indexField); - - const { transaction, store } = _this4.prepare(); - createListRequest(store, indexField, value, remainingFilters, function (_results) { - // we have received all requested records, parking them within - // current scope - results = _results; - }); - transaction.onerror = function (event) { - return reject(new Error(event.target.error)); - }; - transaction.oncomplete = function (event) { - return resolve(results); - }; - }); - - // The resulting list of records is sorted. - // XXX: with some efforts, this could be fully implemented using IDB API. - return params.order ? (0, _utils.sortObjects)(params.order, results) : results; - } catch (e) { - _this4._handleError("list", e); - } - })(); - } - - /** - * Store the lastModified value into metadata store. - * - * @override - * @param {Number} lastModified - * @return {Promise} - */ - saveLastModified(lastModified) { - var _this5 = this; - - return (0, _asyncToGenerator3.default)(function* () { - const value = parseInt(lastModified, 10) || null; - yield _this5.open(); - return new _promise2.default(function (resolve, reject) { - const { transaction, store } = _this5.prepare("readwrite", "__meta__"); - store.put({ name: "lastModified", value: value }); - transaction.onerror = function (event) { - return reject(event.target.error); - }; - transaction.oncomplete = function (event) { - return resolve(value); - }; - }); - })(); - } - - /** - * Retrieve saved lastModified value. - * - * @override - * @return {Promise} - */ - getLastModified() { - var _this6 = this; - - return (0, _asyncToGenerator3.default)(function* () { - yield _this6.open(); - return new _promise2.default(function (resolve, reject) { - const { transaction, store } = _this6.prepare(undefined, "__meta__"); - const request = store.get("lastModified"); - transaction.onerror = function (event) { - return reject(event.target.error); - }; - transaction.oncomplete = function (event) { - resolve(request.result && request.result.value || null); - }; - }); - })(); - } - - /** - * Load a dump of records exported from a server. - * - * @abstract - * @return {Promise} - */ - loadDump(records) { - var _this7 = this; - - return (0, _asyncToGenerator3.default)(function* () { - try { - yield _this7.execute(function (transaction) { - records.forEach(function (record) { - return transaction.update(record); - }); - }); - const previousLastModified = yield _this7.getLastModified(); - const lastModified = Math.max(...records.map(function (record) { - return record.last_modified; - })); - if (lastModified > previousLastModified) { - yield _this7.saveLastModified(lastModified); - } - return records; - } catch (e) { - _this7._handleError("loadDump", e); - } - })(); - } -} - -exports.default = IDB; /** - * IDB transaction proxy. - * - * @param {IDBStore} store The IndexedDB database store. - * @param {Array} preloaded The list of records to make available to - * get() (default: []). - * @return {Object} - */ - -function transactionProxy(store, preloaded = []) { - return { - create(record) { - store.add(record); - }, - - update(record) { - store.put(record); - }, - - delete(id) { - store.delete(id); - }, - - get(id) { - return preloaded[id]; - } - }; -} - -},{"../utils":87,"./base.js":85,"babel-runtime/core-js/object/keys":5,"babel-runtime/core-js/promise":6,"babel-runtime/helpers/asyncToGenerator":7}],85:[function(require,module,exports){ -"use strict"; - -/** - * Base db adapter. - * - * @abstract - */ - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _promise = require("babel-runtime/core-js/promise"); - -var _promise2 = _interopRequireDefault(_promise); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -class BaseAdapter { - /** - * Opens a connection to the database. - * - * @abstract - * @return {Promise} - */ - open() { - return _promise2.default.resolve(); - } - - /** - * Closes current connection to the database. - * - * @abstract - * @return {Promise} - */ - close() { - return _promise2.default.resolve(); - } - - /** - * Deletes every records present in the database. - * - * @abstract - * @return {Promise} - */ - clear() { - throw new Error("Not Implemented."); - } - - /** - * Executes a batch of operations within a single transaction. - * - * @abstract - * @param {Function} callback The operation callback. - * @param {Object} options The options object. - * @return {Promise} - */ - execute(callback, options = { preload: [] }) { - throw new Error("Not Implemented."); - } - - /** - * Retrieve a record by its primary key from the database. - * - * @abstract - * @param {String} id The record id. - * @return {Promise} - */ - get(id) { - throw new Error("Not Implemented."); - } - - /** - * Lists all records from the database. - * - * @abstract - * @param {Object} params The filters and order to apply to the results. - * @return {Promise} - */ - list(params = { filters: {}, order: "" }) { - throw new Error("Not Implemented."); - } - - /** - * Store the lastModified value. - * - * @abstract - * @param {Number} lastModified - * @return {Promise} - */ - saveLastModified(lastModified) { - throw new Error("Not Implemented."); - } - - /** - * Retrieve saved lastModified value. - * - * @abstract - * @return {Promise} - */ - getLastModified() { - throw new Error("Not Implemented."); - } - - /** - * Load a dump of records exported from a server. - * - * @abstract - * @return {Promise} - */ - loadDump(records) { - throw new Error("Not Implemented."); - } -} -exports.default = BaseAdapter; - -},{"babel-runtime/core-js/promise":6}],86:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.CollectionTransaction = exports.SyncResultObject = undefined; - -var _stringify = require("babel-runtime/core-js/json/stringify"); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _promise = require("babel-runtime/core-js/promise"); - -var _promise2 = _interopRequireDefault(_promise); - -var _asyncToGenerator2 = require("babel-runtime/helpers/asyncToGenerator"); - -var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); - -var _extends2 = require("babel-runtime/helpers/extends"); - -var _extends3 = _interopRequireDefault(_extends2); - -var _assign = require("babel-runtime/core-js/object/assign"); - -var _assign2 = _interopRequireDefault(_assign); - -exports.recordsEqual = recordsEqual; - -var _base = require("./adapters/base"); - -var _base2 = _interopRequireDefault(_base); - -var _IDB = require("./adapters/IDB"); - -var _IDB2 = _interopRequireDefault(_IDB); - -var _utils = require("./utils"); - -var _uuid = require("uuid"); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const RECORD_FIELDS_TO_CLEAN = ["_status"]; -const AVAILABLE_HOOKS = ["incoming-changes"]; - -/** - * Compare two records omitting local fields and synchronization - * attributes (like _status and last_modified) - * @param {Object} a A record to compare. - * @param {Object} b A record to compare. - * @return {boolean} - */ -function recordsEqual(a, b, localFields = []) { - const fieldsToClean = RECORD_FIELDS_TO_CLEAN.concat(["last_modified"]).concat(localFields); - const cleanLocal = r => (0, _utils.omitKeys)(r, fieldsToClean); - return (0, _utils.deepEqual)(cleanLocal(a), cleanLocal(b)); -} - -/** - * Synchronization result object. - */ -class SyncResultObject { - /** - * Object default values. - * @type {Object} - */ - static get defaults() { - return { - ok: true, - lastModified: null, - errors: [], - created: [], - updated: [], - deleted: [], - published: [], - conflicts: [], - skipped: [], - resolved: [] - }; - } - - /** - * Public constructor. - */ - constructor() { - /** - * Current synchronization result status; becomes `false` when conflicts or - * errors are registered. - * @type {Boolean} - */ - this.ok = true; - (0, _assign2.default)(this, SyncResultObject.defaults); - } - - /** - * Adds entries for a given result type. - * - * @param {String} type The result type. - * @param {Array} entries The result entries. - * @return {SyncResultObject} - */ - add(type, entries) { - if (!Array.isArray(this[type])) { - return; - } - // Deduplicate entries by id. If the values don't have `id` attribute, just - // keep all. - const deduplicated = this[type].concat(entries).reduce((acc, cur) => { - const existing = acc.filter(r => cur.id && r.id ? cur.id != r.id : true); - return existing.concat(cur); - }, []); - this[type] = deduplicated; - this.ok = this.errors.length + this.conflicts.length === 0; - return this; - } - - /** - * Reinitializes result entries for a given result type. - * - * @param {String} type The result type. - * @return {SyncResultObject} - */ - reset(type) { - this[type] = SyncResultObject.defaults[type]; - this.ok = this.errors.length + this.conflicts.length === 0; - return this; - } -} - -exports.SyncResultObject = SyncResultObject; -function createUUIDSchema() { - return { - generate() { - return (0, _uuid.v4)(); - }, - - validate(id) { - return (0, _utils.isUUID)(id); - } - }; -} - -function markStatus(record, status) { - return (0, _extends3.default)({}, record, { _status: status }); -} - -function markDeleted(record) { - return markStatus(record, "deleted"); -} - -function markSynced(record) { - return markStatus(record, "synced"); -} - -/** - * Import a remote change into the local database. - * - * @param {IDBTransactionProxy} transaction The transaction handler. - * @param {Object} remote The remote change object to import. - * @param {Array<String>} localFields The list of fields that remain local. - * @return {Object} - */ -function importChange(transaction, remote, localFields) { - const local = transaction.get(remote.id); - if (!local) { - // Not found locally but remote change is marked as deleted; skip to - // avoid recreation. - if (remote.deleted) { - return { type: "skipped", data: remote }; - } - const synced = markSynced(remote); - transaction.create(synced); - return { type: "created", data: synced }; - } - // Compare local and remote, ignoring local fields. - const isIdentical = recordsEqual(local, remote, localFields); - // Apply remote changes on local record. - const synced = (0, _extends3.default)({}, local, markSynced(remote)); - // Detect or ignore conflicts if record has also been modified locally. - if (local._status !== "synced") { - // Locally deleted, unsynced: scheduled for remote deletion. - if (local._status === "deleted") { - return { type: "skipped", data: local }; - } - if (isIdentical) { - // If records are identical, import anyway, so we bump the - // local last_modified value from the server and set record - // status to "synced". - transaction.update(synced); - return { type: "updated", data: { old: local, new: synced } }; - } - if (local.last_modified !== undefined && local.last_modified === remote.last_modified) { - // If our local version has the same last_modified as the remote - // one, this represents an object that corresponds to a resolved - // conflict. Our local version represents the final output, so - // we keep that one. (No transaction operation to do.) - // But if our last_modified is undefined, - // that means we've created the same object locally as one on - // the server, which *must* be a conflict. - return { type: "void" }; - } - return { - type: "conflicts", - data: { type: "incoming", local: local, remote: remote } - }; - } - // Local record was synced. - if (remote.deleted) { - transaction.delete(remote.id); - return { type: "deleted", data: local }; - } - // Import locally. - transaction.update(synced); - // if identical, simply exclude it from all SyncResultObject lists - const type = isIdentical ? "void" : "updated"; - return { type, data: { old: local, new: synced } }; -} - -/** - * Abstracts a collection of records stored in the local database, providing - * CRUD operations and synchronization helpers. - */ -class Collection { - /** - * Constructor. - * - * Options: - * - `{BaseAdapter} adapter` The DB adapter (default: `IDB`) - * - `{String} dbPrefix` The DB name prefix (default: `""`) - * - * @param {String} bucket The bucket identifier. - * @param {String} name The collection name. - * @param {Api} api The Api instance. - * @param {Object} options The options object. - */ - constructor(bucket, name, api, options = {}) { - this._bucket = bucket; - this._name = name; - this._lastModified = null; - - const DBAdapter = options.adapter || _IDB2.default; - if (!DBAdapter) { - throw new Error("No adapter provided"); - } - const dbPrefix = options.dbPrefix || ""; - const db = new DBAdapter(`${ dbPrefix }${ bucket }/${ name }`, options.adapterOptions); - if (!(db instanceof _base2.default)) { - throw new Error("Unsupported adapter."); - } - // public properties - /** - * The db adapter instance - * @type {BaseAdapter} - */ - this.db = db; - /** - * The Api instance. - * @type {KintoClient} - */ - this.api = api; - /** - * The event emitter instance. - * @type {EventEmitter} - */ - this.events = options.events; - /** - * The IdSchema instance. - * @type {Object} - */ - this.idSchema = this._validateIdSchema(options.idSchema); - /** - * The list of remote transformers. - * @type {Array} - */ - this.remoteTransformers = this._validateRemoteTransformers(options.remoteTransformers); - /** - * The list of hooks. - * @type {Object} - */ - this.hooks = this._validateHooks(options.hooks); - /** - * The list of fields names that will remain local. - * @type {Array} - */ - this.localFields = options.localFields || []; - } - - /** - * The collection name. - * @type {String} - */ - get name() { - return this._name; - } - - /** - * The bucket name. - * @type {String} - */ - get bucket() { - return this._bucket; - } - - /** - * The last modified timestamp. - * @type {Number} - */ - get lastModified() { - return this._lastModified; - } - - /** - * Synchronization strategies. Available strategies are: - * - * - `MANUAL`: Conflicts will be reported in a dedicated array. - * - `SERVER_WINS`: Conflicts are resolved using remote data. - * - `CLIENT_WINS`: Conflicts are resolved using local data. - * - * @type {Object} - */ - static get strategy() { - return { - CLIENT_WINS: "client_wins", - SERVER_WINS: "server_wins", - MANUAL: "manual" - }; - } - - /** - * Validates an idSchema. - * - * @param {Object|undefined} idSchema - * @return {Object} - */ - _validateIdSchema(idSchema) { - if (typeof idSchema === "undefined") { - return createUUIDSchema(); - } - if (typeof idSchema !== "object") { - throw new Error("idSchema must be an object."); - } else if (typeof idSchema.generate !== "function") { - throw new Error("idSchema must provide a generate function."); - } else if (typeof idSchema.validate !== "function") { - throw new Error("idSchema must provide a validate function."); - } - return idSchema; - } - - /** - * Validates a list of remote transformers. - * - * @param {Array|undefined} remoteTransformers - * @return {Array} - */ - _validateRemoteTransformers(remoteTransformers) { - if (typeof remoteTransformers === "undefined") { - return []; - } - if (!Array.isArray(remoteTransformers)) { - throw new Error("remoteTransformers should be an array."); - } - return remoteTransformers.map(transformer => { - if (typeof transformer !== "object") { - throw new Error("A transformer must be an object."); - } else if (typeof transformer.encode !== "function") { - throw new Error("A transformer must provide an encode function."); - } else if (typeof transformer.decode !== "function") { - throw new Error("A transformer must provide a decode function."); - } - return transformer; - }); - } - - /** - * Validate the passed hook is correct. - * - * @param {Array|undefined} hook. - * @return {Array} - **/ - _validateHook(hook) { - if (!Array.isArray(hook)) { - throw new Error("A hook definition should be an array of functions."); - } - return hook.map(fn => { - if (typeof fn !== "function") { - throw new Error("A hook definition should be an array of functions."); - } - return fn; - }); - } - - /** - * Validates a list of hooks. - * - * @param {Object|undefined} hooks - * @return {Object} - */ - _validateHooks(hooks) { - if (typeof hooks === "undefined") { - return {}; - } - if (Array.isArray(hooks)) { - throw new Error("hooks should be an object, not an array."); - } - if (typeof hooks !== "object") { - throw new Error("hooks should be an object."); - } - - const validatedHooks = {}; - - for (let hook in hooks) { - if (AVAILABLE_HOOKS.indexOf(hook) === -1) { - throw new Error("The hook should be one of " + AVAILABLE_HOOKS.join(", ")); - } - validatedHooks[hook] = this._validateHook(hooks[hook]); - } - return validatedHooks; - } - - /** - * Deletes every records in the current collection and marks the collection as - * never synced. - * - * @return {Promise} - */ - clear() { - var _this = this; - - return (0, _asyncToGenerator3.default)(function* () { - yield _this.db.clear(); - yield _this.db.saveLastModified(null); - return { data: [], permissions: {} }; - })(); - } - - /** - * Encodes a record. - * - * @param {String} type Either "remote" or "local". - * @param {Object} record The record object to encode. - * @return {Promise} - */ - _encodeRecord(type, record) { - if (!this[`${ type }Transformers`].length) { - return _promise2.default.resolve(record); - } - return (0, _utils.waterfall)(this[`${ type }Transformers`].map(transformer => { - return record => transformer.encode(record); - }), record); - } - - /** - * Decodes a record. - * - * @param {String} type Either "remote" or "local". - * @param {Object} record The record object to decode. - * @return {Promise} - */ - _decodeRecord(type, record) { - if (!this[`${ type }Transformers`].length) { - return _promise2.default.resolve(record); - } - return (0, _utils.waterfall)(this[`${ type }Transformers`].reverse().map(transformer => { - return record => transformer.decode(record); - }), record); - } - - /** - * Adds a record to the local database, asserting that none - * already exist with this ID. - * - * Note: If either the `useRecordId` or `synced` options are true, then the - * record object must contain the id field to be validated. If none of these - * options are true, an id is generated using the current IdSchema; in this - * case, the record passed must not have an id. - * - * Options: - * - {Boolean} synced Sets record status to "synced" (default: `false`). - * - {Boolean} useRecordId Forces the `id` field from the record to be used, - * instead of one that is generated automatically - * (default: `false`). - * - * @param {Object} record - * @param {Object} options - * @return {Promise} - */ - create(record, options = { useRecordId: false, synced: false }) { - // Validate the record and its ID (if any), even though this - // validation is also done in the CollectionTransaction method, - // because we need to pass the ID to preloadIds. - const reject = msg => _promise2.default.reject(new Error(msg)); - if (typeof record !== "object") { - return reject("Record is not an object."); - } - if ((options.synced || options.useRecordId) && !record.hasOwnProperty("id")) { - return reject("Missing required Id; synced and useRecordId options require one"); - } - if (!options.synced && !options.useRecordId && record.hasOwnProperty("id")) { - return reject("Extraneous Id; can't create a record having one set."); - } - const newRecord = (0, _extends3.default)({}, record, { - id: options.synced || options.useRecordId ? record.id : this.idSchema.generate(), - _status: options.synced ? "synced" : "created" - }); - if (!this.idSchema.validate(newRecord.id)) { - return reject(`Invalid Id: ${ newRecord.id }`); - } - return this.execute(txn => txn.create(newRecord), { preloadIds: [newRecord.id] }).catch(err => { - if (options.useRecordId) { - throw new Error("Couldn't create record. It may have been virtually deleted."); - } - throw err; - }); - } - - /** - * Like {@link CollectionTransaction#update}, but wrapped in its own transaction. - * - * Options: - * - {Boolean} synced: Sets record status to "synced" (default: false) - * - {Boolean} patch: Extends the existing record instead of overwriting it - * (default: false) - * - * @param {Object} record - * @param {Object} options - * @return {Promise} - */ - update(record, options = { synced: false, patch: false }) { - // Validate the record and its ID, even though this validation is - // also done in the CollectionTransaction method, because we need - // to pass the ID to preloadIds. - if (typeof record !== "object") { - return _promise2.default.reject(new Error("Record is not an object.")); - } - if (!record.hasOwnProperty("id")) { - return _promise2.default.reject(new Error("Cannot update a record missing id.")); - } - if (!this.idSchema.validate(record.id)) { - return _promise2.default.reject(new Error(`Invalid Id: ${ record.id }`)); - } - - return this.execute(txn => txn.update(record, options), { preloadIds: [record.id] }); - } - - /** - * Like {@link CollectionTransaction#upsert}, but wrapped in its own transaction. - * - * @param {Object} record - * @return {Promise} - */ - upsert(record) { - // Validate the record and its ID, even though this validation is - // also done in the CollectionTransaction method, because we need - // to pass the ID to preloadIds. - if (typeof record !== "object") { - return _promise2.default.reject(new Error("Record is not an object.")); - } - if (!record.hasOwnProperty("id")) { - return _promise2.default.reject(new Error("Cannot update a record missing id.")); - } - if (!this.idSchema.validate(record.id)) { - return _promise2.default.reject(new Error(`Invalid Id: ${ record.id }`)); - } - - return this.execute(txn => txn.upsert(record), { preloadIds: [record.id] }); - } - - /** - * Like {@link CollectionTransaction#get}, but wrapped in its own transaction. - * - * Options: - * - {Boolean} includeDeleted: Include virtually deleted records. - * - * @param {String} id - * @param {Object} options - * @return {Promise} - */ - get(id, options = { includeDeleted: false }) { - return this.execute(txn => txn.get(id, options), { preloadIds: [id] }); - } - - /** - * Like {@link CollectionTransaction#getAny}, but wrapped in its own transaction. - * - * @param {String} id - * @return {Promise} - */ - getAny(id) { - return this.execute(txn => txn.getAny(id), { preloadIds: [id] }); - } - - /** - * Same as {@link Collection#delete}, but wrapped in its own transaction. - * - * Options: - * - {Boolean} virtual: When set to `true`, doesn't actually delete the record, - * update its `_status` attribute to `deleted` instead (default: true) - * - * @param {String} id The record's Id. - * @param {Object} options The options object. - * @return {Promise} - */ - delete(id, options = { virtual: true }) { - return this.execute(transaction => { - return transaction.delete(id, options); - }, { preloadIds: [id] }); - } - - /** - * The same as {@link CollectionTransaction#deleteAny}, but wrapped - * in its own transaction. - * - * @param {String} id The record's Id. - * @return {Promise} - */ - deleteAny(id) { - return this.execute(txn => txn.deleteAny(id), { preloadIds: [id] }); - } - - /** - * Lists records from the local database. - * - * Params: - * - {Object} filters Filter the results (default: `{}`). - * - {String} order The order to apply (default: `-last_modified`). - * - * Options: - * - {Boolean} includeDeleted: Include virtually deleted records. - * - * @param {Object} params The filters and order to apply to the results. - * @param {Object} options The options object. - * @return {Promise} - */ - list(params = {}, options = { includeDeleted: false }) { - var _this2 = this; - - return (0, _asyncToGenerator3.default)(function* () { - params = (0, _extends3.default)({ order: "-last_modified", filters: {} }, params); - const results = yield _this2.db.list(params); - let data = results; - if (!options.includeDeleted) { - data = results.filter(function (record) { - return record._status !== "deleted"; - }); - } - return { data, permissions: {} }; - })(); - } - - /** - * Imports remote changes into the local database. - * This method is in charge of detecting the conflicts, and resolve them - * according to the specified strategy. - * @param {SyncResultObject} syncResultObject The sync result object. - * @param {Array} decodedChanges The list of changes to import in the local database. - * @param {String} strategy The {@link Collection.strategy} (default: MANUAL) - * @return {Promise} - */ - importChanges(syncResultObject, decodedChanges, strategy = Collection.strategy.MANUAL) { - var _this3 = this; - - return (0, _asyncToGenerator3.default)(function* () { - // Retrieve records matching change ids. - try { - const { imports, resolved } = yield _this3.db.execute(function (transaction) { - const imports = decodedChanges.map(function (remote) { - // Store remote change into local database. - return importChange(transaction, remote, _this3.localFields); - }); - const conflicts = imports.filter(function (i) { - return i.type === "conflicts"; - }).map(function (i) { - return i.data; - }); - const resolved = _this3._handleConflicts(transaction, conflicts, strategy); - return { imports, resolved }; - }, { preload: decodedChanges.map(function (record) { - return record.id; - }) }); - - // Lists of created/updated/deleted records - imports.forEach(function ({ type, data }) { - return syncResultObject.add(type, data); - }); - - // Automatically resolved conflicts (if not manual) - if (resolved.length > 0) { - syncResultObject.reset("conflicts").add("resolved", resolved); - } - } catch (err) { - const data = { - type: "incoming", - message: err.message, - stack: err.stack - }; - // XXX one error of the whole transaction instead of per atomic op - syncResultObject.add("errors", data); - } - - return syncResultObject; - })(); - } - - /** - * Imports the responses of pushed changes into the local database. - * Basically it stores the timestamp assigned by the server into the local - * database. - * @param {SyncResultObject} syncResultObject The sync result object. - * @param {Array} toApplyLocally The list of changes to import in the local database. - * @param {Array} conflicts The list of conflicts that have to be resolved. - * @param {String} strategy The {@link Collection.strategy}. - * @return {Promise} - */ - _applyPushedResults(syncResultObject, toApplyLocally, conflicts, strategy = Collection.strategy.MANUAL) { - var _this4 = this; - - return (0, _asyncToGenerator3.default)(function* () { - const toDeleteLocally = toApplyLocally.filter(function (r) { - return r.deleted; - }); - const toUpdateLocally = toApplyLocally.filter(function (r) { - return !r.deleted; - }); - - const { published, resolved } = yield _this4.db.execute(function (transaction) { - const updated = toUpdateLocally.map(function (record) { - const synced = markSynced(record); - transaction.update(synced); - return synced; - }); - const deleted = toDeleteLocally.map(function (record) { - transaction.delete(record.id); - // Amend result data with the deleted attribute set - return { id: record.id, deleted: true }; - }); - const published = updated.concat(deleted); - // Handle conflicts, if any - const resolved = _this4._handleConflicts(transaction, conflicts, strategy); - return { published, resolved }; - }); - - syncResultObject.add("published", published); - - if (resolved.length > 0) { - syncResultObject.reset("conflicts").reset("resolved").add("resolved", resolved); - } - return syncResultObject; - })(); - } - - /** - * Handles synchronization conflicts according to specified strategy. - * - * @param {SyncResultObject} result The sync result object. - * @param {String} strategy The {@link Collection.strategy}. - * @return {Promise} - */ - _handleConflicts(transaction, conflicts, strategy) { - if (strategy === Collection.strategy.MANUAL) { - return []; - } - return conflicts.map(conflict => { - const resolution = strategy === Collection.strategy.CLIENT_WINS ? conflict.local : conflict.remote; - const updated = this._resolveRaw(conflict, resolution); - transaction.update(updated); - return updated; - }); - } - - /** - * Execute a bunch of operations in a transaction. - * - * This transaction should be atomic -- either all of its operations - * will succeed, or none will. - * - * The argument to this function is itself a function which will be - * called with a {@link CollectionTransaction}. Collection methods - * are available on this transaction, but instead of returning - * promises, they are synchronous. execute() returns a Promise whose - * value will be the return value of the provided function. - * - * Most operations will require access to the record itself, which - * must be preloaded by passing its ID in the preloadIds option. - * - * Options: - * - {Array} preloadIds: list of IDs to fetch at the beginning of - * the transaction - * - * @return {Promise} Resolves with the result of the given function - * when the transaction commits. - */ - execute(doOperations, { preloadIds = [] } = {}) { - for (let id of preloadIds) { - if (!this.idSchema.validate(id)) { - return _promise2.default.reject(Error(`Invalid Id: ${ id }`)); - } - } - - return this.db.execute(transaction => { - const txn = new CollectionTransaction(this, transaction); - const result = doOperations(txn); - txn.emitEvents(); - return result; - }, { preload: preloadIds }); - } - - /** - * Resets the local records as if they were never synced; existing records are - * marked as newly created, deleted records are dropped. - * - * A next call to {@link Collection.sync} will thus republish the whole - * content of the local collection to the server. - * - * @return {Promise} Resolves with the number of processed records. - */ - resetSyncStatus() { - var _this5 = this; - - return (0, _asyncToGenerator3.default)(function* () { - const unsynced = yield _this5.list({ filters: { _status: ["deleted", "synced"] }, order: "" }, { includeDeleted: true }); - yield _this5.db.execute(function (transaction) { - unsynced.data.forEach(function (record) { - if (record._status === "deleted") { - // Garbage collect deleted records. - transaction.delete(record.id); - } else { - // Records that were synced become «created». - transaction.update((0, _extends3.default)({}, record, { - last_modified: undefined, - _status: "created" - })); - } - }); - }); - _this5._lastModified = null; - yield _this5.db.saveLastModified(null); - return unsynced.data.length; - })(); - } - - /** - * Returns an object containing two lists: - * - * - `toDelete`: unsynced deleted records we can safely delete; - * - `toSync`: local updates to send to the server. - * - * @return {Promise} - */ - gatherLocalChanges() { - var _this6 = this; - - return (0, _asyncToGenerator3.default)(function* () { - const unsynced = yield _this6.list({ filters: { _status: ["created", "updated"] }, order: "" }); - const deleted = yield _this6.list({ filters: { _status: "deleted" }, order: "" }, { includeDeleted: true }); - - const toSync = yield _promise2.default.all(unsynced.data.map(_this6._encodeRecord.bind(_this6, "remote"))); - const toDelete = yield _promise2.default.all(deleted.data.map(_this6._encodeRecord.bind(_this6, "remote"))); - - return { toSync, toDelete }; - })(); - } - - /** - * Fetch remote changes, import them to the local database, and handle - * conflicts according to `options.strategy`. Then, updates the passed - * {@link SyncResultObject} with import results. - * - * Options: - * - {String} strategy: The selected sync strategy. - * - * @param {KintoClient.Collection} client Kinto client Collection instance. - * @param {SyncResultObject} syncResultObject The sync result object. - * @param {Object} options - * @return {Promise} - */ - pullChanges(client, syncResultObject, options = {}) { - var _this7 = this; - - return (0, _asyncToGenerator3.default)(function* () { - if (!syncResultObject.ok) { - return syncResultObject; - } - - const since = _this7.lastModified ? _this7.lastModified : yield _this7.db.getLastModified(); - - options = (0, _extends3.default)({ - strategy: Collection.strategy.MANUAL, - lastModified: since, - headers: {} - }, options); - - // Optionally ignore some records when pulling for changes. - // (avoid redownloading our own changes on last step of #sync()) - let filters; - if (options.exclude) { - // Limit the list of excluded records to the first 50 records in order - // to remain under de-facto URL size limit (~2000 chars). - // http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers/417184#417184 - const exclude_id = options.exclude.slice(0, 50).map(function (r) { - return r.id; - }).join(","); - filters = { exclude_id }; - } - // First fetch remote changes from the server - const { data, last_modified } = yield client.listRecords({ - // Since should be ETag (see https://github.com/Kinto/kinto.js/issues/356) - since: options.lastModified ? `${ options.lastModified }` : undefined, - headers: options.headers, - filters - }); - // last_modified is the ETag header value (string). - // For retro-compatibility with first kinto.js versions - // parse it to integer. - const unquoted = last_modified ? parseInt(last_modified, 10) : undefined; - - // Check if server was flushed. - // This is relevant for the Kinto demo server - // (and thus for many new comers). - const localSynced = options.lastModified; - const serverChanged = unquoted > options.lastModified; - const emptyCollection = data.length === 0; - if (!options.exclude && localSynced && serverChanged && emptyCollection) { - throw Error("Server has been flushed."); - } - - syncResultObject.lastModified = unquoted; - - // Decode incoming changes. - const decodedChanges = yield _promise2.default.all(data.map(function (change) { - return _this7._decodeRecord("remote", change); - })); - // Hook receives decoded records. - const payload = { lastModified: unquoted, changes: decodedChanges }; - const afterHooks = yield _this7.applyHook("incoming-changes", payload); - - // No change, nothing to import. - if (afterHooks.changes.length > 0) { - // Reflect these changes locally - yield _this7.importChanges(syncResultObject, afterHooks.changes, options.strategy); - } - return syncResultObject; - })(); - } - - applyHook(hookName, payload) { - if (typeof this.hooks[hookName] == "undefined") { - return _promise2.default.resolve(payload); - } - return (0, _utils.waterfall)(this.hooks[hookName].map(hook => { - return record => { - const result = hook(payload, this); - const resultThenable = result && typeof result.then === "function"; - const resultChanges = result && result.hasOwnProperty("changes"); - if (!(resultThenable || resultChanges)) { - throw new Error(`Invalid return value for hook: ${ (0, _stringify2.default)(result) } has no 'then()' or 'changes' properties`); - } - return result; - }; - }), payload); - } - - /** - * Publish local changes to the remote server and updates the passed - * {@link SyncResultObject} with publication results. - * - * @param {KintoClient.Collection} client Kinto client Collection instance. - * @param {SyncResultObject} syncResultObject The sync result object. - * @param {Object} changes The change object. - * @param {Array} changes.toDelete The list of records to delete. - * @param {Array} changes.toSync The list of records to create/update. - * @param {Object} options The options object. - * @return {Promise} - */ - pushChanges(client, { toDelete = [], toSync }, syncResultObject, options = {}) { - var _this8 = this; - - return (0, _asyncToGenerator3.default)(function* () { - if (!syncResultObject.ok) { - return syncResultObject; - } - const safe = !options.strategy || options.strategy !== Collection.CLIENT_WINS; - - // Perform a batch request with every changes. - const synced = yield client.batch(function (batch) { - toDelete.forEach(function (r) { - // never published locally deleted records should not be pusblished - if (r.last_modified) { - batch.deleteRecord(r); - } - }); - toSync.forEach(function (r) { - // Clean local fields (like _status) before sending to server. - const published = _this8.cleanLocalFields(r); - if (r._status === "created") { - batch.createRecord(published); - } else { - batch.updateRecord(published); - } - }); - }, { headers: options.headers, safe, aggregate: true }); - - // Store outgoing errors into sync result object - syncResultObject.add("errors", synced.errors.map(function (e) { - return (0, _extends3.default)({}, e, { type: "outgoing" }); - })); - - // Store outgoing conflicts into sync result object - const conflicts = []; - for (let { type, local, remote } of synced.conflicts) { - // Note: we ensure that local data are actually available, as they may - // be missing in the case of a published deletion. - const safeLocal = local && local.data || { id: remote.id }; - const realLocal = yield _this8._decodeRecord("remote", safeLocal); - const realRemote = yield _this8._decodeRecord("remote", remote); - const conflict = { type, local: realLocal, remote: realRemote }; - conflicts.push(conflict); - } - syncResultObject.add("conflicts", conflicts); - - // Records that must be deleted are either deletions that were pushed - // to server (published) or deleted records that were never pushed (skipped). - const missingRemotely = synced.skipped.map(function (r) { - return (0, _extends3.default)({}, r, { deleted: true }); - }); - - // For created and updated records, the last_modified coming from server - // will be stored locally. - // Reflect publication results locally using the response from - // the batch request. - const published = synced.published.map(function (c) { - return c.data; - }); - const toApplyLocally = published.concat(missingRemotely); - - // Apply the decode transformers, if any - const decoded = yield _promise2.default.all(toApplyLocally.map(function (record) { - return _this8._decodeRecord("remote", record); - })); - - // We have to update the local records with the responses of the server - // (eg. last_modified values etc.). - if (decoded.length > 0 || conflicts.length > 0) { - yield _this8._applyPushedResults(syncResultObject, decoded, conflicts, options.strategy); - } - - return syncResultObject; - })(); - } - - /** - * Return a copy of the specified record without the local fields. - * - * @param {Object} record A record with potential local fields. - * @return {Object} - */ - cleanLocalFields(record) { - const localKeys = RECORD_FIELDS_TO_CLEAN.concat(this.localFields); - return (0, _utils.omitKeys)(record, localKeys); - } - - /** - * Resolves a conflict, updating local record according to proposed - * resolution — keeping remote record `last_modified` value as a reference for - * further batch sending. - * - * @param {Object} conflict The conflict object. - * @param {Object} resolution The proposed record. - * @return {Promise} - */ - resolve(conflict, resolution) { - return this.db.execute(transaction => { - const updated = this._resolveRaw(conflict, resolution); - transaction.update(updated); - return { data: updated, permissions: {} }; - }); - } - - /** - * @private - */ - _resolveRaw(conflict, resolution) { - const resolved = (0, _extends3.default)({}, resolution, { - // Ensure local record has the latest authoritative timestamp - last_modified: conflict.remote.last_modified - }); - // If the resolution object is strictly equal to the - // remote record, then we can mark it as synced locally. - // Otherwise, mark it as updated (so that the resolution is pushed). - const synced = (0, _utils.deepEqual)(resolved, conflict.remote); - return markStatus(resolved, synced ? "synced" : "updated"); - } - - /** - * Synchronize remote and local data. The promise will resolve with a - * {@link SyncResultObject}, though will reject: - * - * - if the server is currently backed off; - * - if the server has been detected flushed. - * - * Options: - * - {Object} headers: HTTP headers to attach to outgoing requests. - * - {Collection.strategy} strategy: See {@link Collection.strategy}. - * - {Boolean} ignoreBackoff: Force synchronization even if server is currently - * backed off. - * - {String} bucket: The remove bucket id to use (default: null) - * - {String} collection: The remove collection id to use (default: null) - * - {String} remote The remote Kinto server endpoint to use (default: null). - * - * @param {Object} options Options. - * @return {Promise} - * @throws {Error} If an invalid remote option is passed. - */ - sync(options = { - strategy: Collection.strategy.MANUAL, - headers: {}, - ignoreBackoff: false, - bucket: null, - collection: null, - remote: null - }) { - var _this9 = this; - - return (0, _asyncToGenerator3.default)(function* () { - const previousRemote = _this9.api.remote; - if (options.remote) { - // Note: setting the remote ensures it's valid, throws when invalid. - _this9.api.remote = options.remote; - } - if (!options.ignoreBackoff && _this9.api.backoff > 0) { - const seconds = Math.ceil(_this9.api.backoff / 1000); - return _promise2.default.reject(new Error(`Server is asking clients to back off; retry in ${ seconds }s or use the ignoreBackoff option.`)); - } - - const client = _this9.api.bucket(options.bucket || _this9.bucket).collection(options.collection || _this9.name); - - const result = new SyncResultObject(); - try { - // Fetch last changes from the server. - yield _this9.pullChanges(client, result, options); - const { lastModified } = result; - - // Fetch local changes - const { toDelete, toSync } = yield _this9.gatherLocalChanges(); - - // Publish local changes and pull local resolutions - yield _this9.pushChanges(client, { toDelete, toSync }, result, options); - - // Publish local resolution of push conflicts to server (on CLIENT_WINS) - const resolvedUnsynced = result.resolved.filter(function (r) { - return r._status !== "synced"; - }); - if (resolvedUnsynced.length > 0) { - const resolvedEncoded = yield _promise2.default.all(resolvedUnsynced.map(_this9._encodeRecord.bind(_this9, "remote"))); - yield _this9.pushChanges(client, { toSync: resolvedEncoded }, result, options); - } - // Perform a last pull to catch changes that occured after the last pull, - // while local changes were pushed. Do not do it nothing was pushed. - if (result.published.length > 0) { - // Avoid redownloading our own changes during the last pull. - const pullOpts = (0, _extends3.default)({}, options, { lastModified, exclude: result.published }); - yield _this9.pullChanges(client, result, pullOpts); - } - - // Don't persist lastModified value if any conflict or error occured - if (result.ok) { - // No conflict occured, persist collection's lastModified value - _this9._lastModified = yield _this9.db.saveLastModified(result.lastModified); - } - } finally { - // Ensure API default remote is reverted if a custom one's been used - _this9.api.remote = previousRemote; - } - return result; - })(); - } - - /** - * Load a list of records already synced with the remote server. - * - * The local records which are unsynced or whose timestamp is either missing - * or superior to those being loaded will be ignored. - * - * @param {Array} records The previously exported list of records to load. - * @return {Promise} with the effectively imported records. - */ - loadDump(records) { - var _this10 = this; - - return (0, _asyncToGenerator3.default)(function* () { - if (!Array.isArray(records)) { - throw new Error("Records is not an array."); - } - - for (let record of records) { - if (!record.hasOwnProperty("id") || !_this10.idSchema.validate(record.id)) { - throw new Error("Record has invalid ID: " + (0, _stringify2.default)(record)); - } - - if (!record.last_modified) { - throw new Error("Record has no last_modified value: " + (0, _stringify2.default)(record)); - } - } - - // Fetch all existing records from local database, - // and skip those who are newer or not marked as synced. - - // XXX filter by status / ids in records - - const { data } = yield _this10.list({}, { includeDeleted: true }); - const existingById = data.reduce(function (acc, record) { - acc[record.id] = record; - return acc; - }, {}); - - const newRecords = records.filter(function (record) { - const localRecord = existingById[record.id]; - const shouldKeep = - // No local record with this id. - localRecord === undefined || - // Or local record is synced - localRecord._status === "synced" && - // And was synced from server - localRecord.last_modified !== undefined && - // And is older than imported one. - record.last_modified > localRecord.last_modified; - return shouldKeep; - }); - - return yield _this10.db.loadDump(newRecords.map(markSynced)); - })(); - } -} - -exports.default = Collection; /** - * A Collection-oriented wrapper for an adapter's transaction. - * - * This defines the high-level functions available on a collection. - * The collection itself offers functions of the same name. These will - * perform just one operation in its own transaction. - */ - -class CollectionTransaction { - constructor(collection, adapterTransaction) { - this.collection = collection; - this.adapterTransaction = adapterTransaction; - - this._events = []; - } - - _queueEvent(action, payload) { - this._events.push({ action, payload }); - } - - /** - * Emit queued events, to be called once every transaction operations have - * been executed successfully. - */ - emitEvents() { - for (let { action, payload } of this._events) { - this.collection.events.emit(action, payload); - } - if (this._events.length > 0) { - const targets = this._events.map(({ action, payload }) => (0, _extends3.default)({ action }, payload)); - this.collection.events.emit("change", { targets }); - } - this._events = []; - } - - /** - * Retrieve a record by its id from the local database, or - * undefined if none exists. - * - * This will also return virtually deleted records. - * - * @param {String} id - * @return {Object} - */ - getAny(id) { - const record = this.adapterTransaction.get(id); - return { data: record, permissions: {} }; - } - - /** - * Retrieve a record by its id from the local database. - * - * Options: - * - {Boolean} includeDeleted: Include virtually deleted records. - * - * @param {String} id - * @param {Object} options - * @return {Object} - */ - get(id, options = { includeDeleted: false }) { - const res = this.getAny(id); - if (!res.data || !options.includeDeleted && res.data._status === "deleted") { - throw new Error(`Record with id=${ id } not found.`); - } - - return res; - } - - /** - * Deletes a record from the local database. - * - * Options: - * - {Boolean} virtual: When set to `true`, doesn't actually delete the record, - * update its `_status` attribute to `deleted` instead (default: true) - * - * @param {String} id The record's Id. - * @param {Object} options The options object. - * @return {Object} - */ - delete(id, options = { virtual: true }) { - // Ensure the record actually exists. - const existing = this.adapterTransaction.get(id); - const alreadyDeleted = existing && existing._status == "deleted"; - if (!existing || alreadyDeleted && options.virtual) { - throw new Error(`Record with id=${ id } not found.`); - } - // Virtual updates status. - if (options.virtual) { - this.adapterTransaction.update(markDeleted(existing)); - } else { - // Delete for real. - this.adapterTransaction.delete(id); - } - this._queueEvent("delete", { data: existing }); - return { data: existing, permissions: {} }; - } - - /** - * Deletes a record from the local database, if any exists. - * Otherwise, do nothing. - * - * @param {String} id The record's Id. - * @return {Object} - */ - deleteAny(id) { - const existing = this.adapterTransaction.get(id); - if (existing) { - this.adapterTransaction.update(markDeleted(existing)); - this._queueEvent("delete", { data: existing }); - } - return { data: (0, _extends3.default)({ id }, existing), deleted: !!existing, permissions: {} }; - } - - /** - * Adds a record to the local database, asserting that none - * already exist with this ID. - * - * @param {Object} record, which must contain an ID - * @return {Object} - */ - create(record) { - if (typeof record !== "object") { - throw new Error("Record is not an object."); - } - if (!record.hasOwnProperty("id")) { - throw new Error("Cannot create a record missing id"); - } - if (!this.collection.idSchema.validate(record.id)) { - throw new Error(`Invalid Id: ${ record.id }`); - } - - this.adapterTransaction.create(record); - this._queueEvent("create", { data: record }); - return { data: record, permissions: {} }; - } - - /** - * Updates a record from the local database. - * - * Options: - * - {Boolean} synced: Sets record status to "synced" (default: false) - * - {Boolean} patch: Extends the existing record instead of overwriting it - * (default: false) - * - * @param {Object} record - * @param {Object} options - * @return {Object} - */ - update(record, options = { synced: false, patch: false }) { - if (typeof record !== "object") { - throw new Error("Record is not an object."); - } - if (!record.hasOwnProperty("id")) { - throw new Error("Cannot update a record missing id."); - } - if (!this.collection.idSchema.validate(record.id)) { - throw new Error(`Invalid Id: ${ record.id }`); - } - - const oldRecord = this.adapterTransaction.get(record.id); - if (!oldRecord) { - throw new Error(`Record with id=${ record.id } not found.`); - } - const newRecord = options.patch ? (0, _extends3.default)({}, oldRecord, record) : record; - const updated = this._updateRaw(oldRecord, newRecord, options); - this.adapterTransaction.update(updated); - this._queueEvent("update", { data: updated, oldRecord }); - return { data: updated, oldRecord, permissions: {} }; - } - - /** - * Lower-level primitive for updating a record while respecting - * _status and last_modified. - * - * @param {Object} oldRecord: the record retrieved from the DB - * @param {Object} newRecord: the record to replace it with - * @return {Object} - */ - _updateRaw(oldRecord, newRecord, { synced = false } = {}) { - const updated = (0, _extends3.default)({}, newRecord); - // Make sure to never loose the existing timestamp. - if (oldRecord && oldRecord.last_modified && !updated.last_modified) { - updated.last_modified = oldRecord.last_modified; - } - // If only local fields have changed, then keep record as synced. - // If status is created, keep record as created. - // If status is deleted, mark as updated. - const isIdentical = oldRecord && recordsEqual(oldRecord, updated, this.localFields); - const keepSynced = isIdentical && oldRecord._status == "synced"; - const neverSynced = !oldRecord || oldRecord && oldRecord._status == "created"; - const newStatus = keepSynced || synced ? "synced" : neverSynced ? "created" : "updated"; - return markStatus(updated, newStatus); - } - - /** - * Upsert a record into the local database. - * - * This record must have an ID. - * - * If a record with this ID already exists, it will be replaced. - * Otherwise, this record will be inserted. - * - * @param {Object} record - * @return {Object} - */ - upsert(record) { - if (typeof record !== "object") { - throw new Error("Record is not an object."); - } - if (!record.hasOwnProperty("id")) { - throw new Error("Cannot update a record missing id."); - } - if (!this.collection.idSchema.validate(record.id)) { - throw new Error(`Invalid Id: ${ record.id }`); - } - let oldRecord = this.adapterTransaction.get(record.id); - const updated = this._updateRaw(oldRecord, record); - this.adapterTransaction.update(updated); - // Don't return deleted records -- pretend they are gone - if (oldRecord && oldRecord._status == "deleted") { - oldRecord = undefined; - } - if (oldRecord) { - this._queueEvent("update", { data: updated, oldRecord }); - } else { - this._queueEvent("create", { data: updated }); - } - return { data: updated, oldRecord, permissions: {} }; - } -} -exports.CollectionTransaction = CollectionTransaction; - -},{"./adapters/IDB":84,"./adapters/base":85,"./utils":87,"babel-runtime/core-js/json/stringify":3,"babel-runtime/core-js/object/assign":4,"babel-runtime/core-js/promise":6,"babel-runtime/helpers/asyncToGenerator":7,"babel-runtime/helpers/extends":8,"uuid":9}],87:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.RE_UUID = undefined; - -var _promise = require("babel-runtime/core-js/promise"); - -var _promise2 = _interopRequireDefault(_promise); - -var _keys = require("babel-runtime/core-js/object/keys"); - -var _keys2 = _interopRequireDefault(_keys); - -exports.sortObjects = sortObjects; -exports.filterObject = filterObject; -exports.filterObjects = filterObjects; -exports.isUUID = isUUID; -exports.waterfall = waterfall; -exports.deepEqual = deepEqual; -exports.omitKeys = omitKeys; - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const RE_UUID = exports.RE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - -/** - * Checks if a value is undefined. - * @param {Any} value - * @return {Boolean} - */ -function _isUndefined(value) { - return typeof value === "undefined"; -} - -/** - * Sorts records in a list according to a given ordering. - * - * @param {String} order The ordering, eg. `-last_modified`. - * @param {Array} list The collection to order. - * @return {Array} - */ -function sortObjects(order, list) { - const hasDash = order[0] === "-"; - const field = hasDash ? order.slice(1) : order; - const direction = hasDash ? -1 : 1; - return list.slice().sort((a, b) => { - if (a[field] && _isUndefined(b[field])) { - return direction; - } - if (b[field] && _isUndefined(a[field])) { - return -direction; - } - if (_isUndefined(a[field]) && _isUndefined(b[field])) { - return 0; - } - return a[field] > b[field] ? direction : -direction; - }); -} - -/** - * Test if a single object matches all given filters. - * - * @param {Object} filters The filters object. - * @param {Object} entry The object to filter. - * @return {Function} - */ -function filterObject(filters, entry) { - return (0, _keys2.default)(filters).every(filter => { - const value = filters[filter]; - if (Array.isArray(value)) { - return value.some(candidate => candidate === entry[filter]); - } - return entry[filter] === value; - }); -} - -/** - * Filters records in a list matching all given filters. - * - * @param {Object} filters The filters object. - * @param {Array} list The collection to filter. - * @return {Array} - */ -function filterObjects(filters, list) { - return list.filter(entry => { - return filterObject(filters, entry); - }); -} - -/** - * Checks if a string is an UUID. - * - * @param {String} uuid The uuid to validate. - * @return {Boolean} - */ -function isUUID(uuid) { - return RE_UUID.test(uuid); -} - -/** - * Resolves a list of functions sequentially, which can be sync or async; in - * case of async, functions must return a promise. - * - * @param {Array} fns The list of functions. - * @param {Any} init The initial value. - * @return {Promise} - */ -function waterfall(fns, init) { - if (!fns.length) { - return _promise2.default.resolve(init); - } - return fns.reduce((promise, nextFn) => { - return promise.then(nextFn); - }, _promise2.default.resolve(init)); -} - -/** - * Simple deep object comparison function. This only supports comparison of - * serializable JavaScript objects. - * - * @param {Object} a The source object. - * @param {Object} b The compared object. - * @return {Boolean} - */ -function deepEqual(a, b) { - if (a === b) { - return true; - } - if (typeof a !== typeof b) { - return false; - } - if (!(a && typeof a == "object") || !(b && typeof b == "object")) { - return false; - } - if ((0, _keys2.default)(a).length !== (0, _keys2.default)(b).length) { - return false; - } - for (let k in a) { - if (!deepEqual(a[k], b[k])) { - return false; - } - } - return true; -} - -/** - * Return an object without the specified keys. - * - * @param {Object} obj The original object. - * @param {Array} keys The list of keys to exclude. - * @return {Object} A copy without the specified keys. - */ -function omitKeys(obj, keys = []) { - return (0, _keys2.default)(obj).reduce((acc, key) => { - if (keys.indexOf(key) === -1) { - acc[key] = obj[key]; - } - return acc; - }, {}); -} - -},{"babel-runtime/core-js/object/keys":5,"babel-runtime/core-js/promise":6}]},{},[2])(2) -});
\ No newline at end of file |