PageSourceSearch

https://vidapeps.com/push-sw.js

js vidapeps.com collected 2026-10-04 08:10:39 UTC 3,431 bytes, 96 lines download raw bytes

1/*
2 * VidaPeps Web Push handlers.
3 *
4 * Hand-authored (NOT bundled) and pulled into the Workbox-generated service
5 * worker via `workbox.importScripts: ["/push-sw.js"]` in vite.config.ts. This
6 * keeps the app on the `generateSW` strategy (preserving the tuned precache +
7 * catalog runtime caching) while still adding the custom `push` and
8 * `notificationclick` handlers that `generateSW` cannot emit on its own.
9 *
10 * NOTE: this file is served verbatim from /public, so it MUST stay free of em
11 * dashes (the storefront build guard only strips them from bundled chunks).
12 */
13
14// VidaTools (tools.vidapeps.com) shares this service worker + precache with the
15// storefront but must keep its OWN installed-app identity. The Workbox
16// NavigationRoute serves the precached document for navigations; on the tools
17// host that must be the VidaTools index.html (its own manifest + apple icon +
18// title), never the storefront one. This handler is imported into the generated
19// SW BEFORE the Workbox routes, so for tools-host top-level navigations it wins
20// and goes to the network (where the server emits the VidaTools document),
21// falling back to cache offline. Non-tools requests are left to Workbox.
22self.addEventListener("fetch", (event) => {
23  if (event.request.mode !== "navigate") return;
24  let host = "";
25  try {
26    host = new URL(event.request.url).hostname;
27  } catch (e) {
28    return;
29  }
30  if (!host.startsWith("tools.")) return;
31  event.respondWith(
32    fetch(event.request).catch(() =>
33      caches.match(event.request).then((cached) => cached || caches.match("/index.html")),
34    ),
35  );
36});
37
38self.addEventListener("push", (event) => {
39  let payload = {};
40  try {
41    payload = event.data ? event.data.json() : {};
42  } catch (e) {
43    payload = { title: "VidaPeps", body: event.data ? event.data.text() : "" };
44  }
45
46  const title = payload.title || "VidaPeps";
47  const data = Object.assign({}, payload.data, {
48    url: (payload.data && payload.data.url) || payload.url || "/",
49  });
50  const options = {
51    body: payload.body || "",
52    icon: payload.icon || "/icons/pwa-192.png",
53    badge: payload.badge || "/icons/badge-72.png",
54    tag: payload.tag || undefined,
55    renotify: Boolean(payload.tag),
56    data: data,
57  };
58
59  // iOS requires every push to show a user-visible notification.
60  event.waitUntil(self.registration.showNotification(title, options));
61});
62
63self.addEventListener("notificationclick", (event) => {
64  event.notification.close();
65  const targetUrl = (event.notification.data && event.notification.data.url) || "/";
66
67  event.waitUntil(
68    (async () => {
69      const allClients = await self.clients.matchAll({
70        type: "window",
71        includeUncontrolled: true,
72      });
73      // Focus an already-open tab on this origin and route it in-app.
74      for (const client of allClients) {
75        try {
76          if (new URL(client.url).origin === self.location.origin) {
77            await client.focus();
78            if ("navigate" in client && targetUrl) {
79              try {
80                await client.navigate(targetUrl);
81              } catch (e) {
82                /* navigation across SW scope can throw; focus is enough */
83              }
84            }
85            return;
86          }
87        } catch (e) {
88          /* malformed client url, skip */
89        }
90      }
91      if (self.clients.openWindow) {
92        await self.clients.openWindow(targetUrl);
93      }
94    })(),
95  );
96});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.