mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-24 09:58:21 +03:00
2585 lines
90 KiB
JavaScript
2585 lines
90 KiB
JavaScript
try {
|
|
self["workbox:core:6.5.3"] && _();
|
|
} catch {
|
|
}
|
|
const se = (n, ...e) => {
|
|
let t = n;
|
|
return e.length > 0 && (t += ` :: ${JSON.stringify(e)}`), t;
|
|
}, ne = se;
|
|
let w = class extends Error {
|
|
/**
|
|
*
|
|
* @param {string} errorCode The error code that
|
|
* identifies this particular error.
|
|
* @param {Object=} details Any relevant arguments
|
|
* that will help developers identify issues should
|
|
* be added as a key on the context object.
|
|
*/
|
|
constructor(e, t) {
|
|
const s = ne(e, t);
|
|
super(s), this.name = e, this.details = t;
|
|
}
|
|
};
|
|
const Q = /* @__PURE__ */ new Set();
|
|
function re(n) {
|
|
Q.add(n);
|
|
}
|
|
const d = {
|
|
googleAnalytics: "googleAnalytics",
|
|
precache: "precache-v2",
|
|
prefix: "workbox",
|
|
runtime: "runtime",
|
|
suffix: typeof registration < "u" ? registration.scope : ""
|
|
}, P = (n) => [d.prefix, n, d.suffix].filter((e) => e && e.length > 0).join("-"), ae = (n) => {
|
|
for (const e of Object.keys(d))
|
|
n(e);
|
|
}, z = {
|
|
updateDetails: (n) => {
|
|
ae((e) => {
|
|
typeof n[e] == "string" && (d[e] = n[e]);
|
|
});
|
|
},
|
|
getGoogleAnalyticsName: (n) => n || P(d.googleAnalytics),
|
|
getPrecacheName: (n) => n || P(d.precache),
|
|
getPrefix: () => d.prefix,
|
|
getRuntimeName: (n) => n || P(d.runtime),
|
|
getSuffix: () => d.suffix
|
|
};
|
|
function q(n, e) {
|
|
const t = new URL(n);
|
|
for (const s of e)
|
|
t.searchParams.delete(s);
|
|
return t.href;
|
|
}
|
|
async function ie(n, e, t, s) {
|
|
const r = q(e.url, t);
|
|
if (e.url === r)
|
|
return n.match(e, s);
|
|
const i = Object.assign(Object.assign({}, s), { ignoreSearch: !0 }), a = await n.keys(e, i);
|
|
for (const c of a) {
|
|
const o = q(c.url, t);
|
|
if (r === o)
|
|
return n.match(c, s);
|
|
}
|
|
}
|
|
function J(n) {
|
|
n.then(() => {
|
|
});
|
|
}
|
|
let ce = class {
|
|
/**
|
|
* Creates a promise and exposes its resolve and reject functions as methods.
|
|
*/
|
|
constructor() {
|
|
this.promise = new Promise((e, t) => {
|
|
this.resolve = e, this.reject = t;
|
|
});
|
|
}
|
|
};
|
|
async function oe() {
|
|
for (const n of Q)
|
|
await n();
|
|
}
|
|
const le = (n) => new URL(String(n), location.href).href.replace(new RegExp(`^${location.origin}`), "");
|
|
function he(n) {
|
|
return new Promise((e) => setTimeout(e, n));
|
|
}
|
|
function ue() {
|
|
self.addEventListener("activate", () => self.clients.claim());
|
|
}
|
|
const fe = (n, e) => e.some((t) => n instanceof t);
|
|
let H, B;
|
|
function de() {
|
|
return H || (H = [
|
|
IDBDatabase,
|
|
IDBObjectStore,
|
|
IDBIndex,
|
|
IDBCursor,
|
|
IDBTransaction
|
|
]);
|
|
}
|
|
function pe() {
|
|
return B || (B = [
|
|
IDBCursor.prototype.advance,
|
|
IDBCursor.prototype.continue,
|
|
IDBCursor.prototype.continuePrimaryKey
|
|
]);
|
|
}
|
|
const X = /* @__PURE__ */ new WeakMap(), K = /* @__PURE__ */ new WeakMap(), Y = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap();
|
|
function ge(n) {
|
|
const e = new Promise((t, s) => {
|
|
const r = () => {
|
|
n.removeEventListener("success", i), n.removeEventListener("error", a);
|
|
}, i = () => {
|
|
t(g(n.result)), r();
|
|
}, a = () => {
|
|
s(n.error), r();
|
|
};
|
|
n.addEventListener("success", i), n.addEventListener("error", a);
|
|
});
|
|
return e.then((t) => {
|
|
t instanceof IDBCursor && X.set(t, n);
|
|
}).catch(() => {
|
|
}), j.set(e, n), e;
|
|
}
|
|
function me(n) {
|
|
if (K.has(n))
|
|
return;
|
|
const e = new Promise((t, s) => {
|
|
const r = () => {
|
|
n.removeEventListener("complete", i), n.removeEventListener("error", a), n.removeEventListener("abort", a);
|
|
}, i = () => {
|
|
t(), r();
|
|
}, a = () => {
|
|
s(n.error || new DOMException("AbortError", "AbortError")), r();
|
|
};
|
|
n.addEventListener("complete", i), n.addEventListener("error", a), n.addEventListener("abort", a);
|
|
});
|
|
K.set(n, e);
|
|
}
|
|
let S = {
|
|
get(n, e, t) {
|
|
if (n instanceof IDBTransaction) {
|
|
if (e === "done")
|
|
return K.get(n);
|
|
if (e === "objectStoreNames")
|
|
return n.objectStoreNames || Y.get(n);
|
|
if (e === "store")
|
|
return t.objectStoreNames[1] ? void 0 : t.objectStore(t.objectStoreNames[0]);
|
|
}
|
|
return g(n[e]);
|
|
},
|
|
set(n, e, t) {
|
|
return n[e] = t, !0;
|
|
},
|
|
has(n, e) {
|
|
return n instanceof IDBTransaction && (e === "done" || e === "store") ? !0 : e in n;
|
|
}
|
|
};
|
|
function we(n) {
|
|
S = n(S);
|
|
}
|
|
function ye(n) {
|
|
return n === IDBDatabase.prototype.transaction && !("objectStoreNames" in IDBTransaction.prototype) ? function(e, ...t) {
|
|
const s = n.call(M(this), e, ...t);
|
|
return Y.set(s, e.sort ? e.sort() : [e]), g(s);
|
|
} : pe().includes(n) ? function(...e) {
|
|
return n.apply(M(this), e), g(X.get(this));
|
|
} : function(...e) {
|
|
return g(n.apply(M(this), e));
|
|
};
|
|
}
|
|
function be(n) {
|
|
return typeof n == "function" ? ye(n) : (n instanceof IDBTransaction && me(n), fe(n, de()) ? new Proxy(n, S) : n);
|
|
}
|
|
function g(n) {
|
|
if (n instanceof IDBRequest)
|
|
return ge(n);
|
|
if (T.has(n))
|
|
return T.get(n);
|
|
const e = be(n);
|
|
return e !== n && (T.set(n, e), j.set(e, n)), e;
|
|
}
|
|
const M = (n) => j.get(n);
|
|
function _e(n, e, { blocked: t, upgrade: s, blocking: r, terminated: i } = {}) {
|
|
const a = indexedDB.open(n, e), c = g(a);
|
|
return s && a.addEventListener("upgradeneeded", (o) => {
|
|
s(g(a.result), o.oldVersion, o.newVersion, g(a.transaction), o);
|
|
}), t && a.addEventListener("blocked", (o) => t(
|
|
// Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
|
|
o.oldVersion,
|
|
o.newVersion,
|
|
o
|
|
)), c.then((o) => {
|
|
i && o.addEventListener("close", () => i()), r && o.addEventListener("versionchange", (l) => r(l.oldVersion, l.newVersion, l));
|
|
}).catch(() => {
|
|
}), c;
|
|
}
|
|
function Re(n, { blocked: e } = {}) {
|
|
const t = indexedDB.deleteDatabase(n);
|
|
return e && t.addEventListener("blocked", (s) => e(
|
|
// Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
|
|
s.oldVersion,
|
|
s
|
|
)), g(t).then(() => {
|
|
});
|
|
}
|
|
const Ce = ["get", "getKey", "getAll", "getAllKeys", "count"], xe = ["put", "add", "delete", "clear"], N = /* @__PURE__ */ new Map();
|
|
function $(n, e) {
|
|
if (!(n instanceof IDBDatabase && !(e in n) && typeof e == "string"))
|
|
return;
|
|
if (N.get(e))
|
|
return N.get(e);
|
|
const t = e.replace(/FromIndex$/, ""), s = e !== t, r = xe.includes(t);
|
|
if (
|
|
// Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.
|
|
!(t in (s ? IDBIndex : IDBObjectStore).prototype) || !(r || Ce.includes(t))
|
|
)
|
|
return;
|
|
const i = async function(a, ...c) {
|
|
const o = this.transaction(a, r ? "readwrite" : "readonly");
|
|
let l = o.store;
|
|
return s && (l = l.index(c.shift())), (await Promise.all([
|
|
l[t](...c),
|
|
r && o.done
|
|
]))[0];
|
|
};
|
|
return N.set(e, i), i;
|
|
}
|
|
we((n) => ({
|
|
...n,
|
|
get: (e, t, s) => $(e, t) || n.get(e, t, s),
|
|
has: (e, t) => !!$(e, t) || n.has(e, t)
|
|
}));
|
|
try {
|
|
self["workbox:expiration:6.5.3"] && _();
|
|
} catch {
|
|
}
|
|
const ke = "workbox-expiration", b = "cache-entries", F = (n) => {
|
|
const e = new URL(n, location.href);
|
|
return e.hash = "", e.href;
|
|
};
|
|
class Ee {
|
|
/**
|
|
*
|
|
* @param {string} cacheName
|
|
*
|
|
* @private
|
|
*/
|
|
constructor(e) {
|
|
this._db = null, this._cacheName = e;
|
|
}
|
|
/**
|
|
* Performs an upgrade of indexedDB.
|
|
*
|
|
* @param {IDBPDatabase<CacheDbSchema>} db
|
|
*
|
|
* @private
|
|
*/
|
|
_upgradeDb(e) {
|
|
const t = e.createObjectStore(b, { keyPath: "id" });
|
|
t.createIndex("cacheName", "cacheName", { unique: !1 }), t.createIndex("timestamp", "timestamp", { unique: !1 });
|
|
}
|
|
/**
|
|
* Performs an upgrade of indexedDB and deletes deprecated DBs.
|
|
*
|
|
* @param {IDBPDatabase<CacheDbSchema>} db
|
|
*
|
|
* @private
|
|
*/
|
|
_upgradeDbAndDeleteOldDbs(e) {
|
|
this._upgradeDb(e), this._cacheName && Re(this._cacheName);
|
|
}
|
|
/**
|
|
* @param {string} url
|
|
* @param {number} timestamp
|
|
*
|
|
* @private
|
|
*/
|
|
async setTimestamp(e, t) {
|
|
e = F(e);
|
|
const s = {
|
|
url: e,
|
|
timestamp: t,
|
|
cacheName: this._cacheName,
|
|
// Creating an ID from the URL and cache name won't be necessary once
|
|
// Edge switches to Chromium and all browsers we support work with
|
|
// array keyPaths.
|
|
id: this._getId(e)
|
|
}, i = (await this.getDb()).transaction(b, "readwrite", {
|
|
durability: "relaxed"
|
|
});
|
|
await i.store.put(s), await i.done;
|
|
}
|
|
/**
|
|
* Returns the timestamp stored for a given URL.
|
|
*
|
|
* @param {string} url
|
|
* @return {number | undefined}
|
|
*
|
|
* @private
|
|
*/
|
|
async getTimestamp(e) {
|
|
const s = await (await this.getDb()).get(b, this._getId(e));
|
|
return s == null ? void 0 : s.timestamp;
|
|
}
|
|
/**
|
|
* Iterates through all the entries in the object store (from newest to
|
|
* oldest) and removes entries once either `maxCount` is reached or the
|
|
* entry's timestamp is less than `minTimestamp`.
|
|
*
|
|
* @param {number} minTimestamp
|
|
* @param {number} maxCount
|
|
* @return {Array<string>}
|
|
*
|
|
* @private
|
|
*/
|
|
async expireEntries(e, t) {
|
|
const s = await this.getDb();
|
|
let r = await s.transaction(b).store.index("timestamp").openCursor(null, "prev");
|
|
const i = [];
|
|
let a = 0;
|
|
for (; r; ) {
|
|
const o = r.value;
|
|
o.cacheName === this._cacheName && (e && o.timestamp < e || t && a >= t ? i.push(r.value) : a++), r = await r.continue();
|
|
}
|
|
const c = [];
|
|
for (const o of i)
|
|
await s.delete(b, o.id), c.push(o.url);
|
|
return c;
|
|
}
|
|
/**
|
|
* Takes a URL and returns an ID that will be unique in the object store.
|
|
*
|
|
* @param {string} url
|
|
* @return {string}
|
|
*
|
|
* @private
|
|
*/
|
|
_getId(e) {
|
|
return this._cacheName + "|" + F(e);
|
|
}
|
|
/**
|
|
* Returns an open connection to the database.
|
|
*
|
|
* @private
|
|
*/
|
|
async getDb() {
|
|
return this._db || (this._db = await _e(ke, 1, {
|
|
upgrade: this._upgradeDbAndDeleteOldDbs.bind(this)
|
|
})), this._db;
|
|
}
|
|
}
|
|
class De {
|
|
/**
|
|
* To construct a new CacheExpiration instance you must provide at least
|
|
* one of the `config` properties.
|
|
*
|
|
* @param {string} cacheName Name of the cache to apply restrictions to.
|
|
* @param {Object} config
|
|
* @param {number} [config.maxEntries] The maximum number of entries to cache.
|
|
* Entries used the least will be removed as the maximum is reached.
|
|
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before
|
|
* it's treated as stale and removed.
|
|
* @param {Object} [config.matchOptions] The [`CacheQueryOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete#Parameters)
|
|
* that will be used when calling `delete()` on the cache.
|
|
*/
|
|
constructor(e, t = {}) {
|
|
this._isRunning = !1, this._rerunRequested = !1, this._maxEntries = t.maxEntries, this._maxAgeSeconds = t.maxAgeSeconds, this._matchOptions = t.matchOptions, this._cacheName = e, this._timestampModel = new Ee(e);
|
|
}
|
|
/**
|
|
* Expires entries for the given cache and given criteria.
|
|
*/
|
|
async expireEntries() {
|
|
if (this._isRunning) {
|
|
this._rerunRequested = !0;
|
|
return;
|
|
}
|
|
this._isRunning = !0;
|
|
const e = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1e3 : 0, t = await this._timestampModel.expireEntries(e, this._maxEntries), s = await self.caches.open(this._cacheName);
|
|
for (const r of t)
|
|
await s.delete(r, this._matchOptions);
|
|
this._isRunning = !1, this._rerunRequested && (this._rerunRequested = !1, J(this.expireEntries()));
|
|
}
|
|
/**
|
|
* Update the timestamp for the given URL. This ensures the when
|
|
* removing entries based on maximum entries, most recently used
|
|
* is accurate or when expiring, the timestamp is up-to-date.
|
|
*
|
|
* @param {string} url
|
|
*/
|
|
async updateTimestamp(e) {
|
|
await this._timestampModel.setTimestamp(e, Date.now());
|
|
}
|
|
/**
|
|
* Can be used to check if a URL has expired or not before it's used.
|
|
*
|
|
* This requires a look up from IndexedDB, so can be slow.
|
|
*
|
|
* Note: This method will not remove the cached entry, call
|
|
* `expireEntries()` to remove indexedDB and Cache entries.
|
|
*
|
|
* @param {string} url
|
|
* @return {boolean}
|
|
*/
|
|
async isURLExpired(e) {
|
|
if (this._maxAgeSeconds) {
|
|
const t = await this._timestampModel.getTimestamp(e), s = Date.now() - this._maxAgeSeconds * 1e3;
|
|
return t !== void 0 ? t < s : !0;
|
|
} else
|
|
return !1;
|
|
}
|
|
/**
|
|
* Removes the IndexedDB object store used to keep track of cache expiration
|
|
* metadata.
|
|
*/
|
|
async delete() {
|
|
this._rerunRequested = !1, await this._timestampModel.expireEntries(1 / 0);
|
|
}
|
|
}
|
|
class Ue {
|
|
/**
|
|
* @param {ExpirationPluginOptions} config
|
|
* @param {number} [config.maxEntries] The maximum number of entries to cache.
|
|
* Entries used the least will be removed as the maximum is reached.
|
|
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before
|
|
* it's treated as stale and removed.
|
|
* @param {Object} [config.matchOptions] The [`CacheQueryOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete#Parameters)
|
|
* that will be used when calling `delete()` on the cache.
|
|
* @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to
|
|
* automatic deletion if the available storage quota has been exceeded.
|
|
*/
|
|
constructor(e = {}) {
|
|
this.cachedResponseWillBeUsed = async ({ event: t, request: s, cacheName: r, cachedResponse: i }) => {
|
|
if (!i)
|
|
return null;
|
|
const a = this._isResponseDateFresh(i), c = this._getCacheExpiration(r);
|
|
J(c.expireEntries());
|
|
const o = c.updateTimestamp(s.url);
|
|
if (t)
|
|
try {
|
|
t.waitUntil(o);
|
|
} catch {
|
|
}
|
|
return a ? i : null;
|
|
}, this.cacheDidUpdate = async ({ cacheName: t, request: s }) => {
|
|
const r = this._getCacheExpiration(t);
|
|
await r.updateTimestamp(s.url), await r.expireEntries();
|
|
}, this._config = e, this._maxAgeSeconds = e.maxAgeSeconds, this._cacheExpirations = /* @__PURE__ */ new Map(), e.purgeOnQuotaError && re(() => this.deleteCacheAndMetadata());
|
|
}
|
|
/**
|
|
* A simple helper method to return a CacheExpiration instance for a given
|
|
* cache name.
|
|
*
|
|
* @param {string} cacheName
|
|
* @return {CacheExpiration}
|
|
*
|
|
* @private
|
|
*/
|
|
_getCacheExpiration(e) {
|
|
if (e === z.getRuntimeName())
|
|
throw new w("expire-custom-caches-only");
|
|
let t = this._cacheExpirations.get(e);
|
|
return t || (t = new De(e, this._config), this._cacheExpirations.set(e, t)), t;
|
|
}
|
|
/**
|
|
* @param {Response} cachedResponse
|
|
* @return {boolean}
|
|
*
|
|
* @private
|
|
*/
|
|
_isResponseDateFresh(e) {
|
|
if (!this._maxAgeSeconds)
|
|
return !0;
|
|
const t = this._getDateHeaderTimestamp(e);
|
|
if (t === null)
|
|
return !0;
|
|
const s = Date.now();
|
|
return t >= s - this._maxAgeSeconds * 1e3;
|
|
}
|
|
/**
|
|
* This method will extract the data header and parse it into a useful
|
|
* value.
|
|
*
|
|
* @param {Response} cachedResponse
|
|
* @return {number|null}
|
|
*
|
|
* @private
|
|
*/
|
|
_getDateHeaderTimestamp(e) {
|
|
if (!e.headers.has("date"))
|
|
return null;
|
|
const t = e.headers.get("date"), r = new Date(t).getTime();
|
|
return isNaN(r) ? null : r;
|
|
}
|
|
/**
|
|
* This is a helper method that performs two operations:
|
|
*
|
|
* - Deletes *all* the underlying Cache instances associated with this plugin
|
|
* instance, by calling caches.delete() on your behalf.
|
|
* - Deletes the metadata from IndexedDB used to keep track of expiration
|
|
* details for each Cache instance.
|
|
*
|
|
* When using cache expiration, calling this method is preferable to calling
|
|
* `caches.delete()` directly, since this will ensure that the IndexedDB
|
|
* metadata is also cleanly removed and open IndexedDB instances are deleted.
|
|
*
|
|
* Note that if you're *not* using cache expiration for a given cache, calling
|
|
* `caches.delete()` and passing in the cache's name should be sufficient.
|
|
* There is no Workbox-specific method needed for cleanup in that case.
|
|
*/
|
|
async deleteCacheAndMetadata() {
|
|
for (const [e, t] of this._cacheExpirations)
|
|
await self.caches.delete(e), await t.delete();
|
|
this._cacheExpirations = /* @__PURE__ */ new Map();
|
|
}
|
|
}
|
|
try {
|
|
self["workbox:core:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
const Le = (n, ...e) => {
|
|
let t = n;
|
|
return e.length > 0 && (t += ` :: ${JSON.stringify(e)}`), t;
|
|
}, ve = Le;
|
|
class u extends Error {
|
|
/**
|
|
*
|
|
* @param {string} errorCode The error code that
|
|
* identifies this particular error.
|
|
* @param {Object=} details Any relevant arguments
|
|
* that will help developers identify issues should
|
|
* be added as a key on the context object.
|
|
*/
|
|
constructor(e, t) {
|
|
const s = ve(e, t);
|
|
super(s), this.name = e, this.details = t;
|
|
}
|
|
}
|
|
const p = {
|
|
googleAnalytics: "googleAnalytics",
|
|
precache: "precache-v2",
|
|
prefix: "workbox",
|
|
runtime: "runtime",
|
|
suffix: typeof registration < "u" ? registration.scope : ""
|
|
}, O = (n) => [p.prefix, n, p.suffix].filter((e) => e && e.length > 0).join("-"), Pe = (n) => {
|
|
for (const e of Object.keys(p))
|
|
n(e);
|
|
}, A = {
|
|
updateDetails: (n) => {
|
|
Pe((e) => {
|
|
typeof n[e] == "string" && (p[e] = n[e]);
|
|
});
|
|
},
|
|
getGoogleAnalyticsName: (n) => n || O(p.googleAnalytics),
|
|
getPrecacheName: (n) => n || O(p.precache),
|
|
getPrefix: () => p.prefix,
|
|
getRuntimeName: (n) => n || O(p.runtime),
|
|
getSuffix: () => p.suffix
|
|
};
|
|
function V(n, e) {
|
|
const t = e();
|
|
return n.waitUntil(t), t;
|
|
}
|
|
try {
|
|
self["workbox:precaching:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
const Te = "__WB_REVISION__";
|
|
function Me(n) {
|
|
if (!n)
|
|
throw new u("add-to-cache-list-unexpected-type", { entry: n });
|
|
if (typeof n == "string") {
|
|
const i = new URL(n, location.href);
|
|
return {
|
|
cacheKey: i.href,
|
|
url: i.href
|
|
};
|
|
}
|
|
const { revision: e, url: t } = n;
|
|
if (!t)
|
|
throw new u("add-to-cache-list-unexpected-type", { entry: n });
|
|
if (!e) {
|
|
const i = new URL(t, location.href);
|
|
return {
|
|
cacheKey: i.href,
|
|
url: i.href
|
|
};
|
|
}
|
|
const s = new URL(t, location.href), r = new URL(t, location.href);
|
|
return s.searchParams.set(Te, e), {
|
|
cacheKey: s.href,
|
|
url: r.href
|
|
};
|
|
}
|
|
class Ne {
|
|
constructor() {
|
|
this.updatedURLs = [], this.notUpdatedURLs = [], this.handlerWillStart = async ({ request: e, state: t }) => {
|
|
t && (t.originalRequest = e);
|
|
}, this.cachedResponseWillBeUsed = async ({ event: e, state: t, cachedResponse: s }) => {
|
|
if (e.type === "install" && t && t.originalRequest && t.originalRequest instanceof Request) {
|
|
const r = t.originalRequest.url;
|
|
s ? this.notUpdatedURLs.push(r) : this.updatedURLs.push(r);
|
|
}
|
|
return s;
|
|
};
|
|
}
|
|
}
|
|
class Oe {
|
|
constructor({ precacheController: e }) {
|
|
this.cacheKeyWillBeUsed = async ({ request: t, params: s }) => {
|
|
const r = (s == null ? void 0 : s.cacheKey) || this._precacheController.getCacheKeyForURL(t.url);
|
|
return r ? new Request(r, { headers: t.headers }) : t;
|
|
}, this._precacheController = e;
|
|
}
|
|
}
|
|
let R;
|
|
function Ie() {
|
|
if (R === void 0) {
|
|
const n = new Response("");
|
|
if ("body" in n)
|
|
try {
|
|
new Response(n.body), R = !0;
|
|
} catch {
|
|
R = !1;
|
|
}
|
|
R = !1;
|
|
}
|
|
return R;
|
|
}
|
|
async function Ke(n, e) {
|
|
let t = null;
|
|
if (n.url && (t = new URL(n.url).origin), t !== self.location.origin)
|
|
throw new u("cross-origin-copy-response", { origin: t });
|
|
const s = n.clone(), r = {
|
|
headers: new Headers(s.headers),
|
|
status: s.status,
|
|
statusText: s.statusText
|
|
}, i = e ? e(r) : r, a = Ie() ? s.body : await s.blob();
|
|
return new Response(a, i);
|
|
}
|
|
const Se = (n) => new URL(String(n), location.href).href.replace(new RegExp(`^${location.origin}`), "");
|
|
function G(n, e) {
|
|
const t = new URL(n);
|
|
for (const s of e)
|
|
t.searchParams.delete(s);
|
|
return t.href;
|
|
}
|
|
async function je(n, e, t, s) {
|
|
const r = G(e.url, t);
|
|
if (e.url === r)
|
|
return n.match(e, s);
|
|
const i = Object.assign(Object.assign({}, s), { ignoreSearch: !0 }), a = await n.keys(e, i);
|
|
for (const c of a) {
|
|
const o = G(c.url, t);
|
|
if (r === o)
|
|
return n.match(c, s);
|
|
}
|
|
}
|
|
class Ae {
|
|
/**
|
|
* Creates a promise and exposes its resolve and reject functions as methods.
|
|
*/
|
|
constructor() {
|
|
this.promise = new Promise((e, t) => {
|
|
this.resolve = e, this.reject = t;
|
|
});
|
|
}
|
|
}
|
|
const We = /* @__PURE__ */ new Set();
|
|
async function qe() {
|
|
for (const n of We)
|
|
await n();
|
|
}
|
|
function He(n) {
|
|
return new Promise((e) => setTimeout(e, n));
|
|
}
|
|
try {
|
|
self["workbox:strategies:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
function E(n) {
|
|
return typeof n == "string" ? new Request(n) : n;
|
|
}
|
|
let Be = class {
|
|
/**
|
|
* Creates a new instance associated with the passed strategy and event
|
|
* that's handling the request.
|
|
*
|
|
* The constructor also initializes the state that will be passed to each of
|
|
* the plugins handling this request.
|
|
*
|
|
* @param {workbox-strategies.Strategy} strategy
|
|
* @param {Object} options
|
|
* @param {Request|string} options.request A request to run this strategy for.
|
|
* @param {ExtendableEvent} options.event The event associated with the
|
|
* request.
|
|
* @param {URL} [options.url]
|
|
* @param {*} [options.params] The return value from the
|
|
* {@link workbox-routing~matchCallback} (if applicable).
|
|
*/
|
|
constructor(e, t) {
|
|
this._cacheKeys = {}, Object.assign(this, t), this.event = t.event, this._strategy = e, this._handlerDeferred = new Ae(), this._extendLifetimePromises = [], this._plugins = [...e.plugins], this._pluginStateMap = /* @__PURE__ */ new Map();
|
|
for (const s of this._plugins)
|
|
this._pluginStateMap.set(s, {});
|
|
this.event.waitUntil(this._handlerDeferred.promise);
|
|
}
|
|
/**
|
|
* Fetches a given request (and invokes any applicable plugin callback
|
|
* methods) using the `fetchOptions` (for non-navigation requests) and
|
|
* `plugins` defined on the `Strategy` object.
|
|
*
|
|
* The following plugin lifecycle methods are invoked when using this method:
|
|
* - `requestWillFetch()`
|
|
* - `fetchDidSucceed()`
|
|
* - `fetchDidFail()`
|
|
*
|
|
* @param {Request|string} input The URL or request to fetch.
|
|
* @return {Promise<Response>}
|
|
*/
|
|
async fetch(e) {
|
|
const { event: t } = this;
|
|
let s = E(e);
|
|
if (s.mode === "navigate" && t instanceof FetchEvent && t.preloadResponse) {
|
|
const a = await t.preloadResponse;
|
|
if (a)
|
|
return a;
|
|
}
|
|
const r = this.hasCallback("fetchDidFail") ? s.clone() : null;
|
|
try {
|
|
for (const a of this.iterateCallbacks("requestWillFetch"))
|
|
s = await a({ request: s.clone(), event: t });
|
|
} catch (a) {
|
|
if (a instanceof Error)
|
|
throw new u("plugin-error-request-will-fetch", {
|
|
thrownErrorMessage: a.message
|
|
});
|
|
}
|
|
const i = s.clone();
|
|
try {
|
|
let a;
|
|
a = await fetch(s, s.mode === "navigate" ? void 0 : this._strategy.fetchOptions);
|
|
for (const c of this.iterateCallbacks("fetchDidSucceed"))
|
|
a = await c({
|
|
event: t,
|
|
request: i,
|
|
response: a
|
|
});
|
|
return a;
|
|
} catch (a) {
|
|
throw r && await this.runCallbacks("fetchDidFail", {
|
|
error: a,
|
|
event: t,
|
|
originalRequest: r.clone(),
|
|
request: i.clone()
|
|
}), a;
|
|
}
|
|
}
|
|
/**
|
|
* Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
|
|
* the response generated by `this.fetch()`.
|
|
*
|
|
* The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
|
|
* so you do not have to manually call `waitUntil()` on the event.
|
|
*
|
|
* @param {Request|string} input The request or URL to fetch and cache.
|
|
* @return {Promise<Response>}
|
|
*/
|
|
async fetchAndCachePut(e) {
|
|
const t = await this.fetch(e), s = t.clone();
|
|
return this.waitUntil(this.cachePut(e, s)), t;
|
|
}
|
|
/**
|
|
* Matches a request from the cache (and invokes any applicable plugin
|
|
* callback methods) using the `cacheName`, `matchOptions`, and `plugins`
|
|
* defined on the strategy object.
|
|
*
|
|
* The following plugin lifecycle methods are invoked when using this method:
|
|
* - cacheKeyWillByUsed()
|
|
* - cachedResponseWillByUsed()
|
|
*
|
|
* @param {Request|string} key The Request or URL to use as the cache key.
|
|
* @return {Promise<Response|undefined>} A matching response, if found.
|
|
*/
|
|
async cacheMatch(e) {
|
|
const t = E(e);
|
|
let s;
|
|
const { cacheName: r, matchOptions: i } = this._strategy, a = await this.getCacheKey(t, "read"), c = Object.assign(Object.assign({}, i), { cacheName: r });
|
|
s = await caches.match(a, c);
|
|
for (const o of this.iterateCallbacks("cachedResponseWillBeUsed"))
|
|
s = await o({
|
|
cacheName: r,
|
|
matchOptions: i,
|
|
cachedResponse: s,
|
|
request: a,
|
|
event: this.event
|
|
}) || void 0;
|
|
return s;
|
|
}
|
|
/**
|
|
* Puts a request/response pair in the cache (and invokes any applicable
|
|
* plugin callback methods) using the `cacheName` and `plugins` defined on
|
|
* the strategy object.
|
|
*
|
|
* The following plugin lifecycle methods are invoked when using this method:
|
|
* - cacheKeyWillByUsed()
|
|
* - cacheWillUpdate()
|
|
* - cacheDidUpdate()
|
|
*
|
|
* @param {Request|string} key The request or URL to use as the cache key.
|
|
* @param {Response} response The response to cache.
|
|
* @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
|
|
* not be cached, and `true` otherwise.
|
|
*/
|
|
async cachePut(e, t) {
|
|
const s = E(e);
|
|
await He(0);
|
|
const r = await this.getCacheKey(s, "write");
|
|
if (!t)
|
|
throw new u("cache-put-with-no-response", {
|
|
url: Se(r.url)
|
|
});
|
|
const i = await this._ensureResponseSafeToCache(t);
|
|
if (!i)
|
|
return !1;
|
|
const { cacheName: a, matchOptions: c } = this._strategy, o = await self.caches.open(a), l = this.hasCallback("cacheDidUpdate"), f = l ? await je(
|
|
// TODO(philipwalton): the `__WB_REVISION__` param is a precaching
|
|
// feature. Consider into ways to only add this behavior if using
|
|
// precaching.
|
|
o,
|
|
r.clone(),
|
|
["__WB_REVISION__"],
|
|
c
|
|
) : null;
|
|
try {
|
|
await o.put(r, l ? i.clone() : i);
|
|
} catch (h) {
|
|
if (h instanceof Error)
|
|
throw h.name === "QuotaExceededError" && await qe(), h;
|
|
}
|
|
for (const h of this.iterateCallbacks("cacheDidUpdate"))
|
|
await h({
|
|
cacheName: a,
|
|
oldResponse: f,
|
|
newResponse: i.clone(),
|
|
request: r,
|
|
event: this.event
|
|
});
|
|
return !0;
|
|
}
|
|
/**
|
|
* Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
|
|
* executes any of those callbacks found in sequence. The final `Request`
|
|
* object returned by the last plugin is treated as the cache key for cache
|
|
* reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
|
|
* been registered, the passed request is returned unmodified
|
|
*
|
|
* @param {Request} request
|
|
* @param {string} mode
|
|
* @return {Promise<Request>}
|
|
*/
|
|
async getCacheKey(e, t) {
|
|
const s = `${e.url} | ${t}`;
|
|
if (!this._cacheKeys[s]) {
|
|
let r = e;
|
|
for (const i of this.iterateCallbacks("cacheKeyWillBeUsed"))
|
|
r = E(await i({
|
|
mode: t,
|
|
request: r,
|
|
event: this.event,
|
|
// params has a type any can't change right now.
|
|
params: this.params
|
|
// eslint-disable-line
|
|
}));
|
|
this._cacheKeys[s] = r;
|
|
}
|
|
return this._cacheKeys[s];
|
|
}
|
|
/**
|
|
* Returns true if the strategy has at least one plugin with the given
|
|
* callback.
|
|
*
|
|
* @param {string} name The name of the callback to check for.
|
|
* @return {boolean}
|
|
*/
|
|
hasCallback(e) {
|
|
for (const t of this._strategy.plugins)
|
|
if (e in t)
|
|
return !0;
|
|
return !1;
|
|
}
|
|
/**
|
|
* Runs all plugin callbacks matching the given name, in order, passing the
|
|
* given param object (merged ith the current plugin state) as the only
|
|
* argument.
|
|
*
|
|
* Note: since this method runs all plugins, it's not suitable for cases
|
|
* where the return value of a callback needs to be applied prior to calling
|
|
* the next callback. See
|
|
* {@link workbox-strategies.StrategyHandler#iterateCallbacks}
|
|
* below for how to handle that case.
|
|
*
|
|
* @param {string} name The name of the callback to run within each plugin.
|
|
* @param {Object} param The object to pass as the first (and only) param
|
|
* when executing each callback. This object will be merged with the
|
|
* current plugin state prior to callback execution.
|
|
*/
|
|
async runCallbacks(e, t) {
|
|
for (const s of this.iterateCallbacks(e))
|
|
await s(t);
|
|
}
|
|
/**
|
|
* Accepts a callback and returns an iterable of matching plugin callbacks,
|
|
* where each callback is wrapped with the current handler state (i.e. when
|
|
* you call each callback, whatever object parameter you pass it will
|
|
* be merged with the plugin's current state).
|
|
*
|
|
* @param {string} name The name fo the callback to run
|
|
* @return {Array<Function>}
|
|
*/
|
|
*iterateCallbacks(e) {
|
|
for (const t of this._strategy.plugins)
|
|
if (typeof t[e] == "function") {
|
|
const s = this._pluginStateMap.get(t);
|
|
yield (i) => {
|
|
const a = Object.assign(Object.assign({}, i), { state: s });
|
|
return t[e](a);
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Adds a promise to the
|
|
* [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
|
|
* of the event event associated with the request being handled (usually a
|
|
* `FetchEvent`).
|
|
*
|
|
* Note: you can await
|
|
* {@link workbox-strategies.StrategyHandler~doneWaiting}
|
|
* to know when all added promises have settled.
|
|
*
|
|
* @param {Promise} promise A promise to add to the extend lifetime promises
|
|
* of the event that triggered the request.
|
|
*/
|
|
waitUntil(e) {
|
|
return this._extendLifetimePromises.push(e), e;
|
|
}
|
|
/**
|
|
* Returns a promise that resolves once all promises passed to
|
|
* {@link workbox-strategies.StrategyHandler~waitUntil}
|
|
* have settled.
|
|
*
|
|
* Note: any work done after `doneWaiting()` settles should be manually
|
|
* passed to an event's `waitUntil()` method (not this handler's
|
|
* `waitUntil()` method), otherwise the service worker thread my be killed
|
|
* prior to your work completing.
|
|
*/
|
|
async doneWaiting() {
|
|
let e;
|
|
for (; e = this._extendLifetimePromises.shift(); )
|
|
await e;
|
|
}
|
|
/**
|
|
* Stops running the strategy and immediately resolves any pending
|
|
* `waitUntil()` promises.
|
|
*/
|
|
destroy() {
|
|
this._handlerDeferred.resolve(null);
|
|
}
|
|
/**
|
|
* This method will call cacheWillUpdate on the available plugins (or use
|
|
* status === 200) to determine if the Response is safe and valid to cache.
|
|
*
|
|
* @param {Request} options.request
|
|
* @param {Response} options.response
|
|
* @return {Promise<Response|undefined>}
|
|
*
|
|
* @private
|
|
*/
|
|
async _ensureResponseSafeToCache(e) {
|
|
let t = e, s = !1;
|
|
for (const r of this.iterateCallbacks("cacheWillUpdate"))
|
|
if (t = await r({
|
|
request: this.request,
|
|
response: t,
|
|
event: this.event
|
|
}) || void 0, s = !0, !t)
|
|
break;
|
|
return s || t && t.status !== 200 && (t = void 0), t;
|
|
}
|
|
}, $e = class {
|
|
/**
|
|
* Creates a new instance of the strategy and sets all documented option
|
|
* properties as public instance properties.
|
|
*
|
|
* Note: if a custom strategy class extends the base Strategy class and does
|
|
* not need more than these properties, it does not need to define its own
|
|
* constructor.
|
|
*
|
|
* @param {Object} [options]
|
|
* @param {string} [options.cacheName] Cache name to store and retrieve
|
|
* requests. Defaults to the cache names provided by
|
|
* {@link workbox-core.cacheNames}.
|
|
* @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
|
|
* to use in conjunction with this caching strategy.
|
|
* @param {Object} [options.fetchOptions] Values passed along to the
|
|
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
|
|
* of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
|
|
* `fetch()` requests made by this strategy.
|
|
* @param {Object} [options.matchOptions] The
|
|
* [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
|
|
* for any `cache.match()` or `cache.put()` calls made by this strategy.
|
|
*/
|
|
constructor(e = {}) {
|
|
this.cacheName = A.getRuntimeName(e.cacheName), this.plugins = e.plugins || [], this.fetchOptions = e.fetchOptions, this.matchOptions = e.matchOptions;
|
|
}
|
|
/**
|
|
* Perform a request strategy and returns a `Promise` that will resolve with
|
|
* a `Response`, invoking all relevant plugin callbacks.
|
|
*
|
|
* When a strategy instance is registered with a Workbox
|
|
* {@link workbox-routing.Route}, this method is automatically
|
|
* called when the route matches.
|
|
*
|
|
* Alternatively, this method can be used in a standalone `FetchEvent`
|
|
* listener by passing it to `event.respondWith()`.
|
|
*
|
|
* @param {FetchEvent|Object} options A `FetchEvent` or an object with the
|
|
* properties listed below.
|
|
* @param {Request|string} options.request A request to run this strategy for.
|
|
* @param {ExtendableEvent} options.event The event associated with the
|
|
* request.
|
|
* @param {URL} [options.url]
|
|
* @param {*} [options.params]
|
|
*/
|
|
handle(e) {
|
|
const [t] = this.handleAll(e);
|
|
return t;
|
|
}
|
|
/**
|
|
* Similar to {@link workbox-strategies.Strategy~handle}, but
|
|
* instead of just returning a `Promise` that resolves to a `Response` it
|
|
* it will return an tuple of `[response, done]` promises, where the former
|
|
* (`response`) is equivalent to what `handle()` returns, and the latter is a
|
|
* Promise that will resolve once any promises that were added to
|
|
* `event.waitUntil()` as part of performing the strategy have completed.
|
|
*
|
|
* You can await the `done` promise to ensure any extra work performed by
|
|
* the strategy (usually caching responses) completes successfully.
|
|
*
|
|
* @param {FetchEvent|Object} options A `FetchEvent` or an object with the
|
|
* properties listed below.
|
|
* @param {Request|string} options.request A request to run this strategy for.
|
|
* @param {ExtendableEvent} options.event The event associated with the
|
|
* request.
|
|
* @param {URL} [options.url]
|
|
* @param {*} [options.params]
|
|
* @return {Array<Promise>} A tuple of [response, done]
|
|
* promises that can be used to determine when the response resolves as
|
|
* well as when the handler has completed all its work.
|
|
*/
|
|
handleAll(e) {
|
|
e instanceof FetchEvent && (e = {
|
|
event: e,
|
|
request: e.request
|
|
});
|
|
const t = e.event, s = typeof e.request == "string" ? new Request(e.request) : e.request, r = "params" in e ? e.params : void 0, i = new Be(this, { event: t, request: s, params: r }), a = this._getResponse(i, s, t), c = this._awaitComplete(a, i, s, t);
|
|
return [a, c];
|
|
}
|
|
async _getResponse(e, t, s) {
|
|
await e.runCallbacks("handlerWillStart", { event: s, request: t });
|
|
let r;
|
|
try {
|
|
if (r = await this._handle(t, e), !r || r.type === "error")
|
|
throw new u("no-response", { url: t.url });
|
|
} catch (i) {
|
|
if (i instanceof Error) {
|
|
for (const a of e.iterateCallbacks("handlerDidError"))
|
|
if (r = await a({ error: i, event: s, request: t }), r)
|
|
break;
|
|
}
|
|
if (!r)
|
|
throw i;
|
|
}
|
|
for (const i of e.iterateCallbacks("handlerWillRespond"))
|
|
r = await i({ event: s, request: t, response: r });
|
|
return r;
|
|
}
|
|
async _awaitComplete(e, t, s, r) {
|
|
let i, a;
|
|
try {
|
|
i = await e;
|
|
} catch {
|
|
}
|
|
try {
|
|
await t.runCallbacks("handlerDidRespond", {
|
|
event: r,
|
|
request: s,
|
|
response: i
|
|
}), await t.doneWaiting();
|
|
} catch (c) {
|
|
c instanceof Error && (a = c);
|
|
}
|
|
if (await t.runCallbacks("handlerDidComplete", {
|
|
event: r,
|
|
request: s,
|
|
response: i,
|
|
error: a
|
|
}), t.destroy(), a)
|
|
throw a;
|
|
}
|
|
};
|
|
class m extends $e {
|
|
/**
|
|
*
|
|
* @param {Object} [options]
|
|
* @param {string} [options.cacheName] Cache name to store and retrieve
|
|
* requests. Defaults to the cache names provided by
|
|
* {@link workbox-core.cacheNames}.
|
|
* @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}
|
|
* to use in conjunction with this caching strategy.
|
|
* @param {Object} [options.fetchOptions] Values passed along to the
|
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}
|
|
* of all fetch() requests made by this strategy.
|
|
* @param {Object} [options.matchOptions] The
|
|
* {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}
|
|
* for any `cache.match()` or `cache.put()` calls made by this strategy.
|
|
* @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
|
|
* get the response from the network if there's a precache miss.
|
|
*/
|
|
constructor(e = {}) {
|
|
e.cacheName = A.getPrecacheName(e.cacheName), super(e), this._fallbackToNetwork = e.fallbackToNetwork !== !1, this.plugins.push(m.copyRedirectedCacheableResponsesPlugin);
|
|
}
|
|
/**
|
|
* @private
|
|
* @param {Request|string} request A request to run this strategy for.
|
|
* @param {workbox-strategies.StrategyHandler} handler The event that
|
|
* triggered the request.
|
|
* @return {Promise<Response>}
|
|
*/
|
|
async _handle(e, t) {
|
|
const s = await t.cacheMatch(e);
|
|
return s || (t.event && t.event.type === "install" ? await this._handleInstall(e, t) : await this._handleFetch(e, t));
|
|
}
|
|
async _handleFetch(e, t) {
|
|
let s;
|
|
const r = t.params || {};
|
|
if (this._fallbackToNetwork) {
|
|
const i = r.integrity, a = e.integrity, c = !a || a === i;
|
|
s = await t.fetch(new Request(e, {
|
|
integrity: e.mode !== "no-cors" ? a || i : void 0
|
|
})), i && c && e.mode !== "no-cors" && (this._useDefaultCacheabilityPluginIfNeeded(), await t.cachePut(e, s.clone()));
|
|
} else
|
|
throw new u("missing-precache-entry", {
|
|
cacheName: this.cacheName,
|
|
url: e.url
|
|
});
|
|
return s;
|
|
}
|
|
async _handleInstall(e, t) {
|
|
this._useDefaultCacheabilityPluginIfNeeded();
|
|
const s = await t.fetch(e);
|
|
if (!await t.cachePut(e, s.clone()))
|
|
throw new u("bad-precaching-response", {
|
|
url: e.url,
|
|
status: s.status
|
|
});
|
|
return s;
|
|
}
|
|
/**
|
|
* This method is complex, as there a number of things to account for:
|
|
*
|
|
* The `plugins` array can be set at construction, and/or it might be added to
|
|
* to at any time before the strategy is used.
|
|
*
|
|
* At the time the strategy is used (i.e. during an `install` event), there
|
|
* needs to be at least one plugin that implements `cacheWillUpdate` in the
|
|
* array, other than `copyRedirectedCacheableResponsesPlugin`.
|
|
*
|
|
* - If this method is called and there are no suitable `cacheWillUpdate`
|
|
* plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
|
|
*
|
|
* - If this method is called and there is exactly one `cacheWillUpdate`, then
|
|
* we don't have to do anything (this might be a previously added
|
|
* `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
|
|
*
|
|
* - If this method is called and there is more than one `cacheWillUpdate`,
|
|
* then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
|
|
* we need to remove it. (This situation is unlikely, but it could happen if
|
|
* the strategy is used multiple times, the first without a `cacheWillUpdate`,
|
|
* and then later on after manually adding a custom `cacheWillUpdate`.)
|
|
*
|
|
* See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
|
|
*
|
|
* @private
|
|
*/
|
|
_useDefaultCacheabilityPluginIfNeeded() {
|
|
let e = null, t = 0;
|
|
for (const [s, r] of this.plugins.entries())
|
|
r !== m.copyRedirectedCacheableResponsesPlugin && (r === m.defaultPrecacheCacheabilityPlugin && (e = s), r.cacheWillUpdate && t++);
|
|
t === 0 ? this.plugins.push(m.defaultPrecacheCacheabilityPlugin) : t > 1 && e !== null && this.plugins.splice(e, 1);
|
|
}
|
|
}
|
|
m.defaultPrecacheCacheabilityPlugin = {
|
|
async cacheWillUpdate({ response: n }) {
|
|
return !n || n.status >= 400 ? null : n;
|
|
}
|
|
};
|
|
m.copyRedirectedCacheableResponsesPlugin = {
|
|
async cacheWillUpdate({ response: n }) {
|
|
return n.redirected ? await Ke(n) : n;
|
|
}
|
|
};
|
|
class Fe {
|
|
/**
|
|
* Create a new PrecacheController.
|
|
*
|
|
* @param {Object} [options]
|
|
* @param {string} [options.cacheName] The cache to use for precaching.
|
|
* @param {string} [options.plugins] Plugins to use when precaching as well
|
|
* as responding to fetch events for precached assets.
|
|
* @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
|
|
* get the response from the network if there's a precache miss.
|
|
*/
|
|
constructor({ cacheName: e, plugins: t = [], fallbackToNetwork: s = !0 } = {}) {
|
|
this._urlsToCacheKeys = /* @__PURE__ */ new Map(), this._urlsToCacheModes = /* @__PURE__ */ new Map(), this._cacheKeysToIntegrities = /* @__PURE__ */ new Map(), this._strategy = new m({
|
|
cacheName: A.getPrecacheName(e),
|
|
plugins: [
|
|
...t,
|
|
new Oe({ precacheController: this })
|
|
],
|
|
fallbackToNetwork: s
|
|
}), this.install = this.install.bind(this), this.activate = this.activate.bind(this);
|
|
}
|
|
/**
|
|
* @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and
|
|
* used to cache assets and respond to fetch events.
|
|
*/
|
|
get strategy() {
|
|
return this._strategy;
|
|
}
|
|
/**
|
|
* Adds items to the precache list, removing any duplicates and
|
|
* stores the files in the
|
|
* {@link workbox-core.cacheNames|"precache cache"} when the service
|
|
* worker installs.
|
|
*
|
|
* This method can be called multiple times.
|
|
*
|
|
* @param {Array<Object|string>} [entries=[]] Array of entries to precache.
|
|
*/
|
|
precache(e) {
|
|
this.addToCacheList(e), this._installAndActiveListenersAdded || (self.addEventListener("install", this.install), self.addEventListener("activate", this.activate), this._installAndActiveListenersAdded = !0);
|
|
}
|
|
/**
|
|
* This method will add items to the precache list, removing duplicates
|
|
* and ensuring the information is valid.
|
|
*
|
|
* @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries
|
|
* Array of entries to precache.
|
|
*/
|
|
addToCacheList(e) {
|
|
const t = [];
|
|
for (const s of e) {
|
|
typeof s == "string" ? t.push(s) : s && s.revision === void 0 && t.push(s.url);
|
|
const { cacheKey: r, url: i } = Me(s), a = typeof s != "string" && s.revision ? "reload" : "default";
|
|
if (this._urlsToCacheKeys.has(i) && this._urlsToCacheKeys.get(i) !== r)
|
|
throw new u("add-to-cache-list-conflicting-entries", {
|
|
firstEntry: this._urlsToCacheKeys.get(i),
|
|
secondEntry: r
|
|
});
|
|
if (typeof s != "string" && s.integrity) {
|
|
if (this._cacheKeysToIntegrities.has(r) && this._cacheKeysToIntegrities.get(r) !== s.integrity)
|
|
throw new u("add-to-cache-list-conflicting-integrities", {
|
|
url: i
|
|
});
|
|
this._cacheKeysToIntegrities.set(r, s.integrity);
|
|
}
|
|
if (this._urlsToCacheKeys.set(i, r), this._urlsToCacheModes.set(i, a), t.length > 0) {
|
|
const c = `Workbox is precaching URLs without revision info: ${t.join(", ")}
|
|
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;
|
|
console.warn(c);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Precaches new and updated assets. Call this method from the service worker
|
|
* install event.
|
|
*
|
|
* Note: this method calls `event.waitUntil()` for you, so you do not need
|
|
* to call it yourself in your event handlers.
|
|
*
|
|
* @param {ExtendableEvent} event
|
|
* @return {Promise<workbox-precaching.InstallResult>}
|
|
*/
|
|
install(e) {
|
|
return V(e, async () => {
|
|
const t = new Ne();
|
|
this.strategy.plugins.push(t);
|
|
for (const [i, a] of this._urlsToCacheKeys) {
|
|
const c = this._cacheKeysToIntegrities.get(a), o = this._urlsToCacheModes.get(i), l = new Request(i, {
|
|
integrity: c,
|
|
cache: o,
|
|
credentials: "same-origin"
|
|
});
|
|
await Promise.all(this.strategy.handleAll({
|
|
params: { cacheKey: a },
|
|
request: l,
|
|
event: e
|
|
}));
|
|
}
|
|
const { updatedURLs: s, notUpdatedURLs: r } = t;
|
|
return { updatedURLs: s, notUpdatedURLs: r };
|
|
});
|
|
}
|
|
/**
|
|
* Deletes assets that are no longer present in the current precache manifest.
|
|
* Call this method from the service worker activate event.
|
|
*
|
|
* Note: this method calls `event.waitUntil()` for you, so you do not need
|
|
* to call it yourself in your event handlers.
|
|
*
|
|
* @param {ExtendableEvent} event
|
|
* @return {Promise<workbox-precaching.CleanupResult>}
|
|
*/
|
|
activate(e) {
|
|
return V(e, async () => {
|
|
const t = await self.caches.open(this.strategy.cacheName), s = await t.keys(), r = new Set(this._urlsToCacheKeys.values()), i = [];
|
|
for (const a of s)
|
|
r.has(a.url) || (await t.delete(a), i.push(a.url));
|
|
return { deletedURLs: i };
|
|
});
|
|
}
|
|
/**
|
|
* Returns a mapping of a precached URL to the corresponding cache key, taking
|
|
* into account the revision information for the URL.
|
|
*
|
|
* @return {Map<string, string>} A URL to cache key mapping.
|
|
*/
|
|
getURLsToCacheKeys() {
|
|
return this._urlsToCacheKeys;
|
|
}
|
|
/**
|
|
* Returns a list of all the URLs that have been precached by the current
|
|
* service worker.
|
|
*
|
|
* @return {Array<string>} The precached URLs.
|
|
*/
|
|
getCachedURLs() {
|
|
return [...this._urlsToCacheKeys.keys()];
|
|
}
|
|
/**
|
|
* Returns the cache key used for storing a given URL. If that URL is
|
|
* unversioned, like `/index.html', then the cache key will be the original
|
|
* URL with a search parameter appended to it.
|
|
*
|
|
* @param {string} url A URL whose cache key you want to look up.
|
|
* @return {string} The versioned URL that corresponds to a cache key
|
|
* for the original URL, or undefined if that URL isn't precached.
|
|
*/
|
|
getCacheKeyForURL(e) {
|
|
const t = new URL(e, location.href);
|
|
return this._urlsToCacheKeys.get(t.href);
|
|
}
|
|
/**
|
|
* @param {string} url A cache key whose SRI you want to look up.
|
|
* @return {string} The subresource integrity associated with the cache key,
|
|
* or undefined if it's not set.
|
|
*/
|
|
getIntegrityForCacheKey(e) {
|
|
return this._cacheKeysToIntegrities.get(e);
|
|
}
|
|
/**
|
|
* This acts as a drop-in replacement for
|
|
* [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)
|
|
* with the following differences:
|
|
*
|
|
* - It knows what the name of the precache is, and only checks in that cache.
|
|
* - It allows you to pass in an "original" URL without versioning parameters,
|
|
* and it will automatically look up the correct cache key for the currently
|
|
* active revision of that URL.
|
|
*
|
|
* E.g., `matchPrecache('index.html')` will find the correct precached
|
|
* response for the currently active service worker, even if the actual cache
|
|
* key is `'/index.html?__WB_REVISION__=1234abcd'`.
|
|
*
|
|
* @param {string|Request} request The key (without revisioning parameters)
|
|
* to look up in the precache.
|
|
* @return {Promise<Response|undefined>}
|
|
*/
|
|
async matchPrecache(e) {
|
|
const t = e instanceof Request ? e.url : e, s = this.getCacheKeyForURL(t);
|
|
if (s)
|
|
return (await self.caches.open(this.strategy.cacheName)).match(s);
|
|
}
|
|
/**
|
|
* Returns a function that looks up `url` in the precache (taking into
|
|
* account revision information), and returns the corresponding `Response`.
|
|
*
|
|
* @param {string} url The precached URL which will be used to lookup the
|
|
* `Response`.
|
|
* @return {workbox-routing~handlerCallback}
|
|
*/
|
|
createHandlerBoundToURL(e) {
|
|
const t = this.getCacheKeyForURL(e);
|
|
if (!t)
|
|
throw new u("non-precached-url", { url: e });
|
|
return (s) => (s.request = new Request(e), s.params = Object.assign({ cacheKey: t }, s.params), this.strategy.handle(s));
|
|
}
|
|
}
|
|
let I;
|
|
const W = () => (I || (I = new Fe()), I);
|
|
try {
|
|
self["workbox:routing:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
const Z = "GET", L = (n) => n && typeof n == "object" ? n : { handle: n };
|
|
let k = class {
|
|
/**
|
|
* Constructor for Route class.
|
|
*
|
|
* @param {workbox-routing~matchCallback} match
|
|
* A callback function that determines whether the route matches a given
|
|
* `fetch` event by returning a non-falsy value.
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resolving to a Response.
|
|
* @param {string} [method='GET'] The HTTP method to match the Route
|
|
* against.
|
|
*/
|
|
constructor(e, t, s = Z) {
|
|
this.handler = L(t), this.match = e, this.method = s;
|
|
}
|
|
/**
|
|
*
|
|
* @param {workbox-routing-handlerCallback} handler A callback
|
|
* function that returns a Promise resolving to a Response
|
|
*/
|
|
setCatchHandler(e) {
|
|
this.catchHandler = L(e);
|
|
}
|
|
}, Ve = class extends k {
|
|
/**
|
|
* If the regular expression contains
|
|
* [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},
|
|
* the captured values will be passed to the
|
|
* {@link workbox-routing~handlerCallback} `params`
|
|
* argument.
|
|
*
|
|
* @param {RegExp} regExp The regular expression to match against URLs.
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resulting in a Response.
|
|
* @param {string} [method='GET'] The HTTP method to match the Route
|
|
* against.
|
|
*/
|
|
constructor(e, t, s) {
|
|
const r = ({ url: i }) => {
|
|
const a = e.exec(i.href);
|
|
if (a && !(i.origin !== location.origin && a.index !== 0))
|
|
return a.slice(1);
|
|
};
|
|
super(r, t, s);
|
|
}
|
|
}, Ge = class {
|
|
/**
|
|
* Initializes a new Router.
|
|
*/
|
|
constructor() {
|
|
this._routes = /* @__PURE__ */ new Map(), this._defaultHandlerMap = /* @__PURE__ */ new Map();
|
|
}
|
|
/**
|
|
* @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP
|
|
* method name ('GET', etc.) to an array of all the corresponding `Route`
|
|
* instances that are registered.
|
|
*/
|
|
get routes() {
|
|
return this._routes;
|
|
}
|
|
/**
|
|
* Adds a fetch event listener to respond to events when a route matches
|
|
* the event's request.
|
|
*/
|
|
addFetchListener() {
|
|
self.addEventListener("fetch", (e) => {
|
|
const { request: t } = e, s = this.handleRequest({ request: t, event: e });
|
|
s && e.respondWith(s);
|
|
});
|
|
}
|
|
/**
|
|
* Adds a message event listener for URLs to cache from the window.
|
|
* This is useful to cache resources loaded on the page prior to when the
|
|
* service worker started controlling it.
|
|
*
|
|
* The format of the message data sent from the window should be as follows.
|
|
* Where the `urlsToCache` array may consist of URL strings or an array of
|
|
* URL string + `requestInit` object (the same as you'd pass to `fetch()`).
|
|
*
|
|
* ```
|
|
* {
|
|
* type: 'CACHE_URLS',
|
|
* payload: {
|
|
* urlsToCache: [
|
|
* './script1.js',
|
|
* './script2.js',
|
|
* ['./script3.js', {mode: 'no-cors'}],
|
|
* ],
|
|
* },
|
|
* }
|
|
* ```
|
|
*/
|
|
addCacheListener() {
|
|
self.addEventListener("message", (e) => {
|
|
if (e.data && e.data.type === "CACHE_URLS") {
|
|
const { payload: t } = e.data, s = Promise.all(t.urlsToCache.map((r) => {
|
|
typeof r == "string" && (r = [r]);
|
|
const i = new Request(...r);
|
|
return this.handleRequest({ request: i, event: e });
|
|
}));
|
|
e.waitUntil(s), e.ports && e.ports[0] && s.then(() => e.ports[0].postMessage(!0));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Apply the routing rules to a FetchEvent object to get a Response from an
|
|
* appropriate Route's handler.
|
|
*
|
|
* @param {Object} options
|
|
* @param {Request} options.request The request to handle.
|
|
* @param {ExtendableEvent} options.event The event that triggered the
|
|
* request.
|
|
* @return {Promise<Response>|undefined} A promise is returned if a
|
|
* registered route can handle the request. If there is no matching
|
|
* route and there's no `defaultHandler`, `undefined` is returned.
|
|
*/
|
|
handleRequest({ request: e, event: t }) {
|
|
const s = new URL(e.url, location.href);
|
|
if (!s.protocol.startsWith("http"))
|
|
return;
|
|
const r = s.origin === location.origin, { params: i, route: a } = this.findMatchingRoute({
|
|
event: t,
|
|
request: e,
|
|
sameOrigin: r,
|
|
url: s
|
|
});
|
|
let c = a && a.handler;
|
|
const o = e.method;
|
|
if (!c && this._defaultHandlerMap.has(o) && (c = this._defaultHandlerMap.get(o)), !c)
|
|
return;
|
|
let l;
|
|
try {
|
|
l = c.handle({ url: s, request: e, event: t, params: i });
|
|
} catch (h) {
|
|
l = Promise.reject(h);
|
|
}
|
|
const f = a && a.catchHandler;
|
|
return l instanceof Promise && (this._catchHandler || f) && (l = l.catch(async (h) => {
|
|
if (f)
|
|
try {
|
|
return await f.handle({ url: s, request: e, event: t, params: i });
|
|
} catch (y) {
|
|
y instanceof Error && (h = y);
|
|
}
|
|
if (this._catchHandler)
|
|
return this._catchHandler.handle({ url: s, request: e, event: t });
|
|
throw h;
|
|
})), l;
|
|
}
|
|
/**
|
|
* Checks a request and URL (and optionally an event) against the list of
|
|
* registered routes, and if there's a match, returns the corresponding
|
|
* route along with any params generated by the match.
|
|
*
|
|
* @param {Object} options
|
|
* @param {URL} options.url
|
|
* @param {boolean} options.sameOrigin The result of comparing `url.origin`
|
|
* against the current origin.
|
|
* @param {Request} options.request The request to match.
|
|
* @param {Event} options.event The corresponding event.
|
|
* @return {Object} An object with `route` and `params` properties.
|
|
* They are populated if a matching route was found or `undefined`
|
|
* otherwise.
|
|
*/
|
|
findMatchingRoute({ url: e, sameOrigin: t, request: s, event: r }) {
|
|
const i = this._routes.get(s.method) || [];
|
|
for (const a of i) {
|
|
let c;
|
|
const o = a.match({ url: e, sameOrigin: t, request: s, event: r });
|
|
if (o)
|
|
return c = o, (Array.isArray(c) && c.length === 0 || o.constructor === Object && // eslint-disable-line
|
|
Object.keys(o).length === 0 || typeof o == "boolean") && (c = void 0), { route: a, params: c };
|
|
}
|
|
return {};
|
|
}
|
|
/**
|
|
* Define a default `handler` that's called when no routes explicitly
|
|
* match the incoming request.
|
|
*
|
|
* Each HTTP method ('GET', 'POST', etc.) gets its own default handler.
|
|
*
|
|
* Without a default handler, unmatched requests will go against the
|
|
* network as if there were no service worker present.
|
|
*
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resulting in a Response.
|
|
* @param {string} [method='GET'] The HTTP method to associate with this
|
|
* default handler. Each method has its own default.
|
|
*/
|
|
setDefaultHandler(e, t = Z) {
|
|
this._defaultHandlerMap.set(t, L(e));
|
|
}
|
|
/**
|
|
* If a Route throws an error while handling a request, this `handler`
|
|
* will be called and given a chance to provide a response.
|
|
*
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resulting in a Response.
|
|
*/
|
|
setCatchHandler(e) {
|
|
this._catchHandler = L(e);
|
|
}
|
|
/**
|
|
* Registers a route with the router.
|
|
*
|
|
* @param {workbox-routing.Route} route The route to register.
|
|
*/
|
|
registerRoute(e) {
|
|
this._routes.has(e.method) || this._routes.set(e.method, []), this._routes.get(e.method).push(e);
|
|
}
|
|
/**
|
|
* Unregisters a route with the router.
|
|
*
|
|
* @param {workbox-routing.Route} route The route to unregister.
|
|
*/
|
|
unregisterRoute(e) {
|
|
if (!this._routes.has(e.method))
|
|
throw new u("unregister-route-but-not-found-with-method", {
|
|
method: e.method
|
|
});
|
|
const t = this._routes.get(e.method).indexOf(e);
|
|
if (t > -1)
|
|
this._routes.get(e.method).splice(t, 1);
|
|
else
|
|
throw new u("unregister-route-route-not-registered");
|
|
}
|
|
}, C;
|
|
const Qe = () => (C || (C = new Ge(), C.addFetchListener(), C.addCacheListener()), C);
|
|
function ze(n, e, t) {
|
|
let s;
|
|
if (typeof n == "string") {
|
|
const i = new URL(n, location.href), a = ({ url: c }) => c.href === i.href;
|
|
s = new k(a, e, t);
|
|
} else if (n instanceof RegExp)
|
|
s = new Ve(n, e, t);
|
|
else if (typeof n == "function")
|
|
s = new k(n, e, t);
|
|
else if (n instanceof k)
|
|
s = n;
|
|
else
|
|
throw new u("unsupported-route-type", {
|
|
moduleName: "workbox-routing",
|
|
funcName: "registerRoute",
|
|
paramName: "capture"
|
|
});
|
|
return Qe().registerRoute(s), s;
|
|
}
|
|
function Je(n, e = []) {
|
|
for (const t of [...n.searchParams.keys()])
|
|
e.some((s) => s.test(t)) && n.searchParams.delete(t);
|
|
return n;
|
|
}
|
|
function* Xe(n, { ignoreURLParametersMatching: e = [/^utm_/, /^fbclid$/], directoryIndex: t = "index.html", cleanURLs: s = !0, urlManipulation: r } = {}) {
|
|
const i = new URL(n, location.href);
|
|
i.hash = "", yield i.href;
|
|
const a = Je(i, e);
|
|
if (yield a.href, t && a.pathname.endsWith("/")) {
|
|
const c = new URL(a.href);
|
|
c.pathname += t, yield c.href;
|
|
}
|
|
if (s) {
|
|
const c = new URL(a.href);
|
|
c.pathname += ".html", yield c.href;
|
|
}
|
|
if (r) {
|
|
const c = r({ url: i });
|
|
for (const o of c)
|
|
yield o.href;
|
|
}
|
|
}
|
|
class Ye extends k {
|
|
/**
|
|
* @param {PrecacheController} precacheController A `PrecacheController`
|
|
* instance used to both match requests and respond to fetch events.
|
|
* @param {Object} [options] Options to control how requests are matched
|
|
* against the list of precached URLs.
|
|
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
|
|
* check cache entries for a URLs ending with '/' to see if there is a hit when
|
|
* appending the `directoryIndex` value.
|
|
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An
|
|
* array of regex's to remove search params when looking for a cache match.
|
|
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will
|
|
* check the cache for the URL with a `.html` added to the end of the end.
|
|
* @param {workbox-precaching~urlManipulation} [options.urlManipulation]
|
|
* This is a function that should take a URL and return an array of
|
|
* alternative URLs that should be checked for precache matches.
|
|
*/
|
|
constructor(e, t) {
|
|
const s = ({ request: r }) => {
|
|
const i = e.getURLsToCacheKeys();
|
|
for (const a of Xe(r.url, t)) {
|
|
const c = i.get(a);
|
|
if (c) {
|
|
const o = e.getIntegrityForCacheKey(c);
|
|
return { cacheKey: c, integrity: o };
|
|
}
|
|
}
|
|
};
|
|
super(s, e.strategy);
|
|
}
|
|
}
|
|
function Ze(n) {
|
|
const e = W(), t = new Ye(e, n);
|
|
ze(t);
|
|
}
|
|
function et(n) {
|
|
return W().createHandlerBoundToURL(n);
|
|
}
|
|
function tt(n) {
|
|
W().precache(n);
|
|
}
|
|
function st(n, e) {
|
|
tt(n), Ze(e);
|
|
}
|
|
try {
|
|
self["workbox:routing:6.5.3"] && _();
|
|
} catch {
|
|
}
|
|
const ee = "GET", v = (n) => n && typeof n == "object" ? n : { handle: n };
|
|
class U {
|
|
/**
|
|
* Constructor for Route class.
|
|
*
|
|
* @param {workbox-routing~matchCallback} match
|
|
* A callback function that determines whether the route matches a given
|
|
* `fetch` event by returning a non-falsy value.
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resolving to a Response.
|
|
* @param {string} [method='GET'] The HTTP method to match the Route
|
|
* against.
|
|
*/
|
|
constructor(e, t, s = ee) {
|
|
this.handler = v(t), this.match = e, this.method = s;
|
|
}
|
|
/**
|
|
*
|
|
* @param {workbox-routing-handlerCallback} handler A callback
|
|
* function that returns a Promise resolving to a Response
|
|
*/
|
|
setCatchHandler(e) {
|
|
this.catchHandler = v(e);
|
|
}
|
|
}
|
|
class nt extends U {
|
|
/**
|
|
* If the regular expression contains
|
|
* [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},
|
|
* the captured values will be passed to the
|
|
* {@link workbox-routing~handlerCallback} `params`
|
|
* argument.
|
|
*
|
|
* @param {RegExp} regExp The regular expression to match against URLs.
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resulting in a Response.
|
|
* @param {string} [method='GET'] The HTTP method to match the Route
|
|
* against.
|
|
*/
|
|
constructor(e, t, s) {
|
|
const r = ({ url: i }) => {
|
|
const a = e.exec(i.href);
|
|
if (a && !(i.origin !== location.origin && a.index !== 0))
|
|
return a.slice(1);
|
|
};
|
|
super(r, t, s);
|
|
}
|
|
}
|
|
class rt {
|
|
/**
|
|
* Initializes a new Router.
|
|
*/
|
|
constructor() {
|
|
this._routes = /* @__PURE__ */ new Map(), this._defaultHandlerMap = /* @__PURE__ */ new Map();
|
|
}
|
|
/**
|
|
* @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP
|
|
* method name ('GET', etc.) to an array of all the corresponding `Route`
|
|
* instances that are registered.
|
|
*/
|
|
get routes() {
|
|
return this._routes;
|
|
}
|
|
/**
|
|
* Adds a fetch event listener to respond to events when a route matches
|
|
* the event's request.
|
|
*/
|
|
addFetchListener() {
|
|
self.addEventListener("fetch", (e) => {
|
|
const { request: t } = e, s = this.handleRequest({ request: t, event: e });
|
|
s && e.respondWith(s);
|
|
});
|
|
}
|
|
/**
|
|
* Adds a message event listener for URLs to cache from the window.
|
|
* This is useful to cache resources loaded on the page prior to when the
|
|
* service worker started controlling it.
|
|
*
|
|
* The format of the message data sent from the window should be as follows.
|
|
* Where the `urlsToCache` array may consist of URL strings or an array of
|
|
* URL string + `requestInit` object (the same as you'd pass to `fetch()`).
|
|
*
|
|
* ```
|
|
* {
|
|
* type: 'CACHE_URLS',
|
|
* payload: {
|
|
* urlsToCache: [
|
|
* './script1.js',
|
|
* './script2.js',
|
|
* ['./script3.js', {mode: 'no-cors'}],
|
|
* ],
|
|
* },
|
|
* }
|
|
* ```
|
|
*/
|
|
addCacheListener() {
|
|
self.addEventListener("message", (e) => {
|
|
if (e.data && e.data.type === "CACHE_URLS") {
|
|
const { payload: t } = e.data, s = Promise.all(t.urlsToCache.map((r) => {
|
|
typeof r == "string" && (r = [r]);
|
|
const i = new Request(...r);
|
|
return this.handleRequest({ request: i, event: e });
|
|
}));
|
|
e.waitUntil(s), e.ports && e.ports[0] && s.then(() => e.ports[0].postMessage(!0));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Apply the routing rules to a FetchEvent object to get a Response from an
|
|
* appropriate Route's handler.
|
|
*
|
|
* @param {Object} options
|
|
* @param {Request} options.request The request to handle.
|
|
* @param {ExtendableEvent} options.event The event that triggered the
|
|
* request.
|
|
* @return {Promise<Response>|undefined} A promise is returned if a
|
|
* registered route can handle the request. If there is no matching
|
|
* route and there's no `defaultHandler`, `undefined` is returned.
|
|
*/
|
|
handleRequest({ request: e, event: t }) {
|
|
const s = new URL(e.url, location.href);
|
|
if (!s.protocol.startsWith("http"))
|
|
return;
|
|
const r = s.origin === location.origin, { params: i, route: a } = this.findMatchingRoute({
|
|
event: t,
|
|
request: e,
|
|
sameOrigin: r,
|
|
url: s
|
|
});
|
|
let c = a && a.handler;
|
|
const o = e.method;
|
|
if (!c && this._defaultHandlerMap.has(o) && (c = this._defaultHandlerMap.get(o)), !c)
|
|
return;
|
|
let l;
|
|
try {
|
|
l = c.handle({ url: s, request: e, event: t, params: i });
|
|
} catch (h) {
|
|
l = Promise.reject(h);
|
|
}
|
|
const f = a && a.catchHandler;
|
|
return l instanceof Promise && (this._catchHandler || f) && (l = l.catch(async (h) => {
|
|
if (f)
|
|
try {
|
|
return await f.handle({ url: s, request: e, event: t, params: i });
|
|
} catch (y) {
|
|
y instanceof Error && (h = y);
|
|
}
|
|
if (this._catchHandler)
|
|
return this._catchHandler.handle({ url: s, request: e, event: t });
|
|
throw h;
|
|
})), l;
|
|
}
|
|
/**
|
|
* Checks a request and URL (and optionally an event) against the list of
|
|
* registered routes, and if there's a match, returns the corresponding
|
|
* route along with any params generated by the match.
|
|
*
|
|
* @param {Object} options
|
|
* @param {URL} options.url
|
|
* @param {boolean} options.sameOrigin The result of comparing `url.origin`
|
|
* against the current origin.
|
|
* @param {Request} options.request The request to match.
|
|
* @param {Event} options.event The corresponding event.
|
|
* @return {Object} An object with `route` and `params` properties.
|
|
* They are populated if a matching route was found or `undefined`
|
|
* otherwise.
|
|
*/
|
|
findMatchingRoute({ url: e, sameOrigin: t, request: s, event: r }) {
|
|
const i = this._routes.get(s.method) || [];
|
|
for (const a of i) {
|
|
let c;
|
|
const o = a.match({ url: e, sameOrigin: t, request: s, event: r });
|
|
if (o)
|
|
return c = o, (Array.isArray(c) && c.length === 0 || o.constructor === Object && // eslint-disable-line
|
|
Object.keys(o).length === 0 || typeof o == "boolean") && (c = void 0), { route: a, params: c };
|
|
}
|
|
return {};
|
|
}
|
|
/**
|
|
* Define a default `handler` that's called when no routes explicitly
|
|
* match the incoming request.
|
|
*
|
|
* Each HTTP method ('GET', 'POST', etc.) gets its own default handler.
|
|
*
|
|
* Without a default handler, unmatched requests will go against the
|
|
* network as if there were no service worker present.
|
|
*
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resulting in a Response.
|
|
* @param {string} [method='GET'] The HTTP method to associate with this
|
|
* default handler. Each method has its own default.
|
|
*/
|
|
setDefaultHandler(e, t = ee) {
|
|
this._defaultHandlerMap.set(t, v(e));
|
|
}
|
|
/**
|
|
* If a Route throws an error while handling a request, this `handler`
|
|
* will be called and given a chance to provide a response.
|
|
*
|
|
* @param {workbox-routing~handlerCallback} handler A callback
|
|
* function that returns a Promise resulting in a Response.
|
|
*/
|
|
setCatchHandler(e) {
|
|
this._catchHandler = v(e);
|
|
}
|
|
/**
|
|
* Registers a route with the router.
|
|
*
|
|
* @param {workbox-routing.Route} route The route to register.
|
|
*/
|
|
registerRoute(e) {
|
|
this._routes.has(e.method) || this._routes.set(e.method, []), this._routes.get(e.method).push(e);
|
|
}
|
|
/**
|
|
* Unregisters a route with the router.
|
|
*
|
|
* @param {workbox-routing.Route} route The route to unregister.
|
|
*/
|
|
unregisterRoute(e) {
|
|
if (!this._routes.has(e.method))
|
|
throw new w("unregister-route-but-not-found-with-method", {
|
|
method: e.method
|
|
});
|
|
const t = this._routes.get(e.method).indexOf(e);
|
|
if (t > -1)
|
|
this._routes.get(e.method).splice(t, 1);
|
|
else
|
|
throw new w("unregister-route-route-not-registered");
|
|
}
|
|
}
|
|
let x;
|
|
const at = () => (x || (x = new rt(), x.addFetchListener(), x.addCacheListener()), x);
|
|
function te(n, e, t) {
|
|
let s;
|
|
if (typeof n == "string") {
|
|
const i = new URL(n, location.href), a = ({ url: c }) => c.href === i.href;
|
|
s = new U(a, e, t);
|
|
} else if (n instanceof RegExp)
|
|
s = new nt(n, e, t);
|
|
else if (typeof n == "function")
|
|
s = new U(n, e, t);
|
|
else if (n instanceof U)
|
|
s = n;
|
|
else
|
|
throw new w("unsupported-route-type", {
|
|
moduleName: "workbox-routing",
|
|
funcName: "registerRoute",
|
|
paramName: "capture"
|
|
});
|
|
return at().registerRoute(s), s;
|
|
}
|
|
try {
|
|
self["workbox:strategies:6.5.3"] && _();
|
|
} catch {
|
|
}
|
|
function D(n) {
|
|
return typeof n == "string" ? new Request(n) : n;
|
|
}
|
|
class it {
|
|
/**
|
|
* Creates a new instance associated with the passed strategy and event
|
|
* that's handling the request.
|
|
*
|
|
* The constructor also initializes the state that will be passed to each of
|
|
* the plugins handling this request.
|
|
*
|
|
* @param {workbox-strategies.Strategy} strategy
|
|
* @param {Object} options
|
|
* @param {Request|string} options.request A request to run this strategy for.
|
|
* @param {ExtendableEvent} options.event The event associated with the
|
|
* request.
|
|
* @param {URL} [options.url]
|
|
* @param {*} [options.params] The return value from the
|
|
* {@link workbox-routing~matchCallback} (if applicable).
|
|
*/
|
|
constructor(e, t) {
|
|
this._cacheKeys = {}, Object.assign(this, t), this.event = t.event, this._strategy = e, this._handlerDeferred = new ce(), this._extendLifetimePromises = [], this._plugins = [...e.plugins], this._pluginStateMap = /* @__PURE__ */ new Map();
|
|
for (const s of this._plugins)
|
|
this._pluginStateMap.set(s, {});
|
|
this.event.waitUntil(this._handlerDeferred.promise);
|
|
}
|
|
/**
|
|
* Fetches a given request (and invokes any applicable plugin callback
|
|
* methods) using the `fetchOptions` (for non-navigation requests) and
|
|
* `plugins` defined on the `Strategy` object.
|
|
*
|
|
* The following plugin lifecycle methods are invoked when using this method:
|
|
* - `requestWillFetch()`
|
|
* - `fetchDidSucceed()`
|
|
* - `fetchDidFail()`
|
|
*
|
|
* @param {Request|string} input The URL or request to fetch.
|
|
* @return {Promise<Response>}
|
|
*/
|
|
async fetch(e) {
|
|
const { event: t } = this;
|
|
let s = D(e);
|
|
if (s.mode === "navigate" && t instanceof FetchEvent && t.preloadResponse) {
|
|
const a = await t.preloadResponse;
|
|
if (a)
|
|
return a;
|
|
}
|
|
const r = this.hasCallback("fetchDidFail") ? s.clone() : null;
|
|
try {
|
|
for (const a of this.iterateCallbacks("requestWillFetch"))
|
|
s = await a({ request: s.clone(), event: t });
|
|
} catch (a) {
|
|
if (a instanceof Error)
|
|
throw new w("plugin-error-request-will-fetch", {
|
|
thrownErrorMessage: a.message
|
|
});
|
|
}
|
|
const i = s.clone();
|
|
try {
|
|
let a;
|
|
a = await fetch(s, s.mode === "navigate" ? void 0 : this._strategy.fetchOptions);
|
|
for (const c of this.iterateCallbacks("fetchDidSucceed"))
|
|
a = await c({
|
|
event: t,
|
|
request: i,
|
|
response: a
|
|
});
|
|
return a;
|
|
} catch (a) {
|
|
throw r && await this.runCallbacks("fetchDidFail", {
|
|
error: a,
|
|
event: t,
|
|
originalRequest: r.clone(),
|
|
request: i.clone()
|
|
}), a;
|
|
}
|
|
}
|
|
/**
|
|
* Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
|
|
* the response generated by `this.fetch()`.
|
|
*
|
|
* The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
|
|
* so you do not have to manually call `waitUntil()` on the event.
|
|
*
|
|
* @param {Request|string} input The request or URL to fetch and cache.
|
|
* @return {Promise<Response>}
|
|
*/
|
|
async fetchAndCachePut(e) {
|
|
const t = await this.fetch(e), s = t.clone();
|
|
return this.waitUntil(this.cachePut(e, s)), t;
|
|
}
|
|
/**
|
|
* Matches a request from the cache (and invokes any applicable plugin
|
|
* callback methods) using the `cacheName`, `matchOptions`, and `plugins`
|
|
* defined on the strategy object.
|
|
*
|
|
* The following plugin lifecycle methods are invoked when using this method:
|
|
* - cacheKeyWillByUsed()
|
|
* - cachedResponseWillByUsed()
|
|
*
|
|
* @param {Request|string} key The Request or URL to use as the cache key.
|
|
* @return {Promise<Response|undefined>} A matching response, if found.
|
|
*/
|
|
async cacheMatch(e) {
|
|
const t = D(e);
|
|
let s;
|
|
const { cacheName: r, matchOptions: i } = this._strategy, a = await this.getCacheKey(t, "read"), c = Object.assign(Object.assign({}, i), { cacheName: r });
|
|
s = await caches.match(a, c);
|
|
for (const o of this.iterateCallbacks("cachedResponseWillBeUsed"))
|
|
s = await o({
|
|
cacheName: r,
|
|
matchOptions: i,
|
|
cachedResponse: s,
|
|
request: a,
|
|
event: this.event
|
|
}) || void 0;
|
|
return s;
|
|
}
|
|
/**
|
|
* Puts a request/response pair in the cache (and invokes any applicable
|
|
* plugin callback methods) using the `cacheName` and `plugins` defined on
|
|
* the strategy object.
|
|
*
|
|
* The following plugin lifecycle methods are invoked when using this method:
|
|
* - cacheKeyWillByUsed()
|
|
* - cacheWillUpdate()
|
|
* - cacheDidUpdate()
|
|
*
|
|
* @param {Request|string} key The request or URL to use as the cache key.
|
|
* @param {Response} response The response to cache.
|
|
* @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
|
|
* not be cached, and `true` otherwise.
|
|
*/
|
|
async cachePut(e, t) {
|
|
const s = D(e);
|
|
await he(0);
|
|
const r = await this.getCacheKey(s, "write");
|
|
if (!t)
|
|
throw new w("cache-put-with-no-response", {
|
|
url: le(r.url)
|
|
});
|
|
const i = await this._ensureResponseSafeToCache(t);
|
|
if (!i)
|
|
return !1;
|
|
const { cacheName: a, matchOptions: c } = this._strategy, o = await self.caches.open(a), l = this.hasCallback("cacheDidUpdate"), f = l ? await ie(
|
|
// TODO(philipwalton): the `__WB_REVISION__` param is a precaching
|
|
// feature. Consider into ways to only add this behavior if using
|
|
// precaching.
|
|
o,
|
|
r.clone(),
|
|
["__WB_REVISION__"],
|
|
c
|
|
) : null;
|
|
try {
|
|
await o.put(r, l ? i.clone() : i);
|
|
} catch (h) {
|
|
if (h instanceof Error)
|
|
throw h.name === "QuotaExceededError" && await oe(), h;
|
|
}
|
|
for (const h of this.iterateCallbacks("cacheDidUpdate"))
|
|
await h({
|
|
cacheName: a,
|
|
oldResponse: f,
|
|
newResponse: i.clone(),
|
|
request: r,
|
|
event: this.event
|
|
});
|
|
return !0;
|
|
}
|
|
/**
|
|
* Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
|
|
* executes any of those callbacks found in sequence. The final `Request`
|
|
* object returned by the last plugin is treated as the cache key for cache
|
|
* reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
|
|
* been registered, the passed request is returned unmodified
|
|
*
|
|
* @param {Request} request
|
|
* @param {string} mode
|
|
* @return {Promise<Request>}
|
|
*/
|
|
async getCacheKey(e, t) {
|
|
const s = `${e.url} | ${t}`;
|
|
if (!this._cacheKeys[s]) {
|
|
let r = e;
|
|
for (const i of this.iterateCallbacks("cacheKeyWillBeUsed"))
|
|
r = D(await i({
|
|
mode: t,
|
|
request: r,
|
|
event: this.event,
|
|
// params has a type any can't change right now.
|
|
params: this.params
|
|
// eslint-disable-line
|
|
}));
|
|
this._cacheKeys[s] = r;
|
|
}
|
|
return this._cacheKeys[s];
|
|
}
|
|
/**
|
|
* Returns true if the strategy has at least one plugin with the given
|
|
* callback.
|
|
*
|
|
* @param {string} name The name of the callback to check for.
|
|
* @return {boolean}
|
|
*/
|
|
hasCallback(e) {
|
|
for (const t of this._strategy.plugins)
|
|
if (e in t)
|
|
return !0;
|
|
return !1;
|
|
}
|
|
/**
|
|
* Runs all plugin callbacks matching the given name, in order, passing the
|
|
* given param object (merged ith the current plugin state) as the only
|
|
* argument.
|
|
*
|
|
* Note: since this method runs all plugins, it's not suitable for cases
|
|
* where the return value of a callback needs to be applied prior to calling
|
|
* the next callback. See
|
|
* {@link workbox-strategies.StrategyHandler#iterateCallbacks}
|
|
* below for how to handle that case.
|
|
*
|
|
* @param {string} name The name of the callback to run within each plugin.
|
|
* @param {Object} param The object to pass as the first (and only) param
|
|
* when executing each callback. This object will be merged with the
|
|
* current plugin state prior to callback execution.
|
|
*/
|
|
async runCallbacks(e, t) {
|
|
for (const s of this.iterateCallbacks(e))
|
|
await s(t);
|
|
}
|
|
/**
|
|
* Accepts a callback and returns an iterable of matching plugin callbacks,
|
|
* where each callback is wrapped with the current handler state (i.e. when
|
|
* you call each callback, whatever object parameter you pass it will
|
|
* be merged with the plugin's current state).
|
|
*
|
|
* @param {string} name The name fo the callback to run
|
|
* @return {Array<Function>}
|
|
*/
|
|
*iterateCallbacks(e) {
|
|
for (const t of this._strategy.plugins)
|
|
if (typeof t[e] == "function") {
|
|
const s = this._pluginStateMap.get(t);
|
|
yield (i) => {
|
|
const a = Object.assign(Object.assign({}, i), { state: s });
|
|
return t[e](a);
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Adds a promise to the
|
|
* [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
|
|
* of the event event associated with the request being handled (usually a
|
|
* `FetchEvent`).
|
|
*
|
|
* Note: you can await
|
|
* {@link workbox-strategies.StrategyHandler~doneWaiting}
|
|
* to know when all added promises have settled.
|
|
*
|
|
* @param {Promise} promise A promise to add to the extend lifetime promises
|
|
* of the event that triggered the request.
|
|
*/
|
|
waitUntil(e) {
|
|
return this._extendLifetimePromises.push(e), e;
|
|
}
|
|
/**
|
|
* Returns a promise that resolves once all promises passed to
|
|
* {@link workbox-strategies.StrategyHandler~waitUntil}
|
|
* have settled.
|
|
*
|
|
* Note: any work done after `doneWaiting()` settles should be manually
|
|
* passed to an event's `waitUntil()` method (not this handler's
|
|
* `waitUntil()` method), otherwise the service worker thread my be killed
|
|
* prior to your work completing.
|
|
*/
|
|
async doneWaiting() {
|
|
let e;
|
|
for (; e = this._extendLifetimePromises.shift(); )
|
|
await e;
|
|
}
|
|
/**
|
|
* Stops running the strategy and immediately resolves any pending
|
|
* `waitUntil()` promises.
|
|
*/
|
|
destroy() {
|
|
this._handlerDeferred.resolve(null);
|
|
}
|
|
/**
|
|
* This method will call cacheWillUpdate on the available plugins (or use
|
|
* status === 200) to determine if the Response is safe and valid to cache.
|
|
*
|
|
* @param {Request} options.request
|
|
* @param {Response} options.response
|
|
* @return {Promise<Response|undefined>}
|
|
*
|
|
* @private
|
|
*/
|
|
async _ensureResponseSafeToCache(e) {
|
|
let t = e, s = !1;
|
|
for (const r of this.iterateCallbacks("cacheWillUpdate"))
|
|
if (t = await r({
|
|
request: this.request,
|
|
response: t,
|
|
event: this.event
|
|
}) || void 0, s = !0, !t)
|
|
break;
|
|
return s || t && t.status !== 200 && (t = void 0), t;
|
|
}
|
|
}
|
|
class ct {
|
|
/**
|
|
* Creates a new instance of the strategy and sets all documented option
|
|
* properties as public instance properties.
|
|
*
|
|
* Note: if a custom strategy class extends the base Strategy class and does
|
|
* not need more than these properties, it does not need to define its own
|
|
* constructor.
|
|
*
|
|
* @param {Object} [options]
|
|
* @param {string} [options.cacheName] Cache name to store and retrieve
|
|
* requests. Defaults to the cache names provided by
|
|
* {@link workbox-core.cacheNames}.
|
|
* @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
|
|
* to use in conjunction with this caching strategy.
|
|
* @param {Object} [options.fetchOptions] Values passed along to the
|
|
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
|
|
* of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
|
|
* `fetch()` requests made by this strategy.
|
|
* @param {Object} [options.matchOptions] The
|
|
* [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
|
|
* for any `cache.match()` or `cache.put()` calls made by this strategy.
|
|
*/
|
|
constructor(e = {}) {
|
|
this.cacheName = z.getRuntimeName(e.cacheName), this.plugins = e.plugins || [], this.fetchOptions = e.fetchOptions, this.matchOptions = e.matchOptions;
|
|
}
|
|
/**
|
|
* Perform a request strategy and returns a `Promise` that will resolve with
|
|
* a `Response`, invoking all relevant plugin callbacks.
|
|
*
|
|
* When a strategy instance is registered with a Workbox
|
|
* {@link workbox-routing.Route}, this method is automatically
|
|
* called when the route matches.
|
|
*
|
|
* Alternatively, this method can be used in a standalone `FetchEvent`
|
|
* listener by passing it to `event.respondWith()`.
|
|
*
|
|
* @param {FetchEvent|Object} options A `FetchEvent` or an object with the
|
|
* properties listed below.
|
|
* @param {Request|string} options.request A request to run this strategy for.
|
|
* @param {ExtendableEvent} options.event The event associated with the
|
|
* request.
|
|
* @param {URL} [options.url]
|
|
* @param {*} [options.params]
|
|
*/
|
|
handle(e) {
|
|
const [t] = this.handleAll(e);
|
|
return t;
|
|
}
|
|
/**
|
|
* Similar to {@link workbox-strategies.Strategy~handle}, but
|
|
* instead of just returning a `Promise` that resolves to a `Response` it
|
|
* it will return an tuple of `[response, done]` promises, where the former
|
|
* (`response`) is equivalent to what `handle()` returns, and the latter is a
|
|
* Promise that will resolve once any promises that were added to
|
|
* `event.waitUntil()` as part of performing the strategy have completed.
|
|
*
|
|
* You can await the `done` promise to ensure any extra work performed by
|
|
* the strategy (usually caching responses) completes successfully.
|
|
*
|
|
* @param {FetchEvent|Object} options A `FetchEvent` or an object with the
|
|
* properties listed below.
|
|
* @param {Request|string} options.request A request to run this strategy for.
|
|
* @param {ExtendableEvent} options.event The event associated with the
|
|
* request.
|
|
* @param {URL} [options.url]
|
|
* @param {*} [options.params]
|
|
* @return {Array<Promise>} A tuple of [response, done]
|
|
* promises that can be used to determine when the response resolves as
|
|
* well as when the handler has completed all its work.
|
|
*/
|
|
handleAll(e) {
|
|
e instanceof FetchEvent && (e = {
|
|
event: e,
|
|
request: e.request
|
|
});
|
|
const t = e.event, s = typeof e.request == "string" ? new Request(e.request) : e.request, r = "params" in e ? e.params : void 0, i = new it(this, { event: t, request: s, params: r }), a = this._getResponse(i, s, t), c = this._awaitComplete(a, i, s, t);
|
|
return [a, c];
|
|
}
|
|
async _getResponse(e, t, s) {
|
|
await e.runCallbacks("handlerWillStart", { event: s, request: t });
|
|
let r;
|
|
try {
|
|
if (r = await this._handle(t, e), !r || r.type === "error")
|
|
throw new w("no-response", { url: t.url });
|
|
} catch (i) {
|
|
if (i instanceof Error) {
|
|
for (const a of e.iterateCallbacks("handlerDidError"))
|
|
if (r = await a({ error: i, event: s, request: t }), r)
|
|
break;
|
|
}
|
|
if (!r)
|
|
throw i;
|
|
}
|
|
for (const i of e.iterateCallbacks("handlerWillRespond"))
|
|
r = await i({ event: s, request: t, response: r });
|
|
return r;
|
|
}
|
|
async _awaitComplete(e, t, s, r) {
|
|
let i, a;
|
|
try {
|
|
i = await e;
|
|
} catch {
|
|
}
|
|
try {
|
|
await t.runCallbacks("handlerDidRespond", {
|
|
event: r,
|
|
request: s,
|
|
response: i
|
|
}), await t.doneWaiting();
|
|
} catch (c) {
|
|
c instanceof Error && (a = c);
|
|
}
|
|
if (await t.runCallbacks("handlerDidComplete", {
|
|
event: r,
|
|
request: s,
|
|
response: i,
|
|
error: a
|
|
}), t.destroy(), a)
|
|
throw a;
|
|
}
|
|
}
|
|
const ot = {
|
|
/**
|
|
* Returns a valid response (to allow caching) if the status is 200 (OK) or
|
|
* 0 (opaque).
|
|
*
|
|
* @param {Object} options
|
|
* @param {Response} options.response
|
|
* @return {Response|null}
|
|
*
|
|
* @private
|
|
*/
|
|
cacheWillUpdate: async ({ response: n }) => n.status === 200 || n.status === 0 ? n : null
|
|
};
|
|
class lt extends ct {
|
|
/**
|
|
* @param {Object} [options]
|
|
* @param {string} [options.cacheName] Cache name to store and retrieve
|
|
* requests. Defaults to cache names provided by
|
|
* {@link workbox-core.cacheNames}.
|
|
* @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
|
|
* to use in conjunction with this caching strategy.
|
|
* @param {Object} [options.fetchOptions] Values passed along to the
|
|
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
|
|
* of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
|
|
* `fetch()` requests made by this strategy.
|
|
* @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
|
|
*/
|
|
constructor(e = {}) {
|
|
super(e), this.plugins.some((t) => "cacheWillUpdate" in t) || this.plugins.unshift(ot);
|
|
}
|
|
/**
|
|
* @private
|
|
* @param {Request|string} request A request to run this strategy for.
|
|
* @param {workbox-strategies.StrategyHandler} handler The event that
|
|
* triggered the request.
|
|
* @return {Promise<Response>}
|
|
*/
|
|
async _handle(e, t) {
|
|
const s = t.fetchAndCachePut(e).catch(() => {
|
|
});
|
|
t.waitUntil(s);
|
|
let r = await t.cacheMatch(e), i;
|
|
if (!r)
|
|
try {
|
|
r = await s;
|
|
} catch (a) {
|
|
a instanceof Error && (i = a);
|
|
}
|
|
if (!r)
|
|
throw new w("no-response", { url: e.url, error: i });
|
|
return r;
|
|
}
|
|
}
|
|
const ht = "shlink-web-client", ut = "A React-based progressive web application for shlink", ft = "/shlink-web-client/dependabot/npm_and_yarn/workbox-precaching-7.0.0", dt = "https://github.com/shlinkio/shlink-web-client", pt = "MIT", gt = {
|
|
lint: "npm run lint:css && npm run lint:js",
|
|
"lint:css": "stylelint src/*.scss src/**/*.scss",
|
|
"lint:js": "eslint --ext .js,.ts,.tsx src test",
|
|
"lint:fix": "npm run lint:css:fix && npm run lint:js:fix",
|
|
"lint:css:fix": "npm run lint:css -- --fix",
|
|
"lint:js:fix": "npm run lint:js -- --fix",
|
|
types: "tsc",
|
|
start: "vite serve --host=0.0.0.0",
|
|
preview: "vite preview --host=0.0.0.0",
|
|
build: "npm run types && vite build && node scripts/replace-version.mjs",
|
|
"build:dist": "npm run build && node scripts/create-dist-file.mjs",
|
|
test: "vitest run --run",
|
|
"test:watch": "vitest --watch",
|
|
"test:ci": "npm run test -- --coverage",
|
|
"test:verbose": "npm run test -- --verbose"
|
|
}, mt = {
|
|
"@fortawesome/fontawesome-free": "^6.4.2",
|
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
|
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
|
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
"@json2csv/plainjs": "^7.0.1",
|
|
"@reduxjs/toolkit": "^1.9.5",
|
|
"@shlinkio/shlink-frontend-kit": "^0.2.0",
|
|
"@shlinkio/shlink-web-component": "^0.3.0",
|
|
bootstrap: "5.2.3",
|
|
bottlejs: "^2.0.1",
|
|
classnames: "^2.3.2",
|
|
"compare-versions": "^6.1.0",
|
|
csvtojson: "^2.0.10",
|
|
"date-fns": "^2.30.0",
|
|
ramda: "^0.27.2",
|
|
react: "^18.2.0",
|
|
"react-dom": "^18.2.0",
|
|
"react-external-link": "^2.2.0",
|
|
"react-redux": "^8.1.2",
|
|
"react-router-dom": "^6.14.2",
|
|
reactstrap: "^9.2.0",
|
|
"redux-localstorage-simple": "^2.5.1",
|
|
uuid: "^9.0.0",
|
|
"workbox-core": "^6.5.4",
|
|
"workbox-expiration": "^6.5.4",
|
|
"workbox-precaching": "^7.0.0",
|
|
"workbox-routing": "^6.5.4",
|
|
"workbox-strategies": "^6.5.4"
|
|
}, wt = {
|
|
"@shlinkio/eslint-config-js-coding-standard": "~2.2.0",
|
|
"@shlinkio/stylelint-config-css-coding-standard": "~1.1.1",
|
|
"@testing-library/jest-dom": "^6.0.0",
|
|
"@testing-library/react": "^14.0.0",
|
|
"@testing-library/user-event": "^14.4.3",
|
|
"@total-typescript/shoehorn": "^0.1.1",
|
|
"@types/leaflet": "^1.9.3",
|
|
"@types/ramda": "^0.27.66",
|
|
"@types/react": "^18.2.19",
|
|
"@types/react-dom": "^18.2.7",
|
|
"@types/uuid": "^9.0.2",
|
|
"@vitejs/plugin-react": "^4.0.4",
|
|
"@vitest/coverage-v8": "^0.34.1",
|
|
"adm-zip": "^0.5.10",
|
|
chalk: "^5.3.0",
|
|
eslint: "^8.46.0",
|
|
history: "^5.3.0",
|
|
jsdom: "^22.1.0",
|
|
sass: "^1.64.2",
|
|
stylelint: "^15.10.2",
|
|
typescript: "^5.1.6",
|
|
vite: "^4.4.9",
|
|
"vite-plugin-pwa": "^0.16.4",
|
|
vitest: "^0.34.1"
|
|
}, yt = [
|
|
">0.2%",
|
|
"not dead",
|
|
"not ie <= 11",
|
|
"not op_mini all"
|
|
], bt = {
|
|
name: ht,
|
|
description: ut,
|
|
private: !1,
|
|
homepage: ft,
|
|
repository: dt,
|
|
license: pt,
|
|
scripts: gt,
|
|
dependencies: mt,
|
|
devDependencies: wt,
|
|
browserslist: yt
|
|
};
|
|
ue();
|
|
st([{"revision":null,"url":"assets/index-0e8ab544.css"},{"revision":null,"url":"assets/index-2938603d.js"},{"revision":"cbb744bc91b3e31d91c3505ace6239b0","url":"index.html"},{"revision":"3e8169a112865ef0d76bba4e2dfd3a89","url":"./icons/icon-16x16.png"},{"revision":"20f0b51945ae3c973420e31d6b4d43c1","url":"./icons/icon-24x24.png"},{"revision":"4c9695cfb4cea403c1b66bbb7b4f7420","url":"./icons/icon-32x32.png"},{"revision":"f74edb9cc54ac938c1589876f8da9f21","url":"./icons/icon-40x40.png"},{"revision":"52933970fac8498311687de13549b014","url":"./icons/icon-48x48.png"},{"revision":"40209a194f29b21882571c77bd2bd906","url":"./icons/icon-60x60.png"},{"revision":"c8ba5a8c1da76d74c7bb21827fcf975c","url":"./icons/icon-64x64.png"},{"revision":"ae3435baceb494f9c966e2c2c736ea35","url":"./icons/icon-72x72.png"},{"revision":"850aa5b026fd1452af6c1ddd9d71850f","url":"./icons/icon-76x76.png"},{"revision":"8c62535e6b7a498ece1f6c6ae62ede59","url":"./icons/icon-96x96.png"},{"revision":"2dff586aaefa4c8d17f4c0bcbdef8b53","url":"./icons/icon-114x114.png"},{"revision":"8e28257a68ef1c55bc68bfff80e1a310","url":"./icons/icon-120x120.png"},{"revision":"0059b6f0c97fa871a5c29643b2857585","url":"./icons/icon-128x128.png"},{"revision":"41478c2456281f61e54d714718743ecc","url":"./icons/icon-144x144.png"},{"revision":"625251910295f33a578ae6d8117711c9","url":"./icons/icon-150x150.png"},{"revision":"19b71508b1d05defe32cf16a7d453001","url":"./icons/icon-152x152.png"},{"revision":"9d029a32a54ba3084c67acd5d74f8ac4","url":"./icons/icon-160x160.png"},{"revision":"6ec104aeaf745f003ecdaef2edddce97","url":"./icons/icon-167x167.png"},{"revision":"871d67907434ed0ddf5d2a6c220e09af","url":"./icons/icon-180x180.png"},{"revision":"cee2529402074d73b2135e2ddee25f6b","url":"./icons/icon-192x192.png"},{"revision":"85055b452284c0193142936dee7d2cd1","url":"./icons/icon-196x196.png"},{"revision":"f471155dd70b99924422dd9dd87ea94d","url":"./icons/icon-228x228.png"},{"revision":"4d896c5c7025582605de31fb74f0316b","url":"./icons/icon-256x256.png"},{"revision":"d99b49e5bcad41968313c3e132e7c661","url":"./icons/icon-310x310.png"},{"revision":"f9597636bef677327c3abc0fd1a743c4","url":"./icons/icon-384x384.png"},{"revision":"de22c0eb9e08d3576df5cedb568ca56b","url":"./icons/icon-512x512.png"},{"revision":"583b223ad8e20f05aaa64923d7db2e46","url":"./icons/icon-1024x1024.png"},{"revision":"6b5a0832d130ea1a7df59fc314dac241","url":"manifest.json"}]);
|
|
const _t = new RegExp("/[^/?]+\\.[^/]+$");
|
|
te(
|
|
// Return false to exempt requests from being fulfilled by index.html.
|
|
({ request: n, url: e }) => !(n.mode !== "navigate" || e.pathname.startsWith("/_") || e.pathname.match(_t)),
|
|
et(`${bt.homepage}/index.html`)
|
|
);
|
|
te(
|
|
// Add in any other file extensions or routing criteria as needed.
|
|
({ url: n }) => n.origin === self.location.origin && n.pathname.endsWith(".png"),
|
|
// Customize this strategy as needed, e.g., by changing to CacheFirst.
|
|
new lt({
|
|
cacheName: "images",
|
|
plugins: [
|
|
// Ensure that once this runtime cache reaches a maximum size the
|
|
// least-recently used images are removed.
|
|
new Ue({ maxEntries: 50 })
|
|
]
|
|
})
|
|
);
|
|
self.addEventListener("message", (n) => {
|
|
n.data && n.data.type === "SKIP_WAITING" && self.skipWaiting();
|
|
});
|