@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,42 +1,93 @@
1
- export function generateActivateHandler(clearRuntimeOnUpdate, navigationPreload, maxRuntimeCacheAge) {
2
- const cacheCleanup = clearRuntimeOnUpdate
3
- ? `keys.filter((key) => key !== CACHE_NAME)`
4
- : `keys.filter((key) => key !== CACHE_NAME && key !== CACHE_NAME_RUNTIME)`;
1
+ export function generateActivateHandler(navigationPreload, maxRuntimeCacheAge) {
5
2
  const navPreloadCode = navigationPreload ? `
6
3
  if (self.registration.navigationPreload) {
7
4
  await self.registration.navigationPreload.enable();
8
5
  }` : "";
9
6
  const evictionCode = maxRuntimeCacheAge && maxRuntimeCacheAge > 0 ? `
10
7
  await evictStaleRuntimeCache();` : "";
11
- const evictionHelper = maxRuntimeCacheAge && maxRuntimeCacheAge > 0 ? `
12
- const MAX_RUNTIME_CACHE_AGE = ${maxRuntimeCacheAge};
8
+ const evictionHelper = `
9
+ const MAX_RUNTIME_CACHE_AGE = ${maxRuntimeCacheAge && maxRuntimeCacheAge > 0 ? maxRuntimeCacheAge : Infinity};
13
10
 
11
+ ${maxRuntimeCacheAge && maxRuntimeCacheAge > 0 ? `
14
12
  async function evictStaleRuntimeCache() {
15
- const cache = await caches.open(CACHE_NAME_RUNTIME);
16
- const keys = await cache.keys();
17
- const cutoff = Date.now() - MAX_RUNTIME_CACHE_AGE * 1000;
18
- const promises = [];
19
- for (const request of keys) {
20
- promises.push((async () => {
21
- const response = await cache.match(request);
22
- const cachedAt = response?.headers.get("X-SW-Cached-At");
23
- if (cachedAt && Number(cachedAt) < cutoff) {
24
- await cache.delete(request);
13
+ var cutoff = Date.now() - MAX_RUNTIME_CACHE_AGE * 1000;
14
+ for (const name of ["swoff-runtime", "swoff-runtime-html"]) {
15
+ const cache = await caches.open(name);
16
+ const keys = await cache.keys();
17
+ const promises = [];
18
+ for (const request of keys) {
19
+ promises.push((async () => {
20
+ const response = await cache.match(request);
21
+ const cachedAt = response?.headers.get("X-SW-Cached-At");
22
+ if (cachedAt && Number(cachedAt) < cutoff) {
23
+ await cache.delete(request);
24
+ }
25
+ })());
26
+ }
27
+ try {
28
+ await Promise.all(promises);
29
+ } catch {}
30
+ // Clean orphaned tag DB records
31
+ try {
32
+ const db = await openDB("swoff-cache-tags", 1, function(db) {
33
+ if (!db.objectStoreNames.contains("tags")) {
34
+ db.createObjectStore("tags", { keyPath: "url" });
35
+ }
36
+ });
37
+ try {
38
+ await pruneStaleStore(db, "tags", cutoff);
39
+ } finally {
40
+ db.close();
25
41
  }
26
- })());
42
+ } catch {}
27
43
  }
28
- await Promise.all(promises);
29
44
  }
30
- ` : "";
45
+ ` : ""}`;
31
46
  return `${evictionHelper}
47
+ async function checkCacheVersion() {
48
+ const current = CACHE_NAME;
49
+ let prev = null;
50
+ try {
51
+ const db = await openDB("swoff-meta", 1, function(db) {
52
+ if (!db.objectStoreNames.contains("meta"))
53
+ db.createObjectStore("meta", { keyPath: "key" });
54
+ });
55
+ const tx = db.transaction("meta", "readonly");
56
+ const store = tx.objectStore("meta");
57
+ const entry = await new Promise(function(resolve, reject) {
58
+ const req = store.get("cacheName");
59
+ req.onsuccess = function() { resolve(req.result); };
60
+ req.onerror = function() { reject(req.error); };
61
+ });
62
+ prev = entry ? entry.value : null;
63
+ db.close();
64
+ } catch {}
65
+
66
+ if (prev !== null && prev !== current) {
67
+ await Promise.all(
68
+ ["precache", "swoff-runtime", "swoff-runtime-html"].map(function(n) { return caches.delete(n); })
69
+ );
70
+ }
71
+
72
+ try {
73
+ const db = await openDB("swoff-meta", 1);
74
+ const tx = db.transaction("meta", "readwrite");
75
+ const store = tx.objectStore("meta");
76
+ store.put({ key: "cacheName", value: current });
77
+ await new Promise(function(resolve, reject) {
78
+ tx.oncomplete = function() { resolve(); };
79
+ tx.onerror = function() { reject(tx.error); };
80
+ });
81
+ db.close();
82
+ } catch {}
83
+ }
84
+
32
85
  self.addEventListener("activate", (event) => {
33
86
  event.waitUntil(
34
87
  (async () => {
88
+ if (typeof clearAllReactive === "function") clearAllReactive();
89
+ await checkCacheVersion();
35
90
  await self.clients.claim();${navPreloadCode}${evictionCode}
36
- const keys = await caches.keys();
37
- await Promise.all(
38
- ${cacheCleanup}.map((key) => caches.delete(key))
39
- );
40
91
  })()
41
92
  );
42
93
  });`;
@@ -1 +1 @@
1
- {"version":3,"file":"activate-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/activate-handler.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,uBAAuB,CAAC,oBAA6B,EAAE,iBAA2B,EAAE,kBAA2B;IAC7H,MAAM,YAAY,GAAG,oBAAoB;QACvC,CAAC,CAAC,0CAA0C;QAC5C,CAAC,CAAC,wEAAwE,CAAC;IAE7E,MAAM,cAAc,GAAG,iBAAiB,CAAC,CAAC,CAAC;;;QAGrC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEZ,MAAM,YAAY,GAAG,kBAAkB,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC;sCAChC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1C,MAAM,cAAc,GAAG,kBAAkB,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC;gCACxC,kBAAkB;;;;;;;;;;;;;;;;;;CAkBjD,CAAC,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,GAAG,cAAc;;;;mCAIS,cAAc,GAAG,YAAY;;;UAGtD,YAAY;;;;IAIlB,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"activate-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/activate-handler.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,uBAAuB,CAAC,iBAA2B,EAAE,kBAA2B;IAC9F,MAAM,cAAc,GAAG,iBAAiB,CAAC,CAAC,CAAC;;;QAGrC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEZ,MAAM,YAAY,GAAG,kBAAkB,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC;sCAChC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1C,MAAM,cAAc,GAAG;gCACO,kBAAkB,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ;;EAE1G,kBAAkB,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkChD,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAEP,OAAO,GAAG,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCA4CS,cAAc,GAAG,YAAY;;;IAG5D,CAAC;AACL,CAAC"}
@@ -1,102 +1,23 @@
1
- import { readdirSync, existsSync } from "fs";
2
- import { join, relative } from "path";
3
- import { createHash } from "crypto";
1
+ import { buildFallbackList, scanPrecacheAssets } from "../sw-build-utils.js";
4
2
  import { getDefaultTemplate } from "./default-template.js";
5
3
  import { generateConfigHeader } from "./config-header.js";
6
- import { generateInstallHandler } from "./install-handler.js";
7
- import { generateActivateHandler } from "./activate-handler.js";
8
- import { generateMessageHandler } from "./message-handler.js";
9
- import { generateFetchHandler } from "./fetch-handler.js";
10
- import { generateTagManagement } from "./tag-management.js";
11
- import { generateBackgroundSyncHandler } from "./background-sync-handler.js";
12
- import { generateSwPushHandlers } from "./sw-push.js";
13
- import { generateServerPushHandler } from "./server-push-handler.js";
14
- function collectAssets(dir, baseDir) {
15
- if (!existsSync(dir))
16
- return [];
17
- const entries = readdirSync(dir, { withFileTypes: true });
18
- const assets = [];
19
- for (const entry of entries) {
20
- const fullPath = join(dir, entry.name);
21
- if (entry.isDirectory()) {
22
- assets.push(...collectAssets(fullPath, baseDir));
23
- }
24
- else {
25
- assets.push("/" + relative(baseDir, fullPath));
26
- }
27
- }
28
- return assets;
29
- }
30
- function generateCacheNameFromHash(swContent) {
31
- const hash = createHash("sha256").update(swContent).digest("hex").slice(0, 12);
32
- return `sw-cache-${hash}`;
33
- }
34
- function applyReplacements(sw, config, assetsToCache) {
4
+ import { applySwSections, shouldIncludeBackgroundSync, generateBackgroundSyncCode } from "./shared.js";
5
+ export function assembleSW(config, projectRoot, debug) {
35
6
  const { serviceWorker } = config.features;
36
- const { features } = config;
37
- sw = sw.replace("// [[ASSETS_LIST]]", `ASSETS_TO_CACHE = ${JSON.stringify(assetsToCache, null, 2)}`);
38
- sw = sw.replace("// [[AUTO_SKIP_WAITING]]", `const AUTO_SKIP_WAITING = ${serviceWorker.autoActivate};`);
39
- const { strategy, navigation } = serviceWorker;
40
- const { refetchQueue } = features;
41
- sw = sw.replace("// [[FETCH_HANDLER]]", generateFetchHandler({
42
- strategy,
43
- navigation,
44
- refetchQueue,
45
- }, true, features.mutationQueue.enabled));
46
- sw = sw.replace("// [[ACTIVATE_HANDLER]]", generateActivateHandler(strategy.clearRuntimeOnUpdate, navigation.preload, strategy.maxRuntimeCacheAge));
47
- sw = sw.replace("// [[INSTALL_HANDLER]]", generateInstallHandler());
48
- sw = sw.replace("// [[MESSAGE_HANDLER]]", generateMessageHandler(true, features.tagInvalidation.debounceMs ?? 0));
49
- sw = sw.replace("// [[TAG_MANAGEMENT]]", generateTagManagement());
50
- sw = features.pushNotifications?.enabled
51
- ? sw.replace("// [[PUSH_HANDLERS]]", generateSwPushHandlers())
52
- : sw.replace("// [[PUSH_HANDLERS]]", "");
53
- const baseUrl = config.apiBaseUrl || "";
54
- sw = features.serverPush?.enabled
55
- ? sw.replace("// [[SERVER_PUSH_HANDLER]]", generateServerPushHandler(features.serverPush.type, baseUrl + features.serverPush.endpoint, features.serverPush.reconnectDelayMs))
56
- : sw.replace("// [[SERVER_PUSH_HANDLER]]", "");
57
- return sw;
58
- }
59
- export function assembleSW(config, version, projectRoot) {
60
- const { serviceWorker } = config.features;
61
- const { features } = config;
62
- const outputDir = config.build?.outputDir || "dist";
63
- const swFilename = config.build?.swFilename || "sw";
64
- const versionEnabled = serviceWorker.version !== "hash";
65
- const fallback = ["/index.html"];
66
- if (features.pwa.enabled)
67
- fallback.push("/manifest.json");
68
- const scanned = [];
69
- if (projectRoot) {
70
- const distPath = join(projectRoot, outputDir);
71
- const allAssets = collectAssets(distPath, distPath);
72
- const swFile = versionEnabled ? `${swFilename}-v${version}.js` : `${swFilename}.js`;
73
- for (const a of allAssets) {
74
- if (a !== `/${swFile}` && a !== "/version.json")
75
- scanned.push(a);
76
- }
77
- }
78
- const assetsToCache = scanned.length > 0 ? [...new Set([...fallback, ...scanned])] : fallback;
7
+ const swFilename = config.build.swFilename;
8
+ const swFile = `${swFilename}.js`;
9
+ const fallback = buildFallbackList(config);
10
+ const scanned = projectRoot ? scanPrecacheAssets(config, projectRoot, swFile) : [];
11
+ const assetsToCache = [...new Set([...fallback, ...scanned])];
79
12
  const formattedAssets = assetsToCache.map((url) => ({ url, options: {} }));
80
13
  let sw = getDefaultTemplate();
81
- if (versionEnabled) {
82
- sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = 'sw-v${version}'`);
83
- sw = applyReplacements(sw, config, formattedAssets);
84
- sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
85
- }
86
- else {
87
- const sentinel = "SW_CACHE_SENTINEL";
88
- sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = '${sentinel}'`);
89
- sw = applyReplacements(sw, config, formattedAssets);
90
- const cacheName = generateCacheNameFromHash(sw);
91
- sw = sw.replace(sentinel, cacheName);
92
- sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
93
- }
94
- if (features.backgroundSync) {
95
- if (!features.auth.enabled || features.auth.type === "cookie") {
96
- const authType = features.auth.enabled ? features.auth.type : undefined;
97
- const mq = features.mutationQueue;
98
- sw += `\n\n${generateBackgroundSyncHandler(authType, mq.batchSize, mq.batchDelayMs, mq.maxRetries, mq.retryBackoffMs, true)}`;
99
- }
14
+ sw = applySwSections(sw, config, true, debug);
15
+ sw = sw.replace(/let ASSETS_TO_CACHE = \[\]/, () => `let ASSETS_TO_CACHE = ${JSON.stringify(formattedAssets, null, 2)};`);
16
+ sw = sw.replace(/let AUTO_SKIP_WAITING = (?:true|false)/, () => `let AUTO_SKIP_WAITING = ${serviceWorker.autoActivate};`);
17
+ sw += `\nconst CACHE_NAME = "${Date.now()}";\n`;
18
+ sw = `${generateConfigHeader(config)}\n\n${sw}`;
19
+ if (shouldIncludeBackgroundSync(config)) {
20
+ sw += generateBackgroundSyncCode(config);
100
21
  }
101
22
  return sw;
102
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"assemble-sw.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/assemble-sw.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"assemble-sw.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/assemble-sw.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAEvG,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,WAAoB,EAAE,KAAe;IACnF,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;IAE3C,MAAM,MAAM,GAAG,GAAG,UAAU,KAAK,CAAC;IAClC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnF,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAE3E,IAAI,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAE9B,EAAE,GAAG,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAE9C,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,4BAA4B,EAAE,GAAG,EAAE,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1H,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,wCAAwC,EAAE,GAAG,EAAE,CAAC,2BAA2B,aAAa,CAAC,YAAY,GAAG,CAAC,CAAC;IAC1H,EAAE,IAAI,yBAAyB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;IAChD,EAAE,GAAG,GAAG,oBAAoB,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;IAEhD,IAAI,2BAA2B,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,EAAE,IAAI,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -1,7 +1,8 @@
1
- export function generateBackgroundSyncHandler(authType, batchSize, batchDelayMs, maxRetries, retryBackoffMs, tagInvalidationEnabled) {
1
+ export function generateBackgroundSyncHandler(authType, batchSize, batchDelayMs, retryConfig, tagInvalidationEnabled, maxAge) {
2
2
  const DB_NAME = "swoff-queue";
3
3
  const STORE_NAME = "mutations";
4
- const credentialsLine = authType === "cookie"
4
+ const isCookie = authType ? authType === "cookie" : false;
5
+ const credentialsLine = isCookie
5
6
  ? ` credentials: "same-origin",`
6
7
  : "";
7
8
  return `
@@ -13,15 +14,13 @@ self.addEventListener("sync", (event) => {
13
14
 
14
15
  const SW_BATCH_SIZE = ${batchSize};
15
16
  const SW_BATCH_DELAY_MS = ${batchDelayMs};
16
- const SW_MAX_RETRIES = ${maxRetries};
17
- const SW_RETRY_BACKOFF_MS = ${retryBackoffMs};
17
+ const SW_MAX_RETRIES = ${retryConfig.maxRetries};
18
+ const SW_RETRY_BACKOFF_MS = ${retryConfig.backoffMs};
19
+ const SW_MAX_BACKOFF_MS = ${retryConfig.maxBackoffMs};
20
+ const SW_JITTER_MS = ${retryConfig.jitterMs};
18
21
  // Bump this when adding new indexes/stores for schema migration
19
22
  const SW_DB_VERSION = 1;
20
23
 
21
- function swSleep(ms) {
22
- return new Promise((r) => setTimeout(r, ms));
23
- }
24
-
25
24
  async function processMutationQueueInSW() {
26
25
  // If any client pages are open, skip entirely — the client always wins when open.
27
26
  // Only the SW processes the queue when all tabs are closed (background sync event).
@@ -34,44 +33,55 @@ async function processMutationQueueInSW() {
34
33
  let db;
35
34
 
36
35
  try {
37
- db = await new Promise((resolve, reject) => {
38
- const request = indexedDB.open("${DB_NAME}", SW_DB_VERSION);
39
- request.onupgradeneeded = (e) => {
40
- const idb = e.target.result;
41
- if (!idb.objectStoreNames.contains("${STORE_NAME}")) {
42
- const store = idb.createObjectStore("${STORE_NAME}", { keyPath: "id" });
43
- store.createIndex("by-timestamp", "timestamp");
44
- }
45
- };
46
- request.onsuccess = (e) => resolve(e.target.result);
47
- request.onerror = (e) => reject(e.target.error);
36
+ db = await openDB("${DB_NAME}", SW_DB_VERSION, function(db) {
37
+ if (!db.objectStoreNames.contains("${STORE_NAME}")) {
38
+ const store = db.createObjectStore("${STORE_NAME}", { keyPath: "id" });
39
+ store.createIndex("by-timestamp", "timestamp");
40
+ }
48
41
  });
49
42
 
43
+ // Collect all mutations to update/remove in a single batch at the end
44
+ const toRemove = [];
45
+ const toUpdate = [];
46
+
50
47
  const tx = db.transaction("${STORE_NAME}", "readonly");
51
48
  const store = tx.objectStore("${STORE_NAME}");
52
49
  const index = store.index("by-timestamp");
53
- const queue = await new Promise((resolve, reject) => {
54
- const request = index.getAll();
55
- request.onsuccess = () => resolve(request.result);
56
- request.onerror = () => reject(request.error);
57
- });
58
-
59
- const total = queue.length;
50
+ const queue = await new Promise(function(resolve, reject) {
51
+ var req = index.getAll();
52
+ req.onsuccess = function() { resolve(req.result); };
53
+ req.onerror = function() { reject(req.error); };
54
+ });${maxAge && maxAge > 0
55
+ ? `
56
+ const cutoff = Date.now() - MAX_RUNTIME_CACHE_AGE * 1000;
57
+ for (const item of queue) {
58
+ if (item.retryCount >= SW_MAX_RETRIES || (item.timestamp && item.timestamp < cutoff)) {
59
+ toRemove.push(item.id);
60
+ }
61
+ }`
62
+ : ""}
63
+ // Pre-filter: remove permanently failed items first, then filter for processable ones
64
+ const now = Date.now();
60
65
  for (const item of queue) {
61
66
  if (item.retryCount >= SW_MAX_RETRIES) {
62
- await removeFromSWQueue(db, item.id);
67
+ toRemove.push(item.id);
63
68
  failed++;
64
- continue;
65
- }
66
-
67
- // Skip items whose backoff delay hasn't elapsed yet
68
- if (item.nextRetryAt && Date.now() < item.nextRetryAt) {
69
- continue;
70
69
  }
70
+ }
71
+ const processable = queue.filter(item => {
72
+ if (item.retryCount >= SW_MAX_RETRIES) return false;
73
+ return !item.nextRetryAt || now >= item.nextRetryAt;
74
+ });
75
+ const total = processable.length;
71
76
 
77
+ for (const item of processable) {
72
78
  // Stop processing if browser went offline during sync
73
79
  if (!self.navigator.onLine) break;
74
80
 
81
+ // Stop processing if a client tab opened during sync
82
+ const openClients = await self.clients.matchAll();
83
+ if (openClients.length > 0) break;
84
+
75
85
  // Reconstruct request body based on stored bodyType
76
86
  let replayBody = null;
77
87
  let contentType;
@@ -105,68 +115,60 @@ ${credentialsLine} });
105
115
  if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
106
116
 
107
117
  if (item.tags) {
108
- item.tags.forEach((tag) => {
118
+ for (const tag of item.tags) {
109
119
  tagsToInvalidate.add(tag);
110
- if (typeof invalidateByTag !== "undefined") invalidateByTag(tag);
111
- });
120
+ }
121
+ if (typeof invalidateByTag !== "undefined") {
122
+ await Promise.all(item.tags.map((tag) => invalidateByTag(tag)));
123
+ }
112
124
  }
113
125
 
114
- await removeFromSWQueue(db, item.id);
126
+ toRemove.push(item.id);
115
127
  succeeded++;
116
128
  } catch {
117
129
  item.retryCount++;
118
- item.nextRetryAt = Date.now() + SW_RETRY_BACKOFF_MS * Math.pow(2, item.retryCount - 1);
119
- await updateInSWQueue(db, item);
130
+ if (item.retryCount >= SW_MAX_RETRIES) {
131
+ toRemove.push(item.id);
132
+ } else {
133
+ item.nextRetryAt = Date.now() + backoffDelay(item.retryCount - 1, { backoffMs: SW_RETRY_BACKOFF_MS, maxBackoffMs: SW_MAX_BACKOFF_MS, jitterMs: SW_JITTER_MS });
134
+ toUpdate.push(item);
135
+ }
120
136
  failed++;
121
137
  }
122
138
 
123
139
  // Rate limiting delay between mutations
124
140
  if (SW_BATCH_DELAY_MS > 0 && succeeded + failed < total) {
125
- await swSleep(SW_BATCH_DELAY_MS);
141
+ await sleep(SW_BATCH_DELAY_MS);
126
142
  }
127
143
 
128
144
  // Emit progress after every SW_BATCH_SIZE mutations
129
145
  if ((succeeded + failed) % SW_BATCH_SIZE === 0 || succeeded + failed === total) {
130
- const clients = await self.clients.matchAll();
131
- for (const client of clients) {
132
- client.postMessage({
133
- type: "BACKGROUND_SYNC_PROGRESS",
134
- detail: { succeeded, failed, total, current: succeeded + failed },
135
- });
136
- }
146
+ await broadcastToClients("BACKGROUND_SYNC_PROGRESS", { detail: { succeeded, failed, total, current: succeeded + failed } });
137
147
  }
138
148
  }
149
+
150
+ // Batch all writes in a single transaction
151
+ const writeTx = db.transaction("${STORE_NAME}", "readwrite");
152
+ const writeStore = writeTx.objectStore("${STORE_NAME}");
153
+ for (const id of toRemove) {
154
+ writeStore.delete(id);
155
+ }
156
+ for (const item of toUpdate) {
157
+ writeStore.put(item);
158
+ }
159
+ await new Promise(function(resolve, reject) {
160
+ writeTx.oncomplete = function() { resolve(); };
161
+ writeTx.onerror = function() { reject(writeTx.error); };
162
+ });
139
163
  } catch (err) {
140
164
  console.error("Background sync failed:", err);
165
+ broadcastToClients("SW_NOTIFICATION", { level: "error", code: "BACKGROUND_SYNC_FAILED", message: "Background sync processing failed" });
141
166
  } finally {
142
167
  if (db) db.close();
143
168
  }
144
169
 
145
- const clients = await self.clients.matchAll();
146
- for (const client of clients) {
147
- client.postMessage({
148
- type: "BACKGROUND_SYNC_COMPLETE",
149
- detail: { succeeded, failed, tags: [...tagsToInvalidate] },
150
- });
151
- }
152
- }
153
-
154
- async function removeFromSWQueue(db, id) {
155
- const tx = db.transaction("${STORE_NAME}", "readwrite");
156
- tx.objectStore("${STORE_NAME}").delete(id);
157
- await new Promise((resolve, reject) => {
158
- tx.oncomplete = () => resolve();
159
- tx.onerror = () => reject(tx.error);
160
- });
170
+ broadcastToClients("BACKGROUND_SYNC_COMPLETE", { detail: { succeeded, failed, tags: [...tagsToInvalidate] } });
161
171
  }
162
-
163
- async function updateInSWQueue(db, item) {
164
- const tx = db.transaction("${STORE_NAME}", "readwrite");
165
- tx.objectStore("${STORE_NAME}").put(item);
166
- await new Promise((resolve, reject) => {
167
- tx.oncomplete = () => resolve();
168
- tx.onerror = () => reject(tx.error);
169
- });
170
- }`;
172
+ `;
171
173
  }
172
174
  //# sourceMappingURL=background-sync-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"background-sync-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/background-sync-handler.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,6BAA6B,CAAC,QAA4B,EAAE,SAAiB,EAAE,YAAoB,EAAE,UAAkB,EAAE,cAAsB,EAAE,sBAA+B;IAC9L,MAAM,OAAO,GAAG,aAAa,CAAC;IAC9B,MAAM,UAAU,GAAG,WAAW,CAAC;IAE/B,MAAM,eAAe,GAAG,QAAQ,KAAK,QAAQ;QAC3C,CAAC,CAAC,uCAAuC;QACzC,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;wBAOe,SAAS;4BACL,YAAY;yBACf,UAAU;8BACL,cAAc;;;;;;;;;;;;;;;;;;;;;wCAqBJ,OAAO;;;8CAGD,UAAU;iDACP,UAAU;;;;;;;;iCAQ1B,UAAU;oCACP,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqD5C,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAmDc,UAAU;oBACrB,UAAU;;;;;;;;+BAQC,UAAU;oBACrB,UAAU;;;;;EAK5B,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"background-sync-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/background-sync-handler.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,6BAA6B,CAC3C,QAA4B,EAC5B,SAAiB,EACjB,YAAoB,EACpB,WAA8F,EAC9F,sBAA+B,EAC/B,MAAe;IAEf,MAAM,OAAO,GAAG,aAAa,CAAC;IAC9B,MAAM,UAAU,GAAG,WAAW,CAAC;IAE/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1D,MAAM,eAAe,GAAG,QAAQ;QAC9B,CAAC,CAAC,uCAAuC;QACzC,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;wBAOe,SAAS;4BACL,YAAY;yBACf,WAAW,CAAC,UAAU;8BACjB,WAAW,CAAC,SAAS;4BACvB,WAAW,CAAC,YAAY;uBAC7B,WAAW,CAAC,QAAQ;;;;;;;;;;;;;;;;yBAgBlB,OAAO;2CACW,UAAU;8CACP,UAAU;;;;;;;;;iCASvB,UAAU;oCACP,UAAU;;;;;;SAOxC,MAAM,IAAI,MAAM,GAAG,CAAC;QAClB,CAAC,CAAC;;;;;;MAMJ;QACE,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoDF,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCAqCqB,UAAU;8CACF,UAAU;;;;;;;;;;;;;;;;;;;;CAoBvD,CAAC;AACF,CAAC"}
@@ -0,0 +1,102 @@
1
+ export function generateBatchRefreshQueue(retryConfig, batchSize, batchDelayMs) {
2
+ const retryCode = JSON.stringify(retryConfig);
3
+ return `
4
+ // --- Refetch Retry Config ---
5
+
6
+ const REFETCH_RETRY = ${retryCode};
7
+
8
+ // --- Shared Backoff & Retry Helpers ---
9
+
10
+ function backoffDelay(attempt, config) {
11
+ var delay = Math.min(config.backoffMs * Math.pow(2, attempt), config.maxBackoffMs);
12
+ return delay + (config.jitterMs > 0 ? Math.random() * config.jitterMs : 0);
13
+ }
14
+
15
+ function sleep(ms) {
16
+ return new Promise(function(resolve) { setTimeout(resolve, ms); });
17
+ }
18
+
19
+ async function fetchWithRetry(request, retryConfig) {
20
+ swLog("fetchWithRetry", "ENTER", request.url, 3);
21
+ if (!retryConfig) return fetch(request);
22
+ for (var attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
23
+ try {
24
+ var controller = new AbortController();
25
+ var id = setTimeout(function() { controller.abort(); }, FETCH_TIMEOUT_MS);
26
+ var response = await fetch(request, { signal: controller.signal });
27
+ clearTimeout(id);
28
+ if (response.ok) {
29
+ swLog("fetchWithRetry", "SUCCESS attempt=" + attempt, request.url, 3);
30
+ return response;
31
+ }
32
+ if (response.status >= 400 && response.status < 500) {
33
+ swLog("fetchWithRetry", "4xx skip retry status=" + response.status, request.url, 3);
34
+ return response;
35
+ }
36
+ } catch {}
37
+ swLog("fetchWithRetry", "RETRY attempt=" + attempt, request.url, 3);
38
+ if (attempt < retryConfig.maxRetries) {
39
+ await sleep(backoffDelay(attempt, retryConfig));
40
+ }
41
+ }
42
+ swLog("fetchWithRetry", "EXHAUSTED", request.url, 3);
43
+ return null;
44
+ }
45
+
46
+ // --- Batch Refresh Queue ---
47
+
48
+ var REFRESH_QUEUE = new Map();
49
+ var REFRESH_TIMER = null;
50
+ var REFRESH_RESOLVERS = new Map();
51
+
52
+ /**
53
+ * Queue a URL for background refresh. Deduplicates by cache key.
54
+ * URLs are processed in batches with rate limiting.
55
+ * Returns a promise that resolves when the URL has been refreshed.
56
+ */
57
+ function queueRefresh(url) {
58
+ REFRESH_QUEUE.set(url, true);
59
+ if (!REFRESH_TIMER) {
60
+ REFRESH_TIMER = setTimeout(processRefreshQueue, 50);
61
+ }
62
+ if (!REFRESH_RESOLVERS.has(url)) {
63
+ REFRESH_RESOLVERS.set(url, []);
64
+ }
65
+ var resolvers = REFRESH_RESOLVERS.get(url);
66
+ return new Promise(function(resolve) {
67
+ resolvers.push(resolve);
68
+ });
69
+ }
70
+
71
+ async function processRefreshQueue() {
72
+ if (REFRESH_TIMER) {
73
+ clearTimeout(REFRESH_TIMER);
74
+ REFRESH_TIMER = null;
75
+ }
76
+ if (REFRESH_QUEUE.size === 0) return;
77
+ var urls = [...REFRESH_QUEUE.keys()];
78
+ REFRESH_QUEUE.clear();
79
+ var batch = [];
80
+ for (var i = 0; i < urls.length; i++) {
81
+ batch.push(urls[i]);
82
+ if (batch.length >= ${batchSize} || i === urls.length - 1) {
83
+ await Promise.all(batch.map(async function(url) {
84
+ try {
85
+ await refetchEntry(url);
86
+ } catch {}
87
+ var resolvers = REFRESH_RESOLVERS.get(url);
88
+ if (resolvers) {
89
+ REFRESH_RESOLVERS.delete(url);
90
+ resolvers.forEach(function(r) { r(); });
91
+ }
92
+ }));
93
+ batch = [];
94
+ if (i < urls.length - 1 && ${batchDelayMs} > 0) {
95
+ await sleep(${batchDelayMs});
96
+ }
97
+ }
98
+ }
99
+ }
100
+ `;
101
+ }
102
+ //# sourceMappingURL=batch-refresh-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-refresh-queue.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/batch-refresh-queue.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,yBAAyB,CACvC,WAA8F,EAC9F,SAAiB,EACjB,YAAoB;IAEpB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAE9C,OAAO;;;wBAGe,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA4EP,SAAS;;;;;;;;;;;;mCAYA,YAAY;sBACzB,YAAY;;;;;CAKjC,CAAC;AACF,CAAC"}
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * Generates the SW file header comment with config metadata.
3
3
  */
4
- export function generateConfigHeader(config, resolvedVersion) {
4
+ export function generateConfigHeader(config) {
5
5
  return `/**
6
6
  * Swoff Service Worker - Auto-Generated
7
7
  * Generated from swoff.config.json
8
8
  * DO NOT EDIT MANUALLY
9
- * Version: ${resolvedVersion}
10
- * Features: version=${config.features.serviceWorker.version}, mutationQueue=${config.features.mutationQueue.enabled}, backgroundSync=${config.features.backgroundSync}, tagInvalidation=true
9
+ * Features: auth=${config.features.auth.enabled}, authType=${config.features.auth.type}, mutationQueue=${config.features.mutationQueue.enabled}, backgroundSync=${config.features.mutationQueue.backgroundSync}, pwa=${config.features.pwa.enabled}, graphql=${config.features.graphql.enabled}, tagInvalidation=${config.features.tagInvalidation.enabled}, push=${config.features.pushNotifications}, serverPush=${config.features.serverPush?.enabled}
11
10
  * Default Strategy: ${config.features.serviceWorker.strategy.default}
12
11
  * See: https://swoff.netlify.app/docs
13
12
  */`;
@@ -1 +1 @@
1
- {"version":3,"file":"config-header.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/config-header.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,oBAAoB,CAAC,MAAmB,EAAE,eAAuB;IAC/E,OAAO;;;;cAIK,eAAe;uBACN,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,mBAAmB,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,oBAAoB,MAAM,CAAC,QAAQ,CAAC,cAAc;uBAC/I,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO;;IAEjE,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"config-header.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/config-header.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,oBAAoB,CAAC,MAAmB;IACtD,OAAO;;;;oBAIW,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,cAAc,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,mBAAmB,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,oBAAoB,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,cAAc,SAAS,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,aAAa,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,qBAAqB,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAO,UAAU,MAAM,CAAC,QAAQ,CAAC,iBAAiB,gBAAgB,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO;uBACla,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO;;IAEjE,CAAC;AACL,CAAC"}