Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

workbox-background-sync

Package Overview
Dependencies
Maintainers
3
Versions
98
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

workbox-background-sync - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

build/importScripts/workbox-background-sync.dev.v0.0.3.js

852

build/test/background-sync-queue-plugin.js

@@ -338,3 +338,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -507,9 +506,9 @@ }());

async function putResponse({hash, idbObject, response, idbQDb}) {
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
}

@@ -521,3 +520,3 @@

* @memberof module:workbox-background-sync
*
* @private
* @param {String} id The ID of the request given back by the broaadcast

@@ -528,9 +527,9 @@ * channel

async function getResponse({id, dbName}) {
const _idbQHelper = new IDBHelper(dbName, 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object && object.response) {
return object.response;
} else {
return null;
}
const _idbQHelper = new IDBHelper(dbName, 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object && object.response) {
return object.response;
} else {
return null;
}
}

@@ -561,21 +560,21 @@

async function getQueueableRequest({request, config}) {
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
}

@@ -591,13 +590,13 @@

async function getFetchableRequest({idbRequestObject}) {
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
}

@@ -615,28 +614,28 @@

async function cleanupQueue(dbName) {
let db = new IDBHelper(dbName, 1, 'QueueStore');
let queueObj = await db.get(allQueuesPlaceholder);
let db = new IDBHelper(dbName, 1, 'QueueStore');
let queueObj = await db.get(allQueuesPlaceholder);
if(!queueObj) {
return null;
}
if(!queueObj) {
return null;
}
await Promise.all(queueObj.map(async (queueName)=>{
const requestQueues = await db.get(queueName);
let itemsToKeep = [];
let deletionPromises = [];
await Promise.all(requestQueues.map( async (hash) => {
const requestData = await db.get(hash);
if (requestData && requestData.metadata
&& requestData.metadata.creationTimestamp + requestData.config.maxAge
<= Date.now()) {
// Delete items that are too old.
deletionPromises.push(db.delete(hash));
} else {
// Keep elements whose definition exists in idb.
itemsToKeep.push(hash);
}
}));
await Promise.all(deletionPromises);
db.put(queueName, itemsToKeep);
}));
await Promise.all(queueObj.map(async (queueName)=>{
const requestQueues = await db.get(queueName);
let itemsToKeep = [];
let deletionPromises = [];
await Promise.all(requestQueues.map( async (hash) => {
const requestData = await db.get(hash);
if (requestData && requestData.metadata
&& requestData.metadata.creationTimestamp + requestData.config.maxAge
<= Date.now()) {
// Delete items that are too old.
deletionPromises.push(db.delete(hash));
} else {
// Keep elements whose definition exists in idb.
itemsToKeep.push(hash);
}
}));
await Promise.all(deletionPromises);
db.put(queueName, itemsToKeep);
}));
}

@@ -652,78 +651,78 @@

class RequestManager {
/**
* Initializes the request manager
* stores the callbacks object, maintains config and
* attaches event handler
* @param {Object=} config
*
* @memberOf RequestManager
* @private
*/
constructor({callbacks, queue}) {
this._globalCallbacks = callbacks || {};
this._queue = queue;
this.attachSyncHandler();
}
/**
* Initializes the request manager
* stores the callbacks object, maintains config and
* attaches event handler
* @param {Object=} config
*
* @memberOf RequestManager
* @private
*/
constructor({callbacks, queue}) {
this._globalCallbacks = callbacks || {};
this._queue = queue;
this.attachSyncHandler();
}
/**
* attaches sync handler to replay requests when
* sync event is fired
*
* @memberOf RequestManager
* @private
*/
attachSyncHandler() {
self.addEventListener('sync', (event) => {
if(event.tag === tagNamePrefix + this._queue.queueName
|| event.tag === replayAllQueuesTag) {
event.waitUntil(this.replayRequests());
}
});
}
/**
* attaches sync handler to replay requests when
* sync event is fired
*
* @memberOf RequestManager
* @private
*/
attachSyncHandler() {
self.addEventListener('sync', (event) => {
if(event.tag === tagNamePrefix + this._queue.queueName
|| event.tag === replayAllQueuesTag) {
event.waitUntil(this.replayRequests());
}
});
}
/**
* function to start playing requests
* in sequence
* @return {void}
*
* @memberOf RequestManager
* @private
*/
replayRequests() {
return this._queue.queue.reduce((promise, hash) => {
return promise
.then(async (item) => {
const reqData = await this._queue.getRequestFromQueue({hash});
if(reqData.response) {
// check if request is not played already
return;
}
/**
* function to start playing requests
* in sequence
* @return {void}
*
* @memberOf RequestManager
* @private
*/
replayRequests() {
return this._queue.queue.reduce((promise, hash) => {
return promise
.then(async (item) => {
const reqData = await this._queue.getRequestFromQueue({hash});
if(reqData.response) {
// check if request is not played already
return;
}
const request = await getFetchableRequest({
idbRequestObject: reqData.request,
});
const request = await getFetchableRequest({
idbRequestObject: reqData.request,
});
return fetch(request)
.then((response)=>{
if(!response.ok) {
return Promise.resolve();
} else {
// not blocking on putResponse.
putResponse({
hash,
idbObject: reqData,
response: response.clone(),
idbQDb: this._queue.idbQDb,
});
this._globalCallbacks.onResponse
&& this._globalCallbacks.onResponse(hash, response);
}
})
.catch((err)=>{
this._globalCallbacks.onRetryFailure
&& this._globalCallbacks.onRetryFailure(hash, err);
});
});
}, Promise.resolve());
}
return fetch(request)
.then((response)=>{
if(!response.ok) {
return Promise.resolve();
} else {
// not blocking on putResponse.
putResponse({
hash,
idbObject: reqData,
response: response.clone(),
idbQDb: this._queue.idbQDb,
});
this._globalCallbacks.onResponse
&& this._globalCallbacks.onResponse(hash, response);
}
})
.catch((err)=>{
this._globalCallbacks.onRetryFailure
&& this._globalCallbacks.onRetryFailure(hash, err);
});
});
}, Promise.resolve());
}
}

@@ -1177,16 +1176,16 @@

function broadcastMessage({broadcastChannel, type, url}) {
if(!broadcastChannel)
return;
if(!broadcastChannel)
return;
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
}

@@ -1204,162 +1203,162 @@

