tempest-react-sdk 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -61
- package/dist/sw.cjs +1 -1
- package/dist/sw.cjs.map +1 -1
- package/dist/sw.d.ts +26 -0
- package/dist/sw.js +90 -74
- package/dist/sw.js.map +1 -1
- package/dist/tempest-react-sdk.cjs +3 -3
- package/dist/tempest-react-sdk.cjs.map +1 -1
- package/dist/tempest-react-sdk.d.ts +289 -0
- package/dist/tempest-react-sdk.js +3362 -3198
- package/dist/tempest-react-sdk.js.map +1 -1
- package/package.json +2 -1
package/dist/sw.d.ts
CHANGED
|
@@ -151,6 +151,11 @@ export declare interface PushPayload {
|
|
|
151
151
|
* app keeps full control over the SW file — this helper only handles the
|
|
152
152
|
* boilerplate around `register()` and `updatefound`.
|
|
153
153
|
*
|
|
154
|
+
* With `autoUpdate` enabled it additionally polls `registration.update()` on
|
|
155
|
+
* an interval and (by default) reloads the page when a freshly activated worker
|
|
156
|
+
* takes control — a framework-agnostic equivalent of `vite-plugin-pwa`'s
|
|
157
|
+
* auto-update client, implemented directly on `navigator.serviceWorker`.
|
|
158
|
+
*
|
|
154
159
|
* @returns The registration when it succeeds, or `null` when unsupported.
|
|
155
160
|
*/
|
|
156
161
|
export declare function registerServiceWorker(options: RegisterServiceWorkerOptions): Promise<ServiceWorkerRegistration | null>;
|
|
@@ -170,6 +175,27 @@ export declare interface RegisterServiceWorkerOptions {
|
|
|
170
175
|
onUpdate?: (waiting: ServiceWorker, registration: ServiceWorkerRegistration) => void;
|
|
171
176
|
/** Called on registration failure. */
|
|
172
177
|
onError?: (error: unknown) => void;
|
|
178
|
+
/**
|
|
179
|
+
* When `true`, poll the server for a fresh service worker on an interval
|
|
180
|
+
* (see {@link RegisterServiceWorkerOptions.updateIntervalMs}) and, unless
|
|
181
|
+
* {@link RegisterServiceWorkerOptions.reloadOnActivate} is disabled, reload
|
|
182
|
+
* the page as soon as a new worker takes control. Mirrors the auto-update
|
|
183
|
+
* behaviour of `vite-plugin-pwa` without depending on it. Default `false`.
|
|
184
|
+
*/
|
|
185
|
+
autoUpdate?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* How often, in ms, to call `registration.update()` while `autoUpdate` is
|
|
188
|
+
* on. Browsers already re-check roughly every 24h; an hourly poll keeps
|
|
189
|
+
* long-lived sessions current without flooding the network. Default
|
|
190
|
+
* `3600000` (1 hour).
|
|
191
|
+
*/
|
|
192
|
+
updateIntervalMs?: number;
|
|
193
|
+
/**
|
|
194
|
+
* When `autoUpdate` is on, reload the page once a new worker takes control
|
|
195
|
+
* (`controllerchange`), guarded against reload loops. Set to `false` to
|
|
196
|
+
* keep polling but leave the reload to the host app. Default `true`.
|
|
197
|
+
*/
|
|
198
|
+
reloadOnActivate?: boolean;
|
|
173
199
|
}
|
|
174
200
|
|
|
175
201
|
/** A single runtime-caching rule, matched against each `GET` request. */
|
package/dist/sw.js
CHANGED
|
@@ -1,24 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
let p = !1;
|
|
2
|
+
function S() {
|
|
3
|
+
if (p) return;
|
|
4
|
+
p = !0;
|
|
5
|
+
let t = !1;
|
|
6
|
+
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
|
7
|
+
t || (t = !0, window.location.reload());
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
async function C(t) {
|
|
2
11
|
if (typeof navigator > "u" || !("serviceWorker" in navigator))
|
|
3
12
|
return null;
|
|
4
13
|
try {
|
|
5
14
|
const e = await navigator.serviceWorker.register(t.url, {
|
|
6
15
|
scope: t.scope
|
|
7
16
|
});
|
|
8
|
-
|
|
17
|
+
if (e.active && t.onReady?.(e), e.addEventListener("updatefound", () => {
|
|
9
18
|
const a = e.installing;
|
|
10
19
|
a && a.addEventListener("statechange", () => {
|
|
11
20
|
a.state === "installed" && navigator.serviceWorker.controller && t.onUpdate?.(a, e);
|
|
12
21
|
});
|
|
13
|
-
}),
|
|
22
|
+
}), t.autoUpdate) {
|
|
23
|
+
t.reloadOnActivate !== !1 && S();
|
|
24
|
+
const a = t.updateIntervalMs ?? 36e5;
|
|
25
|
+
window.setInterval(() => {
|
|
26
|
+
e.update();
|
|
27
|
+
}, a);
|
|
28
|
+
}
|
|
29
|
+
return e;
|
|
14
30
|
} catch (e) {
|
|
15
31
|
return t.onError?.(e), null;
|
|
16
32
|
}
|
|
17
33
|
}
|
|
18
|
-
function
|
|
34
|
+
function j(t) {
|
|
19
35
|
t.postMessage({ type: "SKIP_WAITING" });
|
|
20
36
|
}
|
|
21
|
-
async function
|
|
37
|
+
async function q() {
|
|
22
38
|
if (typeof navigator > "u" || !("serviceWorker" in navigator)) return 0;
|
|
23
39
|
const t = await navigator.serviceWorker.getRegistrations();
|
|
24
40
|
let e = 0;
|
|
@@ -41,7 +57,7 @@ function H(t = {}) {
|
|
|
41
57
|
}
|
|
42
58
|
const o = i ? i(s) : s;
|
|
43
59
|
if (!o) return;
|
|
44
|
-
const
|
|
60
|
+
const l = o.title ?? a, u = {
|
|
45
61
|
body: o.body,
|
|
46
62
|
icon: o.icon ?? n,
|
|
47
63
|
badge: o.badge ?? c,
|
|
@@ -49,10 +65,10 @@ function H(t = {}) {
|
|
|
49
65
|
tag: o.tag,
|
|
50
66
|
data: { url: o.url ?? "/", ...o.data ?? {} }
|
|
51
67
|
};
|
|
52
|
-
r.waitUntil(e.registration.showNotification(
|
|
68
|
+
r.waitUntil(e.registration.showNotification(l, u));
|
|
53
69
|
});
|
|
54
70
|
}
|
|
55
|
-
function
|
|
71
|
+
function F(t = {}) {
|
|
56
72
|
const e = w(), a = t.resolveUrl ?? ((n) => {
|
|
57
73
|
if (typeof n == "string") return n;
|
|
58
74
|
if (n && typeof n == "object" && "url" in n) {
|
|
@@ -84,11 +100,11 @@ function G() {
|
|
|
84
100
|
e.data?.type === "SKIP_WAITING" && t.skipWaiting();
|
|
85
101
|
});
|
|
86
102
|
}
|
|
87
|
-
function
|
|
103
|
+
function E() {
|
|
88
104
|
return globalThis;
|
|
89
105
|
}
|
|
90
106
|
let h = "";
|
|
91
|
-
const
|
|
107
|
+
const y = /* @__PURE__ */ new Set();
|
|
92
108
|
function R(t, e) {
|
|
93
109
|
if (!e) return !1;
|
|
94
110
|
const a = t.headers.get("date");
|
|
@@ -101,13 +117,13 @@ async function g(t, e) {
|
|
|
101
117
|
for (const c of n.slice(0, n.length - e))
|
|
102
118
|
await a.delete(c);
|
|
103
119
|
}
|
|
104
|
-
async function
|
|
120
|
+
async function W(t, e) {
|
|
105
121
|
const a = await caches.open(e.cacheName), n = await a.match(t);
|
|
106
122
|
if (n && !R(n, e.maxAgeSeconds)) return n;
|
|
107
123
|
const c = await fetch(t);
|
|
108
124
|
return c.ok && (await a.put(t, c.clone()), await g(e.cacheName, e.maxEntries)), c;
|
|
109
125
|
}
|
|
110
|
-
async function
|
|
126
|
+
async function T(t, e) {
|
|
111
127
|
const a = await caches.open(e.cacheName), n = (async () => {
|
|
112
128
|
const i = await fetch(t);
|
|
113
129
|
return i.ok && (await a.put(t, i.clone()), await g(e.cacheName, e.maxEntries)), i;
|
|
@@ -126,7 +142,7 @@ async function W(t, e) {
|
|
|
126
142
|
throw new Error("network-first: no network and no cache");
|
|
127
143
|
}
|
|
128
144
|
}
|
|
129
|
-
async function
|
|
145
|
+
async function N(t, e) {
|
|
130
146
|
const a = await caches.open(e.cacheName), n = await a.match(t), c = fetch(t).then(async (r) => (r.ok && (await a.put(t, r.clone()), await g(e.cacheName, e.maxEntries)), r)).catch(() => {
|
|
131
147
|
});
|
|
132
148
|
if (n && !R(n, e.maxAgeSeconds)) return n;
|
|
@@ -135,20 +151,20 @@ async function L(t, e) {
|
|
|
135
151
|
if (n) return n;
|
|
136
152
|
throw new Error("stale-while-revalidate: no network and no cache");
|
|
137
153
|
}
|
|
138
|
-
function
|
|
154
|
+
function v(t, e) {
|
|
139
155
|
switch (e.strategy) {
|
|
140
156
|
case "cache-first":
|
|
141
|
-
return S(t, e);
|
|
142
|
-
case "network-first":
|
|
143
157
|
return W(t, e);
|
|
158
|
+
case "network-first":
|
|
159
|
+
return T(t, e);
|
|
144
160
|
case "stale-while-revalidate":
|
|
145
|
-
return
|
|
161
|
+
return N(t, e);
|
|
146
162
|
}
|
|
147
163
|
}
|
|
148
|
-
function
|
|
164
|
+
function U(t, e, a) {
|
|
149
165
|
return typeof t.match == "function" ? t.match(e, a) : t.match.test(e.href);
|
|
150
166
|
}
|
|
151
|
-
async function
|
|
167
|
+
async function A(t, e) {
|
|
152
168
|
const a = t.headers.get("range");
|
|
153
169
|
if (!a) return e;
|
|
154
170
|
const n = /^bytes=(\d*)-(\d*)$/.exec(a.trim());
|
|
@@ -157,8 +173,8 @@ async function T(t, e) {
|
|
|
157
173
|
const c = await e.clone().arrayBuffer(), i = c.byteLength;
|
|
158
174
|
let r, s;
|
|
159
175
|
if (n[1] === "") {
|
|
160
|
-
const
|
|
161
|
-
r = Math.max(0, i -
|
|
176
|
+
const u = Number(n[2]);
|
|
177
|
+
r = Math.max(0, i - u), s = i - 1;
|
|
162
178
|
} else
|
|
163
179
|
r = Number(n[1]), s = n[2] === "" ? i - 1 : Math.min(Number(n[2]), i - 1);
|
|
164
180
|
if (r > s || r >= i)
|
|
@@ -167,40 +183,40 @@ async function T(t, e) {
|
|
|
167
183
|
statusText: "Range Not Satisfiable",
|
|
168
184
|
headers: { "Content-Range": `bytes */${i}` }
|
|
169
185
|
});
|
|
170
|
-
const o = c.slice(r, s + 1),
|
|
171
|
-
return
|
|
186
|
+
const o = c.slice(r, s + 1), l = new Headers(e.headers);
|
|
187
|
+
return l.set("Content-Range", `bytes ${r}-${s}/${i}`), l.set("Content-Length", String(o.byteLength)), l.set("Accept-Ranges", "bytes"), new Response(o, {
|
|
172
188
|
status: 206,
|
|
173
189
|
statusText: "Partial Content",
|
|
174
|
-
headers:
|
|
190
|
+
headers: l
|
|
175
191
|
});
|
|
176
192
|
}
|
|
177
193
|
function B(t) {
|
|
178
|
-
|
|
194
|
+
E().addEventListener("fetch", (a) => {
|
|
179
195
|
const n = a.request;
|
|
180
196
|
if (n.method !== "GET") return;
|
|
181
|
-
const c = new URL(n.url), i = t.find((r) =>
|
|
197
|
+
const c = new URL(n.url), i = t.find((r) => U(r, c, n));
|
|
182
198
|
if (i) {
|
|
183
199
|
if (i.rangeRequests && n.headers.has("range")) {
|
|
184
200
|
const r = new Request(n.url, {
|
|
185
|
-
headers:
|
|
201
|
+
headers: P(n.headers),
|
|
186
202
|
credentials: n.credentials,
|
|
187
203
|
mode: n.mode === "navigate" ? "same-origin" : n.mode
|
|
188
204
|
});
|
|
189
205
|
a.respondWith(
|
|
190
|
-
|
|
206
|
+
v(r, i).then((s) => A(n, s))
|
|
191
207
|
);
|
|
192
208
|
return;
|
|
193
209
|
}
|
|
194
|
-
a.respondWith(
|
|
210
|
+
a.respondWith(v(n, i));
|
|
195
211
|
}
|
|
196
212
|
});
|
|
197
213
|
}
|
|
198
|
-
function
|
|
214
|
+
function P(t) {
|
|
199
215
|
const e = new Headers(t);
|
|
200
216
|
return e.delete("range"), e;
|
|
201
217
|
}
|
|
202
|
-
function
|
|
203
|
-
const e =
|
|
218
|
+
function O(t = {}) {
|
|
219
|
+
const e = E(), {
|
|
204
220
|
manifestUrl: a = "/precache-manifest.json",
|
|
205
221
|
cacheName: n = "tempest-precache",
|
|
206
222
|
navigateFallback: c = "/index.html",
|
|
@@ -210,10 +226,10 @@ function F(t = {}) {
|
|
|
210
226
|
e.addEventListener("install", (s) => {
|
|
211
227
|
s.waitUntil(
|
|
212
228
|
(async () => {
|
|
213
|
-
const
|
|
214
|
-
h = `${n}-${
|
|
215
|
-
for (const
|
|
216
|
-
|
|
229
|
+
const l = await (await fetch(a, { cache: "no-cache" })).json();
|
|
230
|
+
h = `${n}-${l.version}`, await (await caches.open(h)).addAll(l.urls);
|
|
231
|
+
for (const f of l.urls)
|
|
232
|
+
y.add(new URL(f, e.location.origin).pathname);
|
|
217
233
|
r && await e.skipWaiting();
|
|
218
234
|
})()
|
|
219
235
|
);
|
|
@@ -222,69 +238,69 @@ function F(t = {}) {
|
|
|
222
238
|
(async () => {
|
|
223
239
|
const o = await caches.keys();
|
|
224
240
|
await Promise.all(
|
|
225
|
-
o.filter((
|
|
241
|
+
o.filter((l) => l.startsWith(`${n}-`) && l !== h).map((l) => caches.delete(l))
|
|
226
242
|
), await e.clients.claim();
|
|
227
243
|
})()
|
|
228
244
|
);
|
|
229
245
|
}), e.addEventListener("fetch", (s) => {
|
|
230
246
|
const o = s.request;
|
|
231
247
|
if (o.method !== "GET") return;
|
|
232
|
-
const
|
|
233
|
-
if (
|
|
248
|
+
const l = new URL(o.url);
|
|
249
|
+
if (l.origin === e.location.origin) {
|
|
234
250
|
if (o.mode === "navigate") {
|
|
235
|
-
if (i.some((
|
|
251
|
+
if (i.some((u) => u.test(l.pathname))) return;
|
|
236
252
|
s.respondWith(
|
|
237
253
|
(async () => {
|
|
238
254
|
try {
|
|
239
255
|
return await fetch(o);
|
|
240
256
|
} catch {
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
257
|
+
const f = await (await caches.open(h)).match(c);
|
|
258
|
+
if (f) return f;
|
|
243
259
|
throw new Error("offline and no cached app shell");
|
|
244
260
|
}
|
|
245
261
|
})()
|
|
246
262
|
);
|
|
247
263
|
return;
|
|
248
264
|
}
|
|
249
|
-
|
|
265
|
+
y.has(l.pathname) && s.respondWith(
|
|
250
266
|
(async () => await (await caches.open(h)).match(o) ?? fetch(o))()
|
|
251
267
|
);
|
|
252
268
|
}
|
|
253
269
|
});
|
|
254
270
|
}
|
|
255
|
-
function
|
|
271
|
+
function x() {
|
|
256
272
|
return globalThis;
|
|
257
273
|
}
|
|
258
|
-
const
|
|
274
|
+
const d = "requests";
|
|
259
275
|
function m(t) {
|
|
260
276
|
return new Promise((e, a) => {
|
|
261
277
|
const n = indexedDB.open(t, 1);
|
|
262
278
|
n.onupgradeneeded = () => {
|
|
263
|
-
n.result.createObjectStore(
|
|
279
|
+
n.result.createObjectStore(d, { keyPath: "id", autoIncrement: !0 });
|
|
264
280
|
}, n.onsuccess = () => e(n.result), n.onerror = () => a(n.error);
|
|
265
281
|
});
|
|
266
282
|
}
|
|
267
|
-
function
|
|
283
|
+
function L(t) {
|
|
268
284
|
return new Promise((e, a) => {
|
|
269
285
|
t.oncomplete = () => e(), t.onerror = () => a(t.error), t.onabort = () => a(t.error);
|
|
270
286
|
});
|
|
271
287
|
}
|
|
272
|
-
async function
|
|
273
|
-
const a = await m(t), n = a.transaction(
|
|
274
|
-
n.objectStore(
|
|
288
|
+
async function D(t, e) {
|
|
289
|
+
const a = await m(t), n = a.transaction(d, "readwrite");
|
|
290
|
+
n.objectStore(d).add(e), await L(n), a.close();
|
|
275
291
|
}
|
|
276
|
-
async function
|
|
277
|
-
const e = await m(t), a = e.transaction(
|
|
278
|
-
const r = a.objectStore(
|
|
292
|
+
async function I(t) {
|
|
293
|
+
const e = await m(t), a = e.transaction(d, "readonly"), n = await new Promise((c, i) => {
|
|
294
|
+
const r = a.objectStore(d).getAll();
|
|
279
295
|
r.onsuccess = () => c(r.result), r.onerror = () => i(r.error);
|
|
280
296
|
});
|
|
281
297
|
return e.close(), n;
|
|
282
298
|
}
|
|
283
299
|
async function b(t, e) {
|
|
284
|
-
const a = await m(t), n = a.transaction(
|
|
285
|
-
n.objectStore(
|
|
300
|
+
const a = await m(t), n = a.transaction(d, "readwrite");
|
|
301
|
+
n.objectStore(d).delete(e), await L(n), a.close();
|
|
286
302
|
}
|
|
287
|
-
async function
|
|
303
|
+
async function M(t, e) {
|
|
288
304
|
const a = await t.clone().arrayBuffer();
|
|
289
305
|
return {
|
|
290
306
|
url: t.url,
|
|
@@ -294,7 +310,7 @@ async function A(t, e) {
|
|
|
294
310
|
timestamp: e
|
|
295
311
|
};
|
|
296
312
|
}
|
|
297
|
-
function
|
|
313
|
+
function $(t) {
|
|
298
314
|
return new Request(t.url, {
|
|
299
315
|
method: t.method,
|
|
300
316
|
headers: t.headers,
|
|
@@ -302,7 +318,7 @@ function D(t) {
|
|
|
302
318
|
});
|
|
303
319
|
}
|
|
304
320
|
async function k(t, e, a) {
|
|
305
|
-
const n = await
|
|
321
|
+
const n = await I(t);
|
|
306
322
|
let c = 0;
|
|
307
323
|
for (const i of n)
|
|
308
324
|
if (i.id !== void 0) {
|
|
@@ -311,7 +327,7 @@ async function k(t, e, a) {
|
|
|
311
327
|
continue;
|
|
312
328
|
}
|
|
313
329
|
try {
|
|
314
|
-
const r = await fetch(
|
|
330
|
+
const r = await fetch($(i));
|
|
315
331
|
r.ok || r.status >= 400 && r.status < 500 ? await b(t, i.id) : c += 1;
|
|
316
332
|
} catch {
|
|
317
333
|
c += 1;
|
|
@@ -319,24 +335,24 @@ async function k(t, e, a) {
|
|
|
319
335
|
}
|
|
320
336
|
if (c > 0) throw new Error(`background-sync: ${c} request(s) still pending`);
|
|
321
337
|
}
|
|
322
|
-
function
|
|
338
|
+
function _(t, e, a) {
|
|
323
339
|
return t ? typeof t == "function" ? t(e, a) : t.test(e.href) : !0;
|
|
324
340
|
}
|
|
325
|
-
function
|
|
326
|
-
const e =
|
|
341
|
+
function z(t = {}) {
|
|
342
|
+
const e = x(), { match: a, queueName: n = "tempest-bg-sync", maxRetentionMinutes: c = 1440 } = t, i = c * 60 * 1e3;
|
|
327
343
|
e.addEventListener("fetch", (r) => {
|
|
328
344
|
const s = r.request;
|
|
329
345
|
if (s.method === "GET" || s.method === "HEAD") return;
|
|
330
346
|
const o = new URL(s.url);
|
|
331
|
-
|
|
332
|
-
fetch(s.clone()).catch(async (
|
|
333
|
-
const
|
|
334
|
-
await
|
|
347
|
+
_(a, o, s) && r.respondWith(
|
|
348
|
+
fetch(s.clone()).catch(async (l) => {
|
|
349
|
+
const u = await M(s, Date.now());
|
|
350
|
+
await D(n, u);
|
|
335
351
|
try {
|
|
336
352
|
await e.registration.sync?.register(n);
|
|
337
353
|
} catch {
|
|
338
354
|
}
|
|
339
|
-
throw
|
|
355
|
+
throw l;
|
|
340
356
|
})
|
|
341
357
|
);
|
|
342
358
|
}), e.addEventListener("sync", (r) => {
|
|
@@ -347,15 +363,15 @@ function _(t = {}) {
|
|
|
347
363
|
});
|
|
348
364
|
}
|
|
349
365
|
export {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
366
|
+
A as createPartialResponse,
|
|
367
|
+
z as installBackgroundSync,
|
|
368
|
+
F as installNotificationClickHandler,
|
|
369
|
+
O as installPrecache,
|
|
354
370
|
H as installPushHandler,
|
|
355
371
|
B as installRuntimeCache,
|
|
356
372
|
G as installSkipWaitingListener,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
373
|
+
C as registerServiceWorker,
|
|
374
|
+
j as skipWaiting,
|
|
375
|
+
q as unregisterAllServiceWorkers
|
|
360
376
|
};
|
|
361
377
|
//# sourceMappingURL=sw.js.map
|
package/dist/sw.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sw.js","sources":["../src/sw/register-service-worker.ts","../src/sw/create-push-handler.ts","../src/sw/cache.ts","../src/sw/background-sync.ts"],"sourcesContent":["export interface RegisterServiceWorkerOptions {\n /** Public URL of the compiled service worker file (e.g. `/sw.js`). */\n url: string;\n /** SW scope (default: SW directory). */\n scope?: string;\n /** Called once the registration is active. */\n onReady?: (registration: ServiceWorkerRegistration) => void;\n /**\n * Called when a new worker has finished installing while another worker\n * still controls the page. The host app typically prompts the user to\n * reload and then calls {@link skipWaiting} on the returned worker.\n */\n onUpdate?: (waiting: ServiceWorker, registration: ServiceWorkerRegistration) => void;\n /** Called on registration failure. */\n onError?: (error: unknown) => void;\n}\n\n/**\n * Register a service worker with consistent update-detection wiring.\n *\n * Skips silently when the runtime has no `serviceWorker` support. The host\n * app keeps full control over the SW file — this helper only handles the\n * boilerplate around `register()` and `updatefound`.\n *\n * @returns The registration when it succeeds, or `null` when unsupported.\n */\nexport async function registerServiceWorker(\n options: RegisterServiceWorkerOptions,\n): Promise<ServiceWorkerRegistration | null> {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return null;\n }\n\n try {\n const registration = await navigator.serviceWorker.register(options.url, {\n scope: options.scope,\n });\n\n if (registration.active) options.onReady?.(registration);\n\n registration.addEventListener(\"updatefound\", () => {\n const installing = registration.installing;\n if (!installing) return;\n installing.addEventListener(\"statechange\", () => {\n if (installing.state === \"installed\" && navigator.serviceWorker.controller) {\n options.onUpdate?.(installing, registration);\n }\n });\n });\n\n return registration;\n } catch (error) {\n options.onError?.(error);\n return null;\n }\n}\n\n/**\n * Tell a waiting worker to activate immediately. Pair with `onUpdate` to roll\n * out updates after the user confirms a reload prompt.\n */\nexport function skipWaiting(worker: ServiceWorker): void {\n worker.postMessage({ type: \"SKIP_WAITING\" });\n}\n\n/**\n * Unregister all registered service workers for this origin.\n *\n * @returns Number of workers that were unregistered.\n */\nexport async function unregisterAllServiceWorkers(): Promise<number> {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) return 0;\n const registrations = await navigator.serviceWorker.getRegistrations();\n let count = 0;\n for (const registration of registrations) {\n const result = await registration.unregister();\n if (result) count += 1;\n }\n return count;\n}\n","/**\n * Service-worker context helpers for handling `push` and `notificationclick`\n * events. Import these inside your own `sw.ts` — they expect to run in the\n * service-worker global scope, not in the main thread.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installPushHandler, installNotificationClickHandler } from \"tempest-react-sdk\";\n *\n * installPushHandler({ defaultIcon: \"/icons/Logo.png\" });\n * installNotificationClickHandler();\n */\n\ninterface SwGlobal {\n registration: {\n showNotification(title: string, options?: NotificationOptions): Promise<void>;\n };\n clients: {\n matchAll(options: { type: \"window\"; includeUncontrolled?: boolean }): Promise<\n {\n url: string;\n focused: boolean;\n focus(): Promise<unknown>;\n navigate(url: string): Promise<unknown>;\n }[]\n >;\n openWindow(url: string): Promise<unknown>;\n };\n addEventListener(\n type: \"push\",\n listener: (event: {\n data: { json(): unknown; text(): string } | null;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n addEventListener(\n type: \"notificationclick\",\n listener: (event: {\n notification: { close(): void; data?: unknown };\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n skipWaiting(): Promise<void>;\n}\n\nfunction getSwScope(): SwGlobal {\n return globalThis as unknown as SwGlobal;\n}\n\nexport interface PushPayload {\n title?: string;\n body?: string;\n icon?: string;\n badge?: string;\n image?: string;\n tag?: string;\n url?: string;\n /** Arbitrary extra data forwarded to `event.notification.data`. */\n data?: Record<string, unknown>;\n}\n\nexport interface InstallPushHandlerOptions {\n /** Title used when the payload omits one. */\n defaultTitle?: string;\n /** Icon used when the payload omits one. */\n defaultIcon?: string;\n /** Badge image (mobile). */\n defaultBadge?: string;\n /**\n * Transform the raw payload before showing the notification. Return `null`\n * to suppress the notification entirely (e.g. silent pings).\n */\n transform?: (payload: PushPayload) => PushPayload | null;\n}\n\n/**\n * Install a `push` event listener that parses the payload as JSON (with a\n * plain-text fallback) and shows a notification.\n */\nexport function installPushHandler(options: InstallPushHandlerOptions = {}): void {\n const sw = getSwScope();\n const { defaultTitle = \"Notificação\", defaultIcon, defaultBadge, transform } = options;\n\n sw.addEventListener(\"push\", (event) => {\n if (!event.data) return;\n\n let raw: PushPayload;\n try {\n raw = event.data.json() as PushPayload;\n } catch {\n raw = { title: defaultTitle, body: event.data.text() };\n }\n\n const payload = transform ? transform(raw) : raw;\n if (!payload) return;\n\n const title = payload.title ?? defaultTitle;\n const notification: NotificationOptions & { image?: string } = {\n body: payload.body,\n icon: payload.icon ?? defaultIcon,\n badge: payload.badge ?? defaultBadge,\n image: payload.image,\n tag: payload.tag,\n data: { url: payload.url ?? \"/\", ...(payload.data ?? {}) },\n };\n\n event.waitUntil(sw.registration.showNotification(title, notification));\n });\n}\n\nexport interface InstallNotificationClickHandlerOptions {\n /** Resolve the destination URL from the notification data. Default: `data.url`. */\n resolveUrl?: (data: unknown) => string;\n}\n\n/**\n * Install a `notificationclick` handler that focuses an existing client when\n * possible and falls back to opening a new window.\n */\nexport function installNotificationClickHandler(\n options: InstallNotificationClickHandlerOptions = {},\n): void {\n const sw = getSwScope();\n const resolveUrl =\n options.resolveUrl ??\n ((data: unknown) => {\n if (typeof data === \"string\") return data;\n if (data && typeof data === \"object\" && \"url\" in data) {\n const url = (data as Record<string, unknown>).url;\n return typeof url === \"string\" ? url : \"/\";\n }\n return \"/\";\n });\n\n sw.addEventListener(\"notificationclick\", (event) => {\n event.notification.close();\n const target = resolveUrl(event.notification.data);\n\n event.waitUntil(\n (async () => {\n const clients = await sw.clients.matchAll({\n type: \"window\",\n includeUncontrolled: true,\n });\n for (const client of clients) {\n if (client.url.includes(target)) {\n return client.focus();\n }\n }\n return sw.clients.openWindow(target);\n })(),\n );\n });\n}\n\n/**\n * Install a `message` listener that activates a waiting worker when the host\n * app sends `{ type: \"SKIP_WAITING\" }`.\n */\nexport function installSkipWaitingListener(): void {\n const sw = getSwScope() as SwGlobal & {\n addEventListener(\n type: \"message\",\n listener: (event: { data?: { type?: string } }) => void,\n ): void;\n };\n sw.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"SKIP_WAITING\") {\n void sw.skipWaiting();\n }\n });\n}\n","/**\n * Service-worker caching helpers — a small, dependency-free subset of what\n * Workbox provides: precaching of the build's app shell (so the app launches\n * offline) plus runtime caching strategies for fonts, APIs and images.\n *\n * Import these inside your own `sw.ts`. They run in the service-worker global\n * scope, not the main thread. Pair `installPrecache` with the\n * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which\n * emits the `precache-manifest.json` this reads at install time.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installRuntimeCache, installPrecache } from \"tempest-react-sdk/sw\";\n *\n * // Register specific routes FIRST so they win over the precache catch-all.\n * installRuntimeCache([\n * { match: /\\/api\\//, strategy: \"network-first\", cacheName: \"api\", maxAgeSeconds: 300 },\n * ]);\n * installPrecache();\n */\n\n/** Minimal shape of the events we touch, to avoid pulling in `lib.webworker`. */\ninterface ExtendableEventLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface FetchEventLike extends ExtendableEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n}\n\ninterface CacheSwGlobal {\n addEventListener(type: \"install\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"activate\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchEventLike) => void): void;\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n location: { origin: string };\n}\n\nfunction getSwScope(): CacheSwGlobal {\n return globalThis as unknown as CacheSwGlobal;\n}\n\n/** Caching strategy for a runtime route. Mirrors the common Workbox trio. */\nexport type RuntimeStrategy = \"cache-first\" | \"network-first\" | \"stale-while-revalidate\";\n\n/** A single runtime-caching rule, matched against each `GET` request. */\nexport interface RuntimeRoute {\n /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */\n match: RegExp | ((url: URL, request: Request) => boolean);\n /** How to resolve a match. */\n strategy: RuntimeStrategy;\n /** Cache bucket name for this route. */\n cacheName: string;\n /** Trim the cache to at most this many entries (FIFO) after each write. */\n maxEntries?: number;\n /** Treat a cached response older than this (seconds) as a miss. */\n maxAgeSeconds?: number;\n /** For `network-first`: fall back to cache after this timeout (seconds). */\n networkTimeoutSeconds?: number;\n /**\n * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached\n * full response. Enable for audio/video so seeking works offline. The full\n * resource is cached once (the `Range` header is stripped before caching).\n */\n rangeRequests?: boolean;\n}\n\n/** Options for {@link installPrecache}. */\nexport interface InstallPrecacheOptions {\n /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */\n manifestUrl?: string;\n /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */\n cacheName?: string;\n /** App-shell document served for navigation requests offline. Default `/index.html`. */\n navigateFallback?: string;\n /** Navigation paths that should NOT use the fallback (e.g. `[/^\\/api\\//]`). */\n navigateFallbackDenylist?: RegExp[];\n /** Activate the new worker immediately after precaching. Default `true`. */\n skipWaiting?: boolean;\n}\n\ninterface PrecacheManifest {\n version: string;\n urls: string[];\n}\n\nlet precacheName = \"\";\nconst precachedPaths = new Set<string>();\n\n/** Whether a cached response has aged past `maxAgeSeconds` (best-effort, via `Date`). */\nfunction isExpired(response: Response, maxAgeSeconds?: number): boolean {\n if (!maxAgeSeconds) return false;\n const dateHeader = response.headers.get(\"date\");\n if (!dateHeader) return false;\n const age = (Date.now() - new Date(dateHeader).getTime()) / 1000;\n return age > maxAgeSeconds;\n}\n\n/** Drop oldest entries until the cache holds at most `maxEntries`. */\nasync function trimCache(cacheName: string, maxEntries?: number): Promise<void> {\n if (!maxEntries) return;\n const cache = await caches.open(cacheName);\n const keys = await cache.keys();\n if (keys.length <= maxEntries) return;\n for (const key of keys.slice(0, keys.length - maxEntries)) {\n await cache.delete(key);\n }\n}\n\nasync function cacheFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n\n const response = await fetch(request);\n if (response.ok) {\n await cache.put(request, response.clone());\n await trimCache(route.cacheName, route.maxEntries);\n }\n return response;\n}\n\nasync function networkFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n\n const network = (async (): Promise<Response> => {\n const response = await fetch(request);\n if (response.ok) {\n await cache.put(request, response.clone());\n await trimCache(route.cacheName, route.maxEntries);\n }\n return response;\n })();\n\n const timeoutMs = (route.networkTimeoutSeconds ?? 0) * 1000;\n try {\n if (timeoutMs > 0) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\"network-timeout\")), timeoutMs),\n );\n return await Promise.race([network, timeout]);\n }\n return await network;\n } catch {\n const cached = await cache.match(request);\n if (cached) return cached;\n throw new Error(\"network-first: no network and no cache\");\n }\n}\n\nasync function staleWhileRevalidate(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n\n const network = fetch(request)\n .then(async (response) => {\n if (response.ok) {\n await cache.put(request, response.clone());\n await trimCache(route.cacheName, route.maxEntries);\n }\n return response;\n })\n .catch(() => undefined);\n\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n const response = await network;\n if (response) return response;\n if (cached) return cached;\n throw new Error(\"stale-while-revalidate: no network and no cache\");\n}\n\nfunction runRoute(request: Request, route: RuntimeRoute): Promise<Response> {\n switch (route.strategy) {\n case \"cache-first\":\n return cacheFirst(request, route);\n case \"network-first\":\n return networkFirst(request, route);\n case \"stale-while-revalidate\":\n return staleWhileRevalidate(request, route);\n }\n}\n\nfunction routeMatches(route: RuntimeRoute, url: URL, request: Request): boolean {\n return typeof route.match === \"function\"\n ? route.match(url, request)\n : route.match.test(url.href);\n}\n\n/**\n * Build a `206 Partial Content` response from a full one for an HTTP `Range`\n * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix\n * `bytes=-suffixLength`. Returns the original response when there is no usable\n * `Range` header, or a `416` when the range is unsatisfiable.\n *\n * @param request The incoming request (its `Range` header drives the slice).\n * @param response The full (200) response to slice.\n */\nexport async function createPartialResponse(\n request: Request,\n response: Response,\n): Promise<Response> {\n const rangeHeader = request.headers.get(\"range\");\n if (!rangeHeader) return response;\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match || (match[1] === \"\" && match[2] === \"\")) {\n return new Response(null, { status: 416, statusText: \"Range Not Satisfiable\" });\n }\n\n const buffer = await response.clone().arrayBuffer();\n const total = buffer.byteLength;\n\n let start: number;\n let end: number;\n if (match[1] === \"\") {\n // Suffix range: last N bytes.\n const suffix = Number(match[2]);\n start = Math.max(0, total - suffix);\n end = total - 1;\n } else {\n start = Number(match[1]);\n end = match[2] === \"\" ? total - 1 : Math.min(Number(match[2]), total - 1);\n }\n\n if (start > end || start >= total) {\n return new Response(null, {\n status: 416,\n statusText: \"Range Not Satisfiable\",\n headers: { \"Content-Range\": `bytes */${total}` },\n });\n }\n\n const slice = buffer.slice(start, end + 1);\n const headers = new Headers(response.headers);\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${total}`);\n headers.set(\"Content-Length\", String(slice.byteLength));\n headers.set(\"Accept-Ranges\", \"bytes\");\n\n return new Response(slice, {\n status: 206,\n statusText: \"Partial Content\",\n headers,\n });\n}\n\n/**\n * Install a `fetch` handler that resolves matching `GET` requests with the\n * given runtime strategies. Non-matching requests are left untouched (no\n * `respondWith`), so a later {@link installPrecache} can handle them.\n *\n * Register this BEFORE `installPrecache` so specific routes win over the\n * precache catch-all.\n *\n * @param routes Ordered rules; the first whose `match` passes handles the request.\n */\nexport function installRuntimeCache(routes: RuntimeRoute[]): void {\n const sw = getSwScope();\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n const route = routes.find((candidate) => routeMatches(candidate, url, request));\n if (!route) return;\n\n if (route.rangeRequests && request.headers.has(\"range\")) {\n // Cache/serve the FULL resource once (no Range), then slice it.\n const full = new Request(request.url, {\n headers: stripRange(request.headers),\n credentials: request.credentials,\n mode: request.mode === \"navigate\" ? \"same-origin\" : request.mode,\n });\n event.respondWith(\n runRoute(full, route).then((response) => createPartialResponse(request, response)),\n );\n return;\n }\n\n event.respondWith(runRoute(request, route));\n });\n}\n\n/** Copy headers without the `Range` header (so the cached entry is the full file). */\nfunction stripRange(headers: Headers): Headers {\n const out = new Headers(headers);\n out.delete(\"range\");\n return out;\n}\n\n/**\n * Precache the app shell at `install` and serve it offline:\n * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),\n * - caches every listed URL under a versioned cache,\n * - on `activate`, deletes stale precache versions and claims open clients,\n * - on `fetch`, serves precached assets cache-first and falls back to the\n * `navigateFallback` document for offline navigations (SPA routing).\n *\n * Same-origin only. Register this LAST, after any {@link installRuntimeCache}.\n */\nexport function installPrecache(options: InstallPrecacheOptions = {}): void {\n const sw = getSwScope();\n const {\n manifestUrl = \"/precache-manifest.json\",\n cacheName = \"tempest-precache\",\n navigateFallback = \"/index.html\",\n navigateFallbackDenylist = [],\n skipWaiting = true,\n } = options;\n\n sw.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const response = await fetch(manifestUrl, { cache: \"no-cache\" });\n const manifest = (await response.json()) as PrecacheManifest;\n precacheName = `${cacheName}-${manifest.version}`;\n\n const cache = await caches.open(precacheName);\n await cache.addAll(manifest.urls);\n for (const url of manifest.urls) {\n precachedPaths.add(new URL(url, sw.location.origin).pathname);\n }\n if (skipWaiting) await sw.skipWaiting();\n })(),\n );\n });\n\n sw.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const keys = await caches.keys();\n await Promise.all(\n keys\n .filter((key) => key.startsWith(`${cacheName}-`) && key !== precacheName)\n .map((key) => caches.delete(key)),\n );\n await sw.clients.claim();\n })(),\n );\n });\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n if (url.origin !== sw.location.origin) return;\n\n // SPA navigations: serve the cached app shell when offline.\n if (request.mode === \"navigate\") {\n if (navigateFallbackDenylist.some((re) => re.test(url.pathname))) return;\n event.respondWith(\n (async () => {\n try {\n return await fetch(request);\n } catch {\n const cache = await caches.open(precacheName);\n const shell = await cache.match(navigateFallback);\n if (shell) return shell;\n throw new Error(\"offline and no cached app shell\");\n }\n })(),\n );\n return;\n }\n\n // Precached static assets: cache-first.\n if (precachedPaths.has(url.pathname)) {\n event.respondWith(\n (async () => {\n const cache = await caches.open(precacheName);\n const cached = await cache.match(request);\n return cached ?? fetch(request);\n })(),\n );\n }\n });\n}\n","/**\n * Background-sync helper: queue failed mutating requests (POST/PUT/PATCH/DELETE)\n * while offline and replay them when connectivity returns. A dependency-free\n * take on Workbox's `BackgroundSyncPlugin`, backed by a tiny IndexedDB queue.\n *\n * Import inside your `sw.ts`. Uses the Background Sync API (`registration.sync`)\n * when available, and also replays opportunistically on the next request as a\n * fallback for browsers without it (e.g. Safari).\n *\n * @example\n * import { installBackgroundSync } from \"tempest-react-sdk/sw\";\n *\n * installBackgroundSync({ match: (url) => url.pathname.startsWith(\"/api/\") });\n */\n\ninterface SyncEventLike {\n tag: string;\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface BgFetchEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface BgSwGlobal {\n registration: { sync?: { register(tag: string): Promise<void> } };\n addEventListener(type: \"sync\", listener: (event: SyncEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: BgFetchEventLike) => void): void;\n}\n\nfunction getSwScope(): BgSwGlobal {\n return globalThis as unknown as BgSwGlobal;\n}\n\n/** A serialized request stored in the queue. */\ninterface QueuedRequest {\n id?: number;\n url: string;\n method: string;\n headers: [string, string][];\n body: ArrayBuffer | null;\n timestamp: number;\n}\n\n/** Options for {@link installBackgroundSync}. */\nexport interface InstallBackgroundSyncOptions {\n /**\n * Which requests to queue on failure. A `RegExp` against the URL or a\n * predicate. Only non-`GET` requests are ever considered. Default: all\n * non-`GET` requests.\n */\n match?: RegExp | ((url: URL, request: Request) => boolean);\n /** IndexedDB database name, also used as the sync tag. Default `tempest-bg-sync`. */\n queueName?: string;\n /** Drop queued requests older than this (minutes) on replay. Default `1440` (24h). */\n maxRetentionMinutes?: number;\n}\n\nconst STORE = \"requests\";\n\nfunction openQueueDb(name: string): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(name, 1);\n request.onupgradeneeded = () => {\n request.result.createObjectStore(STORE, { keyPath: \"id\", autoIncrement: true });\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction txDone(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error);\n });\n}\n\nasync function enqueue(name: string, entry: QueuedRequest): Promise<void> {\n const db = await openQueueDb(name);\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).add(entry);\n await txDone(tx);\n db.close();\n}\n\nasync function readAll(name: string): Promise<QueuedRequest[]> {\n const db = await openQueueDb(name);\n const tx = db.transaction(STORE, \"readonly\");\n const all = await new Promise<QueuedRequest[]>((resolve, reject) => {\n const req = tx.objectStore(STORE).getAll();\n req.onsuccess = () => resolve(req.result as QueuedRequest[]);\n req.onerror = () => reject(req.error);\n });\n db.close();\n return all;\n}\n\nasync function remove(name: string, id: number): Promise<void> {\n const db = await openQueueDb(name);\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).delete(id);\n await txDone(tx);\n db.close();\n}\n\nasync function serializeRequest(request: Request, timestamp: number): Promise<QueuedRequest> {\n const buffer = await request.clone().arrayBuffer();\n return {\n url: request.url,\n method: request.method,\n headers: [...request.headers.entries()],\n body: buffer.byteLength > 0 ? buffer : null,\n timestamp,\n };\n}\n\nfunction deserializeRequest(entry: QueuedRequest): Request {\n return new Request(entry.url, {\n method: entry.method,\n headers: entry.headers,\n body: entry.body ?? undefined,\n });\n}\n\n/**\n * Replay every queued request once. Successful (or stale) entries are removed;\n * entries that fail again are kept for the next attempt. Throws if any entry\n * still fails, so a `sync` handler keeps the sync pending.\n */\nasync function replayQueue(name: string, maxRetentionMs: number, now: number): Promise<void> {\n const entries = await readAll(name);\n let pending = 0;\n\n for (const entry of entries) {\n if (entry.id === undefined) continue;\n if (now - entry.timestamp > maxRetentionMs) {\n await remove(name, entry.id);\n continue;\n }\n try {\n const response = await fetch(deserializeRequest(entry));\n if (response.ok || (response.status >= 400 && response.status < 500)) {\n // 2xx = done; 4xx = client error that won't fix itself → drop it.\n await remove(name, entry.id);\n } else {\n pending += 1;\n }\n } catch {\n pending += 1;\n }\n }\n\n if (pending > 0) throw new Error(`background-sync: ${pending} request(s) still pending`);\n}\n\nfunction matches(\n match: InstallBackgroundSyncOptions[\"match\"],\n url: URL,\n request: Request,\n): boolean {\n if (!match) return true;\n return typeof match === \"function\" ? match(url, request) : match.test(url.href);\n}\n\n/**\n * Install the background-sync queue: on a failed mutating request, the request\n * is serialized to IndexedDB and a sync is registered; the original fetch still\n * rejects (so your app can show an offline state), and the request is replayed\n * later when the network returns.\n */\nexport function installBackgroundSync(options: InstallBackgroundSyncOptions = {}): void {\n const sw = getSwScope();\n const { match, queueName = \"tempest-bg-sync\", maxRetentionMinutes = 1440 } = options;\n const maxRetentionMs = maxRetentionMinutes * 60 * 1000;\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method === \"GET\" || request.method === \"HEAD\") return;\n\n const url = new URL(request.url);\n if (!matches(match, url, request)) return;\n\n event.respondWith(\n fetch(request.clone()).catch(async (error) => {\n const entry = await serializeRequest(request, Date.now());\n await enqueue(queueName, entry);\n try {\n await sw.registration.sync?.register(queueName);\n } catch {\n // Background Sync API unavailable — opportunistic replay covers it.\n }\n throw error;\n }),\n );\n });\n\n sw.addEventListener(\"sync\", (event) => {\n if (event.tag !== queueName) return;\n event.waitUntil(replayQueue(queueName, maxRetentionMs, Date.now()));\n });\n\n // Fallback for browsers without the Background Sync API: whenever a GET\n // succeeds (a sign we're online), drain the queue opportunistically.\n sw.addEventListener(\"fetch\", (event) => {\n if (sw.registration.sync) return; // native sync will handle it\n if (event.request.method !== \"GET\") return;\n event.waitUntil(replayQueue(queueName, maxRetentionMs, Date.now()).catch(() => undefined));\n });\n}\n"],"names":["registerServiceWorker","options","registration","installing","error","skipWaiting","worker","unregisterAllServiceWorkers","registrations","count","getSwScope","installPushHandler","sw","defaultTitle","defaultIcon","defaultBadge","transform","event","raw","payload","title","notification","installNotificationClickHandler","resolveUrl","data","url","target","clients","client","installSkipWaitingListener","precacheName","precachedPaths","isExpired","response","maxAgeSeconds","dateHeader","trimCache","cacheName","maxEntries","cache","keys","key","cacheFirst","request","route","cached","networkFirst","network","timeoutMs","timeout","_","reject","staleWhileRevalidate","runRoute","routeMatches","createPartialResponse","rangeHeader","match","buffer","total","start","end","suffix","slice","headers","installRuntimeCache","routes","candidate","full","stripRange","out","installPrecache","manifestUrl","navigateFallback","navigateFallbackDenylist","manifest","re","shell","STORE","openQueueDb","name","resolve","txDone","tx","enqueue","entry","db","readAll","all","req","remove","id","serializeRequest","timestamp","deserializeRequest","replayQueue","maxRetentionMs","now","entries","pending","matches","installBackgroundSync","queueName","maxRetentionMinutes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sw.js","sources":["../src/sw/register-service-worker.ts","../src/sw/create-push-handler.ts","../src/sw/cache.ts","../src/sw/background-sync.ts"],"sourcesContent":["export interface RegisterServiceWorkerOptions {\n /** Public URL of the compiled service worker file (e.g. `/sw.js`). */\n url: string;\n /** SW scope (default: SW directory). */\n scope?: string;\n /** Called once the registration is active. */\n onReady?: (registration: ServiceWorkerRegistration) => void;\n /**\n * Called when a new worker has finished installing while another worker\n * still controls the page. The host app typically prompts the user to\n * reload and then calls {@link skipWaiting} on the returned worker.\n */\n onUpdate?: (waiting: ServiceWorker, registration: ServiceWorkerRegistration) => void;\n /** Called on registration failure. */\n onError?: (error: unknown) => void;\n /**\n * When `true`, poll the server for a fresh service worker on an interval\n * (see {@link RegisterServiceWorkerOptions.updateIntervalMs}) and, unless\n * {@link RegisterServiceWorkerOptions.reloadOnActivate} is disabled, reload\n * the page as soon as a new worker takes control. Mirrors the auto-update\n * behaviour of `vite-plugin-pwa` without depending on it. Default `false`.\n */\n autoUpdate?: boolean;\n /**\n * How often, in ms, to call `registration.update()` while `autoUpdate` is\n * on. Browsers already re-check roughly every 24h; an hourly poll keeps\n * long-lived sessions current without flooding the network. Default\n * `3600000` (1 hour).\n */\n updateIntervalMs?: number;\n /**\n * When `autoUpdate` is on, reload the page once a new worker takes control\n * (`controllerchange`), guarded against reload loops. Set to `false` to\n * keep polling but leave the reload to the host app. Default `true`.\n */\n reloadOnActivate?: boolean;\n}\n\nconst DEFAULT_UPDATE_INTERVAL_MS = 60 * 60 * 1000;\n\nlet controllerReloadWired = false;\n\nfunction wireControllerReload(): void {\n if (controllerReloadWired) return;\n controllerReloadWired = true;\n let refreshing = false;\n navigator.serviceWorker.addEventListener(\"controllerchange\", () => {\n if (refreshing) return;\n refreshing = true;\n window.location.reload();\n });\n}\n\n/**\n * Register a service worker with consistent update-detection wiring.\n *\n * Skips silently when the runtime has no `serviceWorker` support. The host\n * app keeps full control over the SW file — this helper only handles the\n * boilerplate around `register()` and `updatefound`.\n *\n * With `autoUpdate` enabled it additionally polls `registration.update()` on\n * an interval and (by default) reloads the page when a freshly activated worker\n * takes control — a framework-agnostic equivalent of `vite-plugin-pwa`'s\n * auto-update client, implemented directly on `navigator.serviceWorker`.\n *\n * @returns The registration when it succeeds, or `null` when unsupported.\n */\nexport async function registerServiceWorker(\n options: RegisterServiceWorkerOptions,\n): Promise<ServiceWorkerRegistration | null> {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return null;\n }\n\n try {\n const registration = await navigator.serviceWorker.register(options.url, {\n scope: options.scope,\n });\n\n if (registration.active) options.onReady?.(registration);\n\n registration.addEventListener(\"updatefound\", () => {\n const installing = registration.installing;\n if (!installing) return;\n installing.addEventListener(\"statechange\", () => {\n if (installing.state === \"installed\" && navigator.serviceWorker.controller) {\n options.onUpdate?.(installing, registration);\n }\n });\n });\n\n if (options.autoUpdate) {\n if (options.reloadOnActivate !== false) wireControllerReload();\n const intervalMs = options.updateIntervalMs ?? DEFAULT_UPDATE_INTERVAL_MS;\n window.setInterval(() => {\n void registration.update();\n }, intervalMs);\n }\n\n return registration;\n } catch (error) {\n options.onError?.(error);\n return null;\n }\n}\n\n/**\n * Tell a waiting worker to activate immediately. Pair with `onUpdate` to roll\n * out updates after the user confirms a reload prompt.\n */\nexport function skipWaiting(worker: ServiceWorker): void {\n worker.postMessage({ type: \"SKIP_WAITING\" });\n}\n\n/**\n * Unregister all registered service workers for this origin.\n *\n * @returns Number of workers that were unregistered.\n */\nexport async function unregisterAllServiceWorkers(): Promise<number> {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) return 0;\n const registrations = await navigator.serviceWorker.getRegistrations();\n let count = 0;\n for (const registration of registrations) {\n const result = await registration.unregister();\n if (result) count += 1;\n }\n return count;\n}\n","/**\n * Service-worker context helpers for handling `push` and `notificationclick`\n * events. Import these inside your own `sw.ts` — they expect to run in the\n * service-worker global scope, not in the main thread.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installPushHandler, installNotificationClickHandler } from \"tempest-react-sdk\";\n *\n * installPushHandler({ defaultIcon: \"/icons/Logo.png\" });\n * installNotificationClickHandler();\n */\n\ninterface SwGlobal {\n registration: {\n showNotification(title: string, options?: NotificationOptions): Promise<void>;\n };\n clients: {\n matchAll(options: { type: \"window\"; includeUncontrolled?: boolean }): Promise<\n {\n url: string;\n focused: boolean;\n focus(): Promise<unknown>;\n navigate(url: string): Promise<unknown>;\n }[]\n >;\n openWindow(url: string): Promise<unknown>;\n };\n addEventListener(\n type: \"push\",\n listener: (event: {\n data: { json(): unknown; text(): string } | null;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n addEventListener(\n type: \"notificationclick\",\n listener: (event: {\n notification: { close(): void; data?: unknown };\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n skipWaiting(): Promise<void>;\n}\n\nfunction getSwScope(): SwGlobal {\n return globalThis as unknown as SwGlobal;\n}\n\nexport interface PushPayload {\n title?: string;\n body?: string;\n icon?: string;\n badge?: string;\n image?: string;\n tag?: string;\n url?: string;\n /** Arbitrary extra data forwarded to `event.notification.data`. */\n data?: Record<string, unknown>;\n}\n\nexport interface InstallPushHandlerOptions {\n /** Title used when the payload omits one. */\n defaultTitle?: string;\n /** Icon used when the payload omits one. */\n defaultIcon?: string;\n /** Badge image (mobile). */\n defaultBadge?: string;\n /**\n * Transform the raw payload before showing the notification. Return `null`\n * to suppress the notification entirely (e.g. silent pings).\n */\n transform?: (payload: PushPayload) => PushPayload | null;\n}\n\n/**\n * Install a `push` event listener that parses the payload as JSON (with a\n * plain-text fallback) and shows a notification.\n */\nexport function installPushHandler(options: InstallPushHandlerOptions = {}): void {\n const sw = getSwScope();\n const { defaultTitle = \"Notificação\", defaultIcon, defaultBadge, transform } = options;\n\n sw.addEventListener(\"push\", (event) => {\n if (!event.data) return;\n\n let raw: PushPayload;\n try {\n raw = event.data.json() as PushPayload;\n } catch {\n raw = { title: defaultTitle, body: event.data.text() };\n }\n\n const payload = transform ? transform(raw) : raw;\n if (!payload) return;\n\n const title = payload.title ?? defaultTitle;\n const notification: NotificationOptions & { image?: string } = {\n body: payload.body,\n icon: payload.icon ?? defaultIcon,\n badge: payload.badge ?? defaultBadge,\n image: payload.image,\n tag: payload.tag,\n data: { url: payload.url ?? \"/\", ...(payload.data ?? {}) },\n };\n\n event.waitUntil(sw.registration.showNotification(title, notification));\n });\n}\n\nexport interface InstallNotificationClickHandlerOptions {\n /** Resolve the destination URL from the notification data. Default: `data.url`. */\n resolveUrl?: (data: unknown) => string;\n}\n\n/**\n * Install a `notificationclick` handler that focuses an existing client when\n * possible and falls back to opening a new window.\n */\nexport function installNotificationClickHandler(\n options: InstallNotificationClickHandlerOptions = {},\n): void {\n const sw = getSwScope();\n const resolveUrl =\n options.resolveUrl ??\n ((data: unknown) => {\n if (typeof data === \"string\") return data;\n if (data && typeof data === \"object\" && \"url\" in data) {\n const url = (data as Record<string, unknown>).url;\n return typeof url === \"string\" ? url : \"/\";\n }\n return \"/\";\n });\n\n sw.addEventListener(\"notificationclick\", (event) => {\n event.notification.close();\n const target = resolveUrl(event.notification.data);\n\n event.waitUntil(\n (async () => {\n const clients = await sw.clients.matchAll({\n type: \"window\",\n includeUncontrolled: true,\n });\n for (const client of clients) {\n if (client.url.includes(target)) {\n return client.focus();\n }\n }\n return sw.clients.openWindow(target);\n })(),\n );\n });\n}\n\n/**\n * Install a `message` listener that activates a waiting worker when the host\n * app sends `{ type: \"SKIP_WAITING\" }`.\n */\nexport function installSkipWaitingListener(): void {\n const sw = getSwScope() as SwGlobal & {\n addEventListener(\n type: \"message\",\n listener: (event: { data?: { type?: string } }) => void,\n ): void;\n };\n sw.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"SKIP_WAITING\") {\n void sw.skipWaiting();\n }\n });\n}\n","/**\n * Service-worker caching helpers — a small, dependency-free subset of what\n * Workbox provides: precaching of the build's app shell (so the app launches\n * offline) plus runtime caching strategies for fonts, APIs and images.\n *\n * Import these inside your own `sw.ts`. They run in the service-worker global\n * scope, not the main thread. Pair `installPrecache` with the\n * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which\n * emits the `precache-manifest.json` this reads at install time.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installRuntimeCache, installPrecache } from \"tempest-react-sdk/sw\";\n *\n * // Register specific routes FIRST so they win over the precache catch-all.\n * installRuntimeCache([\n * { match: /\\/api\\//, strategy: \"network-first\", cacheName: \"api\", maxAgeSeconds: 300 },\n * ]);\n * installPrecache();\n */\n\n/** Minimal shape of the events we touch, to avoid pulling in `lib.webworker`. */\ninterface ExtendableEventLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface FetchEventLike extends ExtendableEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n}\n\ninterface CacheSwGlobal {\n addEventListener(type: \"install\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"activate\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchEventLike) => void): void;\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n location: { origin: string };\n}\n\nfunction getSwScope(): CacheSwGlobal {\n return globalThis as unknown as CacheSwGlobal;\n}\n\n/** Caching strategy for a runtime route. Mirrors the common Workbox trio. */\nexport type RuntimeStrategy = \"cache-first\" | \"network-first\" | \"stale-while-revalidate\";\n\n/** A single runtime-caching rule, matched against each `GET` request. */\nexport interface RuntimeRoute {\n /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */\n match: RegExp | ((url: URL, request: Request) => boolean);\n /** How to resolve a match. */\n strategy: RuntimeStrategy;\n /** Cache bucket name for this route. */\n cacheName: string;\n /** Trim the cache to at most this many entries (FIFO) after each write. */\n maxEntries?: number;\n /** Treat a cached response older than this (seconds) as a miss. */\n maxAgeSeconds?: number;\n /** For `network-first`: fall back to cache after this timeout (seconds). */\n networkTimeoutSeconds?: number;\n /**\n * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached\n * full response. Enable for audio/video so seeking works offline. The full\n * resource is cached once (the `Range` header is stripped before caching).\n */\n rangeRequests?: boolean;\n}\n\n/** Options for {@link installPrecache}. */\nexport interface InstallPrecacheOptions {\n /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */\n manifestUrl?: string;\n /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */\n cacheName?: string;\n /** App-shell document served for navigation requests offline. Default `/index.html`. */\n navigateFallback?: string;\n /** Navigation paths that should NOT use the fallback (e.g. `[/^\\/api\\//]`). */\n navigateFallbackDenylist?: RegExp[];\n /** Activate the new worker immediately after precaching. Default `true`. */\n skipWaiting?: boolean;\n}\n\ninterface PrecacheManifest {\n version: string;\n urls: string[];\n}\n\nlet precacheName = \"\";\nconst precachedPaths = new Set<string>();\n\n/** Whether a cached response has aged past `maxAgeSeconds` (best-effort, via `Date`). */\nfunction isExpired(response: Response, maxAgeSeconds?: number): boolean {\n if (!maxAgeSeconds) return false;\n const dateHeader = response.headers.get(\"date\");\n if (!dateHeader) return false;\n const age = (Date.now() - new Date(dateHeader).getTime()) / 1000;\n return age > maxAgeSeconds;\n}\n\n/** Drop oldest entries until the cache holds at most `maxEntries`. */\nasync function trimCache(cacheName: string, maxEntries?: number): Promise<void> {\n if (!maxEntries) return;\n const cache = await caches.open(cacheName);\n const keys = await cache.keys();\n if (keys.length <= maxEntries) return;\n for (const key of keys.slice(0, keys.length - maxEntries)) {\n await cache.delete(key);\n }\n}\n\nasync function cacheFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n\n const response = await fetch(request);\n if (response.ok) {\n await cache.put(request, response.clone());\n await trimCache(route.cacheName, route.maxEntries);\n }\n return response;\n}\n\nasync function networkFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n\n const network = (async (): Promise<Response> => {\n const response = await fetch(request);\n if (response.ok) {\n await cache.put(request, response.clone());\n await trimCache(route.cacheName, route.maxEntries);\n }\n return response;\n })();\n\n const timeoutMs = (route.networkTimeoutSeconds ?? 0) * 1000;\n try {\n if (timeoutMs > 0) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\"network-timeout\")), timeoutMs),\n );\n return await Promise.race([network, timeout]);\n }\n return await network;\n } catch {\n const cached = await cache.match(request);\n if (cached) return cached;\n throw new Error(\"network-first: no network and no cache\");\n }\n}\n\nasync function staleWhileRevalidate(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n\n const network = fetch(request)\n .then(async (response) => {\n if (response.ok) {\n await cache.put(request, response.clone());\n await trimCache(route.cacheName, route.maxEntries);\n }\n return response;\n })\n .catch(() => undefined);\n\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n const response = await network;\n if (response) return response;\n if (cached) return cached;\n throw new Error(\"stale-while-revalidate: no network and no cache\");\n}\n\nfunction runRoute(request: Request, route: RuntimeRoute): Promise<Response> {\n switch (route.strategy) {\n case \"cache-first\":\n return cacheFirst(request, route);\n case \"network-first\":\n return networkFirst(request, route);\n case \"stale-while-revalidate\":\n return staleWhileRevalidate(request, route);\n }\n}\n\nfunction routeMatches(route: RuntimeRoute, url: URL, request: Request): boolean {\n return typeof route.match === \"function\"\n ? route.match(url, request)\n : route.match.test(url.href);\n}\n\n/**\n * Build a `206 Partial Content` response from a full one for an HTTP `Range`\n * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix\n * `bytes=-suffixLength`. Returns the original response when there is no usable\n * `Range` header, or a `416` when the range is unsatisfiable.\n *\n * @param request The incoming request (its `Range` header drives the slice).\n * @param response The full (200) response to slice.\n */\nexport async function createPartialResponse(\n request: Request,\n response: Response,\n): Promise<Response> {\n const rangeHeader = request.headers.get(\"range\");\n if (!rangeHeader) return response;\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match || (match[1] === \"\" && match[2] === \"\")) {\n return new Response(null, { status: 416, statusText: \"Range Not Satisfiable\" });\n }\n\n const buffer = await response.clone().arrayBuffer();\n const total = buffer.byteLength;\n\n let start: number;\n let end: number;\n if (match[1] === \"\") {\n // Suffix range: last N bytes.\n const suffix = Number(match[2]);\n start = Math.max(0, total - suffix);\n end = total - 1;\n } else {\n start = Number(match[1]);\n end = match[2] === \"\" ? total - 1 : Math.min(Number(match[2]), total - 1);\n }\n\n if (start > end || start >= total) {\n return new Response(null, {\n status: 416,\n statusText: \"Range Not Satisfiable\",\n headers: { \"Content-Range\": `bytes */${total}` },\n });\n }\n\n const slice = buffer.slice(start, end + 1);\n const headers = new Headers(response.headers);\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${total}`);\n headers.set(\"Content-Length\", String(slice.byteLength));\n headers.set(\"Accept-Ranges\", \"bytes\");\n\n return new Response(slice, {\n status: 206,\n statusText: \"Partial Content\",\n headers,\n });\n}\n\n/**\n * Install a `fetch` handler that resolves matching `GET` requests with the\n * given runtime strategies. Non-matching requests are left untouched (no\n * `respondWith`), so a later {@link installPrecache} can handle them.\n *\n * Register this BEFORE `installPrecache` so specific routes win over the\n * precache catch-all.\n *\n * @param routes Ordered rules; the first whose `match` passes handles the request.\n */\nexport function installRuntimeCache(routes: RuntimeRoute[]): void {\n const sw = getSwScope();\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n const route = routes.find((candidate) => routeMatches(candidate, url, request));\n if (!route) return;\n\n if (route.rangeRequests && request.headers.has(\"range\")) {\n // Cache/serve the FULL resource once (no Range), then slice it.\n const full = new Request(request.url, {\n headers: stripRange(request.headers),\n credentials: request.credentials,\n mode: request.mode === \"navigate\" ? \"same-origin\" : request.mode,\n });\n event.respondWith(\n runRoute(full, route).then((response) => createPartialResponse(request, response)),\n );\n return;\n }\n\n event.respondWith(runRoute(request, route));\n });\n}\n\n/** Copy headers without the `Range` header (so the cached entry is the full file). */\nfunction stripRange(headers: Headers): Headers {\n const out = new Headers(headers);\n out.delete(\"range\");\n return out;\n}\n\n/**\n * Precache the app shell at `install` and serve it offline:\n * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),\n * - caches every listed URL under a versioned cache,\n * - on `activate`, deletes stale precache versions and claims open clients,\n * - on `fetch`, serves precached assets cache-first and falls back to the\n * `navigateFallback` document for offline navigations (SPA routing).\n *\n * Same-origin only. Register this LAST, after any {@link installRuntimeCache}.\n */\nexport function installPrecache(options: InstallPrecacheOptions = {}): void {\n const sw = getSwScope();\n const {\n manifestUrl = \"/precache-manifest.json\",\n cacheName = \"tempest-precache\",\n navigateFallback = \"/index.html\",\n navigateFallbackDenylist = [],\n skipWaiting = true,\n } = options;\n\n sw.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const response = await fetch(manifestUrl, { cache: \"no-cache\" });\n const manifest = (await response.json()) as PrecacheManifest;\n precacheName = `${cacheName}-${manifest.version}`;\n\n const cache = await caches.open(precacheName);\n await cache.addAll(manifest.urls);\n for (const url of manifest.urls) {\n precachedPaths.add(new URL(url, sw.location.origin).pathname);\n }\n if (skipWaiting) await sw.skipWaiting();\n })(),\n );\n });\n\n sw.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const keys = await caches.keys();\n await Promise.all(\n keys\n .filter((key) => key.startsWith(`${cacheName}-`) && key !== precacheName)\n .map((key) => caches.delete(key)),\n );\n await sw.clients.claim();\n })(),\n );\n });\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n if (url.origin !== sw.location.origin) return;\n\n // SPA navigations: serve the cached app shell when offline.\n if (request.mode === \"navigate\") {\n if (navigateFallbackDenylist.some((re) => re.test(url.pathname))) return;\n event.respondWith(\n (async () => {\n try {\n return await fetch(request);\n } catch {\n const cache = await caches.open(precacheName);\n const shell = await cache.match(navigateFallback);\n if (shell) return shell;\n throw new Error(\"offline and no cached app shell\");\n }\n })(),\n );\n return;\n }\n\n // Precached static assets: cache-first.\n if (precachedPaths.has(url.pathname)) {\n event.respondWith(\n (async () => {\n const cache = await caches.open(precacheName);\n const cached = await cache.match(request);\n return cached ?? fetch(request);\n })(),\n );\n }\n });\n}\n","/**\n * Background-sync helper: queue failed mutating requests (POST/PUT/PATCH/DELETE)\n * while offline and replay them when connectivity returns. A dependency-free\n * take on Workbox's `BackgroundSyncPlugin`, backed by a tiny IndexedDB queue.\n *\n * Import inside your `sw.ts`. Uses the Background Sync API (`registration.sync`)\n * when available, and also replays opportunistically on the next request as a\n * fallback for browsers without it (e.g. Safari).\n *\n * @example\n * import { installBackgroundSync } from \"tempest-react-sdk/sw\";\n *\n * installBackgroundSync({ match: (url) => url.pathname.startsWith(\"/api/\") });\n */\n\ninterface SyncEventLike {\n tag: string;\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface BgFetchEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface BgSwGlobal {\n registration: { sync?: { register(tag: string): Promise<void> } };\n addEventListener(type: \"sync\", listener: (event: SyncEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: BgFetchEventLike) => void): void;\n}\n\nfunction getSwScope(): BgSwGlobal {\n return globalThis as unknown as BgSwGlobal;\n}\n\n/** A serialized request stored in the queue. */\ninterface QueuedRequest {\n id?: number;\n url: string;\n method: string;\n headers: [string, string][];\n body: ArrayBuffer | null;\n timestamp: number;\n}\n\n/** Options for {@link installBackgroundSync}. */\nexport interface InstallBackgroundSyncOptions {\n /**\n * Which requests to queue on failure. A `RegExp` against the URL or a\n * predicate. Only non-`GET` requests are ever considered. Default: all\n * non-`GET` requests.\n */\n match?: RegExp | ((url: URL, request: Request) => boolean);\n /** IndexedDB database name, also used as the sync tag. Default `tempest-bg-sync`. */\n queueName?: string;\n /** Drop queued requests older than this (minutes) on replay. Default `1440` (24h). */\n maxRetentionMinutes?: number;\n}\n\nconst STORE = \"requests\";\n\nfunction openQueueDb(name: string): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(name, 1);\n request.onupgradeneeded = () => {\n request.result.createObjectStore(STORE, { keyPath: \"id\", autoIncrement: true });\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction txDone(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error);\n });\n}\n\nasync function enqueue(name: string, entry: QueuedRequest): Promise<void> {\n const db = await openQueueDb(name);\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).add(entry);\n await txDone(tx);\n db.close();\n}\n\nasync function readAll(name: string): Promise<QueuedRequest[]> {\n const db = await openQueueDb(name);\n const tx = db.transaction(STORE, \"readonly\");\n const all = await new Promise<QueuedRequest[]>((resolve, reject) => {\n const req = tx.objectStore(STORE).getAll();\n req.onsuccess = () => resolve(req.result as QueuedRequest[]);\n req.onerror = () => reject(req.error);\n });\n db.close();\n return all;\n}\n\nasync function remove(name: string, id: number): Promise<void> {\n const db = await openQueueDb(name);\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).delete(id);\n await txDone(tx);\n db.close();\n}\n\nasync function serializeRequest(request: Request, timestamp: number): Promise<QueuedRequest> {\n const buffer = await request.clone().arrayBuffer();\n return {\n url: request.url,\n method: request.method,\n headers: [...request.headers.entries()],\n body: buffer.byteLength > 0 ? buffer : null,\n timestamp,\n };\n}\n\nfunction deserializeRequest(entry: QueuedRequest): Request {\n return new Request(entry.url, {\n method: entry.method,\n headers: entry.headers,\n body: entry.body ?? undefined,\n });\n}\n\n/**\n * Replay every queued request once. Successful (or stale) entries are removed;\n * entries that fail again are kept for the next attempt. Throws if any entry\n * still fails, so a `sync` handler keeps the sync pending.\n */\nasync function replayQueue(name: string, maxRetentionMs: number, now: number): Promise<void> {\n const entries = await readAll(name);\n let pending = 0;\n\n for (const entry of entries) {\n if (entry.id === undefined) continue;\n if (now - entry.timestamp > maxRetentionMs) {\n await remove(name, entry.id);\n continue;\n }\n try {\n const response = await fetch(deserializeRequest(entry));\n if (response.ok || (response.status >= 400 && response.status < 500)) {\n // 2xx = done; 4xx = client error that won't fix itself → drop it.\n await remove(name, entry.id);\n } else {\n pending += 1;\n }\n } catch {\n pending += 1;\n }\n }\n\n if (pending > 0) throw new Error(`background-sync: ${pending} request(s) still pending`);\n}\n\nfunction matches(\n match: InstallBackgroundSyncOptions[\"match\"],\n url: URL,\n request: Request,\n): boolean {\n if (!match) return true;\n return typeof match === \"function\" ? match(url, request) : match.test(url.href);\n}\n\n/**\n * Install the background-sync queue: on a failed mutating request, the request\n * is serialized to IndexedDB and a sync is registered; the original fetch still\n * rejects (so your app can show an offline state), and the request is replayed\n * later when the network returns.\n */\nexport function installBackgroundSync(options: InstallBackgroundSyncOptions = {}): void {\n const sw = getSwScope();\n const { match, queueName = \"tempest-bg-sync\", maxRetentionMinutes = 1440 } = options;\n const maxRetentionMs = maxRetentionMinutes * 60 * 1000;\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method === \"GET\" || request.method === \"HEAD\") return;\n\n const url = new URL(request.url);\n if (!matches(match, url, request)) return;\n\n event.respondWith(\n fetch(request.clone()).catch(async (error) => {\n const entry = await serializeRequest(request, Date.now());\n await enqueue(queueName, entry);\n try {\n await sw.registration.sync?.register(queueName);\n } catch {\n // Background Sync API unavailable — opportunistic replay covers it.\n }\n throw error;\n }),\n );\n });\n\n sw.addEventListener(\"sync\", (event) => {\n if (event.tag !== queueName) return;\n event.waitUntil(replayQueue(queueName, maxRetentionMs, Date.now()));\n });\n\n // Fallback for browsers without the Background Sync API: whenever a GET\n // succeeds (a sign we're online), drain the queue opportunistically.\n sw.addEventListener(\"fetch\", (event) => {\n if (sw.registration.sync) return; // native sync will handle it\n if (event.request.method !== \"GET\") return;\n event.waitUntil(replayQueue(queueName, maxRetentionMs, Date.now()).catch(() => undefined));\n });\n}\n"],"names":["controllerReloadWired","wireControllerReload","refreshing","registerServiceWorker","options","registration","installing","intervalMs","error","skipWaiting","worker","unregisterAllServiceWorkers","registrations","count","getSwScope","installPushHandler","sw","defaultTitle","defaultIcon","defaultBadge","transform","event","raw","payload","title","notification","installNotificationClickHandler","resolveUrl","data","url","target","clients","client","installSkipWaitingListener","precacheName","precachedPaths","isExpired","response","maxAgeSeconds","dateHeader","trimCache","cacheName","maxEntries","cache","keys","key","cacheFirst","request","route","cached","networkFirst","network","timeoutMs","timeout","_","reject","staleWhileRevalidate","runRoute","routeMatches","createPartialResponse","rangeHeader","match","buffer","total","start","end","suffix","slice","headers","installRuntimeCache","routes","candidate","full","stripRange","out","installPrecache","manifestUrl","navigateFallback","navigateFallbackDenylist","manifest","re","shell","STORE","openQueueDb","name","resolve","txDone","tx","enqueue","entry","db","readAll","all","req","remove","id","serializeRequest","timestamp","deserializeRequest","replayQueue","maxRetentionMs","now","entries","pending","matches","installBackgroundSync","queueName","maxRetentionMinutes"],"mappings":"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"}
|