@swoff/cli 0.0.2 â 0.1.0
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/bin/swoff +0 -0
- package/dist/index.js +263 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +392 -58
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +1137 -810
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/package.json +2 -1
- package/src/lib/templates/sw-template.js +23 -6
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Can be run as CLI or imported as a module.
|
|
6
6
|
*
|
|
7
7
|
* CLI Usage:
|
|
8
|
-
* node sw-generator.js [--project-root <path>] [--package-dir <path>]
|
|
8
|
+
* node sw-generator.js [--project-root <path>] [--package-dir <path>] [--config-path <path>]
|
|
9
9
|
*
|
|
10
10
|
* Module Usage:
|
|
11
11
|
* import { generate } from './sw-generator.js';
|
|
@@ -17,8 +17,10 @@ import { join, dirname } from "path";
|
|
|
17
17
|
const args = process.argv.slice(2);
|
|
18
18
|
const projectRootArg = args.findIndex((arg) => arg === "--project-root");
|
|
19
19
|
const packageDirArg = args.findIndex((arg) => arg === "--package-dir");
|
|
20
|
+
const configPathArg = args.findIndex((arg) => arg === "--config-path");
|
|
20
21
|
const passedProjectRoot = projectRootArg !== -1 ? args[projectRootArg + 1] : null;
|
|
21
22
|
const passedPackageDir = packageDirArg !== -1 ? args[packageDirArg + 1] : null;
|
|
23
|
+
const passedConfigPath = configPathArg !== -1 ? args[configPathArg + 1] : null;
|
|
22
24
|
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
23
25
|
const packageDir = passedPackageDir || join(scriptDir, "..");
|
|
24
26
|
const projectRoot = passedProjectRoot || process.cwd();
|
|
@@ -27,6 +29,7 @@ const defaultConfig = {
|
|
|
27
29
|
version: "from-package",
|
|
28
30
|
minSupportedVersion: "0.0.0",
|
|
29
31
|
serviceWorker: {
|
|
32
|
+
autoRegister: true,
|
|
30
33
|
autoUpdate: false,
|
|
31
34
|
defaultStrategy: "cache-first",
|
|
32
35
|
strategies: {},
|
|
@@ -40,6 +43,8 @@ const defaultConfig = {
|
|
|
40
43
|
auth: false,
|
|
41
44
|
crossTabSync: true,
|
|
42
45
|
tagInvalidation: true,
|
|
46
|
+
clientRegistration: true,
|
|
47
|
+
indexeddb: false,
|
|
43
48
|
},
|
|
44
49
|
build: {
|
|
45
50
|
outputDir: "dist",
|
|
@@ -49,6 +54,7 @@ const defaultConfig = {
|
|
|
49
54
|
export async function generate(options = {}) {
|
|
50
55
|
const optProjectRoot = options.projectRoot || projectRoot;
|
|
51
56
|
const optPackageDir = options.packageDir || packageDir;
|
|
57
|
+
const optConfigPath = options.configPath || passedConfigPath;
|
|
52
58
|
const pkgPath = join(optProjectRoot, "package.json");
|
|
53
59
|
let pkg = { version: "1.0.0" };
|
|
54
60
|
if (existsSync(pkgPath)) {
|
|
@@ -69,10 +75,10 @@ export async function generate(options = {}) {
|
|
|
69
75
|
}
|
|
70
76
|
let userConfig = {};
|
|
71
77
|
let configSource = "defaults";
|
|
72
|
-
const
|
|
73
|
-
if (existsSync(
|
|
78
|
+
const resolvedConfigPath = optConfigPath || join(optProjectRoot, "swoff.config.json");
|
|
79
|
+
if (existsSync(resolvedConfigPath)) {
|
|
74
80
|
try {
|
|
75
|
-
userConfig = JSON.parse(readFileSync(
|
|
81
|
+
userConfig = JSON.parse(readFileSync(resolvedConfigPath, "utf8"));
|
|
76
82
|
configSource = "JSON";
|
|
77
83
|
}
|
|
78
84
|
catch {
|
|
@@ -113,10 +119,10 @@ export async function generate(options = {}) {
|
|
|
113
119
|
configEnabled: config.enabled,
|
|
114
120
|
configSource: configSource,
|
|
115
121
|
}, null, 2));
|
|
116
|
-
console.log(
|
|
117
|
-
console.log(
|
|
118
|
-
console.log(
|
|
119
|
-
console.log(
|
|
122
|
+
console.log(`Service worker generated successfully.`);
|
|
123
|
+
console.log(`Output: ${outputDir}/${swFilename}-v${version}.js`);
|
|
124
|
+
console.log(`Version info: ${outputDir}/version.json`);
|
|
125
|
+
console.log(`Configuration source: ${configSource}`);
|
|
120
126
|
}
|
|
121
127
|
catch (err) {
|
|
122
128
|
console.error(`Error writing files: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -131,81 +137,191 @@ function generateServiceWorker(config, version) {
|
|
|
131
137
|
const assetsToCache = [...baseAssets, ...pwaAssets];
|
|
132
138
|
let sw = getDefaultTemplate();
|
|
133
139
|
sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = 'sw-v${version}'`);
|
|
134
|
-
sw = sw.replace("// [[ASSETS_LIST]]", `ASSETS_TO_CACHE = ${JSON.stringify(assetsToCache, null, 2)}`);
|
|
140
|
+
sw = sw.replace("// [[ASSETS_LIST]]", `ASSETS_TO_CACHE = ${JSON.stringify(assetsToCache.map((url) => ({ url, options: {} })), null, 2)}`);
|
|
141
|
+
sw = sw.replace("// [[AUTO_SKIP_WAITING]]", `const AUTO_SKIP_WAITING = ${config.serviceWorker.autoUpdate};`);
|
|
135
142
|
sw = sw.replace("// [[FETCH_HANDLER]]", generateFetchHandler(serviceWorker, features));
|
|
136
143
|
sw = sw.replace("// [[ACTIVATE_HANDLER]]", generateActivateHandler(features.versionedSw));
|
|
137
144
|
sw = sw.replace("// [[INSTALL_HANDLER]]", generateInstallHandler(features));
|
|
145
|
+
sw = sw.replace("// [[MESSAGE_HANDLER]]", generateMessageHandler(features));
|
|
146
|
+
if (features.tagInvalidation) {
|
|
147
|
+
sw = sw.replace("// [[TAG_MANAGEMENT]]", generateTagManagement());
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
sw = sw.replace("// [[TAG_MANAGEMENT]]", "");
|
|
151
|
+
}
|
|
138
152
|
sw = `// [[CONFIG_HEADER]]\n\n${sw}`;
|
|
139
153
|
sw = sw.replace("// [[CONFIG_HEADER]]", generateConfigHeader(config));
|
|
154
|
+
if (features.backgroundSync) {
|
|
155
|
+
sw += `\n\n${generateBackgroundSyncHandler()}`;
|
|
156
|
+
}
|
|
140
157
|
return sw;
|
|
141
158
|
}
|
|
142
159
|
function generateFetchHandler(swConfig, features) {
|
|
143
160
|
const { defaultStrategy, strategies } = swConfig;
|
|
161
|
+
const tagInvalidationCode = features.tagInvalidation ? `
|
|
162
|
+
const tagsHeader = event.request.headers.get("X-SW-Cache-Tags");
|
|
163
|
+
if (tagsHeader) {
|
|
164
|
+
const url = new URL(event.request.url).href;
|
|
165
|
+
const tags = tagsHeader.split(",").map((t) => t.trim());
|
|
166
|
+
await cacheTagUrl(url, tags);
|
|
167
|
+
}` : "";
|
|
168
|
+
const staleTagCode = features.tagInvalidation ? `
|
|
169
|
+
const tagsHeader = request.headers.get("X-SW-Cache-Tags");
|
|
170
|
+
if (tagsHeader) {
|
|
171
|
+
const url = new URL(request.url).href;
|
|
172
|
+
const tags = tagsHeader.split(",").map((t) => t.trim());
|
|
173
|
+
await cacheTagUrl(url, tags);
|
|
174
|
+
}` : "";
|
|
144
175
|
return `
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
176
|
+
function isReadRequest(request) {
|
|
177
|
+
const strategy = request.headers.get("X-SW-Cache-Strategy");
|
|
178
|
+
if (strategy === "read") return true;
|
|
179
|
+
if (strategy === "mutation") return false;
|
|
180
|
+
return request.method === "GET" || request.method === "HEAD";
|
|
181
|
+
}
|
|
152
182
|
|
|
153
|
-
function determineCacheStrategy(
|
|
183
|
+
function determineCacheStrategy(request, customStrategies, defaultStrategy) {
|
|
184
|
+
const url = request.url;
|
|
154
185
|
for (const [pattern, strategy] of Object.entries(customStrategies)) {
|
|
155
|
-
if (url.includes(pattern)) return strategy;
|
|
186
|
+
if (url.includes(pattern.replace("*", ""))) return strategy;
|
|
156
187
|
}
|
|
157
188
|
return defaultStrategy;
|
|
158
189
|
}
|
|
159
190
|
|
|
160
|
-
|
|
191
|
+
self.addEventListener("fetch", (event) => {
|
|
192
|
+
if (!isReadRequest(event.request)) return;
|
|
193
|
+
|
|
194
|
+
const strategy = determineCacheStrategy(event.request, ${JSON.stringify(strategies)}, "${defaultStrategy}");
|
|
195
|
+
|
|
196
|
+
if (strategy === "stale-while-revalidate" || event.request.headers.get("X-SW-Stale") === "true") {
|
|
197
|
+
event.respondWith(staleWhileRevalidate(event, event.request));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (strategy === "network-first") {
|
|
202
|
+
event.respondWith(networkFirst(event, event.request));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (strategy === "cache-only") {
|
|
207
|
+
event.respondWith(cacheOnly(event, event.request));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (strategy === "network-only") {
|
|
212
|
+
event.respondWith(networkOnly(event, event.request));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// cache-first (default)
|
|
217
|
+
event.respondWith(cacheFirst(event, event.request));
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
async function cacheFirst(event, request) {
|
|
161
221
|
const cache = await caches.open(CACHE_NAME);
|
|
222
|
+
const runtimeCache = await caches.open(CACHE_NAME_RUNTIME);
|
|
223
|
+
const url = new URL(request.url);
|
|
224
|
+
|
|
225
|
+
const byPath = await cache.match(url.pathname);
|
|
226
|
+
if (byPath) return byPath;
|
|
162
227
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
228
|
+
const byRequest = await runtimeCache.match(request);
|
|
229
|
+
if (byRequest) return byRequest;
|
|
230
|
+
|
|
231
|
+
if (request.mode === "navigate") {
|
|
232
|
+
const spa = await cache.match("/index.html");
|
|
233
|
+
if (spa) return spa;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const response = await fetch(request);
|
|
238
|
+
if (response.ok) {
|
|
239
|
+
const cloned = response.clone();
|
|
240
|
+
event.waitUntil(
|
|
241
|
+
(async () => {
|
|
242
|
+
await runtimeCache.put(request, cloned);${tagInvalidationCode}
|
|
243
|
+
})(),
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return response;
|
|
247
|
+
} catch {
|
|
248
|
+
return new Response("Offline: content not available", { status: 503 });
|
|
170
249
|
}
|
|
171
250
|
}
|
|
172
251
|
|
|
173
|
-
async function
|
|
174
|
-
const
|
|
175
|
-
|
|
252
|
+
async function networkFirst(event, request) {
|
|
253
|
+
const runtimeCache = await caches.open(CACHE_NAME_RUNTIME);
|
|
254
|
+
|
|
176
255
|
try {
|
|
177
256
|
const response = await fetch(request);
|
|
178
|
-
if (response.ok)
|
|
257
|
+
if (response.ok) {
|
|
258
|
+
const cloned = response.clone();
|
|
259
|
+
event.waitUntil(
|
|
260
|
+
(async () => {
|
|
261
|
+
await runtimeCache.put(request, cloned);${tagInvalidationCode}
|
|
262
|
+
})(),
|
|
263
|
+
);
|
|
264
|
+
}
|
|
179
265
|
return response;
|
|
180
266
|
} catch {
|
|
181
|
-
|
|
267
|
+
const cached = await runtimeCache.match(request);
|
|
268
|
+
if (cached) return cached;
|
|
269
|
+
|
|
270
|
+
if (request.mode === "navigate") {
|
|
271
|
+
const cache = await caches.open(CACHE_NAME);
|
|
272
|
+
const spa = await cache.match("/index.html");
|
|
273
|
+
if (spa) return spa;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return new Response("Offline: content not available", { status: 503 });
|
|
182
277
|
}
|
|
183
278
|
}
|
|
184
279
|
|
|
185
|
-
async function
|
|
280
|
+
async function staleWhileRevalidate(event, request) {
|
|
281
|
+
const runtimeCache = await caches.open(CACHE_NAME_RUNTIME);
|
|
282
|
+
const cached = await runtimeCache.match(request);
|
|
283
|
+
|
|
284
|
+
if (cached) {
|
|
285
|
+
event.waitUntil(refreshCache(runtimeCache, request));
|
|
286
|
+
return cached;
|
|
287
|
+
}
|
|
288
|
+
|
|
186
289
|
try {
|
|
187
290
|
const response = await fetch(request);
|
|
188
|
-
if (response.ok)
|
|
291
|
+
if (response.ok) {
|
|
292
|
+
await runtimeCache.put(request, response.clone());${staleTagCode}
|
|
293
|
+
}
|
|
189
294
|
return response;
|
|
190
295
|
} catch {
|
|
191
|
-
|
|
192
|
-
return cached || new Response("Offline", { status: 503 });
|
|
296
|
+
return new Response("Offline: content not available", { status: 503 });
|
|
193
297
|
}
|
|
194
298
|
}
|
|
195
299
|
|
|
196
|
-
async function
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (response.ok)
|
|
200
|
-
|
|
201
|
-
|
|
300
|
+
async function refreshCache(cache, request) {
|
|
301
|
+
try {
|
|
302
|
+
const response = await fetch(request);
|
|
303
|
+
if (response.ok) {
|
|
304
|
+
await cache.put(request, response.clone());
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
// Background refresh failed - stale cache remains usable
|
|
308
|
+
}
|
|
202
309
|
}
|
|
203
310
|
|
|
204
|
-
async function
|
|
205
|
-
|
|
311
|
+
async function cacheOnly(event, request) {
|
|
312
|
+
const cache = await caches.open(CACHE_NAME);
|
|
313
|
+
const runtimeCache = await caches.open(CACHE_NAME_RUNTIME);
|
|
314
|
+
|
|
315
|
+
const byPath = await cache.match(new URL(request.url).pathname);
|
|
316
|
+
if (byPath) return byPath;
|
|
317
|
+
|
|
318
|
+
const byRequest = await runtimeCache.match(request);
|
|
319
|
+
if (byRequest) return byRequest;
|
|
320
|
+
|
|
321
|
+
return new Response("Not in cache", { status: 404 });
|
|
206
322
|
}
|
|
207
323
|
|
|
208
|
-
async function
|
|
324
|
+
async function networkOnly(event, request) {
|
|
209
325
|
try {
|
|
210
326
|
return await fetch(request);
|
|
211
327
|
} catch {
|
|
@@ -214,40 +330,237 @@ async function networkOnlyStrategy(request, _cache) {
|
|
|
214
330
|
}`;
|
|
215
331
|
}
|
|
216
332
|
function generateActivateHandler(versionedSw) {
|
|
217
|
-
|
|
218
|
-
return `
|
|
333
|
+
return `
|
|
219
334
|
self.addEventListener("activate", (event) => {
|
|
220
335
|
event.waitUntil(
|
|
221
336
|
caches.keys().then((keys) =>
|
|
222
|
-
Promise.all(
|
|
337
|
+
Promise.all(
|
|
338
|
+
keys.filter((key) => key !== CACHE_NAME && key !== CACHE_NAME_RUNTIME).map((key) => caches.delete(key))
|
|
339
|
+
)
|
|
223
340
|
)
|
|
224
341
|
);
|
|
225
|
-
});`;
|
|
226
|
-
}
|
|
227
|
-
return `
|
|
228
|
-
self.addEventListener("activate", (event) => {
|
|
229
|
-
event.waitUntil(Promise.resolve());
|
|
230
342
|
});`;
|
|
231
343
|
}
|
|
232
344
|
function generateInstallHandler(features) {
|
|
233
|
-
|
|
345
|
+
return `
|
|
234
346
|
self.addEventListener("install", (event) => {
|
|
235
347
|
event.waitUntil(
|
|
236
|
-
|
|
348
|
+
(async () => {
|
|
349
|
+
const cache = await caches.open(CACHE_NAME);
|
|
350
|
+
let downloaded = 0;
|
|
351
|
+
for (const asset of ASSETS_TO_CACHE) {
|
|
352
|
+
try {
|
|
353
|
+
const request = new Request(asset.url, asset.options);
|
|
354
|
+
await cache.add(request);
|
|
355
|
+
downloaded++;
|
|
356
|
+
const percent = Math.round((downloaded / ASSETS_TO_CACHE.length) * 100);
|
|
357
|
+
const clients = await self.clients.matchAll({ includeUncontrolled: true });
|
|
358
|
+
clients.forEach((client) => {
|
|
359
|
+
client.postMessage({
|
|
360
|
+
type: "SW_PROGRESS",
|
|
361
|
+
percent,
|
|
362
|
+
downloaded,
|
|
363
|
+
total: ASSETS_TO_CACHE.length,
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
} catch (err) {
|
|
367
|
+
console.error(\`Failed to cache \${asset.url}:\`, err);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (AUTO_SKIP_WAITING) self.skipWaiting();
|
|
371
|
+
})(),
|
|
237
372
|
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
373
|
+
});`;
|
|
374
|
+
}
|
|
375
|
+
function generateMessageHandler(features) {
|
|
376
|
+
let code = `
|
|
377
|
+
self.addEventListener("message", (event) => {
|
|
378
|
+
if (event.data.type === "SKIP_WAITING") {
|
|
379
|
+
self.skipWaiting();
|
|
380
|
+
}`;
|
|
381
|
+
if (features.tagInvalidation) {
|
|
382
|
+
code += `
|
|
383
|
+
if (event.data.type === "INVALIDATE_TAG" && event.data.tag) {
|
|
384
|
+
event.waitUntil(invalidateByTag(event.data.tag));
|
|
385
|
+
}`;
|
|
241
386
|
}
|
|
387
|
+
code += `
|
|
388
|
+
});`;
|
|
242
389
|
return code;
|
|
243
390
|
}
|
|
391
|
+
function generateTagManagement() {
|
|
392
|
+
return `
|
|
393
|
+
const TAG_DB_NAME = "swoff-cache-tags";
|
|
394
|
+
const TAG_STORE_NAME = "tags";
|
|
395
|
+
|
|
396
|
+
function openTagDB() {
|
|
397
|
+
return new Promise((resolve, reject) => {
|
|
398
|
+
const request = indexedDB.open(TAG_DB_NAME, 1);
|
|
399
|
+
request.onupgradeneeded = (e) => {
|
|
400
|
+
const db = e.target.result;
|
|
401
|
+
if (!db.objectStoreNames.contains(TAG_STORE_NAME)) {
|
|
402
|
+
const store = db.createObjectStore(TAG_STORE_NAME, { keyPath: "url" });
|
|
403
|
+
store.createIndex("by-tag", "tags", { multiEntry: true });
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
request.onsuccess = (e) => resolve(e.target.result);
|
|
407
|
+
request.onerror = (e) => reject(e.target.error);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function cacheTagUrl(url, tags) {
|
|
412
|
+
const db = await openTagDB();
|
|
413
|
+
const tx = db.transaction(TAG_STORE_NAME, "readwrite");
|
|
414
|
+
const store = tx.objectStore(TAG_STORE_NAME);
|
|
415
|
+
store.put({ url, tags });
|
|
416
|
+
await new Promise((resolve, reject) => {
|
|
417
|
+
tx.oncomplete = () => resolve();
|
|
418
|
+
tx.onerror = () => reject(tx.error);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function invalidateByTag(tag) {
|
|
423
|
+
const db = await openTagDB();
|
|
424
|
+
const tx = db.transaction(TAG_STORE_NAME, "readonly");
|
|
425
|
+
const store = tx.objectStore(TAG_STORE_NAME);
|
|
426
|
+
const index = store.index("by-tag");
|
|
427
|
+
const entries = await new Promise((resolve, reject) => {
|
|
428
|
+
const request = index.getAll(tag);
|
|
429
|
+
request.onsuccess = () => resolve(request.result);
|
|
430
|
+
request.onerror = () => reject(request.error);
|
|
431
|
+
});
|
|
432
|
+
await db.close();
|
|
433
|
+
|
|
434
|
+
const runtimeCache = await caches.open(CACHE_NAME_RUNTIME);
|
|
435
|
+
for (const entry of entries) {
|
|
436
|
+
await runtimeCache.delete(entry.url);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const writeDb = await openTagDB();
|
|
440
|
+
const writeTx = writeDb.transaction(TAG_STORE_NAME, "readwrite");
|
|
441
|
+
const writeStore = writeTx.objectStore(TAG_STORE_NAME);
|
|
442
|
+
for (const entry of entries) {
|
|
443
|
+
writeStore.delete(entry.url);
|
|
444
|
+
}
|
|
445
|
+
await new Promise((resolve, reject) => {
|
|
446
|
+
writeTx.oncomplete = () => resolve();
|
|
447
|
+
writeTx.onerror = () => reject(writeTx.error);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const clients = await self.clients.matchAll();
|
|
451
|
+
clients.forEach((client) => {
|
|
452
|
+
client.postMessage({ type: "TAG_INVALIDATED", tag });
|
|
453
|
+
});
|
|
454
|
+
}`;
|
|
455
|
+
}
|
|
456
|
+
function generateBackgroundSyncHandler() {
|
|
457
|
+
return `
|
|
458
|
+
self.addEventListener("sync", (event) => {
|
|
459
|
+
if (event.tag === "sync-mutations") {
|
|
460
|
+
event.waitUntil(processMutationQueueInSW());
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
async function processMutationQueueInSW() {
|
|
465
|
+
const SW_DB_NAME = "swoff-queue";
|
|
466
|
+
const SW_STORE_NAME = "mutations";
|
|
467
|
+
const SW_MAX_RETRIES = 5;
|
|
468
|
+
|
|
469
|
+
let succeeded = 0;
|
|
470
|
+
let failed = 0;
|
|
471
|
+
const tagsToInvalidate = new Set();
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
const db = await new Promise((resolve, reject) => {
|
|
475
|
+
const request = indexedDB.open(SW_DB_NAME, 1);
|
|
476
|
+
request.onupgradeneeded = (e) => {
|
|
477
|
+
const db = e.target.result;
|
|
478
|
+
if (!db.objectStoreNames.contains(SW_STORE_NAME)) {
|
|
479
|
+
const store = db.createObjectStore(SW_STORE_NAME, { keyPath: "id" });
|
|
480
|
+
store.createIndex("by-timestamp", "timestamp");
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
request.onsuccess = (e) => resolve(e.target.result);
|
|
484
|
+
request.onerror = (e) => reject(e.target.error);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const tx = db.transaction(SW_STORE_NAME, "readonly");
|
|
488
|
+
const store = tx.objectStore(SW_STORE_NAME);
|
|
489
|
+
const index = store.index("by-timestamp");
|
|
490
|
+
const queue = await new Promise((resolve, reject) => {
|
|
491
|
+
const request = index.getAll();
|
|
492
|
+
request.onsuccess = () => resolve(request.result);
|
|
493
|
+
request.onerror = () => reject(request.error);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
for (const item of queue) {
|
|
497
|
+
if (item.retryCount >= SW_MAX_RETRIES) {
|
|
498
|
+
await removeFromSWQueue(db, item.id);
|
|
499
|
+
failed++;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
const response = await fetch(item.url, {
|
|
504
|
+
method: item.method,
|
|
505
|
+
headers: { "Content-Type": "application/json", ...item.headers },
|
|
506
|
+
body: JSON.stringify(item.body),
|
|
507
|
+
});
|
|
508
|
+
if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
|
|
509
|
+
|
|
510
|
+
if (item.tags) {
|
|
511
|
+
item.tags.forEach((tag) => tagsToInvalidate.add(tag));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
await removeFromSWQueue(db, item.id);
|
|
515
|
+
succeeded++;
|
|
516
|
+
} catch {
|
|
517
|
+
item.retryCount++;
|
|
518
|
+
await updateInSWQueue(db, item);
|
|
519
|
+
failed++;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
} catch (err) {
|
|
523
|
+
console.error("Background sync failed:", err);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
for (const tag of tagsToInvalidate) {
|
|
527
|
+
await invalidateByTag(tag);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const clients = await self.clients.matchAll();
|
|
531
|
+
for (const client of clients) {
|
|
532
|
+
client.postMessage({
|
|
533
|
+
type: "BACKGROUND_SYNC_COMPLETE",
|
|
534
|
+
detail: { succeeded, failed, tags: [...tagsToInvalidate] },
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function removeFromSWQueue(db, id) {
|
|
540
|
+
const tx = db.transaction(SW_STORE_NAME, "readwrite");
|
|
541
|
+
tx.objectStore(SW_STORE_NAME).delete(id);
|
|
542
|
+
await new Promise((resolve, reject) => {
|
|
543
|
+
tx.oncomplete = () => resolve();
|
|
544
|
+
tx.onerror = () => reject(tx.error);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async function updateInSWQueue(db, item) {
|
|
549
|
+
const tx = db.transaction(SW_STORE_NAME, "readwrite");
|
|
550
|
+
tx.objectStore(SW_STORE_NAME).put(item);
|
|
551
|
+
await new Promise((resolve, reject) => {
|
|
552
|
+
tx.oncomplete = () => resolve();
|
|
553
|
+
tx.onerror = () => reject(tx.error);
|
|
554
|
+
});
|
|
555
|
+
}`;
|
|
556
|
+
}
|
|
244
557
|
function generateConfigHeader(config) {
|
|
245
558
|
return `/**
|
|
246
559
|
* Swoff Service Worker - Auto-Generated
|
|
247
560
|
* Generated from swoff.config.json
|
|
248
561
|
* DO NOT EDIT MANUALLY
|
|
249
562
|
* Version: ${config.version}
|
|
250
|
-
* Features: versionedSw=${config.features.versionedSw}, offlineReads=${config.features.offlineReads}, mutationQueue=${config.features.mutationQueue}
|
|
563
|
+
* Features: versionedSw=${config.features.versionedSw}, offlineReads=${config.features.offlineReads}, mutationQueue=${config.features.mutationQueue}, backgroundSync=${config.features.backgroundSync}, tagInvalidation=${config.features.tagInvalidation}
|
|
251
564
|
* Default Strategy: ${config.serviceWorker.defaultStrategy}
|
|
252
565
|
* See: https://swoff.netlify.app/docs
|
|
253
566
|
*/`;
|
|
@@ -256,9 +569,17 @@ function getDefaultTemplate() {
|
|
|
256
569
|
return `let CACHE_NAME = "";
|
|
257
570
|
let ASSETS_TO_CACHE = [];
|
|
258
571
|
|
|
572
|
+
// [[CACHE_NAME]]
|
|
573
|
+
// [[ASSETS_LIST]]
|
|
574
|
+
// [[AUTO_SKIP_WAITING]]
|
|
575
|
+
|
|
576
|
+
const CACHE_NAME_RUNTIME = "swoff-runtime";
|
|
577
|
+
|
|
259
578
|
// [[INSTALL_HANDLER]]
|
|
260
579
|
// [[ACTIVATE_HANDLER]]
|
|
580
|
+
// [[MESSAGE_HANDLER]]
|
|
261
581
|
// [[FETCH_HANDLER]]
|
|
582
|
+
// [[TAG_MANAGEMENT]]
|
|
262
583
|
|
|
263
584
|
const SWOFF = {
|
|
264
585
|
cache: {
|
|
@@ -269,6 +590,19 @@ const SWOFF = {
|
|
|
269
590
|
async put(request, response) {
|
|
270
591
|
const cache = await caches.open(CACHE_NAME);
|
|
271
592
|
await cache.put(request, response);
|
|
593
|
+
},
|
|
594
|
+
async delete(request) {
|
|
595
|
+
const cache = await caches.open(CACHE_NAME);
|
|
596
|
+
return cache.delete(request);
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
network: {
|
|
600
|
+
async fetch(request, options = {}) {
|
|
601
|
+
try {
|
|
602
|
+
return await fetch(request, options);
|
|
603
|
+
} catch (error) {
|
|
604
|
+
throw new Error(\`Network request failed: \${error.message}\`);
|
|
605
|
+
}
|
|
272
606
|
}
|
|
273
607
|
}
|
|
274
608
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sw-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/sw-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"sw-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/sw-generator.ts"],"names":[],"mappings":"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"}
|