class RequestQueue {
/**
* Creates an instance of RequestQueue.
*
* @param {Object} config
*
* @memberOf RequestQueue
* @private
*/
constructor({
config,
queueName = defaultQueueName + '_' + _queueCounter++,
idbQDb,
broadcastChannel,
}) {
this._isQueueNameAddedToAllQueue = false;
this._queueName = queueName;
this._config = config;
this._idbQDb = idbQDb;
this._broadcastChannel = broadcastChannel;
this._queue = [];
this.initQueue();
}
/**
* Creates an instance of RequestQueue.
*
* @param {Object} config
*
* @memberOf RequestQueue
* @private
*/
constructor({
config,
queueName = defaultQueueName + '_' + _queueCounter++,
idbQDb,
broadcastChannel,
}) {
this._isQueueNameAddedToAllQueue = false;
this._queueName = queueName;
this._config = config;
this._idbQDb = idbQDb;
this._broadcastChannel = broadcastChannel;
this._queue = [];
this.initQueue();
}
/**
* initializes the queue from the IDB store
*
* @memberOf RequestQueue
* @private
*/
async initQueue() {
const idbQueue = await this._idbQDb.get(this._queueName);
this._queue.concat(idbQueue);
}
/**
* initializes the queue from the IDB store
*
* @memberOf RequestQueue
* @private
*/
async initQueue() {
const idbQueue = await this._idbQDb.get(this._queueName);
this._queue.concat(idbQueue);
}
/**
* adds the current queueName to all queue array
*
* @memberOf RequestQueue
* @private
*/
async addQueueNameToAllQueues() {
if(!this._isQueueNameAddedToAllQueue) {
let allQueues = await this._idbQDb.get(allQueuesPlaceholder);
allQueues = allQueues || [];
if(!allQueues.includes(this._queueName)) {
allQueues.push(this._queueName);
}
this._idbQDb.put(allQueuesPlaceholder, allQueues);
this._isQueueNameAddedToAllQueue = true;
}
}
/**
* adds the current queueName to all queue array
*
* @memberOf RequestQueue
* @private
*/
async addQueueNameToAllQueues() {
if(!this._isQueueNameAddedToAllQueue) {
let allQueues = await this._idbQDb.get(allQueuesPlaceholder);
allQueues = allQueues || [];
if(!allQueues.includes(this._queueName)) {
allQueues.push(this._queueName);
}
this._idbQDb.put(allQueuesPlaceholder, allQueues);
this._isQueueNameAddedToAllQueue = true;
}
}
/**
* saves the logical queue to IDB
*
* @memberOf RequestQueue
* @private
*/
async saveQueue() {
await this._idbQDb.put(this._queueName, this._queue);
}
/**
* saves the logical queue to IDB
*
* @memberOf RequestQueue
* @private
*/
async saveQueue() {
await this._idbQDb.put(this._queueName, this._queue);
}
/**
* push any request to background sync queue which would be played later
* preferably when network comes back
*
* @param {Request} request request object to be queued by this
*
* @memberOf Queue
* @private
*/
async push({request}) {
assert.isInstance({request}, Request);
/**
* push any request to background sync queue which would be played later
* preferably when network comes back
*
* @param {Request} request request object to be queued by this
*
* @memberOf Queue
* @private
*/
async push({request}) {
assert.isInstance({request}, Request);
const hash = `${request.url}!${Date.now()}!${_requestCounter++}`;
const queuableRequest =
await getQueueableRequest({
request,
config: this._config,
});
try{
this._queue.push(hash);
const hash = `${request.url}!${Date.now()}!${_requestCounter++}`;
const queuableRequest =
await getQueueableRequest({
request,
config: this._config,
});
try{
this._queue.push(hash);
// add to queue
this.saveQueue();
this._idbQDb.put(hash, queuableRequest);
await this.addQueueNameToAllQueues();
// register sync
self.registration &&
self.registration.sync.register(tagNamePrefix + this._queueName);
// add to queue
this.saveQueue();
this._idbQDb.put(hash, queuableRequest);
await this.addQueueNameToAllQueues();
// register sync
self.registration &&
self.registration.sync.register(tagNamePrefix + this._queueName);
// broadcast the success of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageAddedType,
id: hash,
url: request.url,
});
} catch(e) {
// broadcast the failure of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageFailedType,
id: hash,
url: request.url,
});
}
}
// broadcast the success of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageAddedType,
id: hash,
url: request.url,
});
} catch(e) {
// broadcast the failure of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageFailedType,
id: hash,
url: request.url,
});
}
}
/**
* get the Request from the queue at a particular index
*
* @param {string} hash hash of the request at the given index
* @return {Request} request object corresponding to given hash
* @memberOf Queue
* @private
*/
async getRequestFromQueue({hash}) {
assert.isType({hash}, 'string');
/**
* get the Request from the queue at a particular index
*
* @param {string} hash hash of the request at the given index
* @return {Request} request object corresponding to given hash
* @memberOf Queue
* @private
*/
async getRequestFromQueue({hash}) {
assert.isType({hash}, 'string');
if(this._queue.includes(hash)) {
return await this._idbQDb.get(hash);
}
}
if(this._queue.includes(hash)) {
return await this._idbQDb.get(hash);
}
}
/**
* returns the instance of queue.
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queue() {
return Object.assign([], this._queue);
}
/**
* returns the instance of queue.
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queue() {
return Object.assign([], this._queue);
}
/**
* returns the name of the current queue
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queueName() {
return this._queueName;
}
/**
* returns the name of the current queue
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queueName() {
return this._queueName;
}
/**
* returns the instance of IDBStore
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get idbQDb() {
return this._idbQDb;
}
/**
* returns the instance of IDBStore
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get idbQDb() {
return this._idbQDb;
}
}

@@ -1374,108 +1373,109 @@

* self.addEventListener('fetch', function(e) {
* if (e.request.url.startsWith('https://jsonplaceholder.typicode.com')) {
* const clone = e.request.clone();
* e.respondWith(fetch(e.request).catch((err)=>{
* bgQueue.pushIntoQueue({
* request: clone,
* });
* throw err;
* }));
* }
* if (!e.request.url.startsWith('https://jsonplaceholder.typicode.com')) {
* return;
* }
*
* const clone = e.request.clone();
* e.respondWith(fetch(e.request).catch((err)=>{
* bgQueue.pushIntoQueue({
* request: clone,
* });
* throw err;
* }));
* });
*
* @memberof module:queue
* @memberof module:workbox-background-sync
*/
class Queue {
/**
* Creates an instance of Queue with the given options
*
* @param {Object} [input]
* @param {Number} [input.maxRetentionTime = 5 days] Time for which a queued
* request will live in the queue(irespective of failed/success of replay).
* @param {Object} [input.callbacks] Callbacks for successfull/ failed
* replay of a request.
* @param {string} [input.queueName] Queue name inside db in which
* requests will be queued.
* @param {BroadcastChannel=} [input.broadcastChannel] BroadcastChannel
* which will be used to publish messages when the request will be queued.
*/
constructor({maxRetentionTime = maxAge, callbacks, queueName,
broadcastChannel, dbName = defaultDBName} = {}) {
if(queueName) {
assert.isType({queueName}, 'string');
}
/**
* Creates an instance of Queue with the given options
*
* @param {Object} [input]
* @param {Number} [input.maxRetentionTime = 5 days] Time for which a queued
* request will live in the queue(irespective of failed/success of replay).
* @param {Object} [input.callbacks] Callbacks for successfull/ failed
* replay of a request.
* @param {string} [input.queueName] Queue name inside db in which
* requests will be queued.
* @param {BroadcastChannel=} [input.broadcastChannel] BroadcastChannel
* which will be used to publish messages when the request will be queued.
*/
constructor({maxRetentionTime = maxAge, callbacks, queueName,
broadcastChannel, dbName = defaultDBName} = {}) {
if(queueName) {
assert.isType({queueName}, 'string');
}
if(maxRetentionTime) {
assert.isType({maxRetentionTime}, 'number');
}
if(maxRetentionTime) {
assert.isType({maxRetentionTime}, 'number');
}
if(broadcastChannel) {
assert.isInstance({broadcastChannel}, BroadcastChannel);
}
if(broadcastChannel) {
assert.isInstance({broadcastChannel}, BroadcastChannel);
}
assert.isType({dbName}, 'string');
assert.isType({dbName}, 'string');
this._dbName = dbName;
this._queue = new RequestQueue({
config: {
maxAge: maxRetentionTime,
},
queueName,
idbQDb: new IDBHelper(this._dbName, 1, 'QueueStore'),
broadcastChannel,
});
this._requestManager = new RequestManager({callbacks,
queue: this._queue});
this._dbName = dbName;
this._queue = new RequestQueue({
config: {
maxAge: maxRetentionTime,
},
queueName,
idbQDb: new IDBHelper(this._dbName, 1, 'QueueStore'),
broadcastChannel,
});
this._requestManager = new RequestManager({callbacks,
queue: this._queue});
this.cleanupQueue();
}
this.cleanupQueue();
}
/**
* clean up the queue, deleting all the tasks whose maxAge has expired
*
* @memberOf Queue
* @private
* @return {Promise}
*/
cleanupQueue() {
return cleanupQueue(this._dbName);
}
/**
* clean up the queue, deleting all the tasks whose maxAge has expired
*
* @memberOf Queue
* @private
* @return {Promise}
*/
cleanupQueue() {
return cleanupQueue(this._dbName);
}
/**
* This function pushes a given request into the IndexedDb Queue
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
pushIntoQueue({request}) {
assert.isInstance({request}, Request);
return this._queue.push({request});
}
/**
* This function pushes a given request into the IndexedDb Queue.
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
pushIntoQueue({request}) {
assert.isInstance({request}, Request);
return this._queue.push({request});
}
/**
* Replays all the requests in the queue, this can be used for custom timing
* of replaying requests may be in an environment where sync event is not
* supported.
* @return {Promise} A listener for when the requests have been replayed.
*/
replayRequests() {
return this._requestManager.replayRequests();
}
/**
* Replays all the requests in the queue, this can be used for custom timing
* of replaying requests may be in an environment where sync event is not
* supported.
* @return {Promise} A listener for when the requests have been replayed.
*/
replayRequests() {
return this._requestManager.replayRequests();
}
/**
* sets the dbName, which is used to store the queue and requests
* defaults to bgQueueSyncDB
* @param {String} id
* @param {String} dbName
* @return {Object} response Fetched response of the request.
*/
getResponse({id}) {
return getResponse({
id,
dbName: this._dbName,
});
}
* Sets the dbName, which is used to store the queue and requests
* defaults to bgQueueSyncDB.
* @param {String} id The ID of the request.
* @return {Object} Fetched response of the request.
*/
getResponse({id}) {
return getResponse({
id,
dbName: this._dbName,
});
}
}

