@prabhask5/stellar-engine 1.1.6 → 1.1.8

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 (265) hide show
  1. package/README.md +68 -25
  2. package/dist/actions/remoteChange.d.ts +143 -18
  3. package/dist/actions/remoteChange.d.ts.map +1 -1
  4. package/dist/actions/remoteChange.js +182 -58
  5. package/dist/actions/remoteChange.js.map +1 -1
  6. package/dist/actions/truncateTooltip.d.ts +56 -0
  7. package/dist/actions/truncateTooltip.d.ts.map +1 -0
  8. package/dist/actions/truncateTooltip.js +312 -0
  9. package/dist/actions/truncateTooltip.js.map +1 -0
  10. package/dist/auth/admin.d.ts +40 -3
  11. package/dist/auth/admin.d.ts.map +1 -1
  12. package/dist/auth/admin.js +45 -5
  13. package/dist/auth/admin.js.map +1 -1
  14. package/dist/auth/crypto.d.ts +55 -5
  15. package/dist/auth/crypto.d.ts.map +1 -1
  16. package/dist/auth/crypto.js +58 -5
  17. package/dist/auth/crypto.js.map +1 -1
  18. package/dist/auth/deviceVerification.d.ts +236 -20
  19. package/dist/auth/deviceVerification.d.ts.map +1 -1
  20. package/dist/auth/deviceVerification.js +293 -40
  21. package/dist/auth/deviceVerification.js.map +1 -1
  22. package/dist/auth/displayUtils.d.ts +98 -0
  23. package/dist/auth/displayUtils.d.ts.map +1 -0
  24. package/dist/auth/displayUtils.js +133 -0
  25. package/dist/auth/displayUtils.js.map +1 -0
  26. package/dist/auth/loginGuard.d.ts +108 -14
  27. package/dist/auth/loginGuard.d.ts.map +1 -1
  28. package/dist/auth/loginGuard.js +153 -31
  29. package/dist/auth/loginGuard.js.map +1 -1
  30. package/dist/auth/offlineCredentials.d.ts +132 -15
  31. package/dist/auth/offlineCredentials.d.ts.map +1 -1
  32. package/dist/auth/offlineCredentials.js +167 -23
  33. package/dist/auth/offlineCredentials.js.map +1 -1
  34. package/dist/auth/offlineLogin.d.ts +96 -10
  35. package/dist/auth/offlineLogin.d.ts.map +1 -1
  36. package/dist/auth/offlineLogin.js +82 -15
  37. package/dist/auth/offlineLogin.js.map +1 -1
  38. package/dist/auth/offlineSession.d.ts +83 -9
  39. package/dist/auth/offlineSession.d.ts.map +1 -1
  40. package/dist/auth/offlineSession.js +104 -13
  41. package/dist/auth/offlineSession.js.map +1 -1
  42. package/dist/auth/resolveAuthState.d.ts +70 -8
  43. package/dist/auth/resolveAuthState.d.ts.map +1 -1
  44. package/dist/auth/resolveAuthState.js +142 -46
  45. package/dist/auth/resolveAuthState.js.map +1 -1
  46. package/dist/auth/singleUser.d.ts +390 -37
  47. package/dist/auth/singleUser.d.ts.map +1 -1
  48. package/dist/auth/singleUser.js +505 -133
  49. package/dist/auth/singleUser.js.map +1 -1
  50. package/dist/bin/install-pwa.d.ts +25 -0
  51. package/dist/bin/install-pwa.d.ts.map +1 -0
  52. package/dist/bin/install-pwa.js +2197 -0
  53. package/dist/bin/install-pwa.js.map +1 -0
  54. package/dist/config.d.ts +132 -12
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +87 -9
  57. package/dist/config.js.map +1 -1
  58. package/dist/conflicts.d.ts +246 -23
  59. package/dist/conflicts.d.ts.map +1 -1
  60. package/dist/conflicts.js +495 -46
  61. package/dist/conflicts.js.map +1 -1
  62. package/dist/data.d.ts +338 -18
  63. package/dist/data.d.ts.map +1 -1
  64. package/dist/data.js +385 -34
  65. package/dist/data.js.map +1 -1
  66. package/dist/database.d.ts +72 -14
  67. package/dist/database.d.ts.map +1 -1
  68. package/dist/database.js +120 -29
  69. package/dist/database.js.map +1 -1
  70. package/dist/debug.d.ts +77 -1
  71. package/dist/debug.d.ts.map +1 -1
  72. package/dist/debug.js +88 -1
  73. package/dist/debug.js.map +1 -1
  74. package/dist/deviceId.d.ts +38 -7
  75. package/dist/deviceId.d.ts.map +1 -1
  76. package/dist/deviceId.js +68 -10
  77. package/dist/deviceId.js.map +1 -1
  78. package/dist/engine.d.ts +175 -3
  79. package/dist/engine.d.ts.map +1 -1
  80. package/dist/engine.js +831 -110
  81. package/dist/engine.js.map +1 -1
  82. package/dist/entries/actions.d.ts +14 -0
  83. package/dist/entries/actions.d.ts.map +1 -1
  84. package/dist/entries/actions.js +27 -1
  85. package/dist/entries/actions.js.map +1 -1
  86. package/dist/entries/auth.d.ts +16 -0
  87. package/dist/entries/auth.d.ts.map +1 -1
  88. package/dist/entries/auth.js +73 -1
  89. package/dist/entries/auth.js.map +1 -1
  90. package/dist/entries/config.d.ts +12 -0
  91. package/dist/entries/config.d.ts.map +1 -1
  92. package/dist/entries/config.js +18 -1
  93. package/dist/entries/config.js.map +1 -1
  94. package/dist/entries/kit.d.ts +21 -9
  95. package/dist/entries/kit.d.ts.map +1 -1
  96. package/dist/entries/kit.js +57 -8
  97. package/dist/entries/kit.js.map +1 -1
  98. package/dist/entries/stores.d.ts +11 -0
  99. package/dist/entries/stores.d.ts.map +1 -1
  100. package/dist/entries/stores.js +43 -2
  101. package/dist/entries/stores.js.map +1 -1
  102. package/dist/entries/types.d.ts +11 -1
  103. package/dist/entries/types.d.ts.map +1 -1
  104. package/dist/entries/types.js +10 -0
  105. package/dist/entries/types.js.map +1 -1
  106. package/dist/entries/utils.d.ts +7 -1
  107. package/dist/entries/utils.d.ts.map +1 -1
  108. package/dist/entries/utils.js +23 -2
  109. package/dist/entries/utils.js.map +1 -1
  110. package/dist/entries/vite.d.ts +20 -0
  111. package/dist/entries/vite.d.ts.map +1 -0
  112. package/dist/entries/vite.js +26 -0
  113. package/dist/entries/vite.js.map +1 -0
  114. package/dist/index.d.ts +33 -2
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +176 -21
  117. package/dist/index.js.map +1 -1
  118. package/dist/kit/auth.d.ts +80 -0
  119. package/dist/kit/auth.d.ts.map +1 -0
  120. package/dist/kit/auth.js +72 -0
  121. package/dist/kit/auth.js.map +1 -0
  122. package/dist/kit/confirm.d.ts +111 -0
  123. package/dist/kit/confirm.d.ts.map +1 -0
  124. package/dist/kit/confirm.js +169 -0
  125. package/dist/kit/confirm.js.map +1 -0
  126. package/dist/kit/loads.d.ts +189 -0
  127. package/dist/kit/loads.d.ts.map +1 -0
  128. package/dist/kit/loads.js +205 -0
  129. package/dist/kit/loads.js.map +1 -0
  130. package/dist/kit/server.d.ts +175 -0
  131. package/dist/kit/server.d.ts.map +1 -0
  132. package/dist/kit/server.js +297 -0
  133. package/dist/kit/server.js.map +1 -0
  134. package/dist/kit/sw.d.ts +176 -0
  135. package/dist/kit/sw.d.ts.map +1 -0
  136. package/dist/kit/sw.js +320 -0
  137. package/dist/kit/sw.js.map +1 -0
  138. package/dist/queue.d.ts +274 -0
  139. package/dist/queue.d.ts.map +1 -1
  140. package/dist/queue.js +556 -38
  141. package/dist/queue.js.map +1 -1
  142. package/dist/realtime.d.ts +241 -27
  143. package/dist/realtime.d.ts.map +1 -1
  144. package/dist/realtime.js +633 -109
  145. package/dist/realtime.js.map +1 -1
  146. package/dist/runtime/runtimeConfig.d.ts +91 -16
  147. package/dist/runtime/runtimeConfig.d.ts.map +1 -1
  148. package/dist/runtime/runtimeConfig.js +146 -19
  149. package/dist/runtime/runtimeConfig.js.map +1 -1
  150. package/dist/stores/authState.d.ts +150 -11
  151. package/dist/stores/authState.d.ts.map +1 -1
  152. package/dist/stores/authState.js +169 -17
  153. package/dist/stores/authState.js.map +1 -1
  154. package/dist/stores/network.d.ts +39 -0
  155. package/dist/stores/network.d.ts.map +1 -1
  156. package/dist/stores/network.js +169 -16
  157. package/dist/stores/network.js.map +1 -1
  158. package/dist/stores/remoteChanges.d.ts +327 -52
  159. package/dist/stores/remoteChanges.d.ts.map +1 -1
  160. package/dist/stores/remoteChanges.js +337 -75
  161. package/dist/stores/remoteChanges.js.map +1 -1
  162. package/dist/stores/sync.d.ts +130 -0
  163. package/dist/stores/sync.d.ts.map +1 -1
  164. package/dist/stores/sync.js +167 -7
  165. package/dist/stores/sync.js.map +1 -1
  166. package/dist/supabase/auth.d.ts +326 -19
  167. package/dist/supabase/auth.d.ts.map +1 -1
  168. package/dist/supabase/auth.js +374 -26
  169. package/dist/supabase/auth.js.map +1 -1
  170. package/dist/supabase/client.d.ts +79 -6
  171. package/dist/supabase/client.d.ts.map +1 -1
  172. package/dist/supabase/client.js +158 -15
  173. package/dist/supabase/client.js.map +1 -1
  174. package/dist/supabase/validate.d.ts +101 -7
  175. package/dist/supabase/validate.d.ts.map +1 -1
  176. package/dist/supabase/validate.js +117 -8
  177. package/dist/supabase/validate.js.map +1 -1
  178. package/dist/sw/build/vite-plugin.d.ts +74 -0
  179. package/dist/sw/build/vite-plugin.d.ts.map +1 -0
  180. package/dist/sw/build/vite-plugin.js +183 -0
  181. package/dist/sw/build/vite-plugin.js.map +1 -0
  182. package/dist/sw/sw.js +669 -0
  183. package/dist/types.d.ts +150 -45
  184. package/dist/types.d.ts.map +1 -1
  185. package/dist/types.js +12 -10
  186. package/dist/types.js.map +1 -1
  187. package/dist/utils.d.ts +55 -13
  188. package/dist/utils.d.ts.map +1 -1
  189. package/dist/utils.js +83 -22
  190. package/dist/utils.js.map +1 -1
  191. package/package.json +20 -22
  192. package/src/components/DeferredChangesBanner.svelte +477 -0
  193. package/src/components/SyncStatus.svelte +1732 -0
  194. package/dist/crdt/awareness.d.ts +0 -54
  195. package/dist/crdt/awareness.d.ts.map +0 -1
  196. package/dist/crdt/awareness.js +0 -219
  197. package/dist/crdt/awareness.js.map +0 -1
  198. package/dist/crdt/doc.d.ts +0 -56
  199. package/dist/crdt/doc.d.ts.map +0 -1
  200. package/dist/crdt/doc.js +0 -130
  201. package/dist/crdt/doc.js.map +0 -1
  202. package/dist/crdt/index.d.ts +0 -15
  203. package/dist/crdt/index.d.ts.map +0 -1
  204. package/dist/crdt/index.js +0 -20
  205. package/dist/crdt/index.js.map +0 -1
  206. package/dist/crdt/offline.d.ts +0 -91
  207. package/dist/crdt/offline.d.ts.map +0 -1
  208. package/dist/crdt/offline.js +0 -353
  209. package/dist/crdt/offline.js.map +0 -1
  210. package/dist/crdt/sync.d.ts +0 -58
  211. package/dist/crdt/sync.d.ts.map +0 -1
  212. package/dist/crdt/sync.js +0 -399
  213. package/dist/crdt/sync.js.map +0 -1
  214. package/dist/crdt/types.d.ts +0 -62
  215. package/dist/crdt/types.d.ts.map +0 -1
  216. package/dist/crdt/types.js +0 -7
  217. package/dist/crdt/types.js.map +0 -1
  218. package/dist/email/sendEmail.d.ts +0 -31
  219. package/dist/email/sendEmail.d.ts.map +0 -1
  220. package/dist/email/sendEmail.js +0 -39
  221. package/dist/email/sendEmail.js.map +0 -1
  222. package/dist/email/validateSmtp.d.ts +0 -18
  223. package/dist/email/validateSmtp.d.ts.map +0 -1
  224. package/dist/email/validateSmtp.js +0 -33
  225. package/dist/email/validateSmtp.js.map +0 -1
  226. package/dist/entries/crdt.d.ts +0 -3
  227. package/dist/entries/crdt.d.ts.map +0 -1
  228. package/dist/entries/crdt.js +0 -13
  229. package/dist/entries/crdt.js.map +0 -1
  230. package/dist/entries/email.d.ts +0 -4
  231. package/dist/entries/email.d.ts.map +0 -1
  232. package/dist/entries/email.js +0 -4
  233. package/dist/entries/email.js.map +0 -1
  234. package/dist/kit/authPresets.d.ts +0 -28
  235. package/dist/kit/authPresets.d.ts.map +0 -1
  236. package/dist/kit/authPresets.js +0 -23
  237. package/dist/kit/authPresets.js.map +0 -1
  238. package/dist/kit/configEndpoint.d.ts +0 -18
  239. package/dist/kit/configEndpoint.d.ts.map +0 -1
  240. package/dist/kit/configEndpoint.js +0 -27
  241. package/dist/kit/configEndpoint.js.map +0 -1
  242. package/dist/kit/deployEndpoint.d.ts +0 -22
  243. package/dist/kit/deployEndpoint.d.ts.map +0 -1
  244. package/dist/kit/deployEndpoint.js +0 -79
  245. package/dist/kit/deployEndpoint.js.map +0 -1
  246. package/dist/kit/layoutLoad.d.ts +0 -23
  247. package/dist/kit/layoutLoad.d.ts.map +0 -1
  248. package/dist/kit/layoutLoad.js +0 -41
  249. package/dist/kit/layoutLoad.js.map +0 -1
  250. package/dist/kit/protectedLoad.d.ts +0 -16
  251. package/dist/kit/protectedLoad.d.ts.map +0 -1
  252. package/dist/kit/protectedLoad.js +0 -28
  253. package/dist/kit/protectedLoad.js.map +0 -1
  254. package/dist/kit/setupLoad.d.ts +0 -11
  255. package/dist/kit/setupLoad.d.ts.map +0 -1
  256. package/dist/kit/setupLoad.js +0 -28
  257. package/dist/kit/setupLoad.js.map +0 -1
  258. package/dist/kit/validateEndpoint.d.ts +0 -9
  259. package/dist/kit/validateEndpoint.d.ts.map +0 -1
  260. package/dist/kit/validateEndpoint.js +0 -25
  261. package/dist/kit/validateEndpoint.js.map +0 -1
  262. package/dist/kit/vercelApi.d.ts +0 -6
  263. package/dist/kit/vercelApi.d.ts.map +0 -1
  264. package/dist/kit/vercelApi.js +0 -48
  265. package/dist/kit/vercelApi.js.map +0 -1
