1 | this.workbox = this.workbox || {};
|
2 | this.workbox.backgroundSync = (function (DBWrapper_mjs,WorkboxError_mjs,logger_mjs,assert_mjs,getFriendlyURL_mjs) {
|
3 | ;
|
4 |
|
5 | try {
|
6 | self.workbox.v['workbox:background-sync:3.6.3'] = 1;
|
7 | } catch (e) {} // eslint-disable-line
|
8 |
|
9 | /*
|
10 | Copyright 2017 Google Inc. All Rights Reserved.
|
11 | Licensed under the Apache License, Version 2.0 (the "License");
|
12 | you may not use this file except in compliance with the License.
|
13 | You may obtain a copy of the License at
|
14 |
|
15 | http://www.apache.org/licenses/LICENSE-2.0
|
16 |
|
17 | Unless required by applicable law or agreed to in writing, software
|
18 | distributed under the License is distributed on an "AS IS" BASIS,
|
19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
20 | See the License for the specific language governing permissions and
|
21 | limitations under the License.
|
22 | */
|
23 |
|
24 | const serializableProperties = ['method', 'referrer', 'referrerPolicy', 'mode', 'credentials', 'cache', 'redirect', 'integrity', 'keepalive'];
|
25 |
|
26 | /**
|
27 | * A class to make it easier to serialize and de-serialize requests so they
|
28 | * can be stored in IndexedDB.
|
29 | *
|
30 | * @private
|
31 | */
|
32 | class StorableRequest {
|
33 | /**
|
34 | * Converts a Request object to a plain object that can be structured
|
35 | * cloned or JSON-stringified.
|
36 | *
|
37 | * @param {Request} request
|
38 | * @return {Promise<StorableRequest>}
|
39 | *
|
40 | * @private
|
41 | */
|
42 | static fromRequest(request) {
|
43 | return babelHelpers.asyncToGenerator(function* () {
|
44 | const requestInit = { headers: {} };
|
45 |
|
46 | // Set the body if present.
|
47 | if (request.method !== 'GET') {
|
48 | // Use blob to support non-text request bodies,
|
49 | // and clone first in case the caller still needs the request.
|
50 | requestInit.body = yield request.clone().blob();
|
51 | }
|
52 |
|
53 | // Convert the headers from an iterable to an object.
|
54 | for (const [key, value] of request.headers.entries()) {
|
55 | requestInit.headers[key] = value;
|
56 | }
|
57 |
|
58 | // Add all other serializable request properties
|
59 | for (const prop of serializableProperties) {
|
60 | if (request[prop] !== undefined) {
|
61 | requestInit[prop] = request[prop];
|
62 | }
|
63 | }
|
64 |
|
65 | return new StorableRequest({ url: request.url, requestInit });
|
66 | })();
|
67 | }
|
68 |
|
69 | /**
|
70 | * Accepts a URL and RequestInit dictionary that can be used to create a
|
71 | * new Request object. A timestamp is also generated so consumers can
|
72 | * reference when the object was created.
|
73 | *
|
74 | * @param {Object} param1
|
75 | * @param {string} param1.url
|
76 | * @param {Object} param1.requestInit
|
77 | * See: https://fetch.spec.whatwg.org/#requestinit
|
78 | * @param {number} param1.timestamp The time the request was created,
|
79 | * defaulting to the current time if not specified.
|
80 | *
|
81 | * @private
|
82 | */
|
83 | constructor({ url, requestInit, timestamp = Date.now() }) {
|
84 | this.url = url;
|
85 | this.requestInit = requestInit;
|
86 |
|
87 | // "Private"
|
88 | this._timestamp = timestamp;
|
89 | }
|
90 |
|
91 | /**
|
92 | * Gets the private _timestamp property.
|
93 | *
|
94 | * @return {number}
|
95 | *
|
96 | * @private
|
97 | */
|
98 | get timestamp() {
|
99 | return this._timestamp;
|
100 | }
|
101 |
|
102 | /**
|
103 | * Coverts this instance to a plain Object.
|
104 | *
|
105 | * @return {Object}
|
106 | *
|
107 | * @private
|
108 | */
|
109 | toObject() {
|
110 | return {
|
111 | url: this.url,
|
112 | timestamp: this.timestamp,
|
113 | requestInit: this.requestInit
|
114 | };
|
115 | }
|
116 |
|
117 | /**
|
118 | * Converts this instance to a Request.
|
119 | *
|
120 | * @return {Request}
|
121 | *
|
122 | * @private
|
123 | */
|
124 | toRequest() {
|
125 | return new Request(this.url, this.requestInit);
|
126 | }
|
127 |
|
128 | /**
|
129 | * Creates and returns a deep clone of the instance.
|
130 | *
|
131 | * @return {StorableRequest}
|
132 | *
|
133 | * @private
|
134 | */
|
135 | clone() {
|
136 | const requestInit = Object.assign({}, this.requestInit);
|
137 | requestInit.headers = Object.assign({}, this.requestInit.headers);
|
138 | if (this.requestInit.body) {
|
139 | requestInit.body = this.requestInit.body.slice();
|
140 | }
|
141 |
|
142 | return new StorableRequest({
|
143 | url: this.url,
|
144 | timestamp: this.timestamp,
|
145 | requestInit
|
146 | });
|
147 | }
|
148 | }
|
149 |
|
150 | /*
|
151 | Copyright 2017 Google Inc. All Rights Reserved.
|
152 | Licensed under the Apache License, Version 2.0 (the "License");
|
153 | you may not use this file except in compliance with the License.
|
154 | You may obtain a copy of the License at
|
155 |
|
156 | http://www.apache.org/licenses/LICENSE-2.0
|
157 |
|
158 | Unless required by applicable law or agreed to in writing, software
|
159 | distributed under the License is distributed on an "AS IS" BASIS,
|
160 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
161 | See the License for the specific language governing permissions and
|
162 | limitations under the License.
|
163 | */
|
164 |
|
165 | const DB_NAME = 'workbox-background-sync';
|
166 | const OBJECT_STORE_NAME = 'requests';
|
167 | const INDEXED_PROP = 'queueName';
|
168 | const TAG_PREFIX = 'workbox-background-sync';
|
169 | const MAX_RETENTION_TIME = 60 * 24 * 7; // 7 days in minutes
|
170 |
|
171 | /*
|
172 | Copyright 2017 Google Inc. All Rights Reserved.
|
173 | Licensed under the Apache License, Version 2.0 (the "License");
|
174 | you may not use this file except in compliance with the License.
|
175 | You may obtain a copy of the License at
|
176 |
|
177 | http://www.apache.org/licenses/LICENSE-2.0
|
178 |
|
179 | Unless required by applicable law or agreed to in writing, software
|
180 | distributed under the License is distributed on an "AS IS" BASIS,
|
181 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
182 | See the License for the specific language governing permissions and
|
183 | limitations under the License.
|
184 | */
|
185 |
|
186 | /**
|
187 | * A class to manage storing requests from a Queue in IndexedbDB,
|
188 | * indexed by their queue name for easier access.
|
189 | *
|
190 | * @private
|
191 | */
|
192 | class QueueStore {
|
193 | /**
|
194 | * Associates this instance with a Queue instance, so entries added can be
|
195 | * identified by their queue name.
|
196 | *
|
197 | * @param {Queue} queue
|
198 | *
|
199 | * @private
|
200 | */
|
201 | constructor(queue) {
|
202 | this._queue = queue;
|
203 | this._db = new DBWrapper_mjs.DBWrapper(DB_NAME, 1, {
|
204 | onupgradeneeded: evt => evt.target.result.createObjectStore(OBJECT_STORE_NAME, { autoIncrement: true }).createIndex(INDEXED_PROP, INDEXED_PROP, { unique: false })
|
205 | });
|
206 | }
|
207 |
|
208 | /**
|
209 | * Takes a StorableRequest instance, converts it to an object and adds it
|
210 | * as an entry in the object store.
|
211 | *
|
212 | * @param {StorableRequest} storableRequest
|
213 | *
|
214 | * @private
|
215 | */
|
216 | addEntry(storableRequest) {
|
217 | var _this = this;
|
218 |
|
219 | return babelHelpers.asyncToGenerator(function* () {
|
220 | yield _this._db.add(OBJECT_STORE_NAME, {
|
221 | queueName: _this._queue.name,
|
222 | storableRequest: storableRequest.toObject()
|
223 | });
|
224 | })();
|
225 | }
|
226 |
|
227 | /**
|
228 | * Gets the oldest entry in the object store, removes it, and returns the
|
229 | * value as a StorableRequest instance. If no entry exists, it returns
|
230 | * undefined.
|
231 | *
|
232 | * @return {StorableRequest|undefined}
|
233 | *
|
234 | * @private
|
235 | */
|
236 | getAndRemoveOldestEntry() {
|
237 | var _this2 = this;
|
238 |
|
239 | return babelHelpers.asyncToGenerator(function* () {
|
240 | const [entry] = yield _this2._db.getAllMatching(OBJECT_STORE_NAME, {
|
241 | index: INDEXED_PROP,
|
242 | query: IDBKeyRange.only(_this2._queue.name),
|
243 | count: 1,
|
244 | includeKeys: true
|
245 | });
|
246 |
|
247 | if (entry) {
|
248 | yield _this2._db.delete(OBJECT_STORE_NAME, entry.primaryKey);
|
249 | return new StorableRequest(entry.value.storableRequest);
|
250 | }
|
251 | })();
|
252 | }
|
253 | }
|
254 |
|
255 | /*
|
256 | Copyright 2017 Google Inc. All Rights Reserved.
|
257 | Licensed under the Apache License, Version 2.0 (the "License");
|
258 | you may not use this file except in compliance with the License.
|
259 | You may obtain a copy of the License at
|
260 |
|
261 | http://www.apache.org/licenses/LICENSE-2.0
|
262 |
|
263 | Unless required by applicable law or agreed to in writing, software
|
264 | distributed under the License is distributed on an "AS IS" BASIS,
|
265 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
266 | See the License for the specific language governing permissions and
|
267 | limitations under the License.
|
268 | */
|
269 |
|
270 | const queueNames = new Set();
|
271 |
|
272 | /**
|
273 | * A class to manage storing failed requests in IndexedDB and retrying them
|
274 | * later. All parts of the storing and replaying process are observable via
|
275 | * callbacks.
|
276 | *
|
277 | * @memberof workbox.backgroundSync
|
278 | */
|
279 | class Queue {
|
280 | /**
|
281 | * Creates an instance of Queue with the given options
|
282 | *
|
283 | * @param {string} name The unique name for this queue. This name must be
|
284 | * unique as it's used to register sync events and store requests
|
285 | * in IndexedDB specific to this instance. An error will be thrown if
|
286 | * a duplicate name is detected.
|
287 | * @param {Object} [options]
|
288 | * @param {Object} [options.callbacks] Callbacks to observe the lifecycle of
|
289 | * queued requests. Use these to respond to or modify the requests
|
290 | * during the replay process.
|
291 | * @param {function(StorableRequest):undefined}
|
292 | * [options.callbacks.requestWillEnqueue]
|
293 | * Invoked immediately before the request is stored to IndexedDB. Use
|
294 | * this callback to modify request data at store time.
|
295 | * @param {function(StorableRequest):undefined}
|
296 | * [options.callbacks.requestWillReplay]
|
297 | * Invoked immediately before the request is re-fetched. Use this
|
298 | * callback to modify request data at fetch time.
|
299 | * @param {function(Array<StorableRequest>):undefined}
|
300 | * [options.callbacks.queueDidReplay]
|
301 | * Invoked after all requests in the queue have successfully replayed.
|
302 | * @param {number} [options.maxRetentionTime = 7 days] The amount of time (in
|
303 | * minutes) a request may be retried. After this amount of time has
|
304 | * passed, the request will be deleted from the queue.
|
305 | */
|
306 | constructor(name, {
|
307 | callbacks = {},
|
308 | maxRetentionTime = MAX_RETENTION_TIME
|
309 | } = {}) {
|
310 | // Ensure the store name is not already being used
|
311 | if (queueNames.has(name)) {
|
312 | throw new WorkboxError_mjs.WorkboxError('duplicate-queue-name', { name });
|
313 | } else {
|
314 | queueNames.add(name);
|
315 | }
|
316 |
|
317 | this._name = name;
|
318 | this._callbacks = callbacks;
|
319 | this._maxRetentionTime = maxRetentionTime;
|
320 | this._queueStore = new QueueStore(this);
|
321 |
|
322 | this._addSyncListener();
|
323 | }
|
324 |
|
325 | /**
|
326 | * @return {string}
|
327 | */
|
328 | get name() {
|
329 | return this._name;
|
330 | }
|
331 |
|
332 | /**
|
333 | * Stores the passed request into IndexedDB. The database used is
|
334 | * `workbox-background-sync` and the object store name is the same as
|
335 | * the name this instance was created with (to guarantee it's unique).
|
336 | *
|
337 | * @param {Request} request The request object to store.
|
338 | */
|
339 | addRequest(request) {
|
340 | var _this = this;
|
341 |
|
342 | return babelHelpers.asyncToGenerator(function* () {
|
343 | {
|
344 | assert_mjs.assert.isInstance(request, Request, {
|
345 | moduleName: 'workbox-background-sync',
|
346 | className: 'Queue',
|
347 | funcName: 'addRequest',
|
348 | paramName: 'request'
|
349 | });
|
350 | }
|
351 |
|
352 | const storableRequest = yield StorableRequest.fromRequest(request.clone());
|
353 | yield _this._runCallback('requestWillEnqueue', storableRequest);
|
354 | yield _this._queueStore.addEntry(storableRequest);
|
355 | yield _this._registerSync();
|
356 | {
|
357 | logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(storableRequest.url)}' has been
|
358 | added to background sync queue '${_this._name}'.`);
|
359 | }
|
360 | })();
|
361 | }
|
362 |
|
363 | /**
|
364 | * Retrieves all stored requests in IndexedDB and retries them. If the
|
365 | * queue contained requests that were successfully replayed, the
|
366 | * `queueDidReplay` callback is invoked (which implies the queue is
|
367 | * now empty). If any of the requests fail, a new sync registration is
|
368 | * created to retry again later.
|
369 | */
|
370 | replayRequests() {
|
371 | var _this2 = this;
|
372 |
|
373 | return babelHelpers.asyncToGenerator(function* () {
|
374 | const now = Date.now();
|
375 | const replayedRequests = [];
|
376 | const failedRequests = [];
|
377 |
|
378 | let storableRequest;
|
379 | while (storableRequest = yield _this2._queueStore.getAndRemoveOldestEntry()) {
|
380 | // Make a copy so the unmodified request can be stored
|
381 | // in the event of a replay failure.
|
382 | const storableRequestClone = storableRequest.clone();
|
383 |
|
384 | // Ignore requests older than maxRetentionTime.
|
385 | const maxRetentionTimeInMs = _this2._maxRetentionTime * 60 * 1000;
|
386 | if (now - storableRequest.timestamp > maxRetentionTimeInMs) {
|
387 | continue;
|
388 | }
|
389 |
|
390 | yield _this2._runCallback('requestWillReplay', storableRequest);
|
391 |
|
392 | const replay = { request: storableRequest.toRequest() };
|
393 |
|
394 | try {
|
395 | // Clone the request before fetching so callbacks get an unused one.
|
396 | replay.response = yield fetch(replay.request.clone());
|
397 | {
|
398 | logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(storableRequest.url)}'
|
399 | has been replayed`);
|
400 | }
|
401 | } catch (err) {
|
402 | {
|
403 | logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(storableRequest.url)}'
|
404 | failed to replay`);
|
405 | }
|
406 | replay.error = err;
|
407 | failedRequests.push(storableRequestClone);
|
408 | }
|
409 |
|
410 | replayedRequests.push(replay);
|
411 | }
|
412 |
|
413 | yield _this2._runCallback('queueDidReplay', replayedRequests);
|
414 |
|
415 | // If any requests failed, put the failed requests back in the queue
|
416 | // and rethrow the failed requests count.
|
417 | if (failedRequests.length) {
|
418 | yield Promise.all(failedRequests.map(function (storableRequest) {
|
419 | return _this2._queueStore.addEntry(storableRequest);
|
420 | }));
|
421 |
|
422 | throw new WorkboxError_mjs.WorkboxError('queue-replay-failed', { name: _this2._name, count: failedRequests.length });
|
423 | }
|
424 | })();
|
425 | }
|
426 |
|
427 | /**
|
428 | * Runs the passed callback if it exists.
|
429 | *
|
430 | * @private
|
431 | * @param {string} name The name of the callback on this._callbacks.
|
432 | * @param {...*} args The arguments to invoke the callback with.
|
433 | */
|
434 | _runCallback(name, ...args) {
|
435 | var _this3 = this;
|
436 |
|
437 | return babelHelpers.asyncToGenerator(function* () {
|
438 | if (typeof _this3._callbacks[name] === 'function') {
|
439 | yield _this3._callbacks[name].apply(null, args);
|
440 | }
|
441 | })();
|
442 | }
|
443 |
|
444 | /**
|
445 | * In sync-supporting browsers, this adds a listener for the sync event.
|
446 | * In non-sync-supporting browsers, this will retry the queue on service
|
447 | * worker startup.
|
448 | *
|
449 | * @private
|
450 | */
|
451 | _addSyncListener() {
|
452 | if ('sync' in registration) {
|
453 | self.addEventListener('sync', event => {
|
454 | if (event.tag === `${TAG_PREFIX}:${this._name}`) {
|
455 | {
|
456 | logger_mjs.logger.log(`Background sync for tag '${event.tag}'
|
457 | has been received, starting replay now`);
|
458 | }
|
459 | event.waitUntil(this.replayRequests());
|
460 | }
|
461 | });
|
462 | } else {
|
463 | {
|
464 | logger_mjs.logger.log(`Background sync replaying without background sync event`);
|
465 | }
|
466 | // If the browser doesn't support background sync, retry
|
467 | // every time the service worker starts up as a fallback.
|
468 | this.replayRequests();
|
469 | }
|
470 | }
|
471 |
|
472 | /**
|
473 | * Registers a sync event with a tag unique to this instance.
|
474 | *
|
475 | * @private
|
476 | */
|
477 | _registerSync() {
|
478 | var _this4 = this;
|
479 |
|
480 | return babelHelpers.asyncToGenerator(function* () {
|
481 | if ('sync' in registration) {
|
482 | try {
|
483 | yield registration.sync.register(`${TAG_PREFIX}:${_this4._name}`);
|
484 | } catch (err) {
|
485 | // This means the registration failed for some reason, possibly due to
|
486 | // the user disabling it.
|
487 | {
|
488 | logger_mjs.logger.warn(`Unable to register sync event for '${_this4._name}'.`, err);
|
489 | }
|
490 | }
|
491 | }
|
492 | })();
|
493 | }
|
494 |
|
495 | /**
|
496 | * Returns the set of queue names. This is primarily used to reset the list
|
497 | * of queue names in tests.
|
498 | *
|
499 | * @return {Set}
|
500 | *
|
501 | * @private
|
502 | */
|
503 | static get _queueNames() {
|
504 | return queueNames;
|
505 | }
|
506 | }
|
507 |
|
508 | /*
|
509 | Copyright 2017 Google Inc. All Rights Reserved.
|
510 | Licensed under the Apache License, Version 2.0 (the "License");
|
511 | you may not use this file except in compliance with the License.
|
512 | You may obtain a copy of the License at
|
513 |
|
514 | http://www.apache.org/licenses/LICENSE-2.0
|
515 |
|
516 | Unless required by applicable law or agreed to in writing, software
|
517 | distributed under the License is distributed on an "AS IS" BASIS,
|
518 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
519 | See the License for the specific language governing permissions and
|
520 | limitations under the License.
|
521 | */
|
522 |
|
523 | /**
|
524 | * A class implementing the `fetchDidFail` lifecycle callback. This makes it
|
525 | * easier to add failed requests to a background sync Queue.
|
526 | *
|
527 | * @memberof workbox.backgroundSync
|
528 | */
|
529 | class Plugin {
|
530 | /**
|
531 | * @param {...*} queueArgs Args to forward to the composed Queue instance.
|
532 | * See the [Queue]{@link workbox.backgroundSync.Queue} documentation for
|
533 | * parameter details.
|
534 | */
|
535 | constructor(...queueArgs) {
|
536 | this._queue = new Queue(...queueArgs);
|
537 | this.fetchDidFail = this.fetchDidFail.bind(this);
|
538 | }
|
539 |
|
540 | /**
|
541 | * @param {Object} options
|
542 | * @param {Request} options.request
|
543 | * @private
|
544 | */
|
545 | fetchDidFail({ request }) {
|
546 | var _this = this;
|
547 |
|
548 | return babelHelpers.asyncToGenerator(function* () {
|
549 | yield _this._queue.addRequest(request);
|
550 | })();
|
551 | }
|
552 | }
|
553 |
|
554 | /*
|
555 | Copyright 2017 Google Inc. All Rights Reserved.
|
556 | Licensed under the Apache License, Version 2.0 (the "License");
|
557 | you may not use this file except in compliance with the License.
|
558 | You may obtain a copy of the License at
|
559 |
|
560 | http://www.apache.org/licenses/LICENSE-2.0
|
561 |
|
562 | Unless required by applicable law or agreed to in writing, software
|
563 | distributed under the License is distributed on an "AS IS" BASIS,
|
564 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
565 | See the License for the specific language governing permissions and
|
566 | limitations under the License.
|
567 | */
|
568 |
|
569 | var publicAPI = /*#__PURE__*/Object.freeze({
|
570 | Queue: Queue,
|
571 | Plugin: Plugin
|
572 | });
|
573 |
|
574 | /*
|
575 | Copyright 2017 Google Inc. All Rights Reserved.
|
576 | Licensed under the Apache License, Version 2.0 (the "License");
|
577 | you may not use this file except in compliance with the License.
|
578 | You may obtain a copy of the License at
|
579 |
|
580 | http://www.apache.org/licenses/LICENSE-2.0
|
581 |
|
582 | Unless required by applicable law or agreed to in writing, software
|
583 | distributed under the License is distributed on an "AS IS" BASIS,
|
584 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
585 | See the License for the specific language governing permissions and
|
586 | limitations under the License.
|
587 | */
|
588 |
|
589 | return publicAPI;
|
590 |
|
591 | }(workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private));
|
592 |
|
593 | //# sourceMappingURL=workbox-background-sync.dev.js.map
|