workbox-background-sync.dev.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. this.workbox = this.workbox || {};
  2. this.workbox.backgroundSync = (function (exports, WorkboxError_js, logger_js, assert_js, getFriendlyURL_js, DBWrapper_js) {
  3. 'use strict';
  4. try {
  5. self['workbox:background-sync:5.1.4'] && _();
  6. } catch (e) {}
  7. /*
  8. Copyright 2018 Google LLC
  9. Use of this source code is governed by an MIT-style
  10. license that can be found in the LICENSE file or at
  11. https://opensource.org/licenses/MIT.
  12. */
  13. const DB_VERSION = 3;
  14. const DB_NAME = 'workbox-background-sync';
  15. const OBJECT_STORE_NAME = 'requests';
  16. const INDEXED_PROP = 'queueName';
  17. /**
  18. * A class to manage storing requests from a Queue in IndexedDB,
  19. * indexed by their queue name for easier access.
  20. *
  21. * @private
  22. */
  23. class QueueStore {
  24. /**
  25. * Associates this instance with a Queue instance, so entries added can be
  26. * identified by their queue name.
  27. *
  28. * @param {string} queueName
  29. * @private
  30. */
  31. constructor(queueName) {
  32. this._queueName = queueName;
  33. this._db = new DBWrapper_js.DBWrapper(DB_NAME, DB_VERSION, {
  34. onupgradeneeded: this._upgradeDb
  35. });
  36. }
  37. /**
  38. * Append an entry last in the queue.
  39. *
  40. * @param {Object} entry
  41. * @param {Object} entry.requestData
  42. * @param {number} [entry.timestamp]
  43. * @param {Object} [entry.metadata]
  44. * @private
  45. */
  46. async pushEntry(entry) {
  47. {
  48. assert_js.assert.isType(entry, 'object', {
  49. moduleName: 'workbox-background-sync',
  50. className: 'QueueStore',
  51. funcName: 'pushEntry',
  52. paramName: 'entry'
  53. });
  54. assert_js.assert.isType(entry.requestData, 'object', {
  55. moduleName: 'workbox-background-sync',
  56. className: 'QueueStore',
  57. funcName: 'pushEntry',
  58. paramName: 'entry.requestData'
  59. });
  60. } // Don't specify an ID since one is automatically generated.
  61. delete entry.id;
  62. entry.queueName = this._queueName;
  63. await this._db.add(OBJECT_STORE_NAME, entry);
  64. }
  65. /**
  66. * Prepend an entry first in the queue.
  67. *
  68. * @param {Object} entry
  69. * @param {Object} entry.requestData
  70. * @param {number} [entry.timestamp]
  71. * @param {Object} [entry.metadata]
  72. * @private
  73. */
  74. async unshiftEntry(entry) {
  75. {
  76. assert_js.assert.isType(entry, 'object', {
  77. moduleName: 'workbox-background-sync',
  78. className: 'QueueStore',
  79. funcName: 'unshiftEntry',
  80. paramName: 'entry'
  81. });
  82. assert_js.assert.isType(entry.requestData, 'object', {
  83. moduleName: 'workbox-background-sync',
  84. className: 'QueueStore',
  85. funcName: 'unshiftEntry',
  86. paramName: 'entry.requestData'
  87. });
  88. }
  89. const [firstEntry] = await this._db.getAllMatching(OBJECT_STORE_NAME, {
  90. count: 1
  91. });
  92. if (firstEntry) {
  93. // Pick an ID one less than the lowest ID in the object store.
  94. entry.id = firstEntry.id - 1;
  95. } else {
  96. // Otherwise let the auto-incrementor assign the ID.
  97. delete entry.id;
  98. }
  99. entry.queueName = this._queueName;
  100. await this._db.add(OBJECT_STORE_NAME, entry);
  101. }
  102. /**
  103. * Removes and returns the last entry in the queue matching the `queueName`.
  104. *
  105. * @return {Promise<Object>}
  106. * @private
  107. */
  108. async popEntry() {
  109. return this._removeEntry({
  110. direction: 'prev'
  111. });
  112. }
  113. /**
  114. * Removes and returns the first entry in the queue matching the `queueName`.
  115. *
  116. * @return {Promise<Object>}
  117. * @private
  118. */
  119. async shiftEntry() {
  120. return this._removeEntry({
  121. direction: 'next'
  122. });
  123. }
  124. /**
  125. * Returns all entries in the store matching the `queueName`.
  126. *
  127. * @param {Object} options See {@link module:workbox-background-sync.Queue~getAll}
  128. * @return {Promise<Array<Object>>}
  129. * @private
  130. */
  131. async getAll() {
  132. return await this._db.getAllMatching(OBJECT_STORE_NAME, {
  133. index: INDEXED_PROP,
  134. query: IDBKeyRange.only(this._queueName)
  135. });
  136. }
  137. /**
  138. * Deletes the entry for the given ID.
  139. *
  140. * WARNING: this method does not ensure the deleted enry belongs to this
  141. * queue (i.e. matches the `queueName`). But this limitation is acceptable
  142. * as this class is not publicly exposed. An additional check would make
  143. * this method slower than it needs to be.
  144. *
  145. * @private
  146. * @param {number} id
  147. */
  148. async deleteEntry(id) {
  149. await this._db.delete(OBJECT_STORE_NAME, id);
  150. }
  151. /**
  152. * Removes and returns the first or last entry in the queue (based on the
  153. * `direction` argument) matching the `queueName`.
  154. *
  155. * @return {Promise<Object>}
  156. * @private
  157. */
  158. async _removeEntry({
  159. direction
  160. }) {
  161. const [entry] = await this._db.getAllMatching(OBJECT_STORE_NAME, {
  162. direction,
  163. index: INDEXED_PROP,
  164. query: IDBKeyRange.only(this._queueName),
  165. count: 1
  166. });
  167. if (entry) {
  168. await this.deleteEntry(entry.id);
  169. return entry;
  170. }
  171. }
  172. /**
  173. * Upgrades the database given an `upgradeneeded` event.
  174. *
  175. * @param {Event} event
  176. * @private
  177. */
  178. _upgradeDb(event) {
  179. const db = event.target.result;
  180. if (event.oldVersion > 0 && event.oldVersion < DB_VERSION) {
  181. if (db.objectStoreNames.contains(OBJECT_STORE_NAME)) {
  182. db.deleteObjectStore(OBJECT_STORE_NAME);
  183. }
  184. }
  185. const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
  186. autoIncrement: true,
  187. keyPath: 'id'
  188. });
  189. objStore.createIndex(INDEXED_PROP, INDEXED_PROP, {
  190. unique: false
  191. });
  192. }
  193. }
  194. /*
  195. Copyright 2018 Google LLC
  196. Use of this source code is governed by an MIT-style
  197. license that can be found in the LICENSE file or at
  198. https://opensource.org/licenses/MIT.
  199. */
  200. const serializableProperties = ['method', 'referrer', 'referrerPolicy', 'mode', 'credentials', 'cache', 'redirect', 'integrity', 'keepalive'];
  201. /**
  202. * A class to make it easier to serialize and de-serialize requests so they
  203. * can be stored in IndexedDB.
  204. *
  205. * @private
  206. */
  207. class StorableRequest {
  208. /**
  209. * Accepts an object of request data that can be used to construct a
  210. * `Request` but can also be stored in IndexedDB.
  211. *
  212. * @param {Object} requestData An object of request data that includes the
  213. * `url` plus any relevant properties of
  214. * [requestInit]{@link https://fetch.spec.whatwg.org/#requestinit}.
  215. * @private
  216. */
  217. constructor(requestData) {
  218. {
  219. assert_js.assert.isType(requestData, 'object', {
  220. moduleName: 'workbox-background-sync',
  221. className: 'StorableRequest',
  222. funcName: 'constructor',
  223. paramName: 'requestData'
  224. });
  225. assert_js.assert.isType(requestData.url, 'string', {
  226. moduleName: 'workbox-background-sync',
  227. className: 'StorableRequest',
  228. funcName: 'constructor',
  229. paramName: 'requestData.url'
  230. });
  231. } // If the request's mode is `navigate`, convert it to `same-origin` since
  232. // navigation requests can't be constructed via script.
  233. if (requestData['mode'] === 'navigate') {
  234. requestData['mode'] = 'same-origin';
  235. }
  236. this._requestData = requestData;
  237. }
  238. /**
  239. * Converts a Request object to a plain object that can be structured
  240. * cloned or JSON-stringified.
  241. *
  242. * @param {Request} request
  243. * @return {Promise<StorableRequest>}
  244. *
  245. * @private
  246. */
  247. static async fromRequest(request) {
  248. const requestData = {
  249. url: request.url,
  250. headers: {}
  251. }; // Set the body if present.
  252. if (request.method !== 'GET') {
  253. // Use ArrayBuffer to support non-text request bodies.
  254. // NOTE: we can't use Blobs becuse Safari doesn't support storing
  255. // Blobs in IndexedDB in some cases:
  256. // https://github.com/dfahlander/Dexie.js/issues/618#issuecomment-398348457
  257. requestData.body = await request.clone().arrayBuffer();
  258. } // Convert the headers from an iterable to an object.
  259. for (const [key, value] of request.headers.entries()) {
  260. requestData.headers[key] = value;
  261. } // Add all other serializable request properties
  262. for (const prop of serializableProperties) {
  263. if (request[prop] !== undefined) {
  264. requestData[prop] = request[prop];
  265. }
  266. }
  267. return new StorableRequest(requestData);
  268. }
  269. /**
  270. * Returns a deep clone of the instances `_requestData` object.
  271. *
  272. * @return {Object}
  273. *
  274. * @private
  275. */
  276. toObject() {
  277. const requestData = Object.assign({}, this._requestData);
  278. requestData.headers = Object.assign({}, this._requestData.headers);
  279. if (requestData.body) {
  280. requestData.body = requestData.body.slice(0);
  281. }
  282. return requestData;
  283. }
  284. /**
  285. * Converts this instance to a Request.
  286. *
  287. * @return {Request}
  288. *
  289. * @private
  290. */
  291. toRequest() {
  292. return new Request(this._requestData.url, this._requestData);
  293. }
  294. /**
  295. * Creates and returns a deep clone of the instance.
  296. *
  297. * @return {StorableRequest}
  298. *
  299. * @private
  300. */
  301. clone() {
  302. return new StorableRequest(this.toObject());
  303. }
  304. }
  305. /*
  306. Copyright 2018 Google LLC
  307. Use of this source code is governed by an MIT-style
  308. license that can be found in the LICENSE file or at
  309. https://opensource.org/licenses/MIT.
  310. */
  311. const TAG_PREFIX = 'workbox-background-sync';
  312. const MAX_RETENTION_TIME = 60 * 24 * 7; // 7 days in minutes
  313. const queueNames = new Set();
  314. /**
  315. * Converts a QueueStore entry into the format exposed by Queue. This entails
  316. * converting the request data into a real request and omitting the `id` and
  317. * `queueName` properties.
  318. *
  319. * @param {Object} queueStoreEntry
  320. * @return {Object}
  321. * @private
  322. */
  323. const convertEntry = queueStoreEntry => {
  324. const queueEntry = {
  325. request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
  326. timestamp: queueStoreEntry.timestamp
  327. };
  328. if (queueStoreEntry.metadata) {
  329. queueEntry.metadata = queueStoreEntry.metadata;
  330. }
  331. return queueEntry;
  332. };
  333. /**
  334. * A class to manage storing failed requests in IndexedDB and retrying them
  335. * later. All parts of the storing and replaying process are observable via
  336. * callbacks.
  337. *
  338. * @memberof module:workbox-background-sync
  339. */
  340. class Queue {
  341. /**
  342. * Creates an instance of Queue with the given options
  343. *
  344. * @param {string} name The unique name for this queue. This name must be
  345. * unique as it's used to register sync events and store requests
  346. * in IndexedDB specific to this instance. An error will be thrown if
  347. * a duplicate name is detected.
  348. * @param {Object} [options]
  349. * @param {Function} [options.onSync] A function that gets invoked whenever
  350. * the 'sync' event fires. The function is invoked with an object
  351. * containing the `queue` property (referencing this instance), and you
  352. * can use the callback to customize the replay behavior of the queue.
  353. * When not set the `replayRequests()` method is called.
  354. * Note: if the replay fails after a sync event, make sure you throw an
  355. * error, so the browser knows to retry the sync event later.
  356. * @param {number} [options.maxRetentionTime=7 days] The amount of time (in
  357. * minutes) a request may be retried. After this amount of time has
  358. * passed, the request will be deleted from the queue.
  359. */
  360. constructor(name, {
  361. onSync,
  362. maxRetentionTime
  363. } = {}) {
  364. this._syncInProgress = false;
  365. this._requestsAddedDuringSync = false; // Ensure the store name is not already being used
  366. if (queueNames.has(name)) {
  367. throw new WorkboxError_js.WorkboxError('duplicate-queue-name', {
  368. name
  369. });
  370. } else {
  371. queueNames.add(name);
  372. }
  373. this._name = name;
  374. this._onSync = onSync || this.replayRequests;
  375. this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME;
  376. this._queueStore = new QueueStore(this._name);
  377. this._addSyncListener();
  378. }
  379. /**
  380. * @return {string}
  381. */
  382. get name() {
  383. return this._name;
  384. }
  385. /**
  386. * Stores the passed request in IndexedDB (with its timestamp and any
  387. * metadata) at the end of the queue.
  388. *
  389. * @param {Object} entry
  390. * @param {Request} entry.request The request to store in the queue.
  391. * @param {Object} [entry.metadata] Any metadata you want associated with the
  392. * stored request. When requests are replayed you'll have access to this
  393. * metadata object in case you need to modify the request beforehand.
  394. * @param {number} [entry.timestamp] The timestamp (Epoch time in
  395. * milliseconds) when the request was first added to the queue. This is
  396. * used along with `maxRetentionTime` to remove outdated requests. In
  397. * general you don't need to set this value, as it's automatically set
  398. * for you (defaulting to `Date.now()`), but you can update it if you
  399. * don't want particular requests to expire.
  400. */
  401. async pushRequest(entry) {
  402. {
  403. assert_js.assert.isType(entry, 'object', {
  404. moduleName: 'workbox-background-sync',
  405. className: 'Queue',
  406. funcName: 'pushRequest',
  407. paramName: 'entry'
  408. });
  409. assert_js.assert.isInstance(entry.request, Request, {
  410. moduleName: 'workbox-background-sync',
  411. className: 'Queue',
  412. funcName: 'pushRequest',
  413. paramName: 'entry.request'
  414. });
  415. }
  416. await this._addRequest(entry, 'push');
  417. }
  418. /**
  419. * Stores the passed request in IndexedDB (with its timestamp and any
  420. * metadata) at the beginning of the queue.
  421. *
  422. * @param {Object} entry
  423. * @param {Request} entry.request The request to store in the queue.
  424. * @param {Object} [entry.metadata] Any metadata you want associated with the
  425. * stored request. When requests are replayed you'll have access to this
  426. * metadata object in case you need to modify the request beforehand.
  427. * @param {number} [entry.timestamp] The timestamp (Epoch time in
  428. * milliseconds) when the request was first added to the queue. This is
  429. * used along with `maxRetentionTime` to remove outdated requests. In
  430. * general you don't need to set this value, as it's automatically set
  431. * for you (defaulting to `Date.now()`), but you can update it if you
  432. * don't want particular requests to expire.
  433. */
  434. async unshiftRequest(entry) {
  435. {
  436. assert_js.assert.isType(entry, 'object', {
  437. moduleName: 'workbox-background-sync',
  438. className: 'Queue',
  439. funcName: 'unshiftRequest',
  440. paramName: 'entry'
  441. });
  442. assert_js.assert.isInstance(entry.request, Request, {
  443. moduleName: 'workbox-background-sync',
  444. className: 'Queue',
  445. funcName: 'unshiftRequest',
  446. paramName: 'entry.request'
  447. });
  448. }
  449. await this._addRequest(entry, 'unshift');
  450. }
  451. /**
  452. * Removes and returns the last request in the queue (along with its
  453. * timestamp and any metadata). The returned object takes the form:
  454. * `{request, timestamp, metadata}`.
  455. *
  456. * @return {Promise<Object>}
  457. */
  458. async popRequest() {
  459. return this._removeRequest('pop');
  460. }
  461. /**
  462. * Removes and returns the first request in the queue (along with its
  463. * timestamp and any metadata). The returned object takes the form:
  464. * `{request, timestamp, metadata}`.
  465. *
  466. * @return {Promise<Object>}
  467. */
  468. async shiftRequest() {
  469. return this._removeRequest('shift');
  470. }
  471. /**
  472. * Returns all the entries that have not expired (per `maxRetentionTime`).
  473. * Any expired entries are removed from the queue.
  474. *
  475. * @return {Promise<Array<Object>>}
  476. */
  477. async getAll() {
  478. const allEntries = await this._queueStore.getAll();
  479. const now = Date.now();
  480. const unexpiredEntries = [];
  481. for (const entry of allEntries) {
  482. // Ignore requests older than maxRetentionTime. Call this function
  483. // recursively until an unexpired request is found.
  484. const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
  485. if (now - entry.timestamp > maxRetentionTimeInMs) {
  486. await this._queueStore.deleteEntry(entry.id);
  487. } else {
  488. unexpiredEntries.push(convertEntry(entry));
  489. }
  490. }
  491. return unexpiredEntries;
  492. }
  493. /**
  494. * Adds the entry to the QueueStore and registers for a sync event.
  495. *
  496. * @param {Object} entry
  497. * @param {Request} entry.request
  498. * @param {Object} [entry.metadata]
  499. * @param {number} [entry.timestamp=Date.now()]
  500. * @param {string} operation ('push' or 'unshift')
  501. * @private
  502. */
  503. async _addRequest({
  504. request,
  505. metadata,
  506. timestamp = Date.now()
  507. }, operation) {
  508. const storableRequest = await StorableRequest.fromRequest(request.clone());
  509. const entry = {
  510. requestData: storableRequest.toObject(),
  511. timestamp
  512. }; // Only include metadata if it's present.
  513. if (metadata) {
  514. entry.metadata = metadata;
  515. }
  516. await this._queueStore[`${operation}Entry`](entry);
  517. {
  518. logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`);
  519. } // Don't register for a sync if we're in the middle of a sync. Instead,
  520. // we wait until the sync is complete and call register if
  521. // `this._requestsAddedDuringSync` is true.
  522. if (this._syncInProgress) {
  523. this._requestsAddedDuringSync = true;
  524. } else {
  525. await this.registerSync();
  526. }
  527. }
  528. /**
  529. * Removes and returns the first or last (depending on `operation`) entry
  530. * from the QueueStore that's not older than the `maxRetentionTime`.
  531. *
  532. * @param {string} operation ('pop' or 'shift')
  533. * @return {Object|undefined}
  534. * @private
  535. */
  536. async _removeRequest(operation) {
  537. const now = Date.now();
  538. const entry = await this._queueStore[`${operation}Entry`]();
  539. if (entry) {
  540. // Ignore requests older than maxRetentionTime. Call this function
  541. // recursively until an unexpired request is found.
  542. const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
  543. if (now - entry.timestamp > maxRetentionTimeInMs) {
  544. return this._removeRequest(operation);
  545. }
  546. return convertEntry(entry);
  547. } else {
  548. return undefined;
  549. }
  550. }
  551. /**
  552. * Loops through each request in the queue and attempts to re-fetch it.
  553. * If any request fails to re-fetch, it's put back in the same position in
  554. * the queue (which registers a retry for the next sync event).
  555. */
  556. async replayRequests() {
  557. let entry;
  558. while (entry = await this.shiftRequest()) {
  559. try {
  560. await fetch(entry.request.clone());
  561. if ("dev" !== 'production') {
  562. logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(entry.request.url)}'` + `has been replayed in queue '${this._name}'`);
  563. }
  564. } catch (error) {
  565. await this.unshiftRequest(entry);
  566. {
  567. logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(entry.request.url)}'` + `failed to replay, putting it back in queue '${this._name}'`);
  568. }
  569. throw new WorkboxError_js.WorkboxError('queue-replay-failed', {
  570. name: this._name
  571. });
  572. }
  573. }
  574. {
  575. logger_js.logger.log(`All requests in queue '${this.name}' have successfully ` + `replayed; the queue is now empty!`);
  576. }
  577. }
  578. /**
  579. * Registers a sync event with a tag unique to this instance.
  580. */
  581. async registerSync() {
  582. if ('sync' in self.registration) {
  583. try {
  584. await self.registration.sync.register(`${TAG_PREFIX}:${this._name}`);
  585. } catch (err) {
  586. // This means the registration failed for some reason, possibly due to
  587. // the user disabling it.
  588. {
  589. logger_js.logger.warn(`Unable to register sync event for '${this._name}'.`, err);
  590. }
  591. }
  592. }
  593. }
  594. /**
  595. * In sync-supporting browsers, this adds a listener for the sync event.
  596. * In non-sync-supporting browsers, this will retry the queue on service
  597. * worker startup.
  598. *
  599. * @private
  600. */
  601. _addSyncListener() {
  602. if ('sync' in self.registration) {
  603. self.addEventListener('sync', event => {
  604. if (event.tag === `${TAG_PREFIX}:${this._name}`) {
  605. {
  606. logger_js.logger.log(`Background sync for tag '${event.tag}'` + `has been received`);
  607. }
  608. const syncComplete = async () => {
  609. this._syncInProgress = true;
  610. let syncError;
  611. try {
  612. await this._onSync({
  613. queue: this
  614. });
  615. } catch (error) {
  616. syncError = error; // Rethrow the error. Note: the logic in the finally clause
  617. // will run before this gets rethrown.
  618. throw syncError;
  619. } finally {
  620. // New items may have been added to the queue during the sync,
  621. // so we need to register for a new sync if that's happened...
  622. // Unless there was an error during the sync, in which
  623. // case the browser will automatically retry later, as long
  624. // as `event.lastChance` is not true.
  625. if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) {
  626. await this.registerSync();
  627. }
  628. this._syncInProgress = false;
  629. this._requestsAddedDuringSync = false;
  630. }
  631. };
  632. event.waitUntil(syncComplete());
  633. }
  634. });
  635. } else {
  636. {
  637. logger_js.logger.log(`Background sync replaying without background sync event`);
  638. } // If the browser doesn't support background sync, retry
  639. // every time the service worker starts up as a fallback.
  640. this._onSync({
  641. queue: this
  642. });
  643. }
  644. }
  645. /**
  646. * Returns the set of queue names. This is primarily used to reset the list
  647. * of queue names in tests.
  648. *
  649. * @return {Set}
  650. *
  651. * @private
  652. */
  653. static get _queueNames() {
  654. return queueNames;
  655. }
  656. }
  657. /*
  658. Copyright 2018 Google LLC
  659. Use of this source code is governed by an MIT-style
  660. license that can be found in the LICENSE file or at
  661. https://opensource.org/licenses/MIT.
  662. */
  663. /**
  664. * A class implementing the `fetchDidFail` lifecycle callback. This makes it
  665. * easier to add failed requests to a background sync Queue.
  666. *
  667. * @memberof module:workbox-background-sync
  668. */
  669. class BackgroundSyncPlugin {
  670. /**
  671. * @param {string} name See the [Queue]{@link module:workbox-background-sync.Queue}
  672. * documentation for parameter details.
  673. * @param {Object} [options] See the
  674. * [Queue]{@link module:workbox-background-sync.Queue} documentation for
  675. * parameter details.
  676. */
  677. constructor(name, options) {
  678. /**
  679. * @param {Object} options
  680. * @param {Request} options.request
  681. * @private
  682. */
  683. this.fetchDidFail = async ({
  684. request
  685. }) => {
  686. await this._queue.pushRequest({
  687. request
  688. });
  689. };
  690. this._queue = new Queue(name, options);
  691. }
  692. }
  693. exports.BackgroundSyncPlugin = BackgroundSyncPlugin;
  694. exports.Queue = Queue;
  695. return exports;
  696. }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
  697. //# sourceMappingURL=workbox-background-sync.dev.js.map