package/dist/sw/sw.js ADDED
@@ -0,0 +1,669 @@
1
+ /// <reference lib="webworker" />
2
+ /**
3
+ * @fileoverview Service Worker for the PWA.
4
+ *
5
+ * Implements a **smart caching strategy** designed for SvelteKit's output:
6
+ *
7
+ * - **Immutable assets** (`/_app/immutable/*`) — cache-first, never
8
+ * revalidate. These files have content-hashes in their filenames, so a
9
+ * new hash === a new file. Stored in a persistent `ASSET_CACHE` that
10
+ * survives across deploys.
11
+ *
12
+ * - **Shell / static assets** — cache-first, versioned per deploy.
13
+ * Stored in `SHELL_CACHE` which is keyed by `APP_VERSION` and
14
+ * automatically cleaned up when a new SW activates.
15
+ *
16
+ * - **Navigation requests** (HTML) — network-first with a 3-second
17
+ * timeout, falling back to the cached root `/` document. Ensures the
18
+ * app loads offline while staying fresh when online.
19
+ *
20
+ * - **Background precaching** — after install, the SW can be told to
21
+ * fetch all assets listed in `asset-manifest.json`, downloading only
22
+ * those not already in cache. This makes the entire app available
23
+ * offline without blocking the install event.
24
+ *
25
+ * The `APP_VERSION` constant is patched automatically by the stellarPWA
26
+ * Vite plugin on every production build.
27
+ *
28
+ * @see {@link handleNavigationRequest} for HTML page caching (network-first)
29
+ * @see {@link handleImmutableAsset} for content-hashed asset caching (cache-first)
30
+ * @see {@link handleStaticAsset} for general static asset caching (cache-first)
31
+ * @see {@link backgroundPrecache} for offline-readiness precaching
32
+ * @see {@link cleanupOldAssets} for stale immutable asset removal
33
+ */
34
+ // =============================================================================
35
+ // VERSIONING
36
+ // =============================================================================
37
+ /**
38
+ * Build-stamped version string — updated automatically by the stellarPWA
39
+ * Vite plugin on each build. Used to key the shell cache and reported
40
+ * back to clients via the `GET_VERSION` message handler.
41
+ */
42
+ const APP_VERSION = '__SW_VERSION__';
43
+ // =============================================================================
44
+ // CACHE NAMING
45
+ // =============================================================================
46
+ /**
47
+ * Persistent cache for immutable assets (`/_app/immutable/*`).
48
+ * These files contain content hashes in their filenames, making them safe
49
+ * to cache indefinitely. NOT cleared on deploy — assets accumulate across
50
+ * builds and are pruned by {@link cleanupOldAssets} when triggered.
51
+ */
52
+ const ASSET_CACHE = '__SW_PREFIX__-assets-v1';
53
+ /**
54
+ * Versioned cache for the app shell (HTML, manifest, icons) and other
55
+ * static assets. Re-created on each deploy; old versions are deleted
56
+ * during the `activate` event so only one shell cache exists at a time.
57
+ */
58
+ const SHELL_CACHE = '__SW_PREFIX__-shell-' + APP_VERSION;
59
+ // =============================================================================
60
+ // PRECACHE MANIFEST
61
+ // =============================================================================
62
+ /**
63
+ * Core app shell resources to precache during the `install` event.
64
+ * These are the minimum files needed for the app to render offline.
65
+ *
66
+ * Note: The root HTML (`/`) is cached separately in the install handler
67
+ * because its failure should abort the installation entirely.
68
+ */
69
+ const PRECACHE_ASSETS = [
70
+ '/manifest.json',
71
+ '/favicon.png',
72
+ '/icon-192.png',
73
+ '/icon-512.png',
74
+ '/offline.html'
75
+ ];
76
+ // =============================================================================
77
+ // INSTALL EVENT
78
+ // =============================================================================
79
+ /**
80
+ * Install handler — precaches the minimal app shell.
81
+ *
82
+ * **Strategy:**
83
+ * 1. The root HTML (`/`) is **required** — if it fails, install fails.
84
+ * Better to stay on the working old SW than activate with no cached HTML.
85
+ * 2. Other shell assets (icons, manifest) are **optional** — failures
86
+ * are logged but don't block installation.
87
+ * 3. Notifies all open windows via `postMessage` so the UI can show an
88
+ * "update available" prompt.
89
+ * 4. Auto-promotes via `skipWaiting()` after 5 minutes as a fallback for
90
+ * iOS PWA where the update prompt may never be interacted with.
91
+ *
92
+ * @param event - The `install` {@link ExtendableEvent}.
93
+ */
94
+ self.addEventListener('install', (event) => {
95
+ console.log(`[SW] Installing version: ${APP_VERSION}`);
96
+ event.waitUntil(caches.open(SHELL_CACHE).then(async (cache) => {
97
+ /* Root HTML is REQUIRED — if it fails, install fails */
98
+ await cache.add('/');
99
+ console.log('[SW] Root HTML precached');
100
+ /* Other shell assets are optional — use allSettled so failures don't block */
101
+ await Promise.allSettled(PRECACHE_ASSETS.map((url) => cache.add(url).catch((err) => console.warn(`[SW] Failed to precache ${url}:`, err))));
102
+ console.log('[SW] Minimal precache complete');
103
+ /* Notify all open clients that a new version has been installed */
104
+ self.clients.matchAll({ type: 'window' }).then((clients) => {
105
+ clients.forEach((client) => {
106
+ client.postMessage({ type: 'SW_INSTALLED', version: APP_VERSION });
107
+ });
108
+ });
109
+ }));
110
+ /*
111
+ * Let the UpdatePrompt component control the transition if the user is
112
+ * active, but auto-promote after 5 minutes to handle iOS PWA where the
113
+ * prompt may never show (the app might be backgrounded indefinitely).
114
+ */
115
+ setTimeout(() => {
116
+ self.skipWaiting();
117
+ }, 5 * 60 * 1000);
118
+ });
119
+ // =============================================================================
120
+ // ACTIVATE EVENT
121
+ // =============================================================================
122
+ /**
123
+ * Activate handler — cleans up stale caches and claims all clients.
124
+ *
125
+ * **Deletes:**
126
+ * - Old versioned shell caches (e.g., `__SW_PREFIX__-shell-<old-version>`)
127
+ * - The legacy `__SW_PREFIX__-cache-v1` cache (one-time migration from the
128
+ * original single-cache strategy)
129
+ *
130
+ * **Keeps:**
131
+ * - `ASSET_CACHE` — immutable assets persist across versions
132
+ * - `SHELL_CACHE` — the current deploy's shell cache
133
+ *
134
+ * @param event - The `activate` {@link ExtendableEvent}.
135
+ */
136
+ self.addEventListener('activate', (event) => {
137
+ console.log(`[SW] Activating version: ${APP_VERSION}`);
138
+ event.waitUntil((async () => {
139
+ const cacheNames = await caches.keys();
140
+ await Promise.all(cacheNames
141
+ .filter((name) => {
142
+ /* Delete old versioned shell caches (not the current one) */
143
+ if (name.startsWith('__SW_PREFIX__-shell-') && name !== SHELL_CACHE)
144
+ return true;
145
+ /* Delete legacy shared cache (one-time migration) */
146
+ if (name === '__SW_PREFIX__-cache-v1')
147
+ return true;
148
+ return false;
149
+ })
150
+ .map((name) => {
151
+ console.log(`[SW] Deleting old cache: ${name}`);
152
+ return caches.delete(name);
153
+ }));
154
+ /* Keep ASSET_CACHE — immutable assets persist across versions */
155
+ /* Take control of all open tabs immediately */
156
+ await self.clients.claim();
157
+ })());
158
+ });
159
+ // =============================================================================
160
+ // FETCH EVENT
161
+ // =============================================================================
162
+ /**
163
+ * Fetch handler — routes requests to the appropriate caching strategy.
164
+ *
165
+ * **Routing logic** (in priority order):
166
+ * 1. Skip non-GET requests (mutations should always hit the network)
167
+ * 2. Skip external origins (only cache same-origin resources)
168
+ * 3. Skip `/api/*` routes (backend data — never cache)
169
+ * 4. Navigation requests --> {@link handleNavigationRequest} (network-first)
170
+ * 5. Immutable assets --> {@link handleImmutableAsset} (cache-first, permanent)
171
+ * 6. Static assets --> {@link handleStaticAsset} (cache-first)
172
+ * 7. Everything else --> {@link handleOtherRequest} (network-first)
173
+ *
174
+ * @param event - The {@link FetchEvent} to handle.
175
+ */
176
+ self.addEventListener('fetch', (event) => {
177
+ /* Only intercept GET requests — let POST/PUT/DELETE go straight to network */
178
+ if (event.request.method !== 'GET')
179
+ return;
180
+ const url = new URL(event.request.url);
181
+ /* Skip external requests — we only cache same-origin resources */
182
+ if (url.origin !== self.location.origin)
183
+ return;
184
+ /* Skip API routes — backend data must always be fresh */
185
+ if (url.pathname.startsWith('/api/'))
186
+ return;
187
+ /* ── Navigation Requests (HTML pages) ───────────────────────────── */
188
+ if (event.request.mode === 'navigate') {
189
+ event.respondWith(handleNavigationRequest(event.request));
190
+ return;
191
+ }
192
+ /* ── Immutable Assets (`/_app/immutable/*`) ─────────────────────── */
193
+ /* Content-hashed filenames → cache forever, never revalidate */
194
+ if (url.pathname.includes('/_app/immutable/')) {
195
+ event.respondWith(handleImmutableAsset(event.request));
196
+ return;
197
+ }
198
+ /* ── Other Static Assets (JS, CSS, images, fonts, JSON) ─────────── */
199
+ if (isStaticAsset(url.pathname)) {
200
+ event.respondWith(handleStaticAsset(event.request));
201
+ return;
202
+ }
203
+ /* ── Fallback — network-first for everything else ───────────────── */
204
+ event.respondWith(handleOtherRequest(event.request));
205
+ });
206
+ // =============================================================================
207
+ // HELPER: STATIC ASSET CHECK
208
+ // =============================================================================
209
+ /**
210
+ * Determines whether a given pathname looks like a static asset
211
+ * (scripts, styles, images, fonts, data files).
212
+ *
213
+ * Matches by file extension or by the `/_app/` prefix (SvelteKit's
214
+ * client-side output directory).
215
+ *
216
+ * @param pathname - The URL pathname to test (e.g., `/_app/version.json`).
217
+ * @returns `true` if the path matches a known static-asset extension.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * isStaticAsset('/icon-192.png'); // true
222
+ * isStaticAsset('/api/config'); // false
223
+ * ```
224
+ */
225
+ function isStaticAsset(pathname) {
226
+ return (pathname.startsWith('/_app/') ||
227
+ pathname.endsWith('.js') ||
228
+ pathname.endsWith('.css') ||
229
+ pathname.endsWith('.png') ||
230
+ pathname.endsWith('.jpg') ||
231
+ pathname.endsWith('.jpeg') ||
232
+ pathname.endsWith('.gif') ||
233
+ pathname.endsWith('.svg') ||
234
+ pathname.endsWith('.ico') ||
235
+ pathname.endsWith('.woff') ||
236
+ pathname.endsWith('.woff2') ||
237
+ pathname.endsWith('.json') ||
238
+ pathname.endsWith('.webp'));
239
+ }
240
+ // =============================================================================
241
+ // STRATEGY: NAVIGATION (NETWORK-FIRST)
242
+ // =============================================================================
243
+ /**
244
+ * Handles HTML navigation requests with a **network-first** strategy.
245
+ *
246
+ * **Flow:**
247
+ * 1. Attempt a network fetch with a 3-second timeout (abort via `AbortController`)
248
+ * 2. If successful --> cache the response as `/` and return it
249
+ * 3. If failed --> serve the cached root HTML for offline use
250
+ * 4. If nothing cached --> return a minimal offline fallback page
251
+ *
252
+ * The 3-second timeout prevents the user from staring at a blank screen
253
+ * on flaky connections while still preferring fresh content.
254
+ *
255
+ * @param request - The navigation `Request` object.
256
+ * @returns A `Response` (from network, cache, or inline fallback).
257
+ */
258
+ async function handleNavigationRequest(request) {
259
+ const cache = await caches.open(SHELL_CACHE);
260
+ try {
261
+ /* 3-second timeout — don't leave the user staring at a blank screen */
262
+ const controller = new AbortController();
263
+ const timeoutId = setTimeout(() => controller.abort(), 3000);
264
+ const response = await fetch(request, { signal: controller.signal });
265
+ clearTimeout(timeoutId);
266
+ if (response.ok) {
267
+ /* Cache the fresh HTML for offline fallback */
268
+ cache.put('/', response.clone());
269
+ return response;
270
+ }
271
+ throw new Error('Network response not ok');
272
+ }
273
+ catch {
274
+ /* Network failed or timed out — serve cached HTML */
275
+ console.log('[SW] Navigation offline, serving cache');
276
+ const cached = await cache.match('/');
277
+ if (cached)
278
+ return cached;
279
+ /* Try custom offline page (projects can create static/offline.html) */
280
+ const offlinePage = await cache.match('/offline.html');
281
+ if (offlinePage)
282
+ return offlinePage;
283
+ /* Last resort — minimal inline offline page */
284
+ return new Response(getOfflineHTML(), {
285
+ status: 200,
286
+ headers: { 'Content-Type': 'text/html; charset=utf-8' }
287
+ });
288
+ }
289
+ }
290
+ // =============================================================================
291
+ // STRATEGY: IMMUTABLE ASSETS (CACHE-FIRST, PERMANENT)
292
+ // =============================================================================
293
+ /**
294
+ * Handles requests for immutable assets (`/_app/immutable/*`).
295
+ *
296
+ * **Strategy:** cache-first, NEVER revalidate. These files have content
297
+ * hashes baked into their filenames — if the content changes, the filename
298
+ * changes, so a cached version is always correct.
299
+ *
300
+ * Uses `ASSET_CACHE` which persists across SW versions (not cleared on
301
+ * deploy). Old entries are pruned by {@link cleanupOldAssets}.
302
+ *
303
+ * @param request - The `Request` for an immutable asset.
304
+ * @returns The cached `Response`, or a freshly-fetched one (then cached).
305
+ */
306
+ async function handleImmutableAsset(request) {
307
+ const cache = await caches.open(ASSET_CACHE);
308
+ /* Check cache first — if we have it, it's guaranteed correct */
309
+ const cached = await cache.match(request);
310
+ if (cached) {
311
+ return cached;
312
+ }
313
+ /* Not cached yet — fetch from network and cache for next time */
314
+ try {
315
+ const response = await fetch(request);
316
+ if (response.ok) {
317
+ cache.put(request, response.clone());
318
+ }
319
+ return response;
320
+ }
321
+ catch {
322
+ console.error('[SW] Failed to fetch immutable:', request.url);
323
+ return new Response('Asset not available offline', { status: 503 });
324
+ }
325
+ }
326
+ // =============================================================================
327
+ // STRATEGY: STATIC ASSETS (CACHE-FIRST)
328
+ // =============================================================================
329
+ /**
330
+ * Handles requests for general static assets (non-immutable JS, CSS, images,
331
+ * fonts, JSON files).
332
+ *
333
+ * **Strategy:** cache-first, NO background revalidation. This saves
334
+ * bandwidth — the shell cache is versioned per deploy, so stale assets
335
+ * are cleaned up automatically when the new SW activates.
336
+ *
337
+ * @param request - The `Request` for a static asset.
338
+ * @returns The cached `Response`, or a freshly-fetched one (then cached).
339
+ */
340
+ async function handleStaticAsset(request) {
341
+ const cache = await caches.open(SHELL_CACHE);
342
+ /* Check cache first — return immediately, no background fetch */
343
+ const cached = await cache.match(request);
344
+ if (cached) {
345
+ return cached;
346
+ }
347
+ /* Not cached — fetch and store */
348
+ try {
349
+ const response = await fetch(request);
350
+ if (response.ok) {
351
+ cache.put(request, response.clone());
352
+ }
353
+ return response;
354
+ }
355
+ catch {
356
+ console.log('[SW] Static asset fetch failed:', request.url);
357
+ return new Response('Asset not available offline', { status: 503 });
358
+ }
359
+ }
360
+ // =============================================================================
361
+ // STRATEGY: OTHER REQUESTS (NETWORK-FIRST)
362
+ // =============================================================================
363
+ /**
364
+ * Handles all other same-origin GET requests with a **network-first** strategy.
365
+ *
366
+ * **Flow:**
367
+ * 1. Try the network
368
+ * 2. If successful --> cache and return
369
+ * 3. If failed --> return cached version
370
+ * 4. If nothing cached --> return a 503 "Offline" response
371
+ *
372
+ * @param request - The `Request` object.
373
+ * @returns A `Response` from network or cache.
374
+ */
375
+ async function handleOtherRequest(request) {
376
+ const cache = await caches.open(SHELL_CACHE);
377
+ try {
378
+ const response = await fetch(request);
379
+ if (response.ok) {
380
+ cache.put(request, response.clone());
381
+ }
382
+ return response;
383
+ }
384
+ catch {
385
+ const cached = await cache.match(request);
386
+ if (cached)
387
+ return cached;
388
+ return new Response('Offline', { status: 503 });
389
+ }
390
+ }
391
+ // =============================================================================
392
+ // BACKGROUND PRECACHING
393
+ // =============================================================================
394
+ /**
395
+ * Downloads all assets listed in `asset-manifest.json` that are NOT already
396
+ * cached. This makes the entire app available offline without blocking the
397
+ * install event.
398
+ *
399
+ * **Key behaviours:**
400
+ * - Fetches the manifest with a cache-busting query param (`?_=<timestamp>`)
401
+ * - Checks both `ASSET_CACHE` and `SHELL_CACHE` to avoid redundant downloads
402
+ * - Downloads in batches of 5 with a 50 ms pause between batches to avoid
403
+ * saturating the network
404
+ * - Notifies all open windows with `PRECACHE_COMPLETE` when done
405
+ *
406
+ * Triggered by sending `{ type: 'PRECACHE_ALL' }` to the service worker.
407
+ *
408
+ * @returns A promise that resolves when precaching is complete (or fails).
409
+ *
410
+ * @see {@link cleanupOldAssets} for removing assets no longer in the manifest
411
+ */
412
+ async function backgroundPrecache() {
413
+ try {
414
+ const assetCache = await caches.open(ASSET_CACHE);
415
+ const shellCache = await caches.open(SHELL_CACHE);
416
+ /* Fetch manifest with cache-bust to ensure we get the latest version */
417
+ const manifestResponse = await fetch('/asset-manifest.json?_=' + Date.now(), {
418
+ cache: 'no-store'
419
+ });
420
+ if (!manifestResponse.ok) {
421
+ console.warn('[SW] Asset manifest not found');
422
+ return;
423
+ }
424
+ const manifest = await manifestResponse.json();
425
+ const assets = manifest.assets || [];
426
+ if (assets.length === 0) {
427
+ console.warn('[SW] Asset manifest empty');
428
+ return;
429
+ }
430
+ /* ── Determine which assets still need caching ────────────────── */
431
+ const uncached = [];
432
+ for (const url of assets) {
433
+ /* Route to the correct cache based on whether the asset is immutable */
434
+ const isImmutable = url.includes('/_app/immutable/');
435
+ const cache = isImmutable ? assetCache : shellCache;
436
+ const cached = await cache.match(url);
437
+ if (!cached) {
438
+ uncached.push(url);
439
+ }
440
+ }
441
+ if (uncached.length === 0) {
442
+ console.log('[SW] All assets already cached - full offline ready');
443
+ notifyClients({ type: 'PRECACHE_COMPLETE', cached: assets.length, total: assets.length });
444
+ return;
445
+ }
446
+ console.log(`[SW] Caching ${uncached.length} new assets (${assets.length - uncached.length} already cached)`);
447
+ /* ── Download in batches to avoid network saturation ──────────── */
448
+ let successCount = 0;
449
+ const batchSize = 5;
450
+ for (let i = 0; i < uncached.length; i += batchSize) {
451
+ const batch = uncached.slice(i, i + batchSize);
452
+ const results = await Promise.allSettled(batch.map((url) => {
453
+ const isImmutable = url.includes('/_app/immutable/');
454
+ const cache = isImmutable ? assetCache : shellCache;
455
+ return cache.add(url);
456
+ }));
457
+ results.forEach((r, idx) => {
458
+ if (r.status === 'fulfilled')
459
+ successCount++;
460
+ else
461
+ console.warn(`[SW] Failed to cache: ${batch[idx]}`);
462
+ });
463
+ /* Small delay between batches to be polite to the network */
464
+ if (i + batchSize < uncached.length) {
465
+ await new Promise((r) => setTimeout(r, 50));
466
+ }
467
+ }
468
+ const totalCached = assets.length - uncached.length + successCount;
469
+ console.log(`[SW] Precache complete: ${totalCached}/${assets.length}`);
470
+ notifyClients({ type: 'PRECACHE_COMPLETE', cached: totalCached, total: assets.length });
471
+ }
472
+ catch (e) {
473
+ console.warn('[SW] Precache error:', e);
474
+ }
475
+ }
476
+ // =============================================================================
477
+ // OLD ASSET CLEANUP
478
+ // =============================================================================
479
+ /**
480
+ * Removes stale immutable assets from `ASSET_CACHE` that are no longer
481
+ * referenced in the current `asset-manifest.json`.
482
+ *
483
+ * Only targets `/_app/immutable/*` entries — the shell cache is already
484
+ * versioned and cleaned during the `activate` event.
485
+ *
486
+ * Triggered by sending `{ type: 'CLEANUP_OLD' }` to the service worker.
487
+ *
488
+ * @returns A promise that resolves when cleanup is complete.
489
+ */
490
+ async function cleanupOldAssets() {
491
+ try {
492
+ const cache = await caches.open(ASSET_CACHE);
493
+ /* Fetch the current manifest to know which assets are still valid */
494
+ const manifestResponse = await fetch('/asset-manifest.json', { cache: 'no-store' });
495
+ if (!manifestResponse.ok)
496
+ return;
497
+ const manifest = await manifestResponse.json();
498
+ const currentAssets = new Set(manifest.assets || []);
499
+ /* Walk cached entries and delete any that aren't in the manifest */
500
+ const cachedRequests = await cache.keys();
501
+ let deletedCount = 0;
502
+ for (const request of cachedRequests) {
503
+ const url = new URL(request.url);
504
+ const pathname = url.pathname;
505
+ /* Only clean up immutable assets that are no longer referenced */
506
+ if (pathname.includes('/_app/immutable/') && !currentAssets.has(pathname)) {
507
+ await cache.delete(request);
508
+ deletedCount++;
509
+ }
510
+ }
511
+ if (deletedCount > 0) {
512
+ console.log(`[SW] Cleaned up ${deletedCount} old assets`);
513
+ }
514
+ }
515
+ catch (e) {
516
+ console.warn('[SW] Cleanup error:', e);
517
+ }
518
+ }
519
+ // =============================================================================
520
+ // CLIENT COMMUNICATION
521
+ // =============================================================================
522
+ /**
523
+ * Broadcasts a message to all open windows/tabs.
524
+ *
525
+ * @param message - The message object to send (e.g., `{ type: 'PRECACHE_COMPLETE', ... }`).
526
+ *
527
+ * @example
528
+ * ```ts
529
+ * notifyClients({ type: 'PRECACHE_COMPLETE', cached: 42, total: 50 });
530
+ * ```
531
+ */
532
+ function notifyClients(message) {
533
+ self.clients.matchAll({ type: 'window' }).then((clients) => {
534
+ clients.forEach((client) => client.postMessage(message));
535
+ });
536
+ }
537
+ // =============================================================================
538
+ // OFFLINE FALLBACK PAGE
539
+ // =============================================================================
540
+ /**
541
+ * Returns a minimal offline fallback HTML page. This is only used as a
542
+ * last resort when no cached HTML or custom `/offline.html` is available.
543
+ *
544
+ * Projects should create their own `static/offline.html` with custom
545
+ * styling — it will be precached and served automatically instead of
546
+ * this bare-bones fallback.
547
+ *
548
+ * @returns An unstyled HTML string for the offline fallback.
549
+ */
550
+ function getOfflineHTML() {
551
+ return `<!DOCTYPE html>
552
+ <html lang="en">
553
+ <head>
554
+ <meta charset="UTF-8">
555
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
556
+ <title>Offline - __SW_NAME__</title>
557
+ </head>
558
+ <body>
559
+ <h1>You're Offline</h1>
560
+ <p>Please check your internet connection and try again.</p>
561
+ <button onclick="location.reload()">Try Again</button>
562
+ </body>
563
+ </html>`;
564
+ }
565
+ // =============================================================================
566
+ // MESSAGE HANDLER
567
+ // =============================================================================
568
+ /**
569
+ * Listens for messages from the app's client-side code.
570
+ *
571
+ * **Supported message types:**
572
+ * - `SKIP_WAITING` --> Immediately activate the waiting SW (user accepted update)
573
+ * - `GET_VERSION` --> Responds with the current `APP_VERSION` via `MessagePort`
574
+ * - `PRECACHE_ALL` --> Triggers {@link backgroundPrecache} to download all assets
575
+ * - `CLEANUP_OLD` --> Triggers {@link cleanupOldAssets} to remove stale cache entries
576
+ * - `CACHE_URLS` --> Caches a specific list of URLs (used for route prefetching)
577
+ * - `GET_CACHE_STATUS` --> Responds with cache completeness info via `MessagePort`
578
+ *
579
+ * @param event - The {@link ExtendableMessageEvent} from a client.
580
+ */
581
+ self.addEventListener('message', (event) => {
582
+ const { type } = event.data || {};
583
+ /* ── Force-activate the waiting service worker ──────────────────── */
584
+ if (type === 'SKIP_WAITING') {
585
+ self.skipWaiting();
586
+ }
587
+ /* ── Return the current build version ───────────────────────────── */
588
+ if (type === 'GET_VERSION') {
589
+ event.ports[0]?.postMessage({ version: APP_VERSION });
590
+ }
591
+ /* ── Trigger background precache of all manifest assets ─────────── */
592
+ if (type === 'PRECACHE_ALL') {
593
+ backgroundPrecache();
594
+ }
595
+ /* ── Trigger cleanup of stale immutable assets ──────────────────── */
596
+ if (type === 'CLEANUP_OLD') {
597
+ cleanupOldAssets();
598
+ }
599
+ /* ── Cache specific URLs on demand (e.g., route prefetching) ────── */
600
+ if (type === 'CACHE_URLS') {
601
+ const urls = event.data.urls || [];
602
+ const assetCachePromise = caches.open(ASSET_CACHE);
603
+ const shellCachePromise = caches.open(SHELL_CACHE);
604
+ Promise.all([assetCachePromise, shellCachePromise]).then(([ac, sc]) => {
605
+ urls.forEach((url) => {
606
+ /* Route each URL to the correct cache based on immutability */
607
+ const isImmutable = url.includes('/_app/immutable/');
608
+ const cache = isImmutable ? ac : sc;
609
+ cache.add(url).catch(() => { });
610
+ });
611
+ });
612
+ }
613
+ /* ── Report how many assets are cached vs. total ────────────────── */
614
+ if (type === 'GET_CACHE_STATUS') {
615
+ getCacheStatus().then((status) => {
616
+ event.ports[0]?.postMessage(status);
617
+ });
618
+ }
619
+ });
620
+ // =============================================================================
621
+ // CACHE STATUS REPORTER
622
+ // =============================================================================
623
+ /**
624
+ * Computes the current cache completeness by comparing cached entries
625
+ * against the asset manifest.
626
+ *
627
+ * Attempts to find the manifest in cache first (for offline use), then
628
+ * falls back to a network fetch.
629
+ *
630
+ * @returns An object describing cache readiness:
631
+ * - `cached` — Number of manifest assets currently in cache
632
+ * - `total` — Total number of assets in the manifest
633
+ * - `ready` — `true` if every asset is cached (full offline support)
634
+ * - `version` — The manifest version string (if available)
635
+ * - `error` — Error message string (only present if something went wrong)
636
+ */
637
+ async function getCacheStatus() {
638
+ try {
639
+ const assetCache = await caches.open(ASSET_CACHE);
640
+ const shellCache = await caches.open(SHELL_CACHE);
641
+ /* Try to find the manifest in cache first, then fall back to network */
642
+ const manifestResponse = (await shellCache.match('/asset-manifest.json')) ||
643
+ (await assetCache.match('/asset-manifest.json')) ||
644
+ (await fetch('/asset-manifest.json').catch(() => null));
645
+ if (!manifestResponse) {
646
+ return { cached: 0, total: 0, ready: false };
647
+ }
648
+ const manifest = await manifestResponse.clone().json();
649
+ const assets = manifest.assets || [];
650
+ /* Count how many manifest entries are already cached */
651
+ let cachedCount = 0;
652
+ for (const url of assets) {
653
+ const isImmutable = url.includes('/_app/immutable/');
654
+ const cache = isImmutable ? assetCache : shellCache;
655
+ if (await cache.match(url))
656
+ cachedCount++;
657
+ }
658
+ return {
659
+ cached: cachedCount,
660
+ total: assets.length,
661
+ ready: cachedCount === assets.length,
662
+ version: manifest.version
663
+ };
664
+ }
665
+ catch (e) {
666
+ return { cached: 0, total: 0, ready: false, error: e.message };
667
+ }
668
+ }
669
+ export {};