tempest-react-sdk 0.7.0 → 0.9.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.
Files changed (39) hide show
  1. package/README.md +41 -28
  2. package/bin/create-tempest-app.mjs +141 -70
  3. package/bin/lib/openapi/generate.mjs +259 -0
  4. package/bin/lib/openapi/generate.test.mjs +129 -0
  5. package/bin/lib/openapi/load.mjs +24 -0
  6. package/bin/lib/openapi/schema-to-zod.mjs +123 -0
  7. package/bin/lib/openapi/schema-to-zod.test.mjs +81 -0
  8. package/bin/tempest.mjs +364 -0
  9. package/dist/sw.cjs +2 -0
  10. package/dist/sw.cjs.map +1 -0
  11. package/dist/sw.d.ts +233 -0
  12. package/dist/sw.js +361 -0
  13. package/dist/sw.js.map +1 -0
  14. package/dist/tempest-react-sdk.cjs +3 -3
  15. package/dist/tempest-react-sdk.cjs.map +1 -1
  16. package/dist/tempest-react-sdk.d.ts +130 -0
  17. package/dist/tempest-react-sdk.js +1565 -1646
  18. package/dist/tempest-react-sdk.js.map +1 -1
  19. package/dist/vite.cjs +3 -1
  20. package/dist/vite.cjs.map +1 -1
  21. package/dist/vite.d.ts +136 -0
  22. package/dist/vite.js +253 -34
  23. package/dist/vite.js.map +1 -1
  24. package/package.json +10 -2
  25. package/template/_prettierrc.json +9 -0
  26. package/template/eslint.config.js +18 -1
  27. package/template/package.json +7 -1
  28. package/template-pwa/README.md +64 -0
  29. package/template-pwa/_env.example +7 -0
  30. package/template-pwa/index.html +22 -0
  31. package/template-pwa/package.json +9 -0
  32. package/template-pwa/public/icon.svg +4 -0
  33. package/template-pwa/public/manifest.webmanifest +35 -0
  34. package/template-pwa/src/main.tsx +29 -0
  35. package/template-pwa/src/pages/Dashboard.tsx +73 -0
  36. package/template-pwa/src/sw.ts +70 -0
  37. package/template-pwa/src/vite-env.d.ts +12 -0
  38. package/template-pwa/vite.config.ts +21 -0
  39. package/template-pwa/vite.sw.config.ts +27 -0
