@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.
- package/README.md +28 -32
- package/dist/index.js +2 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +3 -8
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +29 -87
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +16 -30
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +31 -6
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/init.js +188 -11
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +2 -4
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/loader.js +34 -25
- package/dist/lib/config/loader.js.map +1 -1
- package/dist/lib/config/validator.js +202 -96
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/api-config.js +1 -1
- package/dist/lib/generators/file-generators/api-config.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-adapter.js +13 -0
- package/dist/lib/generators/file-generators/auth-adapter.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-check.js +40 -0
- package/dist/lib/generators/file-generators/auth-check.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-store.js +2 -5
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +1 -1
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +2 -1
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js +15 -0
- package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -0
- package/dist/lib/generators/file-generators/client-injector.js +2 -1
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/connectivity-manager.js +12 -0
- package/dist/lib/generators/file-generators/connectivity-manager.js.map +1 -0
- package/dist/lib/generators/file-generators/connectivity.js +12 -0
- package/dist/lib/generators/file-generators/connectivity.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -0
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +29 -20
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
- package/dist/lib/generators/file-generators/gql-wrapper.js +2 -2
- package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +51 -1
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
- package/dist/lib/generators/file-generators/invalidation-tags.js +2 -3
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +2 -2
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
- package/dist/lib/generators/file-generators/notification-generator.js +6 -0
- package/dist/lib/generators/file-generators/notification-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/open-db.js +6 -0
- package/dist/lib/generators/file-generators/open-db.js.map +1 -0
- package/dist/lib/generators/file-generators/push.js +1 -2
- package/dist/lib/generators/file-generators/push.js.map +1 -1
- package/dist/lib/generators/file-generators/pwa-install.js +2 -6
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/server-push.js +1 -1
- package/dist/lib/generators/file-generators/server-push.js.map +1 -1
- package/dist/lib/generators/file-generators/storage-generator.js +6 -0
- package/dist/lib/generators/file-generators/storage-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/storage-notify.js +6 -0
- package/dist/lib/generators/file-generators/storage-notify.js.map +1 -0
- package/dist/lib/generators/file-generators/storage.js +6 -0
- package/dist/lib/generators/file-generators/storage.js.map +1 -0
- package/dist/lib/generators/file-generators/sw-generator-build.js +96 -45
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +0 -4
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +8 -9
- package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-version-gen.js +32 -0
- package/dist/lib/generators/file-generators/sw-version-gen.js.map +1 -0
- package/dist/lib/generators/file-generators/swoff-api-bundle.js +51 -0
- package/dist/lib/generators/file-generators/swoff-api-bundle.js.map +1 -0
- package/dist/lib/generators/sw-build-utils.js +69 -0
- package/dist/lib/generators/sw-build-utils.js.map +1 -0
- package/dist/lib/generators/sw-generator.js +23 -34
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +74 -23
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +15 -94
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-sync-handler.js +74 -72
- package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js +102 -0
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -0
- package/dist/lib/generators/sw-sections/config-header.js +2 -3
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/default-template.js +59 -6
- package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +621 -495
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/install-handler.js +40 -23
- package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/message-handler.js +25 -8
- package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/server-push-handler.js +10 -14
- package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/shared.js +68 -0
- package/dist/lib/generators/sw-sections/shared.js.map +1 -0
- package/dist/lib/generators/sw-sections/sw-push.js +2 -6
- package/dist/lib/generators/sw-sections/sw-push.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +55 -86
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +183 -59
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +67 -63
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/shared/feature-registry.js +198 -0
- package/dist/lib/shared/feature-registry.js.map +1 -0
- package/dist/lib/utils/detect-framework.js +52 -9
- package/dist/lib/utils/detect-framework.js.map +1 -1
- package/dist/runtime/api-config.js +2 -2
- package/dist/runtime/api-config.js.map +1 -1
- package/dist/runtime/auth-adapter.js +179 -0
- package/dist/runtime/auth-adapter.js.map +1 -0
- package/dist/runtime/auth-state.js +16 -6
- package/dist/runtime/auth-state.js.map +1 -1
- package/dist/runtime/auth-store.js +219 -213
- package/dist/runtime/auth-store.js.map +1 -1
- package/dist/runtime/auth-user.js +5 -19
- package/dist/runtime/auth-user.js.map +1 -1
- package/dist/runtime/background-sync.js +5 -2
- package/dist/runtime/background-sync.js.map +1 -1
- package/dist/runtime/cache.js +91 -3
- package/dist/runtime/cache.js.map +1 -1
- package/dist/runtime/client-injector-bundle.js +314 -0
- package/dist/runtime/client-injector-bundle.js.map +1 -0
- package/dist/runtime/client-injector.js +173 -30
- package/dist/runtime/client-injector.js.map +1 -1
- package/dist/runtime/connectivity-manager.js +83 -0
- package/dist/runtime/connectivity-manager.js.map +1 -0
- package/dist/runtime/connectivity.js +83 -0
- package/dist/runtime/connectivity.js.map +1 -0
- package/dist/runtime/fetch-wrapper.js +26 -59
- package/dist/runtime/fetch-wrapper.js.map +1 -1
- package/dist/runtime/gql-wrapper.js +15 -9
- package/dist/runtime/gql-wrapper.js.map +1 -1
- package/dist/runtime/invalidation-tags.js +12 -89
- package/dist/runtime/invalidation-tags.js.map +1 -1
- package/dist/runtime/mutation-queue.js +83 -31
- package/dist/runtime/mutation-queue.js.map +1 -1
- package/dist/runtime/mutation-state.js +3 -3
- package/dist/runtime/mutation-state.js.map +1 -1
- package/dist/runtime/notification.js +65 -0
- package/dist/runtime/notification.js.map +1 -0
- package/dist/runtime/open-db.js +41 -0
- package/dist/runtime/open-db.js.map +1 -0
- package/dist/runtime/push.js +15 -27
- package/dist/runtime/push.js.map +1 -1
- package/dist/runtime/pwa-injector.js +3 -32
- package/dist/runtime/pwa-injector.js.map +1 -1
- package/dist/runtime/pwa-prompt.js +35 -3
- package/dist/runtime/pwa-prompt.js.map +1 -1
- package/dist/runtime/reset.js +39 -42
- package/dist/runtime/reset.js.map +1 -1
- package/dist/runtime/server-push.js +23 -13
- package/dist/runtime/server-push.js.map +1 -1
- package/dist/runtime/storage.js +32 -0
- package/dist/runtime/storage.js.map +1 -0
- package/dist/runtime/sw-injector.js +19 -178
- package/dist/runtime/sw-injector.js.map +1 -1
- package/dist/runtime/sw-version.js +13 -0
- package/dist/runtime/sw-version.js.map +1 -0
- package/dist/runtime/swoff-api-bundle.js +1491 -0
- package/dist/runtime/swoff-api-bundle.js.map +1 -0
- package/dist/runtime/type-definitions.js +8 -7
- package/dist/runtime/type-definitions.js.map +1 -1
- package/dist/runtime/utils.js +4 -0
- package/dist/runtime/utils.js.map +1 -1
- package/dist/swoff-nkdhhmw8. +18 -0
- package/dist/swoff.js +5 -0
- package/package.json +1 -1
- package/templates/react/useAuth.jsx +7 -4
- package/templates/react/useAuth.tsx +14 -14
- package/templates/react/useBackgroundSync.jsx +5 -4
- package/templates/react/useBackgroundSync.tsx +5 -4
- package/templates/react/useCachedFetch.jsx +8 -0
- package/templates/react/useCachedFetch.tsx +8 -0
- package/templates/react/useMutation.jsx +60 -56
- package/templates/react/useMutation.tsx +65 -64
- package/templates/react/useMutationQueue.jsx +5 -1
- package/templates/react/useMutationQueue.tsx +8 -2
- package/templates/react/useMutationState.jsx +3 -4
- package/templates/react/useMutationState.tsx +4 -8
- package/templates/react/useNetworkStatus.jsx +57 -17
- package/templates/react/useNetworkStatus.tsx +42 -20
- package/templates/react/useOfflineAnalytics.jsx +42 -0
- package/templates/react/useOfflineAnalytics.tsx +50 -0
- package/templates/react/usePwaInstall.jsx +26 -0
- package/templates/react/usePwaInstall.tsx +26 -0
- package/templates/react/useSWUpdate.jsx +7 -52
- package/templates/react/useSWUpdate.tsx +12 -51
- package/templates/react/useStorageEstimate.jsx +56 -0
- package/templates/react/useStorageEstimate.tsx +67 -0
- package/dist/__tests__/assemble-sw.test.js +0 -145
- package/dist/__tests__/assemble-sw.test.js.map +0 -1
- package/dist/__tests__/cli-integration.test.js +0 -231
- package/dist/__tests__/cli-integration.test.js.map +0 -1
- package/dist/__tests__/config-types.test.js +0 -124
- package/dist/__tests__/config-types.test.js.map +0 -1
- package/dist/__tests__/detect-language.test.js +0 -51
- package/dist/__tests__/detect-language.test.js.map +0 -1
- package/dist/__tests__/file-generators.test.js +0 -228
- package/dist/__tests__/file-generators.test.js.map +0 -1
- package/dist/__tests__/glob-matcher.test.js +0 -86
- package/dist/__tests__/glob-matcher.test.js.map +0 -1
- package/dist/__tests__/invalidation-tags.test.js +0 -283
- package/dist/__tests__/invalidation-tags.test.js.map +0 -1
- package/dist/__tests__/loader.test.js +0 -77
- package/dist/__tests__/loader.test.js.map +0 -1
- package/dist/__tests__/validator.test.js +0 -340
- package/dist/__tests__/validator.test.js.map +0 -1
- package/templates/react/useCacheInvalidation.jsx +0 -19
- package/templates/react/useCacheInvalidation.tsx +0 -52
- package/templates/react/useIsFetching.jsx +0 -32
- package/templates/react/useIsFetching.tsx +0 -32
|
@@ -1,42 +1,93 @@
|
|
|
1
|
-
export function generateActivateHandler(
|
|
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 =
|
|
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
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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,
|
|
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 {
|
|
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 {
|
|
7
|
-
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
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":"
|
|
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,
|
|
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
|
|
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 = ${
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const
|
|
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
|
-
|
|
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
|
|
118
|
+
for (const tag of item.tags) {
|
|
109
119
|
tagsToInvalidate.add(tag);
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
}
|
|
121
|
+
if (typeof invalidateByTag !== "undefined") {
|
|
122
|
+
await Promise.all(item.tags.map((tag) => invalidateByTag(tag)));
|
|
123
|
+
}
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
|
|
126
|
+
toRemove.push(item.id);
|
|
115
127
|
succeeded++;
|
|
116
128
|
} catch {
|
|
117
129
|
item.retryCount++;
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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"}
|