UNPKG

dsw

Version:

Dynamic Service Worker, offline Progressive Web Apps much easier

1,158 lines (1,027 loc) 108 kB
(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 }); function getBestMatchingRX(str, expressions) { var bestMatchingRX = void 0; var bestMatchingGroupSize = Number.MAX_SAFE_INTEGER; var bestMatchingGroup = void 0; expressions.forEach(function (currentRX) { var regex = new RegExp(currentRX.rx); var groups = str.match(regex); if (groups && groups.length < bestMatchingGroupSize) { bestMatchingRX = currentRX; bestMatchingGroupSize = groups.length; bestMatchingGroup = groups; } }); return bestMatchingRX ? { rule: bestMatchingRX, matching: bestMatchingGroup } : false; } exports.default = getBestMatchingRX; },{}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _indexeddbManager = require('./indexeddb-manager.js'); var _indexeddbManager2 = _interopRequireDefault(_indexeddbManager); var _utils = require('./utils.js'); var _utils2 = _interopRequireDefault(_utils); var _logger = require('./logger.js'); var _logger2 = _interopRequireDefault(_logger); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DEFAULT_CACHE_NAME = 'defaultDSWCached'; var CACHE_CREATED_DBNAME = 'cacheCreatedTime'; var DEFAULT_CACHE_VERSION = null; var DSWManager = void 0, PWASettings = void 0, goFetch = void 0; // finds the real size of an utf-8 string function lengthInUtf8Bytes(str) { // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence. var m = encodeURIComponent(str).match(/%[89ABab]/g); return str.length + (m ? m.length : 0); } var parseExpiration = function parseExpiration(rule, expires) { var duration = expires || -1; if (typeof duration == 'string') { // let's use a formated string to know the expiration time var sizes = { s: 1, m: 60, h: 3600, d: 86400, w: 604800, M: 2592000, Y: 31449600 }; var size = duration.slice(-1), val = duration.slice(0, -1); if (sizes[size]) { duration = val * sizes[size]; } else { _logger2.default.warn('Invalid duration ' + duration, rule); duration = -1; } } if (duration >= 0) { return parseInt(duration, 10) * 1000; } else { return 0; } }; var cacheManager = { setup: function setup(DSWMan, PWASet, ftch) { PWASettings = PWASet; DSWManager = DSWMan; goFetch = ftch; DEFAULT_CACHE_VERSION = PWASettings.dswVersion || '1'; _indexeddbManager2.default.setup(cacheManager); // we will also create an IndexedDB to store the cache creationDates // for rules that have cash expiration _indexeddbManager2.default.create({ version: 1, name: CACHE_CREATED_DBNAME, key: 'url' }); }, registeredCaches: [], createDB: function createDB(db) { return _indexeddbManager2.default.create(db); }, // Delete all the unused caches for the new version of the Service Worker deleteUnusedCaches: function deleteUnusedCaches(keepUnused) { if (!keepUnused) { return caches.keys().then(function (keys) { cacheManager.registeredCaches; return Promise.all(keys.map(function (key) { if (cacheManager.registeredCaches.indexOf(key) < 0) { return caches.delete(key); } })); }); } }, // this method will delete all the caches clear: function clear(_) { if ('window' in self) { // if we are not in the ServiceWorkerScope, we message it // to clear all the cache return window.DSW.sendMessage({ clearEverythingUp: true }, true); } else { // we are in the ServiceWorkerScope, and should delete everything return caches.keys().then(function (keys) { var cleanItUp = keys.map(function (key) { return caches.delete(key); }); // we will also drop the databases from IndexedDB cleanItUp.push(_indexeddbManager2.default.clear()); return Promise.all(cleanItUp); }); } }, // return a name for a default rule or the name for cache using the version // and a separator mountCacheId: function mountCacheId(rule) { if (typeof rule == 'string') { return rule; } var cacheConf = rule ? rule.action.cache : false; if (cacheConf) { return (cacheConf.name || DEFAULT_CACHE_NAME) + '::' + (cacheConf.version || DEFAULT_CACHE_VERSION); } return DEFAULT_CACHE_NAME + '::' + DEFAULT_CACHE_VERSION; }, register: function register(rule) { cacheManager.registeredCaches.push(cacheManager.mountCacheId(rule)); }, addAll: function addAll(bundle) { return new Promise(function (resolve, reject) { // for adding a group of files or rules // we use it as a list if (Array.isArray(bundle)) { bundle = { files: bundle }; } var promises = []; // then, we use the cacheManager.add with a new rule // this way it will be able to expire. bundle.files.map(function (file) { promises.push(cacheManager.add(file, null, null, { action: { fetch: file, cache: { name: bundle.name, version: bundle.version || 1, expires: bundle.expires || false } } })); }); // once all of them have been cached, we resolve it // or in case one or more failed, we reject it Promise.all(promises).then(resolve).catch(reject); }); }, // just a different method signature, for .add put: function put(rule, request, response) { return cacheManager.add(request, typeof rule == 'string' ? rule : cacheManager.mountCacheId(rule), response, rule); }, add: function add(request, cacheId, response, rule) { cacheId = cacheId || cacheManager.mountCacheId(rule); return new Promise(function (resolve, reject) { function addIt(response) { if (response.status == 200 || response.type == 'opaque') { caches.open(cacheId).then(function (cache) { // adding to cache var opts = response.type == 'opaque' ? { mode: 'no-cors' } : {}; if ((request.url || request).indexOf('http') !== 0) { request = _utils2.default.fixURL(request.url || request); } request = _utils2.default.createRequest(request, opts); if (request.method != 'POST') { (function () { var cacheData = {}; if (rule && rule.action && rule.action.cache) { cacheData = rule.action.cache; } else { cacheData = { name: cacheId, version: cacheId.split('::')[1] }; } var clonedResponse = void 0; if (response.bodyUsed) { // sometimes, due to different flows, the // request body may have been already used // In this case, we use cache.add instead // of cache.put cache.add(request).then(function (cached) { DSWManager.traceStep(request, 'Added to cache', { cacheData: cacheData }); }).catch(function (err) { _logger2.default.error('Could not save into cache', err); }); } else { clonedResponse = response.clone(); DSWManager.traceStep(request, 'Added to cache', { cacheData: cacheData }); clonedResponse & request & cache.put(request, clonedResponse); } })(); } resolve(response); // in case it is supposed to expire if (rule && rule.action && rule.action.cache && rule.action.cache.expires) { // saves the current time for further validation cacheManager.setExpiringTime(request, rule || cacheId, rule.action.cache.expires); } }).catch(function (err) { _logger2.default.error('Could not save into cache', err); resolve(response); }); } else { reject(response); } } if (!response) { fetch(goFetch(null, request)).then(addIt).catch(function (err) { DSWManager.traceStep(request, 'Fetch failed'); _logger2.default.error('[ DSW ] :: Failed fetching ' + (request.url || request), err); reject(response); }); } else { addIt(response); } }); }, setExpiringTime: function setExpiringTime(request, rule) { var expiresAt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; if (typeof expiresAt == 'string') { expiresAt = parseExpiration(rule, expiresAt); } _indexeddbManager2.default.addOrUpdate({ url: request.url || request, dateAdded: new Date().getTime(), expiresAt: expiresAt }, CACHE_CREATED_DBNAME); }, hasExpired: function hasExpired(request) { return new Promise(function (resolve, reject) { _indexeddbManager2.default.find(CACHE_CREATED_DBNAME, 'url', request.url || request).then(function (r) { if (r && new Date().getTime() > r.dateAdded + r.expiresAt) { resolve(true); } else { resolve(false); } }).catch(function (_) { resolve(false); }); }); }, get: function get(rule, request, event, matching, forceFromCache) { var treatFailure = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true; var actionType = Object.keys(rule.action)[0], url = request.url || request, pathName = new URL(url).pathname; // requests to / should be cached by default if (rule.action.cache !== false && (pathName == '/' || pathName.match(/^\/index\.([a-z0-9]+)/i))) { rule.action.cache = rule.action.cache || {}; } var opts = rule.options || {}; opts.headers = opts.headers || new Headers(); actionType = actionType.toLowerCase(); // let's allow an idb alias for indexeddb...maybe we could move it to a // separated structure actionType = actionType == 'idb' ? 'indexeddb' : actionType; // cache may expire...if so, we will use this verification afterwards var verifyCache = void 0, urlToMatch = null; if (rule.action.cache && rule.action.cache.expires) { verifyCache = cacheManager.hasExpired(request); } else { // if it will not expire, we just use it as a resolved promise verifyCache = Promise.resolve(); } switch (actionType) { case 'bypass': { // if it is a bypass action (no rule shall be applied, at all) if (rule.action[actionType] == 'request') { // it may be of type request // and we will simple allow it to go ahead // this also means we will NOT treat any result from it //logger.info('Bypassing request, going for the network for', request.url); var treatResponse = function treatResponse(response) { if (response.status >= 200 && response.status < 300) { DSWManager.traceStep(event.request, 'Request bypassed'); return response; } else { DSWManager.traceStep(event.request, 'Bypassed request failed and was ignored'); var resp = new Response(''); // ignored return resp; } }; // here we will use a "raw" fetch, instead of goFetch, which would // create a new Request and define propreties to it return fetch(goFetch(null, event.request)).then(treatResponse).catch(treatResponse); } else { // or of type 'ignore' (or anything else, actually) // and we will simply output nothing, as if ignoring both the // request and response DSWManager.traceStep(event.request, 'Bypassed request'); actionType = 'output'; rule.action[actionType] = ''; } } case 'output': { DSWManager.traceStep(event.request, 'Responding with string output', { output: (rule.action[actionType] + '').substring(0, 180) }); return new Response(_utils2.default.applyMatch(matching, rule.action[actionType])); } case 'indexeddb': { return new Promise(function (resolve, reject) { // function to be used after fetching function treatFetch(response) { if (response && response.status == 200) { // with success or not(saving it), we resolve it var done = function done(err) { if (err) { DSWManager.traceStep(event.request, 'Could not save response into IndexedDB', { err: err }); } else { DSWManager.traceStep(event.request, 'Response object saved into IndexedDB'); } resolve(response); }; // store it in the indexedDB _indexeddbManager2.default.save(rule.name, response.clone(), request, rule).then(done).catch(done); // if failed saving, we still have the reponse to deliver } else { // if it failed, we can look for a fallback url = request.url; pathName = new URL(url).pathname; DSWManager.traceStep(event.request, 'Fetch failed', { url: request.url, status: response.status, statusText: response.statusText }); return DSWManager.treatBadPage(response, pathName, event); } } // let's look for it in our cache, and then in the database // (we use the cache, just so we can user) _indexeddbManager2.default.get(rule.name, request).then(function (result) { // if we did have it in the indexedDB if (result) { // we use it DSWManager.traceStep(event.request, 'Found stored in IndexedDB'); return treatFetch(result); } else { // if it was not stored, let's fetch it DSWManager.traceStep(event.request, 'Will fetch', { url: request.url, method: request.method }); return goFetch(rule, request, event, matching).then(treatFetch).catch(treatFetch); } }); }); } case 'redirect': case 'fetch': { request = DSWManager.createRedirect(rule.action.fetch || rule.action.redirect, event, matching); url = request.url; pathName = new URL(url).pathname; // keep going to be treated with the cache case } case 'cache': { var cacheId = void 0; if (event.request.cachedFrom) { // rule.action.cache && rule.action.cache.from) { urlToMatch = event.request.cachedFrom; } else { urlToMatch = null; } if (rule.action.cache) { cacheId = cacheManager.mountCacheId(rule); } // lets verify if the cache is expired or not return verifyCache.then(function (expired) { var lookForCache = void 0; if (expired && !forceFromCache) { // in case it has expired, it resolves automatically // with no results from cache DSWManager.traceStep(event.request, 'Cache was expired'); lookForCache = Promise.resolve(); } else { // if not expired, let's look for it! lookForCache = caches.match(urlToMatch || request); } // look for the request in the cache return lookForCache.then(function (result) { // if it does not exist (cache could not be verified) if (result && result.status != 200) { DSWManager.traceStep(event.request, 'Not found in cache', { url: request.url, status: result.status, statusText: result.statusText }); // if it has expired in cache, failed requests for // updates should return the previously cached data // even if it has expired if (expired) { DSWManager.traceStep(event.request, 'Forcing ' + (expired ? 'expired ' : '') + 'from cache'); // the true argument flag means it should come from cache, anyways return cacheManager.get(rule, request, event, matching, true); } if (treatFailure) { // look for rules that match for the request and its status (DSWManager.rules[result.status] || []).some(function (cur, idx) { if (pathName.match(cur.rx)) { // if a rule matched for the status and request // and it tries to fetch a different source if (cur.action.fetch || cur.action.redirect) { DSWManager.traceStep(event.request, 'Found fallback for failure', { rule: cur, url: request.url }); // problematic requests should result = goFetch(rule, request, event, matching); return true; // stopping the loop } } }); } // we, then, return the promise of the failed result(for it // could not be loaded and was not in cache) return result; } else { // We will return the result, if successful, or // fetch an anternative resource(or redirect) // and treat both success and failure with the // same "callback" // In case it is a redirect, we also set the header to 302 // and really change the url of the response. if (result) { // when it comes from a redirect, we let the browser know about it // or else...we simply return the result itself if (request.url == event.request.url) { DSWManager.traceStep(event.request, 'Result found in cache', { url: event.request.url, cacheSource: event.request.cachedFrom || event.request.url }); // it was successful return result; } else { // it is a redirect (different urls) DSWManager.traceStep(event.request, 'Redirecting', { from: event.request.url, to: request.url }, false, { // telling the tracker that it has moved url: request.url, id: request.requestId, steps: request.traceSteps, rule: rule }); // let's move the browser's url and return // the appropriate header return Response.redirect(request.url, 302); } } else if (actionType == 'redirect') { // if this is supposed to redirect DSWManager.traceStep(event.request, 'Must redirect', { from: event.request.url, to: request.url }, false, { // telling the tracker that it has moved url: request.url, id: request.requestId, steps: request.traceSteps, rule: rule }); return Response.redirect(request.url, 302); } else { // this is a "normal" request, let's deliver it // but we will be using a new Request with some info // to allow browsers to understand redirects in case // it must be redirected later on var treatFetch = function treatFetch(response) { if (response.type == 'opaque') { // if it is a opaque response, let it go! if (rule.action.cache !== false) { DSWManager.traceStep(event.request, 'Added to cache (opaque)', { url: request.url }); return cacheManager.add(_utils2.default.createRequest(request, { mode: request.mode || 'no-cors' }), cacheManager.mountCacheId(rule), response, rule); } return response; } if (!response.status) { response.status = 404; } // after retrieving it, we cache it // if it was ok if (response.status == 200) { DSWManager.traceStep(event.request, 'Received result OK (200)', { url: request.url }); // if cache is not false, it will be added to cache if (rule.action.cache !== false) { // let's save it into cache DSWManager.traceStep(event.request, 'Saving into cache', { url: request.url }); return cacheManager.add(request, cacheManager.mountCacheId(rule), response, rule); } else { return response; } } else { // if it had expired, but could not be retrieved // from network, let's give its cache a chance! DSWManager.traceStep(request, 'Failed fetching', { url: request.url }); if (expired) { _logger2.default.warn('Cache for ', request.url || request, 'had expired, but the updated version could not be retrieved from the network!\n', 'Delivering the outdated cached data'); DSWManager.traceStep(event.request, 'Using expired cache', { note: 'Failed fetching, loading from cache even though it was expired' }); return cacheManager.get(rule, request, event, matching, true); } // otherwise...let's see if there is a fallback // for the 404 requisition return DSWManager.treatBadPage(response, pathName, event); } }; // if not in cache, let's see if we should look // for it in the network if (treatFailure) { DSWManager.traceStep(event.request, 'Will fetch', { url: request.url, method: request.method }); return goFetch(rule, request, event, matching).then(treatFetch).catch(treatFetch); } } } }); // end lookForCache }); // end verifyCache } default: { // also used in fetch actions return event; } } } }; exports.default = cacheManager; },{"./indexeddb-manager.js":4,"./logger.js":5,"./utils.js":8}],3:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _utils = require('./utils.js'); var _utils2 = _interopRequireDefault(_utils); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var origin = location.origin; // this function basically creates a new Request instance // out of an existing one function createNewRequest(tmpUrl, request, event) { var sameOrigin = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; var mode = request.mode; if (!mode || mode == 'navigate') { mode = sameOrigin ? 'cors' : 'no-cors'; } var req = new Request(tmpUrl, { method: request.method || 'GET', headers: request.headers || {}, mode: mode, cache: 'default', redirect: 'manual' }); if (request.body) { req.body = request.body; } req.requestId = (event ? event.request : request).requestId; req.traceSteps = (event ? event.request : request).traceSteps; return req; } function goFetch(rule, request, event, matching) { var tmpUrl = rule ? rule.action.fetch || rule.action.redirect : ''; if (typeof request == 'string') { // lets fix the url in case it is not valid (not startingh with ./ or /, or a protocol) request = _utils2.default.fixURL(request); request = location.origin + request; } if (!tmpUrl) { tmpUrl = request.url || request; } else { // we also fix the tmpUrl in case it was sent tmpUrl = _utils2.default.fixURL(tmpUrl); tmpUrl = location.origin + tmpUrl; } var originalUrl = tmpUrl; var sameOrigin = void 0; try { sameOrigin = new URL(tmpUrl).origin == origin; } catch (err) { throw new Error('The URL "' + tmpUrl + '" is not valid and could not be parsed.'); } // if there are group variables in the matching expression tmpUrl = _utils2.default.applyMatch(matching, tmpUrl); // if no rule is passed if (request && !rule) { // we will just create a simple request to be used "anywhere" return createNewRequest(tmpUrl, request, event, sameOrigin); } var actionType = Object.keys(rule.action)[0]; var opts = rule.options || {}; opts.headers = opts.headers || new Headers(); // if the cache options is === false, we force it not to be cached if (rule.action.cache === false) { opts.headers.append('pragma', 'no-cache'); opts.headers.append('cache-control', 'no-store,no-cache'); tmpUrl = tmpUrl + (tmpUrl.indexOf('?') > 0 ? '&' : '?') + new Date().getTime(); } // we will create a new request to be used, based on what has been // defined by the rule or current request var reqConfig = { method: opts.method || request.method, headers: opts || request.headers, mode: actionType == 'redirect' ? request.mode || 'same-origin' : 'cors', redirect: actionType == 'redirect' ? 'manual' : request.redirect }; // if the host is not the same if (!sameOrigin) { // we set it to an opaque request reqConfig.mode = request.mode || 'no-cors'; } request = new Request(tmpUrl || request.url, reqConfig); request.requestId = (event ? event.request : request).requestId; request.traceSteps = (event ? event.request : request).traceSteps; if (actionType == 'redirect') { // if this is supposed to redirect return Response.redirect(request.url, 302); } else { // if this is a "normal" request, let's deliver it // but we will be using a new Request with some info // to allow browsers to understand redirects in case // it must be redirected later on return fetch(request, opts); } } exports.default = goFetch; },{"./utils.js":8}],4:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _logger = require('./logger.js'); var _logger2 = _interopRequireDefault(_logger); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DEFAULT_DB_NAME = 'defaultDSWDB'; var INDEXEDDB_REQ_IDS = 'indexeddb-id-request'; var dbs = {}; var cacheManager; function getObjectStore(dbName) { var mode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'readwrite'; var db = dbs[dbName], tx = void 0; if (db) { tx = db.transaction(dbName, mode); return tx.objectStore(dbName); } return false; } var indexedDBManager = { setup: function setup(cm) { cacheManager = cm; }, clear: function clear() { var dbList = []; var _loop = function _loop(db) { dbList.push(new Promise(function (resolve, reject) { var req = indexedDB.deleteDatabase(db); req.onsuccess = function () { resolve(); }; req.onerror = function (err) { reject(); _logger2.default.error('Could not drop indexedDB database\n', err || this.error); }; req.onblocked = function (err) { reject(); _logger2.default.error('Could not drop indexedDB database, it was locked\n', err || this.error); }; })); }; for (var db in dbs) { _loop(db); } return Promise.all([].concat(dbList)); }, create: function create(config) { return new Promise(function (resolve, reject) { var request = indexedDB.open(config.name || DEFAULT_DB_NAME, parseInt(config.version, 10) || undefined); function dataBaseReady(db, dbName, resolve) { db.onversionchange = function (event) { db.close(); //logger.log('There is a new version of the database(IndexedDB) for ' + dbName); }; if (!dbs[dbName]) { dbs[dbName] = db; } resolve(config); } request.onerror = function (event) { reject('Could not open the database (indexedDB) for ' + config.name); }; request.onupgradeneeded = function (event) { var db = event.target.result; var baseData = {}; if (config.key) { baseData.keyPath = config.key; } if (!config.key || config.autoIncrement) { baseData.autoIncrement = true; } if (config.version) { baseData.version = config.version; } else { baseData.version = 1; } if (event.oldVersion && event.oldVersion < baseData.version) { // in case there already is a store with that name // with a previous version db.deleteObjectStore(config.name); } else if (event.oldVersion === 0) { (function () { // if it is the first time it is creating it var objectStore = db.createObjectStore(config.name, baseData); // in case there are indexes defined, we create them if (config.indexes) { config.indexes.forEach(function (index) { if (typeof index == 'string') { objectStore.createIndex(index, index, {}); } else { objectStore.createIndex(index.name, index.path || index.name, index.options); } }); } // we will also make the key, an index objectStore.createIndex(config.key, config.key, { unique: true }); })(); } dataBaseReady(db, config.name, resolve); }; request.onsuccess = function (event) { var db = event.target.result; dataBaseReady(db, config.name, resolve); }; }); }, get: function get(dbName, request) { return new Promise(function (resolve, reject) { // We will actuallly look for its IDs in cache, to use them to find // the real, complete object in the indexedDB caches.match(request).then(function (result) { if (result) { result.json().then(function (obj) { // if the request was in cache, we now have got // the id=value for the indexes(keys) to look for, // in the indexedDB! var store = getObjectStore(dbName), index = store ? store.index(obj.key) : false, getter = index ? index.get(obj.value) : false; // in case we did get the content from indexedDB // let's create a new Response out of it! if (getter) { getter.onsuccess = function (event) { resolve(new Response(JSON.stringify(event.target.result), { headers: { 'Content-Type': 'application/json' } })); }; getter.onerror = function (event) { // if we did not find it (or faced a problem) in // indexeddb, we leave it to the network resolve(); }; } else { // in case it failed for some reason // we leave it and allow it to be requested resolve(); } }); } else { resolve(); } }); }); }, find: function find(dbName, key, value) { return new Promise(function (resolve, reject) { var store = getObjectStore(dbName); if (store) { var index = store.index(key), getter = index.get(value); getter.onsuccess = function (event) { resolve(event.target.result); }; getter.onerror = function (event) { reject(); }; } else { resolve(); } }); }, addOrUpdate: function addOrUpdate(obj, dbName) { return new Promise(function (resolve, reject) { var store = getObjectStore(dbName); if (store) { var req = store.put(obj); req.onsuccess = function addOrUpdateSuccess() { resolve(obj); }; req.onerror = function addOrUpdateError(err) { resolve(obj); }; } else { resolve({}); } }); }, save: function save(dbName, data, request, rule) { var _this = this; return new Promise(function (resolve, reject) { data.json().then(function (obj) { var store = getObjectStore(dbName), req = void 0; if (store) { req = store.put(obj); // We will use the CacheAPI to store, in cache, only the IDs for // the given object req.onsuccess = function () { var tmp = {}; var key = rule.action.indexedDB.key || 'id'; tmp.key = key; tmp.value = obj[key]; cacheManager.put(INDEXEDDB_REQ_IDS, request, new Response(JSON.stringify(tmp), { headers: { 'Content-Type': 'application/json' } })); resolve(); }; req.onerror = function (event) { reject('Failed saving to the indexedDB!\n' + this.error); }; } else { reject('Failed saving into indexedDB...\n' + _this.error); } }).catch(function (err) { _logger2.default.error('Failed saving into indexedDB!\n', err.message || _this.error, err); reject('Failed saving into indexedDB:\n' + _this.error); }); }); } }; exports.default = indexedDBManager; },{"./logger.js":5}],5:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var TYPES = { log: '[ LG ] :: ', info: '[ INFO ] :: ', warn: '[ WARN ] :: ', error: '[ FAIL ] :: ', track: '[ STEP ] :: ' }; var logger = { info: function info() { var args = [].slice.call(arguments); args.unshift('color: blue'); args.unshift('%c ' + TYPES.info); console.info.apply(console, args); }, log: function log() { var args = [].slice.call(arguments); args.unshift('color: gray'); args.unshift('%c ' + TYPES.log); console.log.apply(console, args); }, warn: function warn() { var args = [].slice.call(arguments); args.unshift('font-weight: bold; color: yellow; text-shadow: 0 0 1px black;'); args.unshift('%c ' + TYPES.warn); console.warn.apply(console, args); }, error: function error() { var args = [].slice.call(arguments); args.unshift('font-weight: bold; color: red'); args.unshift('%c ' + TYPES.error); console.error.apply(console, args); }, track: function track() { var args = [].slice.call(arguments); args.unshift('font-weight: bold'); args.unshift('%c ' + TYPES.track); console.debug.apply(console, args); } }; exports.default = logger; },{}],6:[function(require,module,exports){ (function (global){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _logger = require('./logger.js'); var _logger2 = _interopRequireDefault(_logger); var _bestMatchingRx = require('./best-matching-rx.js'); var _bestMatchingRx2 = _interopRequireDefault(_bestMatchingRx); var _cacheManager = require('./cache-manager.js'); var _cacheManager2 = _interopRequireDefault(_cacheManager); var _goFetch = require('./go-fetch.js'); var _goFetch2 = _interopRequireDefault(_goFetch); var _strategies = require('./strategies.js'); var _strategies2 = _interopRequireDefault(_strategies); var _utils = require('./utils.js'); var _utils2 = _interopRequireDefault(_utils); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var isInSWScope = false; var isInTest = typeof global.it === 'function'; var preCache; var failedAppShellFiles = []; var ROOT_SW_SCOPE = null; var DSW = { version: '#@!THE_DSW_VERSION_INFO!@#', build: '#@!THE_DSW_BUILD_TIMESTAMP!@#', ready: null }; var REQUEST_TIME_LIMIT = 5000; var REGISTRATION_TIMEOUT = 12000; var DEFAULT_NOTIF_DURATION = 6000; var currentlyMocking = {}; var installationFailure = function installationFailure(err) { var errMessage = 'Failed storing appshell.\n ' + failedAppShellFiles.join(',') + ' failed loading.\n'; return errMessage; }; function getSWRoot() { if (ROOT_SW_SCOPE) { return ROOT_SW_SCOPE; } ROOT_SW_SCOPE = new URL(location.href).pathname.replace(/\/[^\/]+$/, '/'); return ROOT_SW_SCOPE; } // These will be used in both ServiceWorker and Client scopes DSW.isOffline = DSW.offline = function (_) { return !navigator.onLine; }; DSW.isOnline = DSW.online = function (_) { return navigator.onLine; }; // this try/catch is used simply to figure out the current scope try { var SWScope = ServiceWorkerGlobalScope; if (self instanceof ServiceWorkerGlobalScope) { isInSWScope = true; } } catch (e) {/* nothing...just had to find out the scope */} if (isInSWScope) { (function () { var DSWManager = { requestId: 0, tracking: {}, trackMoved: {}, rules: {}, addRule: function addRule(sts, rule, rx) { this.rules[sts] = this.rules[sts] || []; var newRule = { name: rule.name, rx: rx, strategy: rule.strategy || 'offline-first', action: rule['apply'] }; this.rules[sts].push(newRule); // if there is a rule for cache if (newRule.action.cache) { // we will register it in the cacheManager _cacheManager2.default.register(newRule); } else { // if it is supposed NOT to cache if (newRule.action.cache === false) { newRule.strategy = 'online-first'; } } return newRule; }, treatBadPage: function treatBadPage(response, pathName, event) { var result = void 0; DSWManager.traceStep(event.request, 'Request failed', { status: response.status, statusText: response.statusText, url: response.url, type: response.type }); (DSWManager.rules[response && response.status ? response.status : 404] || []).some(function (cur, idx) { var matching = pathName.match(cur.rx); if (matching) { if (cur.action.redirect && !cur.action.fetch) { cur.action.fetch = cur.action.redirect; } if (cur.action.fetch) { DSWManager.traceStep(event.request, 'Found fallback rule', cur, false, { url: event.request.url, id: event.request.requestId, steps: event.request.traceSteps }); // not found requests should // fetch a different resource var req = new Request(cur.action.fetch); req.requestId = event.request.requestId; req.traceSteps = event.request.traceSteps; // applyMatch result = _cacheManager2.default.get(cur, req, event, matching); return true; // stopping