@@ -1486,24 +1486,23 @@

*
* @example
* When you want the workbox-sw framework to take care of failed
* // requests
* let bgQueue = new workbox.backgroundSync.QueuePlugin({callbacks:
* {
* onResponse: async(hash, res) => {
* self.registration.showNotification('Background sync demo', {
* body: 'Product has been purchased.',
* icon: 'https://shop.polymer-project.org/images/shop-icon-384.png',
* });
* },
* onRetryFailure: (hash) => {},
* },
* @example <caption>When you want the workbox-sw framework to take care of
* failed requests.</caption>
* let bgQueue = new workbox.backgroundSync.QueuePlugin({
* callbacks: {
* onResponse: async(hash, res) => {
* self.registration.showNotification('Background sync demo', {
* body: 'Product has been purchased.',
* icon: '/images/shop-icon-384.png',
* });
* },
* onRetryFailure: (hash) => {},
* },
* });
*
* const requestWrapper = new workbox.runtimeCaching.RequestWrapper({
* plugins: [bgQueue],
* plugins: [bgQueue],
* });
*
* const route = new workbox.routing.RegExpRoute({
* regExp: new RegExp('^https://jsonplaceholder.typicode.com'),
* handler: new workbox.runtimeCaching.NetworkOnly({requestWrapper}),
* regExp: new RegExp('^https://jsonplaceholder.typicode.com'),
* handler: new workbox.runtimeCaching.NetworkOnly({requestWrapper}),
* });

@@ -1514,20 +1513,21 @@ *

*
* @memberof module:queue-plugin
* @memberof module:workbox-background-sync
* @extends module:workbox-background-sync.Queue
*/
class QueuePlugin extends Queue {
/**
* Wraps `pushIntoQueue` in a callback used by higher level framework.
* This function pushes a given request into the IndexedDb Queue.
* NOTE: If you are writting the fetch handler for background sync manually,
* please ignore this.
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
fetchDidFail({request}) {
return this.pushIntoQueue({request});
}
/**
* Wraps `pushIntoQueue` in a callback used by higher level framework.
* This function pushes a given request into the IndexedDb Queue.
* NOTE: If you are writting the fetch handler for background sync manually,
* please ignore this.
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
fetchDidFail({request}) {
return this.pushIntoQueue({request});
}
}

@@ -1534,0 +1534,0 @@

@@ -338,3 +338,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -507,9 +506,9 @@ }());

async function putResponse({hash, idbObject, response, idbQDb}) {
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
}

@@ -521,3 +520,3 @@

* @memberof module:workbox-background-sync
*
* @private
* @param {String} id The ID of the request given back by the broaadcast

@@ -528,9 +527,9 @@ * channel

async function getResponse({id, dbName}) {
const _idbQHelper = new IDBHelper(dbName, 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object && object.response) {
return object.response;
} else {
return null;
}
const _idbQHelper = new IDBHelper(dbName, 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object && object.response) {
return object.response;
} else {
return null;
}
}

@@ -561,21 +560,21 @@

async function getQueueableRequest({request, config}) {
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
}

@@ -591,13 +590,13 @@

async function getFetchableRequest({idbRequestObject}) {
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
}

@@ -615,28 +614,28 @@

async function cleanupQueue(dbName) {
let db = new IDBHelper(dbName, 1, 'QueueStore');
let queueObj = await db.get(allQueuesPlaceholder);
let db = new IDBHelper(dbName, 1, 'QueueStore');
let queueObj = await db.get(allQueuesPlaceholder);
if(!queueObj) {
return null;
}
if(!queueObj) {
return null;
}
await Promise.all(queueObj.map(async (queueName)=>{
const requestQueues = await db.get(queueName);
let itemsToKeep = [];
let deletionPromises = [];
await Promise.all(requestQueues.map( async (hash) => {
const requestData = await db.get(hash);
if (requestData && requestData.metadata
&& requestData.metadata.creationTimestamp + requestData.config.maxAge
<= Date.now()) {
// Delete items that are too old.
deletionPromises.push(db.delete(hash));
} else {
// Keep elements whose definition exists in idb.
itemsToKeep.push(hash);
}
}));
await Promise.all(deletionPromises);
db.put(queueName, itemsToKeep);
}));
await Promise.all(queueObj.map(async (queueName)=>{
const requestQueues = await db.get(queueName);
let itemsToKeep = [];
let deletionPromises = [];
await Promise.all(requestQueues.map( async (hash) => {
const requestData = await db.get(hash);
if (requestData && requestData.metadata
&& requestData.metadata.creationTimestamp + requestData.config.maxAge
<= Date.now()) {
// Delete items that are too old.
deletionPromises.push(db.delete(hash));
} else {
// Keep elements whose definition exists in idb.
itemsToKeep.push(hash);
}
}));
await Promise.all(deletionPromises);
db.put(queueName, itemsToKeep);
}));
}

@@ -652,78 +651,78 @@

class RequestManager {
/**
* Initializes the request manager
* stores the callbacks object, maintains config and
* attaches event handler
* @param {Object=} config
*
* @memberOf RequestManager
* @private
*/
constructor({callbacks, queue}) {
this._globalCallbacks = callbacks || {};
this._queue = queue;
this.attachSyncHandler();
}
/**
* Initializes the request manager
* stores the callbacks object, maintains config and
* attaches event handler
* @param {Object=} config
*
* @memberOf RequestManager
* @private
*/
constructor({callbacks, queue}) {
this._globalCallbacks = callbacks || {};
this._queue = queue;
this.attachSyncHandler();
}
/**
* attaches sync handler to replay requests when
* sync event is fired
*
* @memberOf RequestManager
* @private
*/
attachSyncHandler() {
self.addEventListener('sync', (event) => {
if(event.tag === tagNamePrefix + this._queue.queueName
|| event.tag === replayAllQueuesTag) {
event.waitUntil(this.replayRequests());
}
});
}
/**
* attaches sync handler to replay requests when
* sync event is fired
*
* @memberOf RequestManager
* @private
*/
attachSyncHandler() {
self.addEventListener('sync', (event) => {
if(event.tag === tagNamePrefix + this._queue.queueName
|| event.tag === replayAllQueuesTag) {
event.waitUntil(this.replayRequests());
}
});
}
/**
* function to start playing requests
* in sequence
* @return {void}
*
* @memberOf RequestManager
* @private
*/
replayRequests() {
return this._queue.queue.reduce((promise, hash) => {
return promise
.then(async (item) => {
const reqData = await this._queue.getRequestFromQueue({hash});
if(reqData.response) {
// check if request is not played already
return;
}
/**
* function to start playing requests
* in sequence
* @return {void}
*
* @memberOf RequestManager
* @private
*/
replayRequests() {
return this._queue.queue.reduce((promise, hash) => {
return promise
.then(async (item) => {
const reqData = await this._queue.getRequestFromQueue({hash});
if(reqData.response) {
// check if request is not played already
return;
}
const request = await getFetchableRequest({
idbRequestObject: reqData.request,
});
const request = await getFetchableRequest({
idbRequestObject: reqData.request,
});
return fetch(request)
.then((response)=>{
if(!response.ok) {
return Promise.resolve();
} else {
// not blocking on putResponse.
putResponse({
hash,
idbObject: reqData,
response: response.clone(),
idbQDb: this._queue.idbQDb,
});
this._globalCallbacks.onResponse
&& this._globalCallbacks.onResponse(hash, response);
}
})
.catch((err)=>{
this._globalCallbacks.onRetryFailure
&& this._globalCallbacks.onRetryFailure(hash, err);
});
});
}, Promise.resolve());
}
return fetch(request)
.then((response)=>{
if(!response.ok) {
return Promise.resolve();
} else {
// not blocking on putResponse.
putResponse({
hash,
idbObject: reqData,
response: response.clone(),
idbQDb: this._queue.idbQDb,
});
this._globalCallbacks.onResponse
&& this._globalCallbacks.onResponse(hash, response);
}
})
.catch((err)=>{
this._globalCallbacks.onRetryFailure
&& this._globalCallbacks.onRetryFailure(hash, err);
});
});
}, Promise.resolve());
}
}

@@ -1177,16 +1176,16 @@

function broadcastMessage({broadcastChannel, type, url}) {
if(!broadcastChannel)
return;
if(!broadcastChannel)
return;
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
}

@@ -1204,162 +1203,162 @@

class RequestQueue {
/**
* Creates an instance of RequestQueue.
*
* @param {Object} config
*
* @memberOf RequestQueue
* @private
*/
constructor({
config,
queueName = defaultQueueName + '_' + _queueCounter++,
idbQDb,
broadcastChannel,
}) {
this._isQueueNameAddedToAllQueue = false;
this._queueName = queueName;
this._config = config;
this._idbQDb = idbQDb;
this._broadcastChannel = broadcastChannel;
this._queue = [];
this.initQueue();
}
/**
* Creates an instance of RequestQueue.
*
* @param {Object} config
*
* @memberOf RequestQueue
* @private
*/
constructor({
config,
queueName = defaultQueueName + '_' + _queueCounter++,
idbQDb,
broadcastChannel,
}) {
this._isQueueNameAddedToAllQueue = false;
this._queueName = queueName;
this._config = config;
this._idbQDb = idbQDb;
this._broadcastChannel = broadcastChannel;
this._queue = [];
this.initQueue();
}
/**
* initializes the queue from the IDB store
*
* @memberOf RequestQueue
* @private
*/
async initQueue() {
const idbQueue = await this._idbQDb.get(this._queueName);
this._queue.concat(idbQueue);
}
/**
* initializes the queue from the IDB store
*
* @memberOf RequestQueue
* @private
*/
async initQueue() {
const idbQueue = await this._idbQDb.get(this._queueName);
this._queue.concat(idbQueue);
}
/**
* adds the current queueName to all queue array
*
* @memberOf RequestQueue
* @private
*/
async addQueueNameToAllQueues() {
if(!this._isQueueNameAddedToAllQueue) {
let allQueues = await this._idbQDb.get(allQueuesPlaceholder);
allQueues = allQueues || [];
if(!allQueues.includes(this._queueName)) {
allQueues.push(this._queueName);
}
this._idbQDb.put(allQueuesPlaceholder, allQueues);
this._isQueueNameAddedToAllQueue = true;
}
}
/**
* adds the current queueName to all queue array
*
* @memberOf RequestQueue
* @private
*/
async addQueueNameToAllQueues() {
if(!this._isQueueNameAddedToAllQueue) {
let allQueues = await this._idbQDb.get(allQueuesPlaceholder);
allQueues = allQueues || [];
if(!allQueues.includes(this._queueName)) {
allQueues.push(this._queueName);
}
this._idbQDb.put(allQueuesPlaceholder, allQueues);
this._isQueueNameAddedToAllQueue = true;
}
}
/**
* saves the logical queue to IDB
*
* @memberOf RequestQueue
* @private
*/
async saveQueue() {
await this._idbQDb.put(this._queueName, this._queue);
}
/**
* saves the logical queue to IDB
*
* @memberOf RequestQueue
* @private
*/
async saveQueue() {
await this._idbQDb.put(this._queueName, this._queue);
}
/**
* push any request to background sync queue which would be played later
* preferably when network comes back
*
* @param {Request} request request object to be queued by this
*
* @memberOf Queue
* @private
*/
async push({request}) {
assert.isInstance({request}, Request);
/**
* push any request to background sync queue which would be played later
* preferably when network comes back
*
* @param {Request} request request object to be queued by this
*
* @memberOf Queue
* @private
*/
async push({request}) {
assert.isInstance({request}, Request);
const hash = `${request.url}!${Date.now()}!${_requestCounter++}`;
const queuableRequest =
await getQueueableRequest({
request,
config: this._config,
});
try{
this._queue.push(hash);
const hash = `${request.url}!${Date.now()}!${_requestCounter++}`;
const queuableRequest =
await getQueueableRequest({
request,
config: this._config,
});
try{
this._queue.push(hash);
// add to queue
this.saveQueue();
this._idbQDb.put(hash, queuableRequest);
await this.addQueueNameToAllQueues();
// register sync
self.registration &&
self.registration.sync.register(tagNamePrefix + this._queueName);
// add to queue
this.saveQueue();
this._idbQDb.put(hash, queuableRequest);
await this.addQueueNameToAllQueues();
// register sync
self.registration &&
self.registration.sync.register(tagNamePrefix + this._queueName);
// broadcast the success of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageAddedType,
id: hash,
url: request.url,
});
} catch(e) {
// broadcast the failure of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageFailedType,
id: hash,
url: request.url,
});
}
}
// broadcast the success of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageAddedType,
id: hash,
url: request.url,
});
} catch(e) {
// broadcast the failure of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageFailedType,
id: hash,
url: request.url,
});
}
}
/**
* get the Request from the queue at a particular index
*
* @param {string} hash hash of the request at the given index
* @return {Request} request object corresponding to given hash
* @memberOf Queue
* @private
*/
async getRequestFromQueue({hash}) {
assert.isType({hash}, 'string');
/**
* get the Request from the queue at a particular index
*
* @param {string} hash hash of the request at the given index
* @return {Request} request object corresponding to given hash
* @memberOf Queue
* @private
*/
async getRequestFromQueue({hash}) {
assert.isType({hash}, 'string');
if(this._queue.includes(hash)) {
return await this._idbQDb.get(hash);
}
}
if(this._queue.includes(hash)) {
return await this._idbQDb.get(hash);
}
}
/**
* returns the instance of queue.
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queue() {
return Object.assign([], this._queue);
}
/**
* returns the instance of queue.
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queue() {
return Object.assign([], this._queue);
}
/**
* returns the name of the current queue
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queueName() {
return this._queueName;
}
/**
* returns the name of the current queue
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queueName() {
return this._queueName;
}
/**
* returns the instance of IDBStore
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get idbQDb() {
return this._idbQDb;
}
/**
* returns the instance of IDBStore
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get idbQDb() {
return this._idbQDb;
}
}

@@ -1374,108 +1373,109 @@

* self.addEventListener('fetch', function(e) {
* if (e.request.url.startsWith('https://jsonplaceholder.typicode.com')) {
* const clone = e.request.clone();
* e.respondWith(fetch(e.request).catch((err)=>{
* bgQueue.pushIntoQueue({
* request: clone,
* });
* throw err;
* }));
* }
* if (!e.request.url.startsWith('https://jsonplaceholder.typicode.com')) {
* return;
* }
*
* const clone = e.request.clone();
* e.respondWith(fetch(e.request).catch((err)=>{
* bgQueue.pushIntoQueue({
* request: clone,
* });
* throw err;
* }));
* });
*
* @memberof module:queue
* @memberof module:workbox-background-sync
*/
class Queue {
/**
* Creates an instance of Queue with the given options
*
* @param {Object} [input]
* @param {Number} [input.maxRetentionTime = 5 days] Time for which a queued
* request will live in the queue(irespective of failed/success of replay).
* @param {Object} [input.callbacks] Callbacks for successfull/ failed
* replay of a request.
* @param {string} [input.queueName] Queue name inside db in which
* requests will be queued.
* @param {BroadcastChannel=} [input.broadcastChannel] BroadcastChannel
* which will be used to publish messages when the request will be queued.
*/
constructor({maxRetentionTime = maxAge, callbacks, queueName,
broadcastChannel, dbName = defaultDBName} = {}) {
if(queueName) {
assert.isType({queueName}, 'string');
}
/**
* Creates an instance of Queue with the given options
*
* @param {Object} [input]
* @param {Number} [input.maxRetentionTime = 5 days] Time for which a queued
* request will live in the queue(irespective of failed/success of replay).
* @param {Object} [input.callbacks] Callbacks for successfull/ failed
* replay of a request.
* @param {string} [input.queueName] Queue name inside db in which
* requests will be queued.
* @param {BroadcastChannel=} [input.broadcastChannel] BroadcastChannel
* which will be used to publish messages when the request will be queued.
*/
constructor({maxRetentionTime = maxAge, callbacks, queueName,
broadcastChannel, dbName = defaultDBName} = {}) {
if(queueName) {
assert.isType({queueName}, 'string');
}
if(maxRetentionTime) {
assert.isType({maxRetentionTime}, 'number');
}
if(maxRetentionTime) {
assert.isType({maxRetentionTime}, 'number');
}
if(broadcastChannel) {
assert.isInstance({broadcastChannel}, BroadcastChannel);
}
if(broadcastChannel) {
assert.isInstance({broadcastChannel}, BroadcastChannel);
}
assert.isType({dbName}, 'string');
assert.isType({dbName}, 'string');
this._dbName = dbName;
this._queue = new RequestQueue({
config: {
maxAge: maxRetentionTime,
},
queueName,
idbQDb: new IDBHelper(this._dbName, 1, 'QueueStore'),
broadcastChannel,
});
this._requestManager = new RequestManager({callbacks,
queue: this._queue});
this._dbName = dbName;
this._queue = new RequestQueue({
config: {
maxAge: maxRetentionTime,
},
queueName,
idbQDb: new IDBHelper(this._dbName, 1, 'QueueStore'),
broadcastChannel,
});
this._requestManager = new RequestManager({callbacks,
queue: this._queue});
this.cleanupQueue();
}
this.cleanupQueue();
}
/**
* clean up the queue, deleting all the tasks whose maxAge has expired
*
* @memberOf Queue
* @private
* @return {Promise}
*/
cleanupQueue() {
return cleanupQueue(this._dbName);
}
/**
* clean up the queue, deleting all the tasks whose maxAge has expired
*
* @memberOf Queue
* @private
* @return {Promise}
*/
cleanupQueue() {
return cleanupQueue(this._dbName);
}
/**
* This function pushes a given request into the IndexedDb Queue
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
pushIntoQueue({request}) {
assert.isInstance({request}, Request);
return this._queue.push({request});
}
/**
* This function pushes a given request into the IndexedDb Queue.
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
pushIntoQueue({request}) {
assert.isInstance({request}, Request);
return this._queue.push({request});
}
/**
* Replays all the requests in the queue, this can be used for custom timing
* of replaying requests may be in an environment where sync event is not
* supported.
* @return {Promise} A listener for when the requests have been replayed.
*/
replayRequests() {
return this._requestManager.replayRequests();
}
/**
* Replays all the requests in the queue, this can be used for custom timing
* of replaying requests may be in an environment where sync event is not
* supported.
* @return {Promise} A listener for when the requests have been replayed.
*/
replayRequests() {
return this._requestManager.replayRequests();
}
/**
* sets the dbName, which is used to store the queue and requests
* defaults to bgQueueSyncDB
* @param {String} id
* @param {String} dbName
* @return {Object} response Fetched response of the request.
*/
getResponse({id}) {
return getResponse({
id,
dbName: this._dbName,
});
}
* Sets the dbName, which is used to store the queue and requests
* defaults to bgQueueSyncDB.
* @param {String} id The ID of the request.
* @return {Object} Fetched response of the request.
*/
getResponse({id}) {
return getResponse({
id,
dbName: this._dbName,
});
}
}

