@swoff/cli 0.3.13 → 0.3.14

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 (224) hide show
  1. package/README.md +28 -32
  2. package/dist/index.js +2 -13
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/cli/help.js +3 -8
  5. package/dist/lib/cli/help.js.map +1 -1
  6. package/dist/lib/commands/add.js +29 -87
  7. package/dist/lib/commands/add.js.map +1 -1
  8. package/dist/lib/commands/generate-guide.js +16 -30
  9. package/dist/lib/commands/generate-guide.js.map +1 -1
  10. package/dist/lib/commands/generate.js +31 -6
  11. package/dist/lib/commands/generate.js.map +1 -1
  12. package/dist/lib/commands/init.js +188 -11
  13. package/dist/lib/commands/init.js.map +1 -1
  14. package/dist/lib/commands/validate.js +2 -4
  15. package/dist/lib/commands/validate.js.map +1 -1
  16. package/dist/lib/config/loader.js +34 -25
  17. package/dist/lib/config/loader.js.map +1 -1
  18. package/dist/lib/config/validator.js +202 -96
  19. package/dist/lib/config/validator.js.map +1 -1
  20. package/dist/lib/generators/file-generators/api-config.js +1 -1
  21. package/dist/lib/generators/file-generators/api-config.js.map +1 -1
  22. package/dist/lib/generators/file-generators/auth-adapter.js +13 -0
  23. package/dist/lib/generators/file-generators/auth-adapter.js.map +1 -0
  24. package/dist/lib/generators/file-generators/auth-check.js +40 -0
  25. package/dist/lib/generators/file-generators/auth-check.js.map +1 -0
  26. package/dist/lib/generators/file-generators/auth-store.js +2 -5
  27. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  28. package/dist/lib/generators/file-generators/background-sync.js +1 -1
  29. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  30. package/dist/lib/generators/file-generators/cache.js +2 -1
  31. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  32. package/dist/lib/generators/file-generators/client-injector-bundle.js +15 -0
  33. package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -0
  34. package/dist/lib/generators/file-generators/client-injector.js +2 -1
  35. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  36. package/dist/lib/generators/file-generators/connectivity-manager.js +12 -0
  37. package/dist/lib/generators/file-generators/connectivity-manager.js.map +1 -0
  38. package/dist/lib/generators/file-generators/connectivity.js +12 -0
  39. package/dist/lib/generators/file-generators/connectivity.js.map +1 -0
  40. package/dist/lib/generators/file-generators/context.js +7 -0
  41. package/dist/lib/generators/file-generators/context.js.map +1 -1
  42. package/dist/lib/generators/file-generators/fetch-wrapper.js +1 -1
  43. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  44. package/dist/lib/generators/file-generators/generate-framework-adapters.js +29 -20
  45. package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
  46. package/dist/lib/generators/file-generators/gql-wrapper.js +2 -2
  47. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  48. package/dist/lib/generators/file-generators/guide-generator.js +51 -1
  49. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  50. package/dist/lib/generators/file-generators/invalidation-tags.js +2 -3
  51. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  52. package/dist/lib/generators/file-generators/mutation-queue.js +2 -2
  53. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  54. package/dist/lib/generators/file-generators/mutation-state.js +1 -1
  55. package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
  56. package/dist/lib/generators/file-generators/notification-generator.js +6 -0
  57. package/dist/lib/generators/file-generators/notification-generator.js.map +1 -0
  58. package/dist/lib/generators/file-generators/open-db.js +6 -0
  59. package/dist/lib/generators/file-generators/open-db.js.map +1 -0
  60. package/dist/lib/generators/file-generators/push.js +1 -2
  61. package/dist/lib/generators/file-generators/push.js.map +1 -1
  62. package/dist/lib/generators/file-generators/pwa-install.js +2 -6
  63. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  64. package/dist/lib/generators/file-generators/server-push.js +1 -1
  65. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  66. package/dist/lib/generators/file-generators/storage-generator.js +6 -0
  67. package/dist/lib/generators/file-generators/storage-generator.js.map +1 -0
  68. package/dist/lib/generators/file-generators/storage-notify.js +6 -0
  69. package/dist/lib/generators/file-generators/storage-notify.js.map +1 -0
  70. package/dist/lib/generators/file-generators/storage.js +6 -0
  71. package/dist/lib/generators/file-generators/storage.js.map +1 -0
  72. package/dist/lib/generators/file-generators/sw-generator-build.js +96 -45
  73. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  74. package/dist/lib/generators/file-generators/sw-injector.js +0 -4
  75. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  76. package/dist/lib/generators/file-generators/sw-template.js +8 -9
  77. package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
  78. package/dist/lib/generators/file-generators/sw-version-gen.js +32 -0
  79. package/dist/lib/generators/file-generators/sw-version-gen.js.map +1 -0
  80. package/dist/lib/generators/file-generators/swoff-api-bundle.js +51 -0
  81. package/dist/lib/generators/file-generators/swoff-api-bundle.js.map +1 -0
  82. package/dist/lib/generators/sw-build-utils.js +69 -0
  83. package/dist/lib/generators/sw-build-utils.js.map +1 -0
  84. package/dist/lib/generators/sw-generator.js +23 -34
  85. package/dist/lib/generators/sw-generator.js.map +1 -1
  86. package/dist/lib/generators/sw-sections/activate-handler.js +74 -23
  87. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  88. package/dist/lib/generators/sw-sections/assemble-sw.js +15 -94
  89. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  90. package/dist/lib/generators/sw-sections/background-sync-handler.js +74 -72
  91. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  92. package/dist/lib/generators/sw-sections/batch-refresh-queue.js +102 -0
  93. package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -0
  94. package/dist/lib/generators/sw-sections/config-header.js +2 -3
  95. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  96. package/dist/lib/generators/sw-sections/default-template.js +59 -6
  97. package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
  98. package/dist/lib/generators/sw-sections/fetch-handler.js +621 -495
  99. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  100. package/dist/lib/generators/sw-sections/install-handler.js +40 -23
  101. package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
  102. package/dist/lib/generators/sw-sections/message-handler.js +25 -8
  103. package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
  104. package/dist/lib/generators/sw-sections/server-push-handler.js +10 -14
  105. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  106. package/dist/lib/generators/sw-sections/shared.js +68 -0
  107. package/dist/lib/generators/sw-sections/shared.js.map +1 -0
  108. package/dist/lib/generators/sw-sections/sw-push.js +2 -6
  109. package/dist/lib/generators/sw-sections/sw-push.js.map +1 -1
  110. package/dist/lib/generators/sw-sections/tag-management.js +55 -86
  111. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  112. package/dist/lib/generators/swoff-files-generator.js +183 -59
  113. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  114. package/dist/lib/shared/config-types.js +67 -63
  115. package/dist/lib/shared/config-types.js.map +1 -1
  116. package/dist/lib/shared/feature-registry.js +198 -0
  117. package/dist/lib/shared/feature-registry.js.map +1 -0
  118. package/dist/lib/utils/detect-framework.js +52 -9
  119. package/dist/lib/utils/detect-framework.js.map +1 -1
  120. package/dist/runtime/api-config.js +2 -2
  121. package/dist/runtime/api-config.js.map +1 -1
  122. package/dist/runtime/auth-adapter.js +179 -0
  123. package/dist/runtime/auth-adapter.js.map +1 -0
  124. package/dist/runtime/auth-state.js +16 -6
  125. package/dist/runtime/auth-state.js.map +1 -1
  126. package/dist/runtime/auth-store.js +219 -213
  127. package/dist/runtime/auth-store.js.map +1 -1
  128. package/dist/runtime/auth-user.js +5 -19
  129. package/dist/runtime/auth-user.js.map +1 -1
  130. package/dist/runtime/background-sync.js +5 -2
  131. package/dist/runtime/background-sync.js.map +1 -1
  132. package/dist/runtime/cache.js +91 -3
  133. package/dist/runtime/cache.js.map +1 -1
  134. package/dist/runtime/client-injector-bundle.js +314 -0
  135. package/dist/runtime/client-injector-bundle.js.map +1 -0
  136. package/dist/runtime/client-injector.js +173 -30
  137. package/dist/runtime/client-injector.js.map +1 -1
  138. package/dist/runtime/connectivity-manager.js +83 -0
  139. package/dist/runtime/connectivity-manager.js.map +1 -0
  140. package/dist/runtime/connectivity.js +83 -0
  141. package/dist/runtime/connectivity.js.map +1 -0
  142. package/dist/runtime/fetch-wrapper.js +26 -59
  143. package/dist/runtime/fetch-wrapper.js.map +1 -1
  144. package/dist/runtime/gql-wrapper.js +15 -9
  145. package/dist/runtime/gql-wrapper.js.map +1 -1
  146. package/dist/runtime/invalidation-tags.js +12 -89
  147. package/dist/runtime/invalidation-tags.js.map +1 -1
  148. package/dist/runtime/mutation-queue.js +83 -31
  149. package/dist/runtime/mutation-queue.js.map +1 -1
  150. package/dist/runtime/mutation-state.js +3 -3
  151. package/dist/runtime/mutation-state.js.map +1 -1
  152. package/dist/runtime/notification.js +65 -0
  153. package/dist/runtime/notification.js.map +1 -0
  154. package/dist/runtime/open-db.js +41 -0
  155. package/dist/runtime/open-db.js.map +1 -0
  156. package/dist/runtime/push.js +15 -27
  157. package/dist/runtime/push.js.map +1 -1
  158. package/dist/runtime/pwa-injector.js +3 -32
  159. package/dist/runtime/pwa-injector.js.map +1 -1
  160. package/dist/runtime/pwa-prompt.js +35 -3
  161. package/dist/runtime/pwa-prompt.js.map +1 -1
  162. package/dist/runtime/reset.js +39 -42
  163. package/dist/runtime/reset.js.map +1 -1
  164. package/dist/runtime/server-push.js +23 -13
  165. package/dist/runtime/server-push.js.map +1 -1
  166. package/dist/runtime/storage.js +32 -0
  167. package/dist/runtime/storage.js.map +1 -0
  168. package/dist/runtime/sw-injector.js +19 -178
  169. package/dist/runtime/sw-injector.js.map +1 -1
  170. package/dist/runtime/sw-version.js +13 -0
  171. package/dist/runtime/sw-version.js.map +1 -0
  172. package/dist/runtime/swoff-api-bundle.js +1491 -0
  173. package/dist/runtime/swoff-api-bundle.js.map +1 -0
  174. package/dist/runtime/type-definitions.js +8 -7
  175. package/dist/runtime/type-definitions.js.map +1 -1
  176. package/dist/runtime/utils.js +4 -0
  177. package/dist/runtime/utils.js.map +1 -1
  178. package/dist/swoff-nkdhhmw8. +18 -0
  179. package/dist/swoff.js +5 -0
  180. package/package.json +1 -1
  181. package/templates/react/useAuth.jsx +7 -4
  182. package/templates/react/useAuth.tsx +14 -14
  183. package/templates/react/useBackgroundSync.jsx +5 -4
  184. package/templates/react/useBackgroundSync.tsx +5 -4
  185. package/templates/react/useCachedFetch.jsx +8 -0
  186. package/templates/react/useCachedFetch.tsx +8 -0
  187. package/templates/react/useMutation.jsx +60 -56
  188. package/templates/react/useMutation.tsx +65 -64
  189. package/templates/react/useMutationQueue.jsx +5 -1
  190. package/templates/react/useMutationQueue.tsx +8 -2
  191. package/templates/react/useMutationState.jsx +3 -4
  192. package/templates/react/useMutationState.tsx +4 -8
  193. package/templates/react/useNetworkStatus.jsx +57 -17
  194. package/templates/react/useNetworkStatus.tsx +42 -20
  195. package/templates/react/useOfflineAnalytics.jsx +42 -0
  196. package/templates/react/useOfflineAnalytics.tsx +50 -0
  197. package/templates/react/usePwaInstall.jsx +26 -0
  198. package/templates/react/usePwaInstall.tsx +26 -0
  199. package/templates/react/useSWUpdate.jsx +7 -52
  200. package/templates/react/useSWUpdate.tsx +12 -51
  201. package/templates/react/useStorageEstimate.jsx +56 -0
  202. package/templates/react/useStorageEstimate.tsx +67 -0
  203. package/dist/__tests__/assemble-sw.test.js +0 -145
  204. package/dist/__tests__/assemble-sw.test.js.map +0 -1
  205. package/dist/__tests__/cli-integration.test.js +0 -231
  206. package/dist/__tests__/cli-integration.test.js.map +0 -1
  207. package/dist/__tests__/config-types.test.js +0 -124
  208. package/dist/__tests__/config-types.test.js.map +0 -1
  209. package/dist/__tests__/detect-language.test.js +0 -51
  210. package/dist/__tests__/detect-language.test.js.map +0 -1
  211. package/dist/__tests__/file-generators.test.js +0 -228
  212. package/dist/__tests__/file-generators.test.js.map +0 -1
  213. package/dist/__tests__/glob-matcher.test.js +0 -86
  214. package/dist/__tests__/glob-matcher.test.js.map +0 -1
  215. package/dist/__tests__/invalidation-tags.test.js +0 -283
  216. package/dist/__tests__/invalidation-tags.test.js.map +0 -1
  217. package/dist/__tests__/loader.test.js +0 -77
  218. package/dist/__tests__/loader.test.js.map +0 -1
  219. package/dist/__tests__/validator.test.js +0 -340
  220. package/dist/__tests__/validator.test.js.map +0 -1
  221. package/templates/react/useCacheInvalidation.jsx +0 -19
  222. package/templates/react/useCacheInvalidation.tsx +0 -52
  223. package/templates/react/useIsFetching.jsx +0 -32
  224. package/templates/react/useIsFetching.tsx +0 -32
