296ef9214b
SHA256:17ea8854a3504752a8ae4540ecb09e62f6f8aa260221d3619fda46f66b2ec43f
1579 lines
51 KiB
JavaScript
1579 lines
51 KiB
JavaScript
try {
|
|
self["workbox:core:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
const Z = (s, ...e) => {
|
|
let t = s;
|
|
return e.length > 0 && (t += ` :: ${JSON.stringify(e)}`), t;
|
|
}, ee = Z;
|
|
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 n = ee(e, t);
|
|
super(n), this.name = e, this.details = t;
|
|
}
|
|
}
|
|
const te = (s) => new URL(String(s), location.href).href.replace(new RegExp(`^${location.origin}`), "");
|
|
try {
|
|
self["workbox:cacheable-response:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
class se {
|
|
/**
|
|
* To construct a new CacheableResponse instance you must provide at least
|
|
* one of the `config` properties.
|
|
*
|
|
* If both `statuses` and `headers` are specified, then both conditions must
|
|
* be met for the `Response` to be considered cacheable.
|
|
*
|
|
* @param {Object} config
|
|
* @param {Array<number>} [config.statuses] One or more status codes that a
|
|
* `Response` can have and be considered cacheable.
|
|
* @param {Object<string,string>} [config.headers] A mapping of header names
|
|
* and expected values that a `Response` can have and be considered cacheable.
|
|
* If multiple headers are provided, only one needs to be present.
|
|
*/
|
|
constructor(e = {}) {
|
|
this._statuses = e.statuses, this._headers = e.headers;
|
|
}
|
|
/**
|
|
* Checks a response to see whether it's cacheable or not, based on this
|
|
* object's configuration.
|
|
*
|
|
* @param {Response} response The response whose cacheability is being
|
|
* checked.
|
|
* @return {boolean} `true` if the `Response` is cacheable, and `false`
|
|
* otherwise.
|
|
*/
|
|
isResponseCacheable(e) {
|
|
let t = !0;
|
|
return this._statuses && (t = this._statuses.includes(e.status)), this._headers && t && (t = Object.keys(this._headers).some((n) => e.headers.get(n) === this._headers[n])), t;
|
|
}
|
|
}
|
|
class x {
|
|
/**
|
|
* To construct a new CacheableResponsePlugin instance you must provide at
|
|
* least one of the `config` properties.
|
|
*
|
|
* If both `statuses` and `headers` are specified, then both conditions must
|
|
* be met for the `Response` to be considered cacheable.
|
|
*
|
|
* @param {Object} config
|
|
* @param {Array<number>} [config.statuses] One or more status codes that a
|
|
* `Response` can have and be considered cacheable.
|
|
* @param {Object<string,string>} [config.headers] A mapping of header names
|
|
* and expected values that a `Response` can have and be considered cacheable.
|
|
* If multiple headers are provided, only one needs to be present.
|
|
*/
|
|
constructor(e) {
|
|
this.cacheWillUpdate = async ({ response: t }) => this._cacheableResponse.isResponseCacheable(t) ? t : null, this._cacheableResponse = new se(e);
|
|
}
|
|
}
|
|
function q(s) {
|
|
s.then(() => {
|
|
});
|
|
}
|
|
const ne = (s, e) => e.some((t) => s instanceof t);
|
|
let j, W;
|
|
function ae() {
|
|
return j || (j = [
|
|
IDBDatabase,
|
|
IDBObjectStore,
|
|
IDBIndex,
|
|
IDBCursor,
|
|
IDBTransaction
|
|
]);
|
|
}
|
|
function ie() {
|
|
return W || (W = [
|
|
IDBCursor.prototype.advance,
|
|
IDBCursor.prototype.continue,
|
|
IDBCursor.prototype.continuePrimaryKey
|
|
]);
|
|
}
|
|
const V = /* @__PURE__ */ new WeakMap(), S = /* @__PURE__ */ new WeakMap(), $ = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap();
|
|
function re(s) {
|
|
const e = new Promise((t, n) => {
|
|
const a = () => {
|
|
s.removeEventListener("success", r), s.removeEventListener("error", i);
|
|
}, r = () => {
|
|
t(f(s.result)), a();
|
|
}, i = () => {
|
|
n(s.error), a();
|
|
};
|
|
s.addEventListener("success", r), s.addEventListener("error", i);
|
|
});
|
|
return e.then((t) => {
|
|
t instanceof IDBCursor && V.set(t, s);
|
|
}).catch(() => {
|
|
}), P.set(e, s), e;
|
|
}
|
|
function oe(s) {
|
|
if (S.has(s))
|
|
return;
|
|
const e = new Promise((t, n) => {
|
|
const a = () => {
|
|
s.removeEventListener("complete", r), s.removeEventListener("error", i), s.removeEventListener("abort", i);
|
|
}, r = () => {
|
|
t(), a();
|
|
}, i = () => {
|
|
n(s.error || new DOMException("AbortError", "AbortError")), a();
|
|
};
|
|
s.addEventListener("complete", r), s.addEventListener("error", i), s.addEventListener("abort", i);
|
|
});
|
|
S.set(s, e);
|
|
}
|
|
let M = {
|
|
get(s, e, t) {
|
|
if (s instanceof IDBTransaction) {
|
|
if (e === "done")
|
|
return S.get(s);
|
|
if (e === "objectStoreNames")
|
|
return s.objectStoreNames || $.get(s);
|
|
if (e === "store")
|
|
return t.objectStoreNames[1] ? void 0 : t.objectStore(t.objectStoreNames[0]);
|
|
}
|
|
return f(s[e]);
|
|
},
|
|
set(s, e, t) {
|
|
return s[e] = t, !0;
|
|
},
|
|
has(s, e) {
|
|
return s instanceof IDBTransaction && (e === "done" || e === "store") ? !0 : e in s;
|
|
}
|
|
};
|
|
function ce(s) {
|
|
M = s(M);
|
|
}
|
|
function le(s) {
|
|
return s === IDBDatabase.prototype.transaction && !("objectStoreNames" in IDBTransaction.prototype) ? function(e, ...t) {
|
|
const n = s.call(N(this), e, ...t);
|
|
return $.set(n, e.sort ? e.sort() : [e]), f(n);
|
|
} : ie().includes(s) ? function(...e) {
|
|
return s.apply(N(this), e), f(V.get(this));
|
|
} : function(...e) {
|
|
return f(s.apply(N(this), e));
|
|
};
|
|
}
|
|
function he(s) {
|
|
return typeof s == "function" ? le(s) : (s instanceof IDBTransaction && oe(s), ne(s, ae()) ? new Proxy(s, M) : s);
|
|
}
|
|
function f(s) {
|
|
if (s instanceof IDBRequest)
|
|
return re(s);
|
|
if (T.has(s))
|
|
return T.get(s);
|
|
const e = he(s);
|
|
return e !== s && (T.set(s, e), P.set(e, s)), e;
|
|
}
|
|
const N = (s) => P.get(s);
|
|
function ue(s, e, { blocked: t, upgrade: n, blocking: a, terminated: r } = {}) {
|
|
const i = indexedDB.open(s, e), o = f(i);
|
|
return n && i.addEventListener("upgradeneeded", (c) => {
|
|
n(f(i.result), c.oldVersion, c.newVersion, f(i.transaction), c);
|
|
}), t && i.addEventListener("blocked", (c) => t(
|
|
// Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
|
|
c.oldVersion,
|
|
c.newVersion,
|
|
c
|
|
)), o.then((c) => {
|
|
r && c.addEventListener("close", () => r()), a && c.addEventListener("versionchange", (l) => a(l.oldVersion, l.newVersion, l));
|
|
}).catch(() => {
|
|
}), o;
|
|
}
|
|
function de(s, { blocked: e } = {}) {
|
|
const t = indexedDB.deleteDatabase(s);
|
|
return e && t.addEventListener("blocked", (n) => e(
|
|
// Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
|
|
n.oldVersion,
|
|
n
|
|
)), f(t).then(() => {
|
|
});
|
|
}
|
|
const fe = ["get", "getKey", "getAll", "getAllKeys", "count"], pe = ["put", "add", "delete", "clear"], O = /* @__PURE__ */ new Map();
|
|
function F(s, e) {
|
|
if (!(s instanceof IDBDatabase && !(e in s) && typeof e == "string"))
|
|
return;
|
|
if (O.get(e))
|
|
return O.get(e);
|
|
const t = e.replace(/FromIndex$/, ""), n = e !== t, a = pe.includes(t);
|
|
if (
|
|
// Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.
|
|
!(t in (n ? IDBIndex : IDBObjectStore).prototype) || !(a || fe.includes(t))
|
|
)
|
|
return;
|
|
const r = async function(i, ...o) {
|
|
const c = this.transaction(i, a ? "readwrite" : "readonly");
|
|
let l = c.store;
|
|
return n && (l = l.index(o.shift())), (await Promise.all([
|
|
l[t](...o),
|
|
a && c.done
|
|
]))[0];
|
|
};
|
|
return O.set(e, r), r;
|
|
}
|
|
ce((s) => ({
|
|
...s,
|
|
get: (e, t, n) => F(e, t) || s.get(e, t, n),
|
|
has: (e, t) => !!F(e, t) || s.has(e, t)
|
|
}));
|
|
try {
|
|
self["workbox:expiration:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
const me = "workbox-expiration", b = "cache-entries", H = (s) => {
|
|
const e = new URL(s, location.href);
|
|
return e.hash = "", e.href;
|
|
};
|
|
class we {
|
|
/**
|
|
*
|
|
* @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 && de(this._cacheName);
|
|
}
|
|
/**
|
|
* @param {string} url
|
|
* @param {number} timestamp
|
|
*
|
|
* @private
|
|
*/
|
|
async setTimestamp(e, t) {
|
|
e = H(e);
|
|
const n = {
|
|
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)
|
|
}, r = (await this.getDb()).transaction(b, "readwrite", {
|
|
durability: "relaxed"
|
|
});
|
|
await r.store.put(n), await r.done;
|
|
}
|
|
/**
|
|
* Returns the timestamp stored for a given URL.
|
|
*
|
|
* @param {string} url
|
|
* @return {number | undefined}
|
|
*
|
|
* @private
|
|
*/
|
|
async getTimestamp(e) {
|
|
const n = await (await this.getDb()).get(b, this._getId(e));
|
|
return n == null ? void 0 : n.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 n = await this.getDb();
|
|
let a = await n.transaction(b).store.index("timestamp").openCursor(null, "prev");
|
|
const r = [];
|
|
let i = 0;
|
|
for (; a; ) {
|
|
const c = a.value;
|
|
c.cacheName === this._cacheName && (e && c.timestamp < e || t && i >= t ? r.push(a.value) : i++), a = await a.continue();
|
|
}
|
|
const o = [];
|
|
for (const c of r)
|
|
await n.delete(b, c.id), o.push(c.url);
|
|
return o;
|
|
}
|
|
/**
|
|
* 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 + "|" + H(e);
|
|
}
|
|
/**
|
|
* Returns an open connection to the database.
|
|
*
|
|
* @private
|
|
*/
|
|
async getDb() {
|
|
return this._db || (this._db = await ue(me, 1, {
|
|
upgrade: this._upgradeDbAndDeleteOldDbs.bind(this)
|
|
})), this._db;
|
|
}
|
|
}
|
|
class ge {
|
|
/**
|
|
* 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 we(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), n = await self.caches.open(this._cacheName);
|
|
for (const a of t)
|
|
await n.delete(a, this._matchOptions);
|
|
this._isRunning = !1, this._rerunRequested && (this._rerunRequested = !1, q(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), n = Date.now() - this._maxAgeSeconds * 1e3;
|
|
return t !== void 0 ? t < n : !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);
|
|
}
|
|
}
|
|
const d = {
|
|
googleAnalytics: "googleAnalytics",
|
|
precache: "precache-v2",
|
|
prefix: "workbox",
|
|
runtime: "runtime",
|
|
suffix: typeof registration < "u" ? registration.scope : ""
|
|
}, A = (s) => [d.prefix, s, d.suffix].filter((e) => e && e.length > 0).join("-"), _e = (s) => {
|
|
for (const e of Object.keys(d))
|
|
s(e);
|
|
}, Q = {
|
|
updateDetails: (s) => {
|
|
_e((e) => {
|
|
typeof s[e] == "string" && (d[e] = s[e]);
|
|
});
|
|
},
|
|
getGoogleAnalyticsName: (s) => s || A(d.googleAnalytics),
|
|
getPrecacheName: (s) => s || A(d.precache),
|
|
getPrefix: () => d.prefix,
|
|
getRuntimeName: (s) => s || A(d.runtime),
|
|
getSuffix: () => d.suffix
|
|
}, G = /* @__PURE__ */ new Set();
|
|
function ye(s) {
|
|
G.add(s);
|
|
}
|
|
class k {
|
|
/**
|
|
* @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: n, cacheName: a, cachedResponse: r }) => {
|
|
if (!r)
|
|
return null;
|
|
const i = this._isResponseDateFresh(r), o = this._getCacheExpiration(a);
|
|
q(o.expireEntries());
|
|
const c = o.updateTimestamp(n.url);
|
|
if (t)
|
|
try {
|
|
t.waitUntil(c);
|
|
} catch {
|
|
}
|
|
return i ? r : null;
|
|
}, this.cacheDidUpdate = async ({ cacheName: t, request: n }) => {
|
|
const a = this._getCacheExpiration(t);
|
|
await a.updateTimestamp(n.url), await a.expireEntries();
|
|
}, this._config = e, this._maxAgeSeconds = e.maxAgeSeconds, this._cacheExpirations = /* @__PURE__ */ new Map(), e.purgeOnQuotaError && ye(() => 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 === Q.getRuntimeName())
|
|
throw new u("expire-custom-caches-only");
|
|
let t = this._cacheExpirations.get(e);
|
|
return t || (t = new ge(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 n = Date.now();
|
|
return t >= n - 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"), a = new Date(t).getTime();
|
|
return isNaN(a) ? null : a;
|
|
}
|
|
/**
|
|
* 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:routing:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
const z = "GET", D = (s) => s && typeof s == "object" ? s : { handle: s };
|
|
class p {
|
|
/**
|
|
* 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, n = z) {
|
|
this.handler = D(t), this.match = e, this.method = n;
|
|
}
|
|
/**
|
|
*
|
|
* @param {workbox-routing-handlerCallback} handler A callback
|
|
* function that returns a Promise resolving to a Response
|
|
*/
|
|
setCatchHandler(e) {
|
|
this.catchHandler = D(e);
|
|
}
|
|
}
|
|
class L extends p {
|
|
/**
|
|
* 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, n) {
|
|
const a = ({ url: r }) => {
|
|
const i = e.exec(r.href);
|
|
if (i && !(r.origin !== location.origin && i.index !== 0))
|
|
return i.slice(1);
|
|
};
|
|
super(a, t, n);
|
|
}
|
|
}
|
|
class be {
|
|
/**
|
|
* 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, n = this.handleRequest({ request: t, event: e });
|
|
n && e.respondWith(n);
|
|
});
|
|
}
|
|
/**
|
|
* 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, n = Promise.all(t.urlsToCache.map((a) => {
|
|
typeof a == "string" && (a = [a]);
|
|
const r = new Request(...a);
|
|
return this.handleRequest({ request: r, event: e });
|
|
}));
|
|
e.waitUntil(n), e.ports && e.ports[0] && n.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 n = new URL(e.url, location.href);
|
|
if (!n.protocol.startsWith("http"))
|
|
return;
|
|
const a = n.origin === location.origin, { params: r, route: i } = this.findMatchingRoute({
|
|
event: t,
|
|
request: e,
|
|
sameOrigin: a,
|
|
url: n
|
|
});
|
|
let o = i && i.handler;
|
|
const c = e.method;
|
|
if (!o && this._defaultHandlerMap.has(c) && (o = this._defaultHandlerMap.get(c)), !o)
|
|
return;
|
|
let l;
|
|
try {
|
|
l = o.handle({ url: n, request: e, event: t, params: r });
|
|
} catch (h) {
|
|
l = Promise.reject(h);
|
|
}
|
|
const m = i && i.catchHandler;
|
|
return l instanceof Promise && (this._catchHandler || m) && (l = l.catch(async (h) => {
|
|
if (m)
|
|
try {
|
|
return await m.handle({ url: n, request: e, event: t, params: r });
|
|
} catch (I) {
|
|
I instanceof Error && (h = I);
|
|
}
|
|
if (this._catchHandler)
|
|
return this._catchHandler.handle({ url: n, 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: n, event: a }) {
|
|
const r = this._routes.get(n.method) || [];
|
|
for (const i of r) {
|
|
let o;
|
|
const c = i.match({ url: e, sameOrigin: t, request: n, event: a });
|
|
if (c)
|
|
return o = c, (Array.isArray(o) && o.length === 0 || c.constructor === Object && // eslint-disable-line
|
|
Object.keys(c).length === 0 || typeof c == "boolean") && (o = void 0), { route: i, params: o };
|
|
}
|
|
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, D(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 = D(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");
|
|
}
|
|
}
|
|
let C;
|
|
const Ce = () => (C || (C = new be(), C.addFetchListener(), C.addCacheListener()), C);
|
|
function E(s, e, t) {
|
|
let n;
|
|
if (typeof s == "string") {
|
|
const r = new URL(s, location.href), i = ({ url: o }) => o.href === r.href;
|
|
n = new p(i, e, t);
|
|
} else if (s instanceof RegExp)
|
|
n = new L(s, e, t);
|
|
else if (typeof s == "function")
|
|
n = new p(s, e, t);
|
|
else if (s instanceof p)
|
|
n = s;
|
|
else
|
|
throw new u("unsupported-route-type", {
|
|
moduleName: "workbox-routing",
|
|
funcName: "registerRoute",
|
|
paramName: "capture"
|
|
});
|
|
return Ce().registerRoute(n), n;
|
|
}
|
|
function K(s, e) {
|
|
const t = new URL(s);
|
|
for (const n of e)
|
|
t.searchParams.delete(n);
|
|
return t.href;
|
|
}
|
|
async function xe(s, e, t, n) {
|
|
const a = K(e.url, t);
|
|
if (e.url === a)
|
|
return s.match(e, n);
|
|
const r = Object.assign(Object.assign({}, n), { ignoreSearch: !0 }), i = await s.keys(e, r);
|
|
for (const o of i) {
|
|
const c = K(o.url, t);
|
|
if (a === c)
|
|
return s.match(o, n);
|
|
}
|
|
}
|
|
class Ee {
|
|
/**
|
|
* 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 Re() {
|
|
for (const s of G)
|
|
await s();
|
|
}
|
|
function De(s) {
|
|
return new Promise((e) => setTimeout(e, s));
|
|
}
|
|
try {
|
|
self["workbox:strategies:7.0.0"] && _();
|
|
} catch {
|
|
}
|
|
function R(s) {
|
|
return typeof s == "string" ? new Request(s) : s;
|
|
}
|
|
class ke {
|
|
/**
|
|
* 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 Ee(), this._extendLifetimePromises = [], this._plugins = [...e.plugins], this._pluginStateMap = /* @__PURE__ */ new Map();
|
|
for (const n of this._plugins)
|
|
this._pluginStateMap.set(n, {});
|
|
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 n = R(e);
|
|
if (n.mode === "navigate" && t instanceof FetchEvent && t.preloadResponse) {
|
|
const i = await t.preloadResponse;
|
|
if (i)
|
|
return i;
|
|
}
|
|
const a = this.hasCallback("fetchDidFail") ? n.clone() : null;
|
|
try {
|
|
for (const i of this.iterateCallbacks("requestWillFetch"))
|
|
n = await i({ request: n.clone(), event: t });
|
|
} catch (i) {
|
|
if (i instanceof Error)
|
|
throw new u("plugin-error-request-will-fetch", {
|
|
thrownErrorMessage: i.message
|
|
});
|
|
}
|
|
const r = n.clone();
|
|
try {
|
|
let i;
|
|
i = await fetch(n, n.mode === "navigate" ? void 0 : this._strategy.fetchOptions);
|
|
for (const o of this.iterateCallbacks("fetchDidSucceed"))
|
|
i = await o({
|
|
event: t,
|
|
request: r,
|
|
response: i
|
|
});
|
|
return i;
|
|
} catch (i) {
|
|
throw a && await this.runCallbacks("fetchDidFail", {
|
|
error: i,
|
|
event: t,
|
|
originalRequest: a.clone(),
|
|
request: r.clone()
|
|
}), i;
|
|
}
|
|
}
|
|
/**
|
|
* 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), n = t.clone();
|
|
return this.waitUntil(this.cachePut(e, n)), 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 = R(e);
|
|
let n;
|
|
const { cacheName: a, matchOptions: r } = this._strategy, i = await this.getCacheKey(t, "read"), o = Object.assign(Object.assign({}, r), { cacheName: a });
|
|
n = await caches.match(i, o);
|
|
for (const c of this.iterateCallbacks("cachedResponseWillBeUsed"))
|
|
n = await c({
|
|
cacheName: a,
|
|
matchOptions: r,
|
|
cachedResponse: n,
|
|
request: i,
|
|
event: this.event
|
|
}) || void 0;
|
|
return n;
|
|
}
|
|
/**
|
|
* 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 n = R(e);
|
|
await De(0);
|
|
const a = await this.getCacheKey(n, "write");
|
|
if (!t)
|
|
throw new u("cache-put-with-no-response", {
|
|
url: te(a.url)
|
|
});
|
|
const r = await this._ensureResponseSafeToCache(t);
|
|
if (!r)
|
|
return !1;
|
|
const { cacheName: i, matchOptions: o } = this._strategy, c = await self.caches.open(i), l = this.hasCallback("cacheDidUpdate"), m = l ? await xe(
|
|
// TODO(philipwalton): the `__WB_REVISION__` param is a precaching
|
|
// feature. Consider into ways to only add this behavior if using
|
|
// precaching.
|
|
c,
|
|
a.clone(),
|
|
["__WB_REVISION__"],
|
|
o
|
|
) : null;
|
|
try {
|
|
await c.put(a, l ? r.clone() : r);
|
|
} catch (h) {
|
|
if (h instanceof Error)
|
|
throw h.name === "QuotaExceededError" && await Re(), h;
|
|
}
|
|
for (const h of this.iterateCallbacks("cacheDidUpdate"))
|
|
await h({
|
|
cacheName: i,
|
|
oldResponse: m,
|
|
newResponse: r.clone(),
|
|
request: a,
|
|
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 n = `${e.url} | ${t}`;
|
|
if (!this._cacheKeys[n]) {
|
|
let a = e;
|
|
for (const r of this.iterateCallbacks("cacheKeyWillBeUsed"))
|
|
a = R(await r({
|
|
mode: t,
|
|
request: a,
|
|
event: this.event,
|
|
// params has a type any can't change right now.
|
|
params: this.params
|
|
// eslint-disable-line
|
|
}));
|
|
this._cacheKeys[n] = a;
|
|
}
|
|
return this._cacheKeys[n];
|
|
}
|
|
/**
|
|
* 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 n of this.iterateCallbacks(e))
|
|
await n(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 n = this._pluginStateMap.get(t);
|
|
yield (r) => {
|
|
const i = Object.assign(Object.assign({}, r), { state: n });
|
|
return t[e](i);
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* 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, n = !1;
|
|
for (const a of this.iterateCallbacks("cacheWillUpdate"))
|
|
if (t = await a({
|
|
request: this.request,
|
|
response: t,
|
|
event: this.event
|
|
}) || void 0, n = !0, !t)
|
|
break;
|
|
return n || t && t.status !== 200 && (t = void 0), t;
|
|
}
|
|
}
|
|
class v {
|
|
/**
|
|
* 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 = Q.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, n = typeof e.request == "string" ? new Request(e.request) : e.request, a = "params" in e ? e.params : void 0, r = new ke(this, { event: t, request: n, params: a }), i = this._getResponse(r, n, t), o = this._awaitComplete(i, r, n, t);
|
|
return [i, o];
|
|
}
|
|
async _getResponse(e, t, n) {
|
|
await e.runCallbacks("handlerWillStart", { event: n, request: t });
|
|
let a;
|
|
try {
|
|
if (a = await this._handle(t, e), !a || a.type === "error")
|
|
throw new u("no-response", { url: t.url });
|
|
} catch (r) {
|
|
if (r instanceof Error) {
|
|
for (const i of e.iterateCallbacks("handlerDidError"))
|
|
if (a = await i({ error: r, event: n, request: t }), a)
|
|
break;
|
|
}
|
|
if (!a)
|
|
throw r;
|
|
}
|
|
for (const r of e.iterateCallbacks("handlerWillRespond"))
|
|
a = await r({ event: n, request: t, response: a });
|
|
return a;
|
|
}
|
|
async _awaitComplete(e, t, n, a) {
|
|
let r, i;
|
|
try {
|
|
r = await e;
|
|
} catch {
|
|
}
|
|
try {
|
|
await t.runCallbacks("handlerDidRespond", {
|
|
event: a,
|
|
request: n,
|
|
response: r
|
|
}), await t.doneWaiting();
|
|
} catch (o) {
|
|
o instanceof Error && (i = o);
|
|
}
|
|
if (await t.runCallbacks("handlerDidComplete", {
|
|
event: a,
|
|
request: n,
|
|
response: r,
|
|
error: i
|
|
}), t.destroy(), i)
|
|
throw i;
|
|
}
|
|
}
|
|
class J extends v {
|
|
/**
|
|
* @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) {
|
|
let n = await t.cacheMatch(e), a;
|
|
if (!n)
|
|
try {
|
|
n = await t.fetchAndCachePut(e);
|
|
} catch (r) {
|
|
r instanceof Error && (a = r);
|
|
}
|
|
if (!n)
|
|
throw new u("no-response", { url: e.url, error: a });
|
|
return n;
|
|
}
|
|
}
|
|
const X = {
|
|
/**
|
|
* 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: s }) => s.status === 200 || s.status === 0 ? s : null
|
|
};
|
|
class Y extends v {
|
|
/**
|
|
* @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)
|
|
* @param {number} [options.networkTimeoutSeconds] If set, any network requests
|
|
* that fail to respond within the timeout will fallback to the cache.
|
|
*
|
|
* This option can be used to combat
|
|
* "[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}"
|
|
* scenarios.
|
|
*/
|
|
constructor(e = {}) {
|
|
super(e), this.plugins.some((t) => "cacheWillUpdate" in t) || this.plugins.unshift(X), this._networkTimeoutSeconds = e.networkTimeoutSeconds || 0;
|
|
}
|
|
/**
|
|
* @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 n = [], a = [];
|
|
let r;
|
|
if (this._networkTimeoutSeconds) {
|
|
const { id: c, promise: l } = this._getTimeoutPromise({ request: e, logs: n, handler: t });
|
|
r = c, a.push(l);
|
|
}
|
|
const i = this._getNetworkPromise({
|
|
timeoutId: r,
|
|
request: e,
|
|
logs: n,
|
|
handler: t
|
|
});
|
|
a.push(i);
|
|
const o = await t.waitUntil((async () => await t.waitUntil(Promise.race(a)) || // If Promise.race() resolved with null, it might be due to a network
|
|
// timeout + a cache miss. If that were to happen, we'd rather wait until
|
|
// the networkPromise resolves instead of returning null.
|
|
// Note that it's fine to await an already-resolved promise, so we don't
|
|
// have to check to see if it's still "in flight".
|
|
await i)());
|
|
if (!o)
|
|
throw new u("no-response", { url: e.url });
|
|
return o;
|
|
}
|
|
/**
|
|
* @param {Object} options
|
|
* @param {Request} options.request
|
|
* @param {Array} options.logs A reference to the logs array
|
|
* @param {Event} options.event
|
|
* @return {Promise<Response>}
|
|
*
|
|
* @private
|
|
*/
|
|
_getTimeoutPromise({ request: e, logs: t, handler: n }) {
|
|
let a;
|
|
return {
|
|
promise: new Promise((i) => {
|
|
a = setTimeout(async () => {
|
|
i(await n.cacheMatch(e));
|
|
}, this._networkTimeoutSeconds * 1e3);
|
|
}),
|
|
id: a
|
|
};
|
|
}
|
|
/**
|
|
* @param {Object} options
|
|
* @param {number|undefined} options.timeoutId
|
|
* @param {Request} options.request
|
|
* @param {Array} options.logs A reference to the logs Array.
|
|
* @param {Event} options.event
|
|
* @return {Promise<Response>}
|
|
*
|
|
* @private
|
|
*/
|
|
async _getNetworkPromise({ timeoutId: e, request: t, logs: n, handler: a }) {
|
|
let r, i;
|
|
try {
|
|
i = await a.fetchAndCachePut(t);
|
|
} catch (o) {
|
|
o instanceof Error && (r = o);
|
|
}
|
|
return e && clearTimeout(e), (r || !i) && (i = await a.cacheMatch(t)), i;
|
|
}
|
|
}
|
|
class Ie extends v {
|
|
/**
|
|
* @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(X);
|
|
}
|
|
/**
|
|
* @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 n = t.fetchAndCachePut(e).catch(() => {
|
|
});
|
|
t.waitUntil(n);
|
|
let a = await t.cacheMatch(e), r;
|
|
if (!a)
|
|
try {
|
|
a = await n;
|
|
} catch (i) {
|
|
i instanceof Error && (r = i);
|
|
}
|
|
if (!a)
|
|
throw new u("no-response", { url: e.url, error: r });
|
|
return a;
|
|
}
|
|
}
|
|
self.__WB_DISABLE_DEV_LOGS = !0;
|
|
const Te = new p(
|
|
({ request: s, sameOrigin: e }) => {
|
|
const t = s.destination === "style" || s.destination === "script", n = /-[0-9a-f]{4,}\./i.test(s.url);
|
|
return e && t && n;
|
|
},
|
|
new Y({
|
|
cacheName: "assets",
|
|
networkTimeoutSeconds: 5,
|
|
plugins: [
|
|
new x({
|
|
statuses: [0, 200]
|
|
})
|
|
]
|
|
})
|
|
);
|
|
E(Te);
|
|
const Ne = new p(
|
|
({ request: s, sameOrigin: e }) => {
|
|
const t = !e, n = s.destination === "image", a = s.url.includes("/avatars/"), r = s.url.includes("/emoji/");
|
|
return t && n && (a || r);
|
|
},
|
|
new J({
|
|
cacheName: "remote-images",
|
|
plugins: [
|
|
new k({
|
|
maxEntries: 50,
|
|
maxAgeSeconds: 3 * 24 * 60 * 60,
|
|
// 3 days
|
|
purgeOnQuotaError: !0
|
|
}),
|
|
new x({
|
|
statuses: [0, 200]
|
|
})
|
|
]
|
|
})
|
|
);
|
|
E(Ne);
|
|
const Oe = new p(
|
|
({ request: s, sameOrigin: e }) => {
|
|
const t = s.url.includes("/icons/");
|
|
return e && t;
|
|
},
|
|
new J({
|
|
cacheName: "icons",
|
|
plugins: [
|
|
new k({
|
|
maxEntries: 50,
|
|
maxAgeSeconds: 3 * 24 * 60 * 60,
|
|
// 3 days
|
|
purgeOnQuotaError: !0
|
|
}),
|
|
new x({
|
|
statuses: [0, 200]
|
|
})
|
|
]
|
|
})
|
|
);
|
|
E(Oe);
|
|
const Ae = new L(
|
|
/^https?:\/\/[^\/]+\/api\/v\d+\/(instance|custom_emojis|preferences|lists\/\d+|announcements)$/,
|
|
new Ie({
|
|
cacheName: "api-extended",
|
|
plugins: [
|
|
new k({
|
|
maxAgeSeconds: 24 * 60 * 60
|
|
// 1 day
|
|
}),
|
|
new x({
|
|
statuses: [0, 200]
|
|
})
|
|
]
|
|
})
|
|
);
|
|
E(Ae);
|
|
const Se = new L(
|
|
// Matches:
|
|
// - statuses/:id/context - some contexts are really huge
|
|
/^https?:\/\/[^\/]+\/api\/v\d+\/(statuses\/\d+\/context)/,
|
|
new Y({
|
|
cacheName: "api",
|
|
networkTimeoutSeconds: 5,
|
|
plugins: [
|
|
new k({
|
|
maxAgeSeconds: 5 * 60
|
|
// 5 minutes
|
|
}),
|
|
new x({
|
|
statuses: [0, 200]
|
|
})
|
|
]
|
|
})
|
|
);
|
|
E(Se);
|
|
self.addEventListener("push", (s) => {
|
|
const { data: e } = s;
|
|
if (e) {
|
|
const t = e.json();
|
|
console.log("PUSH payload", t);
|
|
const {
|
|
access_token: n,
|
|
title: a,
|
|
body: r,
|
|
icon: i,
|
|
notification_id: o,
|
|
notification_type: c,
|
|
preferred_locale: l
|
|
} = t;
|
|
navigator.setAppBadge && c === "mention" && navigator.setAppBadge(1), s.waitUntil(
|
|
self.registration.showNotification(a, {
|
|
body: r,
|
|
icon: i,
|
|
dir: "auto",
|
|
badge: "/logo-badge-72.png",
|
|
lang: l,
|
|
tag: o,
|
|
timestamp: Date.now(),
|
|
data: {
|
|
access_token: n,
|
|
notification_type: c
|
|
}
|
|
})
|
|
);
|
|
}
|
|
});
|
|
self.addEventListener("notificationclick", (s) => {
|
|
const e = s.notification;
|
|
console.log("NOTIFICATION CLICK payload", e);
|
|
const { badge: t, body: n, data: a, dir: r, icon: i, lang: o, tag: c, timestamp: l, title: m } = e, { access_token: h, notification_type: I } = a, y = `/#/notifications?id=${c}&access_token=${btoa(h)}`;
|
|
s.waitUntil(
|
|
(async () => {
|
|
var U;
|
|
const w = await self.clients.matchAll({
|
|
type: "window",
|
|
includeUncontrolled: !0
|
|
});
|
|
if (console.log("NOTIFICATION CLICK clients 1", w), w.length && "navigate" in w[0]) {
|
|
console.log("NOTIFICATION CLICK clients 2", w);
|
|
const g = w.find(
|
|
(B) => B.focused || B.visibilityState === "visible"
|
|
) || w[0];
|
|
console.log("NOTIFICATION CLICK navigate", y), g ? (console.log("NOTIFICATION CLICK postMessage", g), g.focus(), (U = g.postMessage) == null || U.call(g, {
|
|
type: "notification",
|
|
id: c,
|
|
accessToken: h
|
|
})) : (console.log("NOTIFICATION CLICK openWindow", y), await self.clients.openWindow(y));
|
|
} else
|
|
console.log("NOTIFICATION CLICK openWindow", y), await self.clients.openWindow(y);
|
|
await s.notification.close();
|
|
})()
|
|
);
|
|
});
|
|
//# sourceMappingURL=sw.js.map
|