@@ -1482,0 +1482,0 @@

@@ -480,16 +480,16 @@ /*

function broadcastMessage({broadcastChannel, type, url}) {
if(!broadcastChannel)
return;
if(!broadcastChannel)
return;
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
}

@@ -496,0 +496,0 @@

@@ -332,3 +332,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -335,0 +334,0 @@ }());

@@ -332,3 +332,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -509,21 +508,21 @@ }());

async function getQueueableRequest({request, config}) {
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
}

@@ -539,13 +538,13 @@

async function getFetchableRequest({idbRequestObject}) {
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
}

@@ -563,28 +562,28 @@

async function cleanupQueue(dbName) {
let db = new IDBHelper(dbName, 1, 'QueueStore');
let queueObj = await db.get(allQueuesPlaceholder);
let db = new IDBHelper(dbName, 1, 'QueueStore');
let queueObj = await db.get(allQueuesPlaceholder);
if(!queueObj) {
return null;
}
if(!queueObj) {
return null;
}
await Promise.all(queueObj.map(async (queueName)=>{
const requestQueues = await db.get(queueName);
let itemsToKeep = [];
let deletionPromises = [];
await Promise.all(requestQueues.map( async (hash) => {
const requestData = await db.get(hash);
if (requestData && requestData.metadata
&& requestData.metadata.creationTimestamp + requestData.config.maxAge
<= Date.now()) {
// Delete items that are too old.
deletionPromises.push(db.delete(hash));
} else {
// Keep elements whose definition exists in idb.
itemsToKeep.push(hash);
}
}));
await Promise.all(deletionPromises);
db.put(queueName, itemsToKeep);
}));
await Promise.all(queueObj.map(async (queueName)=>{
const requestQueues = await db.get(queueName);
let itemsToKeep = [];
let deletionPromises = [];
await Promise.all(requestQueues.map( async (hash) => {
const requestData = await db.get(hash);
if (requestData && requestData.metadata
&& requestData.metadata.creationTimestamp + requestData.config.maxAge
<= Date.now()) {
// Delete items that are too old.
deletionPromises.push(db.delete(hash));
} else {
// Keep elements whose definition exists in idb.
itemsToKeep.push(hash);
}
}));
await Promise.all(deletionPromises);
db.put(queueName, itemsToKeep);
}));
}

@@ -591,0 +590,0 @@

@@ -332,3 +332,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -362,9 +361,9 @@ }());

async function putResponse({hash, idbObject, response, idbQDb}) {
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
}

@@ -383,13 +382,13 @@

async function getFetchableRequest({idbRequestObject}) {
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
let reqObject = {
mode: idbRequestObject.mode,
method: idbRequestObject.method,
redirect: idbRequestObject.redirect,
headers: new Headers(JSON.parse(idbRequestObject.headers)),
credentials: idbRequestObject.credentials,
};
if(idbRequestObject.body) {
reqObject.body = idbRequestObject.body;
}
return new Request(idbRequestObject.url, reqObject);
}

@@ -405,78 +404,78 @@

class RequestManager {
/**
* Initializes the request manager
* stores the callbacks object, maintains config and
* attaches event handler
* @param {Object=} config
*
* @memberOf RequestManager
* @private
*/
constructor({callbacks, queue}) {
this._globalCallbacks = callbacks || {};
this._queue = queue;
this.attachSyncHandler();
}
/**
* Initializes the request manager
* stores the callbacks object, maintains config and
* attaches event handler
* @param {Object=} config
*
* @memberOf RequestManager
* @private
*/
constructor({callbacks, queue}) {
this._globalCallbacks = callbacks || {};
this._queue = queue;
this.attachSyncHandler();
}
/**
* attaches sync handler to replay requests when
* sync event is fired
*
* @memberOf RequestManager
* @private
*/
attachSyncHandler() {
self.addEventListener('sync', (event) => {
if(event.tag === tagNamePrefix + this._queue.queueName
|| event.tag === replayAllQueuesTag) {
event.waitUntil(this.replayRequests());
}
});
}
/**
* attaches sync handler to replay requests when
* sync event is fired
*
* @memberOf RequestManager
* @private
*/
attachSyncHandler() {
self.addEventListener('sync', (event) => {
if(event.tag === tagNamePrefix + this._queue.queueName
|| event.tag === replayAllQueuesTag) {
event.waitUntil(this.replayRequests());
}
});
}
/**
* function to start playing requests
* in sequence
* @return {void}
*
* @memberOf RequestManager
* @private
*/
replayRequests() {
return this._queue.queue.reduce((promise, hash) => {
return promise
.then(async (item) => {
const reqData = await this._queue.getRequestFromQueue({hash});
if(reqData.response) {
// check if request is not played already
return;
}
/**
* function to start playing requests
* in sequence
* @return {void}
*
* @memberOf RequestManager
* @private
*/
replayRequests() {
return this._queue.queue.reduce((promise, hash) => {
return promise
.then(async (item) => {
const reqData = await this._queue.getRequestFromQueue({hash});
if(reqData.response) {
// check if request is not played already
return;
}
const request = await getFetchableRequest({
idbRequestObject: reqData.request,
});
const request = await getFetchableRequest({
idbRequestObject: reqData.request,
});
return fetch(request)
.then((response)=>{
if(!response.ok) {
return Promise.resolve();
} else {
// not blocking on putResponse.
putResponse({
hash,
idbObject: reqData,
response: response.clone(),
idbQDb: this._queue.idbQDb,
});
this._globalCallbacks.onResponse
&& this._globalCallbacks.onResponse(hash, response);
}
})
.catch((err)=>{
this._globalCallbacks.onRetryFailure
&& this._globalCallbacks.onRetryFailure(hash, err);
});
});
}, Promise.resolve());
}
return fetch(request)
.then((response)=>{
if(!response.ok) {
return Promise.resolve();
} else {
// not blocking on putResponse.
putResponse({
hash,
idbObject: reqData,
response: response.clone(),
idbQDb: this._queue.idbQDb,
});
this._globalCallbacks.onResponse
&& this._globalCallbacks.onResponse(hash, response);
}
})
.catch((err)=>{
this._globalCallbacks.onRetryFailure
&& this._globalCallbacks.onRetryFailure(hash, err);
});
});
}, Promise.resolve());
}
}