@@ -1,37 +1,79 @@
1
- export function generateClientInjectorCode(ctx, pwaEnabled, mutationQueueEnabled, serverPushEnabled) {
1
+ export function generateClientInjectorCode(ctx, pwaEnabled, mutationQueueEnabled, serverPushEnabled, navMode, authEnabled, connectivityEnabled, tagInvalidationEnabled) {
2
2
  const { ext, ts } = ctx;
3
+ const ssrNav = navMode === "ssr" && tagInvalidationEnabled;
3
4
  const pwaImport = pwaEnabled
4
- ? `import { setupPwaInstall } from "./pwa/injector.${ext}";
5
+ ? `import { setupPwaInstall } from "./pwa/prompt.${ext}";
5
6
  `
6
7
  : "";
7
- const pwaCall = pwaEnabled ? `setupPwaInstall();\n` : "";
8
+ const pwaCall = pwaEnabled
9
+ ? `if (typeof window !== "undefined" && typeof document !== "undefined") { setupPwaInstall(); }\n`
10
+ : "";
8
11
  const pushImport = serverPushEnabled
9
- ? `import { startPushEvents } from "./realtime/server-push.${ext}";
12
+ ? `import { startPushEvents } from "./server-push/client.${ext}";
10
13
  `
11
14
  : "";
12
- const pushCall = serverPushEnabled ? `startPushEvents();\n` : "";
15
+ const pushCall = serverPushEnabled
16
+ ? `if (typeof navigator !== "undefined" && "serviceWorker" in navigator) { startPushEvents(); }\n`
17
+ : "";
13
18
  const mutationImport = mutationQueueEnabled
14
- ? `import { processMutationQueue } from "./offline/queue.${ext}";
19
+ ? `import { processMutationQueue, clearQueue } from "./mutation/queue.${ext}";
15
20
  `
16
21
  : "";
17
- const mutationOnlineListener = mutationQueueEnabled
18
- ? `
19
- // --- Mutation Queue Online Listener ---
20
- window.addEventListener("online", processMutationQueue);
22
+ const authImport = authEnabled
23
+ ? `import { ensureValidAuth, clearMemoryAuth } from "./auth/store.${ext}";
24
+ `
25
+ : "";
26
+ const storageImport = connectivityEnabled
27
+ ? `import { getStorageEstimate, formatBytes } from "./storage.${ext}";
28
+ `
29
+ : "";
30
+ const connectivityImport = connectivityEnabled
31
+ ? `import {
32
+ dispatchState,
33
+ startHeartbeat,
34
+ stopHeartbeat,
35
+ verifyAndNotify,
36
+ } from './connectivity.${ext}'
21
37
  `
22
38
  : "";
23
- const onlineRefetchListener = `
24
- // --- Online Refetch Listener ---
39
+ const onlineListener = connectivityEnabled
40
+ ? `
41
+ // --- Online Listener ---
25
42
  // When connectivity returns, the SW checks stale cache entries and refetches them.
26
- if (typeof window !== "undefined") {
27
- window.addEventListener("online", () => {
28
- if (navigator.serviceWorker.controller) {
29
- navigator.serviceWorker.controller.postMessage({ type: "ONLINE" });
43
+ if (typeof window !== 'undefined') {
44
+ window.addEventListener('online', async () => {
45
+ const isOnline = await verifyAndNotify()
46
+ startHeartbeat()
47
+ if (isOnline) {
48
+ ${mutationQueueEnabled ? `processMutationQueue()` : ""}
30
49
  }
31
- });
50
+ })
51
+
52
+ window.addEventListener('offline', () => {
53
+ stopHeartbeat()
54
+ dispatchState(false)
55
+ })
56
+
57
+ document.addEventListener('visibilitychange', () => {
58
+ if (!document.hidden) {
59
+ verifyAndNotify()
60
+ startHeartbeat()
61
+ } else {
62
+ stopHeartbeat()
63
+ }
64
+ })
65
+
66
+ if (navigator.onLine) {
67
+ verifyAndNotify()
68
+ startHeartbeat()
69
+ } else {
70
+ queueMicrotask(() => dispatchState(false))
71
+ }
32
72
  }
33
- `;
34
- const focusListener = `
73
+ `
74
+ : "";
75
+ const focusListener = tagInvalidationEnabled
76
+ ? `
35
77
  // --- Focus Listener for Reactive Strategy ---
36
78
  // Notifies the SW when the tab gains focus so it can refresh stale reactive entries.
37
79
  // Uses visibilitychange only (covers tab switch, window refocus, alt-tab) — single source avoids duplicate FOCUS messages.
@@ -42,8 +84,10 @@ if (typeof document !== "undefined") {
42
84
  }
43
85
  });
44
86
  }
45
- `;
46
- const invalidationHandler = `
87
+ `
88
+ : "";
89
+ const invalidationHandler = tagInvalidationEnabled
90
+ ? `
47
91
  if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
48
92
  window.dispatchEvent(
49
93
  new CustomEvent("cache-invalidated", {
@@ -51,9 +95,53 @@ if (typeof document !== "undefined") {
51
95
  })
52
96
  );
53
97
  }
54
- `;
98
+ `
99
+ : "";
55
100
  const swImport = `import { initServiceWorker as swInit } from "./sw/injector.${ext}";
56
101
  `;
102
+ const autoPrefetchImport = ssrNav
103
+ ? `import { prefetchCache } from "./fetch/core.${ext}";
104
+ `
105
+ : "";
106
+ const autoPrefetchCode = ssrNav
107
+ ? `
108
+ // --- Auto-prefetch HTML on client-side navigation (SSR mode) ---
109
+ // Intercepts history.pushState/replaceState to warm the SW cache with HTML
110
+ // for routes the user navigates to via client-side routing.
111
+ if (typeof history !== "undefined") {
112
+ const origPushState = history.pushState.bind(history);
113
+ history.pushState = function (data, unused, url) {
114
+ origPushState(data, unused, url);
115
+ if (typeof url === "string" && url.startsWith("/")) {
116
+ prefetchCache(url);
117
+ }
118
+ };
119
+ const origReplaceState = history.replaceState.bind(history);
120
+ history.replaceState = function (data, unused, url) {
121
+ origReplaceState(data, unused, url);
122
+ if (typeof url === "string" && url.startsWith("/")) {
123
+ prefetchCache(url);
124
+ }
125
+ };
126
+ }
127
+ `
128
+ : "";
129
+ const storageInit = connectivityEnabled
130
+ ? `
131
+ const storage = await getStorageEstimate();
132
+ if (storage.percentUsed > 80) {
133
+ window.dispatchEvent(
134
+ new CustomEvent("swoff:notification", {
135
+ detail: {
136
+ level: "warn",
137
+ code: "STORAGE_QUOTA_HIGH",
138
+ message: \`Storage at \${storage.percentUsed}% capacity (\${formatBytes(storage.usage)} / \${formatBytes(storage.quota)})\`,
139
+ },
140
+ }),
141
+ );
142
+ }
143
+ `
144
+ : "";
57
145
  return `/**
58
146
  * Swoff Client Injector
59
147
  * Orchestrates SW registration, PWA install, and cross-tab sync.
@@ -68,7 +156,9 @@ if (typeof document !== "undefined") {
68
156
  * sw-ready - SW active and controlling page
69
157
  * sw-error - SW registration failed
70
158
  * cache-invalidated - Cache entries cleared on SW confirmation (detail: { tags })
71
- * swoff:cache-updated - Background refresh completed (detail: { url })
159
+ * swoff:cache-updated - Background refresh completed (detail: { url })
160
+ * swoff:offline-fallback - Offline fallback page served (detail: { route, fallbackLevel, timestamp })
161
+ * swoff:notification - SW or storage notification (detail: { level, code, message })
72
162
  * mutation-sync-complete - Queued mutations synced (detail: { succeeded, failed })
73
163
  * mutation-sync-progress - Batch progress during sync (detail: { succeeded, failed, total, current })
74
164
  * mutation-queue-changed - Queue modified
@@ -78,11 +168,9 @@ if (typeof document !== "undefined") {
78
168
  * pwa-installed - User accepted install (detail: { outcome: 'accepted' })
79
169
  * sw-auth-unauthorized - 401 response received
80
170
  * sw-auth-state-change - Login or logout (detail: { authenticated: boolean })
81
- * sw-update-available - New version ready (detail: { version })
82
- * sw-version-detected - Version info available
83
171
  */
84
- ${pwaImport}${mutationImport}${swImport}${pushImport}
85
- ${pwaCall}${mutationOnlineListener}${pushCall}${onlineRefetchListener}${focusListener}
172
+ ${pwaImport}${mutationImport}${authImport}${swImport}${storageImport}${connectivityImport}${pushImport}${autoPrefetchImport}
173
+ ${pwaCall}${pushCall}${onlineListener}${focusListener}${autoPrefetchCode}
86
174
  // --- SW Message Listener ---
87
175
  if (typeof window !== "undefined" && "serviceWorker" in navigator) {
88
176
  navigator.serviceWorker.addEventListener("message", (event) => {
@@ -93,6 +181,20 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
93
181
  })
94
182
  );
95
183
  }
184
+ if (event.data.type === "NAV_RETRY_SUCCESS") {
185
+ window.dispatchEvent(
186
+ new CustomEvent("swoff:navigation-online", {
187
+ detail: { url: event.data.url },
188
+ })
189
+ );
190
+ }
191
+ if (event.data.type === "OFFLINE_FALLBACK_ACTIVATED") {
192
+ window.dispatchEvent(
193
+ new CustomEvent("swoff:offline-fallback", {
194
+ detail: event.data.detail,
195
+ })
196
+ );
197
+ }
96
198
  if (event.data.type === "SW_PROGRESS") {
97
199
  const { percent, downloaded, total } = event.data;
98
200
  window.dispatchEvent(
@@ -101,6 +203,17 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
101
203
  })
102
204
  );
103
205
  }
206
+ if (event.data.type === "SW_NOTIFICATION") {
207
+ window.dispatchEvent(
208
+ new CustomEvent("swoff:notification", {
209
+ detail: {
210
+ level: event.data.level,
211
+ code: event.data.code,
212
+ message: event.data.message,
213
+ },
214
+ })
215
+ );
216
+ }
104
217
  if (event.data.type === "BACKGROUND_SYNC_PROGRESS") {
105
218
  window.dispatchEvent(
106
219
  new CustomEvent("mutation-sync-progress", {
@@ -122,14 +235,44 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
122
235
  }
123
236
  window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
124
237
  }
125
- if (event.data.type === "MUTATION_STORED") {
126
- typeof processMutationQueue !== "undefined" && processMutationQueue();
238
+ if (event.data.type === "MUTATION_STORED" && typeof processMutationQueue !== "undefined") {
239
+ processMutationQueue();
240
+ }
241
+ if (event.data.type === "AUTH_CLEARED") {
242
+ // Another tab cleared auth — clear memory only (IndexedDB + caches already cleaned by initiator)
243
+ if (typeof clearMemoryAuth !== "undefined") {
244
+ clearMemoryAuth();
245
+ }
246
+ window.dispatchEvent(new CustomEvent("sw-auth-state-change", { detail: { type: "clear" } }));
247
+ }
248
+ if (event.data.type === "AUTH_FAILURE") {
249
+ // SW detected 401 during background refetch — check if session is still valid
250
+ (async () => {
251
+ if (typeof ensureValidAuth === "undefined") return;
252
+ const refreshed = await ensureValidAuth();
253
+ if (!refreshed) {
254
+ // Session expired — clear queue and runtime caches
255
+ if (typeof clearQueue !== "undefined") {
256
+ await clearQueue();
257
+ }
258
+ try {
259
+ for (const name of ["swoff-runtime", "swoff-runtime-html"]) {
260
+ const cache = await caches.open(name);
261
+ const keys = await cache.keys();
262
+ await Promise.all(keys.map((k) => cache.delete(k)));
263
+ }
264
+ } catch {
265
+ // Handle cache deletion errors
266
+ }
267
+ window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
268
+ }
269
+ })();
127
270
  }${invalidationHandler} });
128
271
  }
129
272
 
130
273
  /** Initialize SW registration and all client-side features (PWA install, mutation queue, cross-tab sync). Call once at app startup. */
131
274
  export async function initServiceWorker()${ts ? ": Promise<void>" : " "}{
132
- await swInit();
275
+ await swInit();${storageInit}
133
276
  }
134
277
  `;
135
278
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../src/runtime/client-injector.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,0BAA0B,CACxC,GAAmB,EACnB,UAAmB,EACnB,oBAA6B,EAC7B,iBAA0B;IAE1B,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAExB,MAAM,SAAS,GAAG,UAAU;QAC1B,CAAC,CAAC,mDAAmD,GAAG;CAC3D;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzD,MAAM,UAAU,GAAG,iBAAiB;QAClC,CAAC,CAAC,2DAA2D,GAAG;CACnE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,iBAAiB,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IAEjE,MAAM,cAAc,GAAG,oBAAoB;QACzC,CAAC,CAAC,yDAAyD,GAAG;CACjE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,sBAAsB,GAAG,oBAAoB;QACjD,CAAC,CAAC;;;CAGL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,qBAAqB,GAAG;;;;;;;;;;CAU/B,CAAC;IAEA,MAAM,aAAa,GAAG;;;;;;;;;;;CAWvB,CAAC;IAEA,MAAM,mBAAmB,GAAG;;;;;;;;CAQ7B,CAAC;IAEA,MAAM,QAAQ,GAAG,8DAA8D,GAAG;CACnF,CAAC;IAEA,OAAO;;;;;;kEAMyD,GAAG;;;;;;;;;;;;;;;;;;;;;EAqBnE,SAAS,GAAG,cAAc,GAAG,QAAQ,GAAG,UAAU;EAClD,OAAO,GAAG,sBAAsB,GAAG,QAAQ,GAAG,qBAAqB,GAAG,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0C9E,mBAAmB;;;;2CAIiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG;;;CAGtE,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../src/runtime/client-injector.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,0BAA0B,CACxC,GAAmB,EACnB,UAAmB,EACnB,oBAA6B,EAC7B,iBAA0B,EAC1B,OAAgB,EAChB,WAAqB,EACrB,mBAA6B,EAC7B,sBAAgC;IAEhC,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,IAAI,sBAAsB,CAAC;IAE3D,MAAM,SAAS,GAAG,UAAU;QAC1B,CAAC,CAAC,iDAAiD,GAAG;CACzD;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,UAAU;QACxB,CAAC,CAAC,gGAAgG;QAClG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GAAG,iBAAiB;QAClC,CAAC,CAAC,yDAAyD,GAAG;CACjE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,iBAAiB;QAChC,CAAC,CAAC,gGAAgG;QAClG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG,oBAAoB;QACzC,CAAC,CAAC,sEAAsE,GAAG;CAC9E;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GAAG,WAAW;QAC5B,CAAC,CAAC,kEAAkE,GAAG;CAC1E;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,aAAa,GAAG,mBAAmB;QACvC,CAAC,CAAC,8DAA8D,GAAG;CACtE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,kBAAkB,GAAG,mBAAmB;QAC5C,CAAC,CAAC;;;;;yBAKmB,GAAG;CAC3B;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG,mBAAmB;QACxC,CAAC,CAAC;;;;;;;;QAQE,oBAAoB,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyB3D;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,aAAa,GAAG,sBAAsB;QAC1C,CAAC,CAAC;;;;;;;;;;;CAWL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,mBAAmB,GAAG,sBAAsB;QAChD,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,8DAA8D,GAAG;CACnF,CAAC;IAEA,MAAM,kBAAkB,GAAG,MAAM;QAC/B,CAAC,CAAC,+CAA+C,GAAG;CACvD;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,gBAAgB,GAAG,MAAM;QAC7B,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;CAoBL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,WAAW,GAAG,mBAAmB;QACrC,CAAC,CAAC;;;;;;;;;;;;;CAaL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;kEAMyD,GAAG;;;;;;;;;;;;;;;;;;;;;EAqBnE,SAAS,GAAG,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,aAAa,GAAG,kBAAkB,GAAG,UAAU,GAAG,kBAAkB;EACzH,OAAO,GAAG,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiGjE,mBAAmB;;;;2CAIiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG;mBACpD,WAAW;;CAE7B,CAAC;AACF,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { T } from "./utils.js";
2
+ export function generateConnectivityManagerCode(ctx) {
3
+ const { ts } = ctx;
4
+ return `/**
5
+ **/
6
+ export const CONNECTIVITY_EVENT = 'app-connectivity-change'
7
+
8
+ let heartbeatIntervalId${T(ts, "ReturnType<typeof setInterval> | null")} = null
9
+ const HEARTBEAT_DELAY = 30000
10
+
11
+ let _currentOnlineStatus = typeof navigator !== 'undefined' ? navigator.onLine : true
12
+
13
+ export function getCurrentOnlineStatus(): boolean {
14
+ return _currentOnlineStatus
15
+ }
16
+
17
+ function createTimeoutSignal(ms) {
18
+ if (typeof AbortSignal.timeout === "function") return AbortSignal.timeout(ms);
19
+ var ctrl = new AbortController();
20
+ setTimeout(function() { ctrl.abort(); }, ms);
21
+ return ctrl.signal;
22
+ }
23
+
24
+ export async function verifyAndNotify() {
25
+ if (typeof window === 'undefined') return false
26
+
27
+ if (!navigator.onLine) {
28
+ dispatchState(false)
29
+ return false
30
+ }
31
+
32
+ try {
33
+ await fetch(\`/\${Date.now()}?hb=1\`, {
34
+ method: 'HEAD',
35
+ cache: 'no-cache',
36
+ signal: createTimeoutSignal(5000),
37
+ })
38
+
39
+ if (navigator.serviceWorker.controller) {
40
+ navigator.serviceWorker.controller.postMessage({ type: 'ONLINE' })
41
+ }
42
+
43
+ dispatchState(true)
44
+ return true
45
+ } catch (error) {
46
+ if (navigator.serviceWorker.controller) {
47
+ navigator.serviceWorker.controller.postMessage({ type: 'OFFLINE' })
48
+ }
49
+ dispatchState(false)
50
+ return false
51
+ }
52
+ }
53
+
54
+ export function dispatchState(isTrulyOnline: boolean) {
55
+ _currentOnlineStatus = isTrulyOnline
56
+ const event = new CustomEvent(CONNECTIVITY_EVENT, {
57
+ detail: { online: isTrulyOnline },
58
+ })
59
+ window.dispatchEvent(event)
60
+ }
61
+
62
+ export function startHeartbeat() {
63
+ if (heartbeatIntervalId) return
64
+ heartbeatIntervalId = setInterval(async () => {
65
+ if (document.hidden) return
66
+ await verifyAndNotify()
67
+ }, HEARTBEAT_DELAY)
68
+ }
69
+
70
+ export function stopHeartbeat() {
71
+ if (!heartbeatIntervalId) return
72
+ clearInterval(heartbeatIntervalId)
73
+ heartbeatIntervalId = null
74
+ }
75
+
76
+ export async function forceRetry() {
77
+ stopHeartbeat()
78
+ await verifyAndNotify()
79
+ startHeartbeat()
80
+ }
81
+ `;
82
+ }
83
+ //# sourceMappingURL=connectivity-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectivity-manager.js","sourceRoot":"","sources":["../../src/runtime/connectivity-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,+BAA+B,CAAC,GAAmB;IACjE,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACnB,OAAO;;;;yBAIgB,CAAC,CAAC,EAAE,EAAE,uCAAuC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyEtE,CAAC;AACF,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { T } from "./utils.js";
2
+ export function generateConnectivityCode(ctx) {
3
+ const { ts } = ctx;
4
+ return `/**
5
+ **/
6
+ export const CONNECTIVITY_EVENT = 'app-connectivity-change'
7
+
8
+ let heartbeatIntervalId${T(ts, "ReturnType<typeof setInterval> | null")} = null
9
+ const HEARTBEAT_DELAY = 30000
10
+
11
+ let _currentOnlineStatus = typeof navigator !== 'undefined' ? navigator.onLine : true
12
+
13
+ export function getCurrentOnlineStatus(): boolean {
14
+ return _currentOnlineStatus
15
+ }
16
+
17
+ function createTimeoutSignal(ms) {
18
+ if (typeof AbortSignal.timeout === "function") return AbortSignal.timeout(ms);
19
+ var ctrl = new AbortController();
20
+ setTimeout(function() { ctrl.abort(); }, ms);
21
+ return ctrl.signal;
22
+ }
23
+
24
+ export async function verifyAndNotify() {
25
+ if (typeof window === 'undefined') return false
26
+
27
+ if (!navigator.onLine) {
28
+ dispatchState(false)
29
+ return false
30
+ }
31
+
32
+ try {
33
+ await fetch(\`/\${Date.now()}?hb=1\`, {
34
+ method: 'HEAD',
35
+ cache: 'no-cache',
36
+ signal: createTimeoutSignal(5000),
37
+ })
38
+
39
+ if (navigator.serviceWorker.controller) {
40
+ navigator.serviceWorker.controller.postMessage({ type: 'ONLINE' })
41
+ }
42
+
43
+ dispatchState(true)
44
+ return true
45
+ } catch (error) {
46
+ if (navigator.serviceWorker.controller) {
47
+ navigator.serviceWorker.controller.postMessage({ type: 'OFFLINE' })
48
+ }
49
+ dispatchState(false)
50
+ return false
51
+ }
52
+ }
53
+
54
+ export function dispatchState(isTrulyOnline: boolean) {
55
+ _currentOnlineStatus = isTrulyOnline
56
+ const event = new CustomEvent(CONNECTIVITY_EVENT, {
57
+ detail: { online: isTrulyOnline },
58
+ })
59
+ window.dispatchEvent(event)
60
+ }
61
+
62
+ export function startHeartbeat() {
63
+ if (heartbeatIntervalId) return
64
+ heartbeatIntervalId = setInterval(async () => {
65
+ if (document.hidden) return
66
+ await verifyAndNotify()
67
+ }, HEARTBEAT_DELAY)
68
+ }
69
+
70
+ export function stopHeartbeat() {
71
+ if (!heartbeatIntervalId) return
72
+ clearInterval(heartbeatIntervalId)
73
+ heartbeatIntervalId = null
74
+ }
75
+
76
+ export async function forceRetry() {
77
+ stopHeartbeat()
78
+ await verifyAndNotify()
79
+ startHeartbeat()
80
+ }
81
+ `;
82
+ }
83
+ //# sourceMappingURL=connectivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectivity.js","sourceRoot":"","sources":["../../src/runtime/connectivity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,wBAAwB,CAAC,GAAmB;IAC1D,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACnB,OAAO;;;;yBAIgB,CAAC,CAAC,EAAE,EAAE,uCAAuC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyEtE,CAAC;AACF,CAAC"}
@@ -1,17 +1,15 @@
1
- import { T, R, G } from "./utils.js";
2
- export function generateFetchWrapperCode(ctx, authEnabled, userEndpoint, mutationQueue, requestBatchWindowMs) {
1
+ import { T, R, G, AS } from "./utils.js";
2
+ export function generateFetchWrapperCode(ctx, authEnabled, mutationQueue, requestBatchWindowMs) {
3
3
  const { ext, ts } = ctx;
4
4
  const importLines = [
5
- `import { API_BASE } from "../config.${ext}";`,
6
- `import { generateTags, invalidateUrl${mutationQueue ? ", expandCascading" : ""} } from "../cache/tags.${ext}";`,
7
- `import { invalidateByTags } from "../cache/index.${ext}";`,
5
+ `import { generateTags } from "../cache/tags.${ext}";`,
6
+ `import { invalidateByTags, invalidateUrl${mutationQueue ? ", expandCascading" : ""} } from "../cache/invalidate.${ext}";`,
8
7
  authEnabled
9
- ? `import { getAuth, clearAuth, withAuthHeaders, isAuthUrl, ensureValidAuth, AUTH_WITH_CREDENTIALS } from "../auth/store.${ext}";`
8
+ ? `import { getAuth, withAuthHeaders, AUTH_WITH_CREDENTIALS } from "../auth/store.${ext}";`
10
9
  : "",
11
10
  mutationQueue
12
- ? `import { queueMutation } from "../offline/queue.${ext}";`
11
+ ? `import { queueMutation } from "../mutation/queue.${ext}";`
13
12
  : "",
14
- `import { incrementFetchCount, decrementFetchCount } from "../fetch/state.${ext}";`,
15
13
  ]
16
14
  .filter(Boolean)
17
15
  .join("\n");
@@ -41,8 +39,7 @@ export interface FetchWithCacheOptions extends RequestInit {
41
39
  validateSuccess?: (response: Response) => boolean | Promise<boolean>;
42
40
  /** Override the URL used for auto-invalidation. Defaults to the request URL. Useful when the mutation URL differs from the cache tag URL. */
43
41
  invalidateUrl?: string;
44
- /** Skip global fetch counter tracking. Used by prefetchCache to avoid triggering loading bars. */
45
- skipFetchCount?: boolean;
42
+
46
43
  }
47
44
  `
48
45
  : "";
@@ -51,48 +48,12 @@ export interface FetchWithCacheOptions extends RequestInit {
51
48
  if (options.auth) {
52
49
  const auth = await getAuth();
53
50
  withAuthHeaders(headers, auth);
54
- }`
55
- : "";
56
- const authUrlsBlock = authEnabled
57
- ? `
58
- // Auth endpoints bypass SW cache
59
- if (options.auth && isAuthUrl(url) && !headers.has("X-SW-Cache-Strategy")) {
60
- headers.set("X-SW-Cache-Strategy", "mutation");
61
51
  }`
62
52
  : "";
63
53
  const authCredentialsBlock = authEnabled
64
54
  ? `
65
55
  if (AUTH_WITH_CREDENTIALS) {
66
56
  fetchOptions.credentials = "include";
67
- }`
68
- : "";
69
- const auth401Block = authEnabled
70
- ? `
71
- if (options.auth && response.status === 401) {
72
- // Check if the token is actually expired by probing the user endpoint
73
- try {
74
- const authCheck = await fetch(API_BASE + "${userEndpoint}", {
75
- headers: { "Authorization": headers.get("Authorization") } as HeadersInit,
76
- credentials: AUTH_WITH_CREDENTIALS ? "include" : undefined,
77
- });
78
- if (authCheck.status === 401) {
79
- // Token expired — try silent refresh
80
- const refreshed = await ensureValidAuth();
81
- if (refreshed?.token) {
82
- // Retry original request with fresh token
83
- const retryHeaders = new Headers(options.headers);
84
- withAuthHeaders(retryHeaders, refreshed);
85
- response = await fetch(resolvedInput, { ...fetchOptions, headers: retryHeaders });
86
- } else {
87
- await clearAuth();
88
- window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
89
- }
90
- }
91
- // authCheck 200: user IS authenticated but lacks permission — let original 401 propagate
92
- } catch {
93
- await clearAuth();
94
- window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
95
- }
96
57
  }`
97
58
  : "";
98
59
  const mutationTagsBlock = mutationQueue
@@ -228,18 +189,16 @@ const BATCH_WINDOW_MS = ${requestBatchWindowMs};
228
189
 
229
190
  /** Fetch with caching, auth, offline queue, auto-invalidation, and per-request strategy override. Returns { response, fromCache }. Use { auth: true } for authenticated requests — works with bearer, cookie, and custom auth types. */
230
191
  export async function fetchWithCache${G(ts, "T")}(input${T(ts, "RequestInfo")}, options${T(ts, "RequestInit & FetchWithCacheOptions")} = {})${R(ts, "Promise<FetchWithCacheResult<T>>")}{
231
- if (!options.skipFetchCount) incrementFetchCount();
232
- try {
233
192
  const method = (options.method || "GET").toUpperCase();
234
193
  const isRead = options.type === "read" || (options.type !== "mutation" && (method === "GET" || method === "HEAD" || method === "OPTIONS"));
235
- const resolvedInput = typeof input === "string" && !input.startsWith("http") && !input.startsWith("//") ? API_BASE + input : input;
194
+ const resolvedInput = input;
236
195
  const url = typeof resolvedInput === "string" ? resolvedInput : resolvedInput.url;
237
196
 
238
197
  const headers = new Headers(options.headers);
239
198
 
240
199
  // Set cache strategy
241
- if (!headers.has("X-SW-Cache-Strategy")) {
242
- headers.set("X-SW-Cache-Strategy", isRead ? "read" : "mutation");
200
+ if (!headers.has("X-SW-Type")) {
201
+ headers.set("X-SW-Type", isRead ? "read" : "mutation");
243
202
  }
244
203
  ${autoTagsBlock}
245
204
  // Custom tags override auto-generated
@@ -262,7 +221,7 @@ ${autoTagsBlock}
262
221
  if (options.refetchOnReconnect !== undefined) {
263
222
  headers.set("X-SW-Refetch-On-Reconnect", String(options.refetchOnReconnect));
264
223
  }
265
- ${authBlock}${authUrlsBlock}
224
+ ${authBlock}
266
225
  // Forward no-queue option to SW
267
226
  if (options.queueOffline === false) {
268
227
  headers.set("X-SW-No-Queue", "true");
@@ -288,12 +247,18 @@ ${mutationTagsBlock}
288
247
  } else {
289
248
  const abortHandler = () => {
290
249
  inFlightRequests.delete(url);
250
+ if (pendingBatches.has(url)) {
251
+ const batch = pendingBatches.get(url)!;
252
+ clearTimeout(batch.timer);
253
+ pendingBatches.delete(url);
254
+ batch.rejectors.forEach((rej) => rej(new DOMException("The operation was aborted", "AbortError")));
255
+ }
291
256
  };
292
257
  if (options.signal) {
293
258
  options.signal.addEventListener("abort", abortHandler, { once: true });
294
259
  }
295
260
  if (isRead && BATCH_WINDOW_MS > 0) {
296
- const batch${T(ts, "{ resolvers: Array<(r: Response) => void>; rejectors: Array<(e: unknown) => void>; timer: ReturnType<typeof setTimeout> }")} = { resolvers: [], rejectors: [], timer: 0 };
261
+ const batch${T(ts, "{ resolvers: Array<(r: Response) => void>; rejectors: Array<(e: unknown) => void>; timer: ReturnType<typeof setTimeout> }")} = { resolvers: [], rejectors: [], timer: 0${AS(ts, "unknown as ReturnType<typeof setTimeout>")} };
297
262
  pendingBatches.set(url, batch);
298
263
  batch.timer = setTimeout(() => {
299
264
  pendingBatches.delete(url);
@@ -316,7 +281,12 @@ ${mutationTagsBlock}
316
281
  batch.rejectors.push(reject);
317
282
  });
318
283
  } else {
319
- responsePromise = fetch(resolvedInput, fetchOptions);
284
+ responsePromise = fetch(resolvedInput, fetchOptions).finally(() => {
285
+ inFlightRequests.delete(url);
286
+ if (options.signal) {
287
+ options.signal.removeEventListener("abort", abortHandler);
288
+ }
289
+ });
320
290
  }
321
291
  }
322
292
 
@@ -335,18 +305,15 @@ ${offlineWriteFallbackBlock}
335
305
  }
336
306
  throw err;
337
307
  }
338
- ${autoInvalidateBlock}${auth401Block}
308
+ ${autoInvalidateBlock}
339
309
  const fromCache = response.headers.get("X-SW-From-Cache") === "true";
340
310
  const queued = response.headers.get("X-SW-Mutation-Queued") === "true";
341
311
  return { response, fromCache, queued };
342
- } finally {
343
- if (!options.skipFetchCount) decrementFetchCount();
344
- }
345
312
  }
346
313
 
347
314
  /** Fire-and-forget prefetch: warms the cache for a URL without blocking. Useful for route prefetching or link hover prefetching. */
348
315
  export function prefetchCache(input${T(ts, "RequestInfo")}, options${T(ts, "RequestInit & FetchWithCacheOptions")} = {})${R(ts, "void")}{
349
- fetchWithCache(input, { ...options, skipFetchCount: true }).catch(() => {
316
+ fetchWithCache(input, options).catch(() => {
350
317
  // Prefetch failures are intentionally silent
351
318
  });
352
319
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../src/runtime/fetch-wrapper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../src/runtime/fetch-wrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,UAAU,wBAAwB,CACtC,GAAmB,EACnB,WAAoB,EACpB,aAAsB,EACtB,oBAA4B;IAE5B,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAExB,MAAM,WAAW,GAAG;QAClB,+CAA+C,GAAG,IAAI;QACtD,2CAA2C,aAAa,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,gCAAgC,GAAG,IAAI;QAE1H,WAAW;YACT,CAAC,CAAC,kFAAkF,GAAG,IAAI;YAC3F,CAAC,CAAC,EAAE;QACN,aAAa;YACX,CAAC,CAAC,oDAAoD,GAAG,IAAI;YAC7D,CAAC,CAAC,EAAE;KACP;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,cAAc,GAAG,EAAE;QACvB,CAAC,CAAC;uCACiC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC;YACrC,CAAC,CAAC,EAAE,EAAE,mCAAmC,CAAC;aACzC,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;UACnB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;CAEzB;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,gBAAgB,GAAG,EAAE;QACzB,CAAC,CAAC;;;;;;;;;;;;;;;;;;CAkBL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,SAAS,GAAG,WAAW;QAC3B,CAAC,CAAC;;;;IAIF;QACA,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,oBAAoB,GAAG,WAAW;QACtC,CAAC,CAAC;;;IAGF;QACA,CAAC,CAAC,EAAE,CAAC;IAIP,MAAM,iBAAiB,GAAG,aAAa;QACrC,CAAC,CAAC;;;;;;;;;;;;;IAaF;QACA,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,qBAAqB,GAAG;;;4EAG4C,CAAC;IAE3E,MAAM,yBAAyB,GAAG,aAAa;QAC7C,CAAC,CAAC;;;;;;;;;;;;;;;MAeA;QACF,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;IAc1B,CAAC;IAEH,MAAM,aAAa,GAAG;;;;;;;IAOpB,CAAC;IAEH,OAAO;;;;;;yEAMgE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0D1E,WAAW;EACX,cAAc,GAAG,gBAAgB;kCACD,CAAC,CAAC,EAAE,EAAE,2BAA2B,CAAC;gCACpC,CAAC,CAAC,EAAE,EAAE,mIAAmI,CAAC;0BAChJ,oBAAoB;;;sCAGR,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,qCAAqC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,kCAAkC,CAAC;;;;;;;;;;;;EAYrL,aAAa;;;;;;;;;;;;;;;;;;;;;EAqBb,SAAS;;;;;;sBAMW,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC;EACxC,oBAAoB;;;;;;EAMpB,iBAAiB;;uBAEI,CAAC,CAAC,EAAE,EAAE,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;;;mBAsB9B,CAAC,CAAC,EAAE,EAAE,2HAA2H,CAAC,8CAA8C,EAAE,CAAC,EAAE,EAAE,0CAA0C,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAgCrO,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC;;;;;;EAM/B,qBAAqB;;;;EAIrB,yBAAyB;;;;;EAKzB,mBAAmB;;;;;;;qCAOgB,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,qCAAqC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;CAKtI,CAAC;AACF,CAAC"}