workbox-background-sync
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
952468
8795
29
7