@@ -483,0 +482,0 @@

@@ -338,3 +338,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -381,21 +380,21 @@ }());

async function getQueueableRequest({request, config}) {
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
let requestObject={
config,
metadata: {
creationTimestamp: Date.now(),
},
};
requestObject.request = {
url: request.url,
headers: JSON.stringify([...request.headers]),
mode: request.mode,
method: request.method,
redirect: request.redirect,
credentials: request.credentials,
};
const requestBody = await request.text();
if (requestBody.length > 0) {
requestObject.request.body = requestBody;
}
return requestObject;
}

@@ -849,16 +848,16 @@

function broadcastMessage({broadcastChannel, type, url}) {
if(!broadcastChannel)
return;
if(!broadcastChannel)
return;
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
assert.isInstance({broadcastChannel}, BroadcastChannel);
assert.isType({type}, 'string');
assert.isType({url}, 'string');
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
broadcastChannel.postMessage({
type: type,
meta: broadcastMeta,
payload: {
url: url,
},
});
}

@@ -876,162 +875,162 @@

class RequestQueue {
/**
* Creates an instance of RequestQueue.
*
* @param {Object} config
*
* @memberOf RequestQueue
* @private
*/
constructor({
config,
queueName = defaultQueueName + '_' + _queueCounter++,
idbQDb,
broadcastChannel,
}) {
this._isQueueNameAddedToAllQueue = false;
this._queueName = queueName;
this._config = config;
this._idbQDb = idbQDb;
this._broadcastChannel = broadcastChannel;
this._queue = [];
this.initQueue();
}
/**
* Creates an instance of RequestQueue.
*
* @param {Object} config
*
* @memberOf RequestQueue
* @private
*/
constructor({
config,
queueName = defaultQueueName + '_' + _queueCounter++,
idbQDb,
broadcastChannel,
}) {
this._isQueueNameAddedToAllQueue = false;
this._queueName = queueName;
this._config = config;
this._idbQDb = idbQDb;
this._broadcastChannel = broadcastChannel;
this._queue = [];
this.initQueue();
}
/**
* initializes the queue from the IDB store
*
* @memberOf RequestQueue
* @private
*/
async initQueue() {
const idbQueue = await this._idbQDb.get(this._queueName);
this._queue.concat(idbQueue);
}
/**
* initializes the queue from the IDB store
*
* @memberOf RequestQueue
* @private
*/
async initQueue() {
const idbQueue = await this._idbQDb.get(this._queueName);
this._queue.concat(idbQueue);
}
/**
* adds the current queueName to all queue array
*
* @memberOf RequestQueue
* @private
*/
async addQueueNameToAllQueues() {
if(!this._isQueueNameAddedToAllQueue) {
let allQueues = await this._idbQDb.get(allQueuesPlaceholder);
allQueues = allQueues || [];
if(!allQueues.includes(this._queueName)) {
allQueues.push(this._queueName);
}
this._idbQDb.put(allQueuesPlaceholder, allQueues);
this._isQueueNameAddedToAllQueue = true;
}
}
/**
* adds the current queueName to all queue array
*
* @memberOf RequestQueue
* @private
*/
async addQueueNameToAllQueues() {
if(!this._isQueueNameAddedToAllQueue) {
let allQueues = await this._idbQDb.get(allQueuesPlaceholder);
allQueues = allQueues || [];
if(!allQueues.includes(this._queueName)) {
allQueues.push(this._queueName);
}
this._idbQDb.put(allQueuesPlaceholder, allQueues);
this._isQueueNameAddedToAllQueue = true;
}
}
/**
* saves the logical queue to IDB
*
* @memberOf RequestQueue
* @private
*/
async saveQueue() {
await this._idbQDb.put(this._queueName, this._queue);
}
/**
* saves the logical queue to IDB
*
* @memberOf RequestQueue
* @private
*/
async saveQueue() {
await this._idbQDb.put(this._queueName, this._queue);
}
/**
* push any request to background sync queue which would be played later
* preferably when network comes back
*
* @param {Request} request request object to be queued by this
*
* @memberOf Queue
* @private
*/
async push({request}) {
assert.isInstance({request}, Request);
/**
* push any request to background sync queue which would be played later
* preferably when network comes back
*
* @param {Request} request request object to be queued by this
*
* @memberOf Queue
* @private
*/
async push({request}) {
assert.isInstance({request}, Request);
const hash = `${request.url}!${Date.now()}!${_requestCounter++}`;
const queuableRequest =
await getQueueableRequest({
request,
config: this._config,
});
try{
this._queue.push(hash);
const hash = `${request.url}!${Date.now()}!${_requestCounter++}`;
const queuableRequest =
await getQueueableRequest({
request,
config: this._config,
});
try{
this._queue.push(hash);
// add to queue
this.saveQueue();
this._idbQDb.put(hash, queuableRequest);
await this.addQueueNameToAllQueues();
// register sync
self.registration &&
self.registration.sync.register(tagNamePrefix + this._queueName);
// add to queue
this.saveQueue();
this._idbQDb.put(hash, queuableRequest);
await this.addQueueNameToAllQueues();
// register sync
self.registration &&
self.registration.sync.register(tagNamePrefix + this._queueName);
// broadcast the success of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageAddedType,
id: hash,
url: request.url,
});
} catch(e) {
// broadcast the failure of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageFailedType,
id: hash,
url: request.url,
});
}
}
// broadcast the success of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageAddedType,
id: hash,
url: request.url,
});
} catch(e) {
// broadcast the failure of request added to the queue
broadcastMessage({
broadcastChannel: this._broadcastChannel,
type: broadcastMessageFailedType,
id: hash,
url: request.url,
});
}
}
/**
* get the Request from the queue at a particular index
*
* @param {string} hash hash of the request at the given index
* @return {Request} request object corresponding to given hash
* @memberOf Queue
* @private
*/
async getRequestFromQueue({hash}) {
assert.isType({hash}, 'string');
/**
* get the Request from the queue at a particular index
*
* @param {string} hash hash of the request at the given index
* @return {Request} request object corresponding to given hash
* @memberOf Queue
* @private
*/
async getRequestFromQueue({hash}) {
assert.isType({hash}, 'string');
if(this._queue.includes(hash)) {
return await this._idbQDb.get(hash);
}
}
if(this._queue.includes(hash)) {
return await this._idbQDb.get(hash);
}
}
/**
* returns the instance of queue.
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queue() {
return Object.assign([], this._queue);
}
/**
* returns the instance of queue.
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queue() {
return Object.assign([], this._queue);
}
/**
* returns the name of the current queue
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queueName() {
return this._queueName;
}
/**
* returns the name of the current queue
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get queueName() {
return this._queueName;
}
/**
* returns the instance of IDBStore
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get idbQDb() {
return this._idbQDb;
}
/**
* returns the instance of IDBStore
*
* @readonly
*
* @memberOf RequestQueue
* @private
*/
get idbQDb() {
return this._idbQDb;
}
}