package/dist/sw.d.ts ADDED
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Build a `206 Partial Content` response from a full one for an HTTP `Range`
3
+ * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix
4
+ * `bytes=-suffixLength`. Returns the original response when there is no usable
5
+ * `Range` header, or a `416` when the range is unsatisfiable.
6
+ *
7
+ * @param request The incoming request (its `Range` header drives the slice).
8
+ * @param response The full (200) response to slice.
9
+ */
10
+ export declare function createPartialResponse(request: Request, response: Response): Promise<Response>;
11
+
12
+ /**
13
+ * Install the background-sync queue: on a failed mutating request, the request
14
+ * is serialized to IndexedDB and a sync is registered; the original fetch still
15
+ * rejects (so your app can show an offline state), and the request is replayed
16
+ * later when the network returns.
17
+ */
18
+ export declare function installBackgroundSync(options?: InstallBackgroundSyncOptions): void;
19
+
20
+ /**
21
+ * Background-sync helper: queue failed mutating requests (POST/PUT/PATCH/DELETE)
22
+ * while offline and replay them when connectivity returns. A dependency-free
23
+ * take on Workbox's `BackgroundSyncPlugin`, backed by a tiny IndexedDB queue.
24
+ *
25
+ * Import inside your `sw.ts`. Uses the Background Sync API (`registration.sync`)
26
+ * when available, and also replays opportunistically on the next request as a
27
+ * fallback for browsers without it (e.g. Safari).
28
+ *
29
+ * @example
30
+ * import { installBackgroundSync } from "tempest-react-sdk/sw";
31
+ *
32
+ * installBackgroundSync({ match: (url) => url.pathname.startsWith("/api/") });
33
+ */
34
+ /** Options for {@link installBackgroundSync}. */
35
+ export declare interface InstallBackgroundSyncOptions {
36
+ /**
37
+ * Which requests to queue on failure. A `RegExp` against the URL or a
38
+ * predicate. Only non-`GET` requests are ever considered. Default: all
39
+ * non-`GET` requests.
40
+ */
41
+ match?: RegExp | ((url: URL, request: Request) => boolean);
42
+ /** IndexedDB database name, also used as the sync tag. Default `tempest-bg-sync`. */
43
+ queueName?: string;
44
+ /** Drop queued requests older than this (minutes) on replay. Default `1440` (24h). */
45
+ maxRetentionMinutes?: number;
46
+ }
47
+
48
+ /**
49
+ * Install a `notificationclick` handler that focuses an existing client when
50
+ * possible and falls back to opening a new window.
51
+ */
52
+ export declare function installNotificationClickHandler(options?: InstallNotificationClickHandlerOptions): void;
53
+
54
+ export declare interface InstallNotificationClickHandlerOptions {
55
+ /** Resolve the destination URL from the notification data. Default: `data.url`. */
56
+ resolveUrl?: (data: unknown) => string;
57
+ }
58
+
59
+ /**
60
+ * Precache the app shell at `install` and serve it offline:
61
+ * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),
62
+ * - caches every listed URL under a versioned cache,
63
+ * - on `activate`, deletes stale precache versions and claims open clients,
64
+ * - on `fetch`, serves precached assets cache-first and falls back to the
65
+ * `navigateFallback` document for offline navigations (SPA routing).
66
+ *
67
+ * Same-origin only. Register this LAST, after any {@link installRuntimeCache}.
68
+ */
69
+ export declare function installPrecache(options?: InstallPrecacheOptions): void;
70
+
71
+ /** Options for {@link installPrecache}. */
72
+ export declare interface InstallPrecacheOptions {
73
+ /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */
74
+ manifestUrl?: string;
75
+ /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */
76
+ cacheName?: string;
77
+ /** App-shell document served for navigation requests offline. Default `/index.html`. */
78
+ navigateFallback?: string;
79
+ /** Navigation paths that should NOT use the fallback (e.g. `[/^\/api\//]`). */
80
+ navigateFallbackDenylist?: RegExp[];
81
+ /** Activate the new worker immediately after precaching. Default `true`. */
82
+ skipWaiting?: boolean;
83
+ }
84
+
85
+ /**
86
+ * Install a `push` event listener that parses the payload as JSON (with a
87
+ * plain-text fallback) and shows a notification.
88
+ */
89
+ export declare function installPushHandler(options?: InstallPushHandlerOptions): void;
90
+
91
+ export declare interface InstallPushHandlerOptions {
92
+ /** Title used when the payload omits one. */
93
+ defaultTitle?: string;
94
+ /** Icon used when the payload omits one. */
95
+ defaultIcon?: string;
96
+ /** Badge image (mobile). */
97
+ defaultBadge?: string;
98
+ /**
99
+ * Transform the raw payload before showing the notification. Return `null`
100
+ * to suppress the notification entirely (e.g. silent pings).
101
+ */
102
+ transform?: (payload: PushPayload) => PushPayload | null;
103
+ }
104
+
105
+ /**
106
+ * Install a `fetch` handler that resolves matching `GET` requests with the
107
+ * given runtime strategies. Non-matching requests are left untouched (no
108
+ * `respondWith`), so a later {@link installPrecache} can handle them.
109
+ *
110
+ * Register this BEFORE `installPrecache` so specific routes win over the
111
+ * precache catch-all.
112
+ *
113
+ * @param routes Ordered rules; the first whose `match` passes handles the request.
114
+ */
115
+ export declare function installRuntimeCache(routes: RuntimeRoute[]): void;
116
+
117
+ /**
118
+ * Install a `message` listener that activates a waiting worker when the host
119
+ * app sends `{ type: "SKIP_WAITING" }`.
120
+ */
121
+ export declare function installSkipWaitingListener(): void;
122
+
123
+ /**
124
+ * Service-worker context helpers for handling `push` and `notificationclick`
125
+ * events. Import these inside your own `sw.ts` — they expect to run in the
126
+ * service-worker global scope, not in the main thread.
127
+ *
128
+ * @example
129
+ * /// <reference lib="webworker" />
130
+ * import { installPushHandler, installNotificationClickHandler } from "tempest-react-sdk";
131
+ *
132
+ * installPushHandler({ defaultIcon: "/icons/Logo.png" });
133
+ * installNotificationClickHandler();
134
+ */
135
+ export declare interface PushPayload {
136
+ title?: string;
137
+ body?: string;
138
+ icon?: string;
139
+ badge?: string;
140
+ image?: string;
141
+ tag?: string;
142
+ url?: string;
143
+ /** Arbitrary extra data forwarded to `event.notification.data`. */
144
+ data?: Record<string, unknown>;
145
+ }
146
+
147
+ /**
148
+ * Register a service worker with consistent update-detection wiring.
149
+ *
150
+ * Skips silently when the runtime has no `serviceWorker` support. The host
151
+ * app keeps full control over the SW file — this helper only handles the
152
+ * boilerplate around `register()` and `updatefound`.
153
+ *
154
+ * @returns The registration when it succeeds, or `null` when unsupported.
155
+ */
156
+ export declare function registerServiceWorker(options: RegisterServiceWorkerOptions): Promise<ServiceWorkerRegistration | null>;
157
+
158
+ export declare interface RegisterServiceWorkerOptions {
159
+ /** Public URL of the compiled service worker file (e.g. `/sw.js`). */
160
+ url: string;
161
+ /** SW scope (default: SW directory). */
162
+ scope?: string;
163
+ /** Called once the registration is active. */
164
+ onReady?: (registration: ServiceWorkerRegistration) => void;
165
+ /**
166
+ * Called when a new worker has finished installing while another worker
167
+ * still controls the page. The host app typically prompts the user to
168
+ * reload and then calls {@link skipWaiting} on the returned worker.
169
+ */
170
+ onUpdate?: (waiting: ServiceWorker, registration: ServiceWorkerRegistration) => void;
171
+ /** Called on registration failure. */
172
+ onError?: (error: unknown) => void;
173
+ }
174
+
175
+ /** A single runtime-caching rule, matched against each `GET` request. */
176
+ export declare interface RuntimeRoute {
177
+ /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */
178
+ match: RegExp | ((url: URL, request: Request) => boolean);
179
+ /** How to resolve a match. */
180
+ strategy: RuntimeStrategy;
181
+ /** Cache bucket name for this route. */
182
+ cacheName: string;
183
+ /** Trim the cache to at most this many entries (FIFO) after each write. */
184
+ maxEntries?: number;
185
+ /** Treat a cached response older than this (seconds) as a miss. */
186
+ maxAgeSeconds?: number;
187
+ /** For `network-first`: fall back to cache after this timeout (seconds). */
188
+ networkTimeoutSeconds?: number;
189
+ /**
190
+ * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached
191
+ * full response. Enable for audio/video so seeking works offline. The full
192
+ * resource is cached once (the `Range` header is stripped before caching).
193
+ */
194
+ rangeRequests?: boolean;
195
+ }
196
+
197
+ /**
198
+ * Service-worker caching helpers — a small, dependency-free subset of what
199
+ * Workbox provides: precaching of the build's app shell (so the app launches
200
+ * offline) plus runtime caching strategies for fonts, APIs and images.
201
+ *
202
+ * Import these inside your own `sw.ts`. They run in the service-worker global
203
+ * scope, not the main thread. Pair `installPrecache` with the
204
+ * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which
205
+ * emits the `precache-manifest.json` this reads at install time.
206
+ *
207
+ * @example
208
+ * /// <reference lib="webworker" />
209
+ * import { installRuntimeCache, installPrecache } from "tempest-react-sdk/sw";
210
+ *
211
+ * // Register specific routes FIRST so they win over the precache catch-all.
212
+ * installRuntimeCache([
213
+ * { match: /\/api\//, strategy: "network-first", cacheName: "api", maxAgeSeconds: 300 },
214
+ * ]);
215
+ * installPrecache();
216
+ */
217
+ /** Caching strategy for a runtime route. Mirrors the common Workbox trio. */
218
+ export declare type RuntimeStrategy = "cache-first" | "network-first" | "stale-while-revalidate";
219
+
220
+ /**
221
+ * Tell a waiting worker to activate immediately. Pair with `onUpdate` to roll
222
+ * out updates after the user confirms a reload prompt.
223
+ */
224
+ export declare function skipWaiting(worker: ServiceWorker): void;
225
+
226
+ /**
227
+ * Unregister all registered service workers for this origin.
228
+ *
229
+ * @returns Number of workers that were unregistered.
230
+ */
231
+ export declare function unregisterAllServiceWorkers(): Promise<number>;
232
+
233
+ export { }
package/dist/sw.js ADDED
@@ -0,0 +1,361 @@
1
+ async function j(t) {
2
+ if (typeof navigator > "u" || !("serviceWorker" in navigator))
3
+ return null;
4
+ try {
5
+ const e = await navigator.serviceWorker.register(t.url, {
6
+ scope: t.scope
7
+ });
8
+ return e.active && t.onReady?.(e), e.addEventListener("updatefound", () => {
9
+ const a = e.installing;
10
+ a && a.addEventListener("statechange", () => {
11
+ a.state === "installed" && navigator.serviceWorker.controller && t.onUpdate?.(a, e);
12
+ });
13
+ }), e;
14
+ } catch (e) {
15
+ return t.onError?.(e), null;
16
+ }
17
+ }
18
+ function q(t) {
19
+ t.postMessage({ type: "SKIP_WAITING" });
20
+ }
21
+ async function C() {
22
+ if (typeof navigator > "u" || !("serviceWorker" in navigator)) return 0;
23
+ const t = await navigator.serviceWorker.getRegistrations();
24
+ let e = 0;
25
+ for (const a of t)
26
+ await a.unregister() && (e += 1);
27
+ return e;
28
+ }
29
+ function w() {
30
+ return globalThis;
31
+ }
32
+ function H(t = {}) {
33
+ const e = w(), { defaultTitle: a = "Notificação", defaultIcon: n, defaultBadge: c, transform: i } = t;
34
+ e.addEventListener("push", (r) => {
35
+ if (!r.data) return;
36
+ let s;
37
+ try {
38
+ s = r.data.json();
39
+ } catch {
40
+ s = { title: a, body: r.data.text() };
41
+ }
42
+ const o = i ? i(s) : s;
43
+ if (!o) return;
44
+ const u = o.title ?? a, l = {
45
+ body: o.body,
46
+ icon: o.icon ?? n,
47
+ badge: o.badge ?? c,
48
+ image: o.image,
49
+ tag: o.tag,
50
+ data: { url: o.url ?? "/", ...o.data ?? {} }
51
+ };
52
+ r.waitUntil(e.registration.showNotification(u, l));
53
+ });
54
+ }
55
+ function M(t = {}) {
56
+ const e = w(), a = t.resolveUrl ?? ((n) => {
57
+ if (typeof n == "string") return n;
58
+ if (n && typeof n == "object" && "url" in n) {
59
+ const c = n.url;
60
+ return typeof c == "string" ? c : "/";
61
+ }
62
+ return "/";
63
+ });
64
+ e.addEventListener("notificationclick", (n) => {
65
+ n.notification.close();
66
+ const c = a(n.notification.data);
67
+ n.waitUntil(
68
+ (async () => {
69
+ const i = await e.clients.matchAll({
70
+ type: "window",
71
+ includeUncontrolled: !0
72
+ });
73
+ for (const r of i)
74
+ if (r.url.includes(c))
75
+ return r.focus();
76
+ return e.clients.openWindow(c);
77
+ })()
78
+ );
79
+ });
80
+ }
81
+ function G() {
82
+ const t = w();
83
+ t.addEventListener("message", (e) => {
84
+ e.data?.type === "SKIP_WAITING" && t.skipWaiting();
85
+ });
86
+ }
87
+ function v() {
88
+ return globalThis;
89
+ }
90
+ let h = "";
91
+ const p = /* @__PURE__ */ new Set();
92
+ function R(t, e) {
93
+ if (!e) return !1;
94
+ const a = t.headers.get("date");
95
+ return a ? (Date.now() - new Date(a).getTime()) / 1e3 > e : !1;
96
+ }
97
+ async function g(t, e) {
98
+ if (!e) return;
99
+ const a = await caches.open(t), n = await a.keys();
100
+ if (!(n.length <= e))
101
+ for (const c of n.slice(0, n.length - e))
102
+ await a.delete(c);
103
+ }
104
+ async function S(t, e) {
105
+ const a = await caches.open(e.cacheName), n = await a.match(t);
106
+ if (n && !R(n, e.maxAgeSeconds)) return n;
107
+ const c = await fetch(t);
108
+ return c.ok && (await a.put(t, c.clone()), await g(e.cacheName, e.maxEntries)), c;
109
+ }
110
+ async function W(t, e) {
111
+ const a = await caches.open(e.cacheName), n = (async () => {
112
+ const i = await fetch(t);
113
+ return i.ok && (await a.put(t, i.clone()), await g(e.cacheName, e.maxEntries)), i;
114
+ })(), c = (e.networkTimeoutSeconds ?? 0) * 1e3;
115
+ try {
116
+ if (c > 0) {
117
+ const i = new Promise(
118
+ (r, s) => setTimeout(() => s(new Error("network-timeout")), c)
119
+ );
120
+ return await Promise.race([n, i]);
121
+ }
122
+ return await n;
123
+ } catch {
124
+ const i = await a.match(t);
125
+ if (i) return i;
126
+ throw new Error("network-first: no network and no cache");
127
+ }
128
+ }
129
+ async function L(t, e) {
130
+ 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
+ });
132
+ if (n && !R(n, e.maxAgeSeconds)) return n;
133
+ const i = await c;
134
+ if (i) return i;
135
+ if (n) return n;
136
+ throw new Error("stale-while-revalidate: no network and no cache");
137
+ }
138
+ function y(t, e) {
139
+ switch (e.strategy) {
140
+ case "cache-first":
141
+ return S(t, e);
142
+ case "network-first":
143
+ return W(t, e);
144
+ case "stale-while-revalidate":
145
+ return L(t, e);
146
+ }
147
+ }
148
+ function N(t, e, a) {
149
+ return typeof t.match == "function" ? t.match(e, a) : t.match.test(e.href);
150
+ }
151
+ async function T(t, e) {
152
+ const a = t.headers.get("range");
153
+ if (!a) return e;
154
+ const n = /^bytes=(\d*)-(\d*)$/.exec(a.trim());
155
+ if (!n || n[1] === "" && n[2] === "")
156
+ return new Response(null, { status: 416, statusText: "Range Not Satisfiable" });
157
+ const c = await e.clone().arrayBuffer(), i = c.byteLength;
158
+ let r, s;
159
+ if (n[1] === "") {
160
+ const l = Number(n[2]);
161
+ r = Math.max(0, i - l), s = i - 1;
162
+ } else
163
+ r = Number(n[1]), s = n[2] === "" ? i - 1 : Math.min(Number(n[2]), i - 1);
164
+ if (r > s || r >= i)
165
+ return new Response(null, {
166
+ status: 416,
167
+ statusText: "Range Not Satisfiable",
168
+ headers: { "Content-Range": `bytes */${i}` }
169
+ });
170
+ const o = c.slice(r, s + 1), u = new Headers(e.headers);
171
+ return u.set("Content-Range", `bytes ${r}-${s}/${i}`), u.set("Content-Length", String(o.byteLength)), u.set("Accept-Ranges", "bytes"), new Response(o, {
172
+ status: 206,
173
+ statusText: "Partial Content",
174
+ headers: u
175
+ });
176
+ }
177
+ function B(t) {
178
+ v().addEventListener("fetch", (a) => {
179
+ const n = a.request;
180
+ if (n.method !== "GET") return;
181
+ const c = new URL(n.url), i = t.find((r) => N(r, c, n));
182
+ if (i) {
183
+ if (i.rangeRequests && n.headers.has("range")) {
184
+ const r = new Request(n.url, {
185
+ headers: x(n.headers),
186
+ credentials: n.credentials,
187
+ mode: n.mode === "navigate" ? "same-origin" : n.mode
188
+ });
189
+ a.respondWith(
190
+ y(r, i).then((s) => T(n, s))
191
+ );
192
+ return;
193
+ }
194
+ a.respondWith(y(n, i));
195
+ }
196
+ });
197
+ }
198
+ function x(t) {
199
+ const e = new Headers(t);
200
+ return e.delete("range"), e;
201
+ }
202
+ function F(t = {}) {
203
+ const e = v(), {
204
+ manifestUrl: a = "/precache-manifest.json",
205
+ cacheName: n = "tempest-precache",
206
+ navigateFallback: c = "/index.html",
207
+ navigateFallbackDenylist: i = [],
208
+ skipWaiting: r = !0
209
+ } = t;
210
+ e.addEventListener("install", (s) => {
211
+ s.waitUntil(
212
+ (async () => {
213
+ const u = await (await fetch(a, { cache: "no-cache" })).json();
214
+ h = `${n}-${u.version}`, await (await caches.open(h)).addAll(u.urls);
215
+ for (const d of u.urls)
216
+ p.add(new URL(d, e.location.origin).pathname);
217
+ r && await e.skipWaiting();
218
+ })()
219
+ );
220
+ }), e.addEventListener("activate", (s) => {
221
+ s.waitUntil(
222
+ (async () => {
223
+ const o = await caches.keys();
224
+ await Promise.all(
225
+ o.filter((u) => u.startsWith(`${n}-`) && u !== h).map((u) => caches.delete(u))
226
+ ), await e.clients.claim();
227
+ })()
228
+ );
229
+ }), e.addEventListener("fetch", (s) => {
230
+ const o = s.request;
231
+ if (o.method !== "GET") return;
232
+ const u = new URL(o.url);
233
+ if (u.origin === e.location.origin) {
234
+ if (o.mode === "navigate") {
235
+ if (i.some((l) => l.test(u.pathname))) return;
236
+ s.respondWith(
237
+ (async () => {
238
+ try {
239
+ return await fetch(o);
240
+ } catch {
241
+ const d = await (await caches.open(h)).match(c);
242
+ if (d) return d;
243
+ throw new Error("offline and no cached app shell");
244
+ }
245
+ })()
246
+ );
247
+ return;
248
+ }
249
+ p.has(u.pathname) && s.respondWith(
250
+ (async () => await (await caches.open(h)).match(o) ?? fetch(o))()
251
+ );
252
+ }
253
+ });
254
+ }
255
+ function U() {
256
+ return globalThis;
257
+ }
258
+ const f = "requests";
259
+ function m(t) {
260
+ return new Promise((e, a) => {
261
+ const n = indexedDB.open(t, 1);
262
+ n.onupgradeneeded = () => {
263
+ n.result.createObjectStore(f, { keyPath: "id", autoIncrement: !0 });
264
+ }, n.onsuccess = () => e(n.result), n.onerror = () => a(n.error);
265
+ });
266
+ }
267
+ function E(t) {
268
+ return new Promise((e, a) => {
269
+ t.oncomplete = () => e(), t.onerror = () => a(t.error), t.onabort = () => a(t.error);
270
+ });
271
+ }
272
+ async function P(t, e) {
273
+ const a = await m(t), n = a.transaction(f, "readwrite");
274
+ n.objectStore(f).add(e), await E(n), a.close();
275
+ }
276
+ async function $(t) {
277
+ const e = await m(t), a = e.transaction(f, "readonly"), n = await new Promise((c, i) => {
278
+ const r = a.objectStore(f).getAll();
279
+ r.onsuccess = () => c(r.result), r.onerror = () => i(r.error);
280
+ });
281
+ return e.close(), n;
282
+ }
283
+ async function b(t, e) {
284
+ const a = await m(t), n = a.transaction(f, "readwrite");
285
+ n.objectStore(f).delete(e), await E(n), a.close();
286
+ }
287
+ async function A(t, e) {
288
+ const a = await t.clone().arrayBuffer();
289
+ return {
290
+ url: t.url,
291
+ method: t.method,
292
+ headers: [...t.headers.entries()],
293
+ body: a.byteLength > 0 ? a : null,
294
+ timestamp: e
295
+ };
296
+ }
297
+ function D(t) {
298
+ return new Request(t.url, {
299
+ method: t.method,
300
+ headers: t.headers,
301
+ body: t.body ?? void 0
302
+ });
303
+ }
304
+ async function k(t, e, a) {
305
+ const n = await $(t);
306
+ let c = 0;
307
+ for (const i of n)
308
+ if (i.id !== void 0) {
309
+ if (a - i.timestamp > e) {
310
+ await b(t, i.id);
311
+ continue;
312
+ }
313
+ try {
314
+ const r = await fetch(D(i));
315
+ r.ok || r.status >= 400 && r.status < 500 ? await b(t, i.id) : c += 1;
316
+ } catch {
317
+ c += 1;
318
+ }
319
+ }
320
+ if (c > 0) throw new Error(`background-sync: ${c} request(s) still pending`);
321
+ }
322
+ function I(t, e, a) {
323
+ return t ? typeof t == "function" ? t(e, a) : t.test(e.href) : !0;
324
+ }
325
+ function _(t = {}) {
326
+ const e = U(), { match: a, queueName: n = "tempest-bg-sync", maxRetentionMinutes: c = 1440 } = t, i = c * 60 * 1e3;
327
+ e.addEventListener("fetch", (r) => {
328
+ const s = r.request;
329
+ if (s.method === "GET" || s.method === "HEAD") return;
330
+ const o = new URL(s.url);
331
+ I(a, o, s) && r.respondWith(
332
+ fetch(s.clone()).catch(async (u) => {
333
+ const l = await A(s, Date.now());
334
+ await P(n, l);
335
+ try {
336
+ await e.registration.sync?.register(n);
337
+ } catch {
338
+ }
339
+ throw u;
340
+ })
341
+ );
342
+ }), e.addEventListener("sync", (r) => {
343
+ r.tag === n && r.waitUntil(k(n, i, Date.now()));
344
+ }), e.addEventListener("fetch", (r) => {
345
+ e.registration.sync || r.request.method === "GET" && r.waitUntil(k(n, i, Date.now()).catch(() => {
346
+ }));
347
+ });
348
+ }
349
+ export {
350
+ T as createPartialResponse,
351
+ _ as installBackgroundSync,
352
+ M as installNotificationClickHandler,
353
+ F as installPrecache,
354
+ H as installPushHandler,
355
+ B as installRuntimeCache,
356
+ G as installSkipWaitingListener,
357
+ j as registerServiceWorker,
358
+ q as skipWaiting,
359
+ C as unregisterAllServiceWorkers
360
+ };
361
+ //# sourceMappingURL=sw.js.map