@@ -1038,0 +1037,0 @@

@@ -332,3 +332,2 @@ /*

module.exports = exp;
module.exports.default = module.exports;
}

@@ -501,9 +500,9 @@ }());

async function putResponse({hash, idbObject, response, idbQDb}) {
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
}

@@ -515,3 +514,3 @@

* @memberof module:workbox-background-sync
*
* @private
* @param {String} id The ID of the request given back by the broaadcast

@@ -522,9 +521,9 @@ * channel

async function getResponse({id, dbName}) {
const _idbQHelper = new IDBHelper(dbName, 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object && object.response) {
return object.response;
} else {
return null;
}
const _idbQHelper = new IDBHelper(dbName, 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object && object.response) {
return object.response;
} else {
return null;
}
}

@@ -531,0 +530,0 @@

{
"name": "workbox-background-sync",
"version": "0.0.2",
"description": "Queues failed requests and uses the Background Sync API to replay those requests at a later time when the network state has changed.",
"version": "0.0.3",
"description": "Queues failed requests and uses the Background Sync API to replay them when the network is available",
"keywords": [

@@ -22,6 +22,6 @@ "workbox",

"repository": "googlechrome/sw-helpers",
"bugs": "https://github.com/googlechrome/sw-helpers/issues",
"homepage": "https://github.com/GoogleChrome/sw-helpers/tree/master/packages/workbox-background-sync",
"main": "build/importScripts/workbox-background-sync.prod.v0.0.2.js",
"module": "build/modules/workbox-background-sync.prod.v0.0.2.mjs"
"bugs": "https://github.com/GoogleChrome/workbox/issues",
"homepage": "https://github.com/GoogleChrome/workbox/tree/master/packages/workbox-background-sync",
"main": "build/importScripts/workbox-background-sync.prod.v0.0.3.js",
"module": "build/modules/workbox-background-sync.prod.v0.0.3.mjs"
}

@@ -1,18 +0,28 @@

<!-- DO NOT EDIT. This page is autogenerated. -->
<!-- To make changes, edit templates/Project-README.hbs, not this file. -->
# workbox-background-sync
Queues failed requests and uses the Background Sync API to replay those requests at a later time when the network state has changed.
Queues failed requests and uses the
[Background Sync API](https://developers.google.com/web/updates/2015/12/background-sync)
to replay them when the network is available.
## Installation
`npm install --save-dev workbox-background-sync`
```sh
npm install --save-dev workbox-background-sync
```
## Demo
## Documentation
Browse sample source code in the [demo directory](https://github.com/GoogleChrome/workbox/tree/master/packages/workbox-background-sync/demo).
Read more at this module's [documentation page](https://workboxjs.org/reference-docs/latest/module-workbox-background-sync.html).
## Reference Docs
## Sample Code and Examples
You can find [documentation for this module here](https://googlechrome.github.io/workbox/reference-docs/stable/latest/module-workbox-background-sync.html#main).
View the
[sample code](https://github.com/GoogleChrome/workbox/tree/master/packages/workbox-background-sync/demo)
to see this module put to use.
# What's Workbox?
This module is a part of Workbox, which is a collection of JavaScript libraries
for [Progressive Web Apps](https://developers.google.com/web/progressive-web-apps/).
Visit https://workboxjs.org/ to learn more about what Workbox can do for you.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc