@swoff/cli 0.3.5 → 0.3.6

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 (91) hide show
  1. package/README.md +327 -25
  2. package/dist/__tests__/assemble-sw.test.js +15 -1
  3. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  4. package/dist/__tests__/cli-integration.test.js +9 -8
  5. package/dist/__tests__/cli-integration.test.js.map +1 -1
  6. package/dist/__tests__/config-types.test.js +6 -6
  7. package/dist/__tests__/config-types.test.js.map +1 -1
  8. package/dist/__tests__/file-generators.test.js +67 -59
  9. package/dist/__tests__/file-generators.test.js.map +1 -1
  10. package/dist/__tests__/loader.test.js +12 -13
  11. package/dist/__tests__/loader.test.js.map +1 -1
  12. package/dist/__tests__/validator.test.js +41 -21
  13. package/dist/__tests__/validator.test.js.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/lib/cli/help.js +3 -3
  17. package/dist/lib/cli/help.js.map +1 -1
  18. package/dist/lib/commands/add.js +7 -1
  19. package/dist/lib/commands/add.js.map +1 -1
  20. package/dist/lib/commands/clean.js +5 -0
  21. package/dist/lib/commands/clean.js.map +1 -1
  22. package/dist/lib/commands/generate-guide.js +12 -9
  23. package/dist/lib/commands/generate-guide.js.map +1 -1
  24. package/dist/lib/commands/generate.js +7 -3
  25. package/dist/lib/commands/generate.js.map +1 -1
  26. package/dist/lib/commands/info.js +135 -32
  27. package/dist/lib/commands/info.js.map +1 -1
  28. package/dist/lib/commands/validate.js +3 -1
  29. package/dist/lib/commands/validate.js.map +1 -1
  30. package/dist/lib/config/validator.js +25 -16
  31. package/dist/lib/config/validator.js.map +1 -1
  32. package/dist/lib/generators/file-generators/auth-fetch.js +29 -34
  33. package/dist/lib/generators/file-generators/auth-fetch.js.map +1 -1
  34. package/dist/lib/generators/file-generators/auth-state.js +8 -5
  35. package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
  36. package/dist/lib/generators/file-generators/auth-store.js +71 -40
  37. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  38. package/dist/lib/generators/file-generators/auth-user.js +27 -31
  39. package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
  40. package/dist/lib/generators/file-generators/background-sync.js +9 -5
  41. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  42. package/dist/lib/generators/file-generators/cache.js +7 -2
  43. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  44. package/dist/lib/generators/file-generators/client-injector.js +113 -0
  45. package/dist/lib/generators/file-generators/client-injector.js.map +1 -0
  46. package/dist/lib/generators/file-generators/context.js +7 -6
  47. package/dist/lib/generators/file-generators/context.js.map +1 -1
  48. package/dist/lib/generators/file-generators/fetch-wrapper.js +170 -40
  49. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  50. package/dist/lib/generators/file-generators/generate-hooks.js +61 -3
  51. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  52. package/dist/lib/generators/file-generators/guide-generator.js +333 -0
  53. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -0
  54. package/dist/lib/generators/file-generators/invalidation-tags.js +12 -10
  55. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  56. package/dist/lib/generators/file-generators/mutation-queue.js +34 -84
  57. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  58. package/dist/lib/generators/file-generators/mutation-reconcile.js +57 -0
  59. package/dist/lib/generators/file-generators/mutation-reconcile.js.map +1 -0
  60. package/dist/lib/generators/file-generators/pwa-install.js +41 -9
  61. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  62. package/dist/lib/generators/file-generators/quick-readme.js +78 -0
  63. package/dist/lib/generators/file-generators/quick-readme.js.map +1 -0
  64. package/dist/lib/generators/file-generators/reconcile.js +7 -12
  65. package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
  66. package/dist/lib/generators/file-generators/store.js +30 -20
  67. package/dist/lib/generators/file-generators/store.js.map +1 -1
  68. package/dist/lib/generators/file-generators/sw-generator-build.js +37 -16
  69. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  70. package/dist/lib/generators/file-generators/sw-injector.js +81 -110
  71. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  72. package/dist/lib/generators/file-generators/sw-template.js +1 -1
  73. package/dist/lib/generators/file-generators/type-definitions.js +33 -10
  74. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  75. package/dist/lib/generators/sw-generator.js +19 -10
  76. package/dist/lib/generators/sw-generator.js.map +1 -1
  77. package/dist/lib/generators/sw-sections/assemble-sw.js +32 -12
  78. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  79. package/dist/lib/generators/sw-sections/config-header.js +1 -1
  80. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  81. package/dist/lib/generators/sw-sections/fetch-handler.js +25 -10
  82. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  83. package/dist/lib/generators/sw-sections/tag-management.js +25 -5
  84. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  85. package/dist/lib/generators/swoff-files-generator.js +7 -5
  86. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  87. package/dist/lib/shared/config-types.js +21 -6
  88. package/dist/lib/shared/config-types.js.map +1 -1
  89. package/dist/lib/utils/build-script.js +35 -0
  90. package/dist/lib/utils/build-script.js.map +1 -0
  91. package/package.json +1 -1
@@ -6,21 +6,22 @@ export function generateSwGeneratorBuild(ctx) {
6
6
  const code = `#!/usr/bin/env node
7
7
  /**
8
8
  * Swoff SW Generator Build Script
9
- * Reads swoff/sw-template.js and generates versioned SW output.
9
+ * Reads swoff/sw/template.js and generates versioned SW output.
10
10
  *
11
11
  * Add to package.json:
12
- * "build": "your-build && node swoff/sw-generator.js"
12
+ * "build": "your-build && node swoff/sw/generator.js"
13
13
  */
14
14
 
15
15
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
16
16
  import { join, dirname, relative } from 'path';
17
+ import { createHash } from 'crypto';
17
18
  import { fileURLToPath } from 'url';
18
19
 
19
20
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
21
  const projectRoot = join(__dirname, '..');
21
22
 
22
23
  const pkgPath = join(projectRoot, 'package.json');
23
- const templatePath = join(__dirname, 'sw-template.js');
24
+ const templatePath = join(__dirname, 'template.js');
24
25
  const configPath = join(projectRoot, 'swoff.config.json');
25
26
 
26
27
  if (!existsSync(configPath)) {
@@ -30,7 +31,7 @@ if (!existsSync(configPath)) {
30
31
  }
31
32
 
32
33
  if (!existsSync(templatePath)) {
33
- console.error('Error: swoff/sw-template.js not found');
34
+ console.error('Error: swoff/sw/template.js not found');
34
35
  process.exit(1);
35
36
  }
36
37
 
@@ -38,7 +39,12 @@ const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, 'utf8')) : {
38
39
  const config = JSON.parse(readFileSync(configPath, 'utf8'));
39
40
  const template = readFileSync(templatePath, 'utf8');
40
41
 
41
- const version = config.version === 'from-package' ? pkg.version || '1.0.0' : config.version;
42
+ const swConfig = config.features?.serviceWorker || {};
43
+ const versionConfig = swConfig.version || {};
44
+ const versionEnabled = versionConfig.enabled !== false;
45
+ const version = versionConfig.source === 'manual' && versionConfig.value
46
+ ? versionConfig.value
47
+ : pkg.version || '1.0.0';
42
48
  const outputDir = config.build?.outputDir || 'dist';
43
49
  const swFilename = config.build?.swFilename || 'sw';
44
50
 
@@ -61,8 +67,12 @@ function collectAssets(dir) {
61
67
  return assets;
62
68
  }
63
69
 
70
+ function generateCacheNameHash(swContent) {
71
+ return 'sw-cache-' + createHash('sha256').update(swContent).digest('hex').slice(0, 12);
72
+ }
73
+
64
74
  const allAssets = collectAssets(outDir);
65
- const swFile = \`\${swFilename}-v\${version}.js\`;
75
+ const swFile = versionEnabled ? \`\${swFilename}-v\${version}.js\` : \`\${swFilename}.js\`;
66
76
  const filtered = allAssets.filter(a => !a.endsWith(swFile) && a !== '/version.json');
67
77
  const fallback = ['/index.html'];
68
78
  if (config.features?.pwa?.enabled) fallback.push('/manifest.json');
@@ -70,19 +80,30 @@ const combined = [...new Set([...fallback, ...filtered])];
70
80
  const assetsToCache = combined.map(url => ({ url, options: {} }));
71
81
 
72
82
  let sw = template;
73
- sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = 'sw-v\${version}'\`);
83
+ if (versionEnabled) {
84
+ sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = 'sw-v\${version}'\`);
85
+ } else {
86
+ const sentinel = 'SW_CACHE_SENTINEL';
87
+ sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = '\${sentinel}'\`);
88
+ }
74
89
  sw = sw.replace('// [[ASSETS_LIST]]', \`ASSETS_TO_CACHE = \${JSON.stringify(assetsToCache, null, 2)}\`);
75
90
  sw = sw.replace('// [[AUTO_SKIP_WAITING]]', \`const AUTO_SKIP_WAITING = \${config.features?.serviceWorker?.autoActivate || false};\`);
76
91
 
77
- writeFileSync(join(outDir, swFile), sw);
78
- writeFileSync(join(outDir, 'version.json'), JSON.stringify({
79
- version,
80
- minSupportedVersion: config.minSupportedVersion || '0.0.0',
81
- generatedAt: new Date().toISOString(),
82
- }, null, 2));
83
-
84
- console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
92
+ if (!versionEnabled) {
93
+ const cacheName = generateCacheNameHash(sw);
94
+ sw = sw.replace(sentinel, cacheName);
95
+ writeFileSync(join(outDir, swFile), sw);
96
+ console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
97
+ } else {
98
+ writeFileSync(join(outDir, swFile), sw);
99
+ writeFileSync(join(outDir, 'version.json'), JSON.stringify({
100
+ version,
101
+ minSupportedVersion: versionConfig.minSupportedVersion || '0.0.0',
102
+ generatedAt: new Date().toISOString(),
103
+ }, null, 2));
104
+ console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
105
+ }
85
106
  `;
86
- writeFile(ctx, "sw-generator.js", code);
107
+ writeFile(ctx, "sw/generator.js", code);
87
108
  }
88
109
  //# sourceMappingURL=sw-generator-build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sw-generator-build.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-generator-build.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+Ed,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"sw-generator-build.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-generator-build.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoGd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
@@ -1,24 +1,21 @@
1
1
  /**
2
- * Generates sw-injector.{js|ts} - client-side SW registration + PWA + cross-tab sync.
2
+ * Generates sw/injector.{js|ts} - core SW registration logic only.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateSwInjector(ctx) {
6
- const autoRegister = ctx.config.features.serviceWorker.autoRegister;
6
+ const autoUpdate = ctx.config.features.serviceWorker.autoUpdate;
7
7
  const autoActivate = ctx.config.features.serviceWorker.autoActivate;
8
- const pwaEnabled = ctx.config.features.pwa.enabled;
9
- const preventDefaultInstall = ctx.config.features.pwa.preventDefaultInstall;
10
- const crossTabSync = ctx.config.features.crossTabSync;
8
+ const versionEnabled = ctx.config.features.serviceWorker.version.enabled;
11
9
  const ext = ctx.ext;
12
- const mutationQueueEnabled = ctx.config.features.mutationQueue;
13
- const authEnabled = ctx.config.features.auth.enabled;
14
- const code = `/**
10
+ const ts = ext === "ts";
11
+ const T = (type) => (ts ? `: ${type}` : "");
12
+ const R = (type) => (ts ? `: ${type} ` : " ");
13
+ const versionedCode = `/**
15
14
  * Swoff SW Injector
16
- * Framework-agnostic SW registration, PWA install support, and cross-tab sync.
15
+ * Framework-agnostic SW registration with versioned URLs and update flow.
17
16
  *
18
17
  * Usage:
19
- * import { initServiceWorker } from './swoff/sw-injector.${ext}';
20
- *
21
- * // Call in your app entry point (e.g., main.tsx, app.js):
18
+ * import { initServiceWorker } from './swoff/sw/injector.${ext}';
22
19
  * initServiceWorker();
23
20
  *
24
21
  * Window events:
@@ -27,32 +24,28 @@ export function generateSwInjector(ctx) {
27
24
  * sw-progress - Download progress (detail: { percent, downloaded, total })
28
25
  * sw-ready - SW active and controlling page
29
26
  * sw-error - SW registration failed
30
- * pwa-installable - PWA can be installed (detail: { isInstallable: true })
31
- * pwa-installed - User accepted install (detail: { outcome: 'accepted' })
32
- * cache-invalidated - Cache entries with given tags cleared (detail: { tags })
33
- * mutation-sync-complete - Queued mutations synced (detail: { succeeded, failed })
34
- * mutation-queue-changed - Queue modified
35
- * mutation-rollback - Mutation exhausted retries (detail: { method, url, tempId, previousData })
36
- * sw-auth-unauthorized - 401 response received (token expired or invalid)${authEnabled ? `
37
- * sw-auth-state-change - Login or logout (detail: { authenticated: boolean })` : ""}
38
27
  *
39
28
  * Window properties:
40
29
  * window.latestSWVersion - Latest version from version.json
41
30
  * window.currentSWVersion - Active SW version
42
31
  * window.swAvailableVersion - Pending update version
43
32
  * window.swUpdateRequired - Forced update needed (version < minSupportedVersion)
44
- * window.swMinSupportedVersion - Minimum supported version from version.json
33
+ * window.swMinSupportedVersion - Minimum supported version
45
34
  * window.swReady - SW is active
46
35
  * window.swError - Registration failed
47
- * window.deferredInstallPrompt - Captured BeforeInstallPromptEvent
48
- * window.pwaInstallable - Whether PWA can be installed
49
36
  */
50
- ${mutationQueueEnabled ? `import { processMutationQueue } from "./mutation-queue.${ext}";
51
- ` : ""}
52
- const AUTO_REGISTER = ${autoRegister};
37
+ const AUTO_UPDATE = ${autoUpdate};
53
38
  const AUTO_ACTIVATE = ${autoActivate};
54
39
 
55
- // --- SW Registration ---
40
+ function semverCompare(a${T("string")}, b${T("string")})${R("number")}{
41
+ const pa = a.split('.').map(Number);
42
+ const pb = b.split('.').map(Number);
43
+ for (let i = 0; i < 3; i++) {
44
+ if (pa[i] > pb[i]) return 1;
45
+ if (pa[i] < pb[i]) return -1;
46
+ }
47
+ return 0;
48
+ }
56
49
 
57
50
  async function checkForUpdate() {
58
51
  const response = await fetch("/version.json");
@@ -62,17 +55,17 @@ async function checkForUpdate() {
62
55
  return response.json();
63
56
  }
64
57
 
65
- async function waitForController() {
66
- return new Promise((resolve) => {
58
+ async function waitForController()${R("Promise<void>")}{
59
+ return new Promise<void>((resolve) => {
67
60
  if (navigator.serviceWorker.controller) {
68
61
  resolve();
69
62
  } else {
70
- navigator.serviceWorker.addEventListener("controllerchange", resolve, { once: true });
63
+ navigator.serviceWorker.addEventListener("controllerchange", () => resolve(), { once: true });
71
64
  }
72
65
  });
73
66
  }
74
67
 
75
- async function doRegisterServiceWorker(version) {
68
+ async function doRegisterServiceWorker(version${T("string")})${R("Promise<ServiceWorkerRegistration>")}{
76
69
  const swUrl = \`/sw-v\${version}.js\`;
77
70
  const registration = await navigator.serviceWorker.register(swUrl);
78
71
  localStorage.setItem("swRegisteredVersion", version);
@@ -84,18 +77,13 @@ async function doRegisterServiceWorker(version) {
84
77
  return registration;
85
78
  }
86
79
 
87
- function shouldRegister() {
88
- return true;
89
- }
90
-
91
- export async function initServiceWorker() {
80
+ /** Register the SW with version checking and update flow. Checks version.json, handles updates, and dispatches sw-ready/sw-error events. */
81
+ export async function initServiceWorker()${R("Promise<void>")}{
92
82
  if (!("serviceWorker" in navigator)) {
93
83
  console.warn("Service Workers not supported");
94
84
  return;
95
85
  }
96
86
 
97
- if (!shouldRegister()) return;
98
-
99
87
  try {
100
88
  const manifest = await checkForUpdate();
101
89
  const currentVersion = localStorage.getItem("swRegisteredVersion");
@@ -105,7 +93,7 @@ export async function initServiceWorker() {
105
93
  if (currentVersion === manifest.version) {
106
94
  const registration = await navigator.serviceWorker.getRegistration();
107
95
  if (registration && registration.active) {
108
- window.currentSWVersion = currentVersion;
96
+ window.currentSWVersion = currentVersion ?? undefined;
109
97
  window.dispatchEvent(new CustomEvent("sw-version-detected"));
110
98
  await waitForController();
111
99
  if (!window.swReady) {
@@ -115,9 +103,9 @@ export async function initServiceWorker() {
115
103
  } else if (currentVersion && currentVersion !== manifest.version) {
116
104
  window.swAvailableVersion = manifest.version;
117
105
  window.swUpdateRequired =
118
- currentVersion < (manifest.minSupportedVersion || "0.0.0");
106
+ semverCompare(currentVersion, manifest.minSupportedVersion || "0.0.0") < 0;
119
107
 
120
- if (AUTO_REGISTER) {
108
+ if (AUTO_UPDATE) {
121
109
  const registration = await navigator.serviceWorker.getRegistration();
122
110
  if (registration && registration.waiting) {
123
111
  if (AUTO_ACTIVATE) {
@@ -163,7 +151,9 @@ export async function initServiceWorker() {
163
151
  }
164
152
  return;
165
153
  }
166
- } catch {}
154
+ } catch {
155
+ // Handle the case where no existing active registration is found
156
+ }
167
157
 
168
158
  console.error("Service Worker initialization failed:", error);
169
159
  window.swError = true;
@@ -171,7 +161,8 @@ export async function initServiceWorker() {
171
161
  }
172
162
  }
173
163
 
174
- export async function handleUpdateApproved(newVersion) {
164
+ /** Accept a pending SW update. Reloads the page once the new SW takes control. */
165
+ export async function handleUpdateApproved(newVersion${T("string")})${R("Promise<void>")}{
175
166
  const registration = await navigator.serviceWorker.getRegistration();
176
167
  if (registration && registration.waiting) {
177
168
  registration.waiting.postMessage({ type: "SKIP_WAITING" });
@@ -186,81 +177,61 @@ export async function handleUpdateApproved(newVersion) {
186
177
  }
187
178
  }
188
179
 
189
- export async function skipWaiting() {
180
+ /** Activate a waiting SW without reloading. Useful when you handle the transition yourself. */
181
+ export async function skipWaiting()${R("Promise<void>")}{
190
182
  const registration = await navigator.serviceWorker.ready;
191
183
  if (registration.waiting) {
192
184
  registration.waiting.postMessage({ type: "SKIP_WAITING" });
193
185
  }
194
186
  }
187
+ `;
188
+ const simpleCode = `/**
189
+ * Swoff SW Injector (Simple Mode)
190
+ * Fixed SW URL with hash-based cache name — no versioned URLs or update events.
191
+ *
192
+ * Usage:
193
+ * import { initServiceWorker } from './swoff/sw/injector.${ext}';
194
+ * initServiceWorker();
195
+ *
196
+ * Window events:
197
+ * sw-ready - SW active and controlling page
198
+ * sw-error - SW registration failed
199
+ *
200
+ * Window properties:
201
+ * window.currentSWVersion - "simple"
202
+ * window.swReady - SW is active
203
+ * window.swError - Registration failed
204
+ */
205
+ async function waitForController()${R("Promise<void>")}{
206
+ return new Promise<void>((resolve) => {
207
+ if (navigator.serviceWorker.controller) {
208
+ resolve();
209
+ } else {
210
+ navigator.serviceWorker.addEventListener("controllerchange", () => resolve(), { once: true });
211
+ }
212
+ });
213
+ }
195
214
 
196
- // --- PWA Install Support ---
197
- ${pwaEnabled ? `
198
- window.addEventListener("beforeinstallprompt", (e) => {
199
- window.deferredInstallPrompt = e;
200
- window.pwaInstallable = true;
201
-
202
- if (${preventDefaultInstall}) {
203
- e.preventDefault();
215
+ /** Register the SW with a fixed URL (non-versioned mode). Dispatches sw-ready/sw-error. */
216
+ export async function initServiceWorker()${R("Promise<void>")}{
217
+ if (!("serviceWorker" in navigator)) {
218
+ console.warn("Service Workers not supported");
219
+ return;
220
+ }
221
+ try {
222
+ const registration = await navigator.serviceWorker.register("/sw.js");
223
+ window.currentSWVersion = "simple";
224
+ window.dispatchEvent(new CustomEvent("sw-version-detected"));
225
+ await waitForController();
226
+ window.dispatchEvent(new CustomEvent("sw-ready"));
227
+ } catch (error) {
228
+ console.error("Service Worker registration failed:", error);
229
+ window.swError = true;
230
+ window.dispatchEvent(new CustomEvent("sw-error"));
204
231
  }
205
-
206
- window.dispatchEvent(
207
- new CustomEvent("pwa-installable", {
208
- detail: { isInstallable: true },
209
- })
210
- );
211
- });
212
-
213
- window.addEventListener("appinstalled", () => {
214
- window.deferredInstallPrompt = null;
215
- window.pwaInstallable = false;
216
-
217
- window.dispatchEvent(
218
- new CustomEvent("pwa-installed", {
219
- detail: { outcome: "accepted" },
220
- })
221
- );
222
- });
223
- ` : ""}
224
- ${mutationQueueEnabled ? `
225
- // --- Mutation Queue Listener ---
226
- window.addEventListener("online", processMutationQueue);
227
- ` : ""}
228
- // --- SW Message Listener ---
229
- if (typeof window !== "undefined" && "serviceWorker" in navigator) {
230
- navigator.serviceWorker.addEventListener("message", (event) => {
231
- if (event.data.type === "SW_PROGRESS") {
232
- const { percent, downloaded, total } = event.data;
233
- window.dispatchEvent(
234
- new CustomEvent("sw-progress", {
235
- detail: { percent, downloaded, total },
236
- })
237
- );
238
- }
239
- if (event.data.type === "BACKGROUND_SYNC_COMPLETE") {
240
- const { succeeded, failed, tags } = event.data.detail;
241
- window.dispatchEvent(
242
- new CustomEvent("mutation-sync-complete", {
243
- detail: { succeeded, failed },
244
- })
245
- );
246
- if (tags && tags.length > 0) {
247
- window.dispatchEvent(
248
- new CustomEvent("cache-invalidated", { detail: { tags } })
249
- );
250
- }
251
- window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
252
- }
253
- ${crossTabSync ? `
254
- if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
255
- window.dispatchEvent(
256
- new CustomEvent("cache-invalidated", {
257
- detail: { tags: [event.data.tag] },
258
- })
259
- );
260
- }
261
- ` : ""} });
262
232
  }
263
233
  `;
264
- writeFile(ctx, `sw-injector.${ext}`, code);
234
+ const code = versionEnabled ? versionedCode : simpleCode;
235
+ writeFile(ctx, `sw/injector.${ext}`, code);
265
236
  }
266
237
  //# sourceMappingURL=sw-injector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;IACnD,MAAM,qBAAqB,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC5E,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAEpB,MAAM,oBAAoB,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;IAE/D,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;IAErD,MAAM,IAAI,GAAG;;;;;8DAK+C,GAAG;;;;;;;;;;;;;;;;;+EAiBc,WAAW,CAAC,CAAC,CAAC;kFACX,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;EAarF,oBAAoB,CAAC,CAAC,CAAC,0DAA0D,GAAG;CACrF,CAAC,CAAC,CAAC,EAAE;wBACkB,YAAY;wBACZ,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgJlC,UAAU,CAAC,CAAC,CAAC;;;;;QAKP,qBAAqB;;;;;;;;;;;;;;;;;;;;;CAqB5B,CAAC,CAAC,CAAC,EAAE;EACJ,oBAAoB,CAAC,CAAC,CAAC;;;CAGxB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BJ,YAAY,CAAC,CAAC,CAAC;;;;;;;;CAQhB,CAAC,CAAC,CAAC,EAAE;;CAEL,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;IAChE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC;IACzE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,aAAa,GAAG;;;;;8DAKsC,GAAG;;;;;;;;;;;;;;;;;;;sBAmB3C,UAAU;wBACR,YAAY;;0BAEV,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;oCAkBjC,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;gDAUN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oCAAoC,CAAC;;;;;;;;;;;;;2CAa3D,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAoFN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;qCAgBnD,CAAC,CAAC,eAAe,CAAC;;;;;;CAMtD,CAAC;IAEA,MAAM,UAAU,GAAG;;;;;8DAKyC,GAAG;;;;;;;;;;;;oCAY7B,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;2CAWX,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;CAiB5D,CAAC;IAEA,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC;IAEzD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
@@ -11,6 +11,6 @@ export function generateSwTemplate(ctx) {
11
11
  code = code.replace(`CACHE_NAME = 'sw-v${placeholderVersion}'`, "// [[CACHE_NAME]]");
12
12
  code = code.replace(/^ASSETS_TO_CACHE = \[[\s\S]*?\]$/m, "// [[ASSETS_LIST]]");
13
13
  code = code.replace(/^const AUTO_SKIP_WAITING = (true|false);?$/m, "// [[AUTO_SKIP_WAITING]]");
14
- writeFile(ctx, "sw-template.js", code);
14
+ writeFile(ctx, "sw/template.js", code);
15
15
  }
16
16
  //# sourceMappingURL=sw-template.js.map
@@ -5,17 +5,35 @@ import { writeFile } from "./context.js";
5
5
  export function generateTypeDefinitions(ctx) {
6
6
  if (ctx.ext !== "ts")
7
7
  return;
8
- const authBlock = ctx.config.features.auth.enabled ? `
9
- swAuthState?: "authenticated" | "unauthenticated" | "loading";
10
- swCurrentUser?: object | null;` : "";
11
- const code = `interface BeforeInstallPromptEvent extends Event {
12
- prompt(): Promise<void>;
13
- userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
8
+ const code = `
9
+ // Background Sync API types
10
+ interface SyncManager {
11
+ register(tag: string): Promise<void>;
12
+ getTags(): Promise<string[]>;
13
+ }
14
+
15
+ interface SyncEvent extends Event {
16
+ readonly tag: string;
17
+ readonly lastChance: boolean;
18
+ }
19
+
20
+ interface ServiceWorkerGlobalScope {
21
+ onsync: ((this: ServiceWorkerGlobalScope, ev: SyncEvent) => unknown) | null;
14
22
  }
15
23
 
16
24
  declare global {
25
+ interface BeforeInstallPromptEvent extends Event {
26
+ prompt(): Promise<void>;
27
+ userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
28
+ }
29
+
30
+ interface WindowEventMap {
31
+ beforeinstallprompt: BeforeInstallPromptEvent;
32
+ }
33
+
17
34
  interface Window {
18
35
  deferredInstallPrompt: BeforeInstallPromptEvent | null;
36
+ pwaInstallable?: boolean;
19
37
  latestSWVersion?: string;
20
38
  currentSWVersion?: string;
21
39
  swRegisteredVersion?: string;
@@ -23,7 +41,15 @@ declare global {
23
41
  swUpdateRequired?: boolean;
24
42
  swMinSupportedVersion?: string;
25
43
  swReady?: boolean;
26
- swError?: boolean;${authBlock}
44
+ swError?: boolean;
45
+ swAuthState?: "authenticated" | "unauthenticated" | "loading";
46
+ swCurrentUser?: Record<string, unknown> | null;
47
+ SyncManager?: { new(): SyncManager };
48
+ }
49
+
50
+ // Extend ServiceWorkerRegistration in the global scope for TS
51
+ interface ServiceWorkerRegistration {
52
+ readonly sync: SyncManager;
27
53
  }
28
54
  }
29
55
 
@@ -52,12 +78,9 @@ export interface MutationQueueItem {
52
78
  url: string;
53
79
  body: unknown;
54
80
  headers: Record<string, string>;
55
- previousData: unknown | null;
56
81
  timestamp: number;
57
82
  retryCount: number;
58
83
  tags: string[];
59
- storeName: string | null;
60
- tempId: string | null;
61
84
  }
62
85
 
63
86
  export interface MutationQueueResult {
@@ -1 +1 @@
1
- {"version":3,"file":"type-definitions.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/type-definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,uBAAuB,CAAC,GAAqB;IAC3D,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO;IAE7B,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;;mCAEpB,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;wBAeS,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2ChC,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"type-definitions.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/type-definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,uBAAuB,CAAC,GAAqB;IAC3D,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO;IAE7B,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoFd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
@@ -22,6 +22,12 @@ const configPathArg = args.findIndex((arg) => arg === "--config-path");
22
22
  const passedProjectRoot = projectRootArg !== -1 ? args[projectRootArg + 1] : null;
23
23
  const passedConfigPath = configPathArg !== -1 ? args[configPathArg + 1] : null;
24
24
  const projectRoot = passedProjectRoot || process.cwd();
25
+ function resolveVersion(config, pkgVersion) {
26
+ const v = config.features.serviceWorker.version;
27
+ if (v.source === "manual" && v.value)
28
+ return v.value;
29
+ return pkgVersion || "1.0.0";
30
+ }
25
31
  export async function generateSW(options = {}) {
26
32
  const optProjectRoot = options.projectRoot || projectRoot;
27
33
  const optConfigPath = options.configPath || passedConfigPath || undefined;
@@ -41,23 +47,26 @@ export async function generateSW(options = {}) {
41
47
  status("Swoff config generation disabled. Using custom code mode.");
42
48
  return { version: "", outputFile: "" };
43
49
  }
44
- const version = config.version === "from-package" ? pkg.version || "1.0.0" : config.version;
50
+ const versionEnabled = config.features.serviceWorker.version.enabled;
51
+ const version = resolveVersion(config, pkg.version || "1.0.0");
45
52
  const sw = assembleSW(config, version, optProjectRoot);
46
53
  const outputDir = join(optProjectRoot, config.build.outputDir);
47
- const swFilename = config.build.swFilename;
48
54
  if (!existsSync(outputDir)) {
49
55
  mkdirSync(outputDir, { recursive: true });
50
56
  }
51
- const outputFile = `${swFilename}-v${version}.js`;
57
+ const swFilename = config.build.swFilename;
58
+ const outputFile = versionEnabled ? `${swFilename}-v${version}.js` : `${swFilename}.js`;
52
59
  try {
53
60
  writeFileSync(join(outputDir, outputFile), sw);
54
- writeFileSync(join(outputDir, "version.json"), JSON.stringify({
55
- version,
56
- minSupportedVersion: config.minSupportedVersion,
57
- generatedAt: new Date().toISOString(),
58
- configEnabled: config.enabled,
59
- configSource,
60
- }, null, 2));
61
+ if (versionEnabled) {
62
+ writeFileSync(join(outputDir, "version.json"), JSON.stringify({
63
+ version,
64
+ minSupportedVersion: config.features.serviceWorker.version.minSupportedVersion,
65
+ generatedAt: new Date().toISOString(),
66
+ configEnabled: config.enabled,
67
+ configSource,
68
+ }, null, 2));
69
+ }
61
70
  status(`✓ ${outputDir}/${outputFile}`);
62
71
  }
63
72
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"sw-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/sw-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sw-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/sw-generator.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { readdirSync, existsSync } from "fs";
6
6
  import { join, relative } from "path";
7
+ import { createHash } from "crypto";
7
8
  import { getDefaultTemplate } from "./default-template.js";
8
9
  import { generateConfigHeader } from "./config-header.js";
9
10
  import { generateInstallHandler } from "./install-handler.js";
@@ -28,11 +29,30 @@ function collectAssets(dir, baseDir) {
28
29
  }
29
30
  return assets;
30
31
  }
32
+ function generateCacheNameFromHash(swContent) {
33
+ const hash = createHash("sha256").update(swContent).digest("hex").slice(0, 12);
34
+ return `sw-cache-${hash}`;
35
+ }
36
+ function applyReplacements(sw, config, assetsToCache) {
37
+ const { serviceWorker } = config.features;
38
+ const { features } = config;
39
+ sw = sw.replace("// [[ASSETS_LIST]]", `ASSETS_TO_CACHE = ${JSON.stringify(assetsToCache, null, 2)}`);
40
+ sw = sw.replace("// [[AUTO_SKIP_WAITING]]", `const AUTO_SKIP_WAITING = ${serviceWorker.autoActivate};`);
41
+ sw = sw.replace("// [[FETCH_HANDLER]]", generateFetchHandler(serviceWorker, features.tagInvalidation));
42
+ sw = sw.replace("// [[ACTIVATE_HANDLER]]", generateActivateHandler(serviceWorker.clearRuntimeOnUpdate));
43
+ sw = sw.replace("// [[INSTALL_HANDLER]]", generateInstallHandler());
44
+ sw = sw.replace("// [[MESSAGE_HANDLER]]", generateMessageHandler(features.tagInvalidation, features.auth.enabled));
45
+ sw = features.tagInvalidation
46
+ ? sw.replace("// [[TAG_MANAGEMENT]]", generateTagManagement())
47
+ : sw.replace("// [[TAG_MANAGEMENT]]", "");
48
+ return sw;
49
+ }
31
50
  export function assembleSW(config, version, projectRoot) {
32
51
  const { serviceWorker } = config.features;
33
52
  const { features } = config;
34
53
  const outputDir = config.build?.outputDir || "dist";
35
54
  const swFilename = config.build?.swFilename || "sw";
55
+ const versionEnabled = serviceWorker.version.enabled;
36
56
  const fallback = ["/index.html"];
37
57
  if (features.pwa.enabled)
38
58
  fallback.push("/manifest.json");
@@ -40,28 +60,28 @@ export function assembleSW(config, version, projectRoot) {
40
60
  if (projectRoot) {
41
61
  const distPath = join(projectRoot, outputDir);
42
62
  const allAssets = collectAssets(distPath, distPath);
43
- const swFile = `${swFilename}-v${version}.js`;
63
+ const swFile = versionEnabled ? `${swFilename}-v${version}.js` : `${swFilename}.js`;
44
64
  for (const a of allAssets) {
45
65
  if (a !== `/${swFile}` && a !== "/version.json")
46
66
  scanned.push(a);
47
67
  }
48
68
  }
49
69
  const assetsToCache = scanned.length > 0 ? [...new Set([...fallback, ...scanned])] : fallback;
70
+ const formattedAssets = assetsToCache.map((url) => ({ url, options: {} }));
50
71
  let sw = getDefaultTemplate();
51
- sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = 'sw-v${version}'`);
52
- sw = sw.replace("// [[ASSETS_LIST]]", `ASSETS_TO_CACHE = ${JSON.stringify(assetsToCache.map((url) => ({ url, options: {} })), null, 2)}`);
53
- sw = sw.replace("// [[AUTO_SKIP_WAITING]]", `const AUTO_SKIP_WAITING = ${serviceWorker.autoActivate};`);
54
- sw = sw.replace("// [[FETCH_HANDLER]]", generateFetchHandler(serviceWorker, features.tagInvalidation));
55
- sw = sw.replace("// [[ACTIVATE_HANDLER]]", generateActivateHandler(serviceWorker.clearRuntimeOnUpdate));
56
- sw = sw.replace("// [[INSTALL_HANDLER]]", generateInstallHandler());
57
- sw = sw.replace("// [[MESSAGE_HANDLER]]", generateMessageHandler(features.tagInvalidation, features.auth.enabled));
58
- if (features.tagInvalidation) {
59
- sw = sw.replace("// [[TAG_MANAGEMENT]]", generateTagManagement());
72
+ if (versionEnabled) {
73
+ sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = 'sw-v${version}'`);
74
+ sw = applyReplacements(sw, config, formattedAssets);
75
+ sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
60
76
  }
61
77
  else {
62
- sw = sw.replace("// [[TAG_MANAGEMENT]]", "");
78
+ const sentinel = "SW_CACHE_SENTINEL";
79
+ sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = '${sentinel}'`);
80
+ sw = applyReplacements(sw, config, formattedAssets);
81
+ const cacheName = generateCacheNameFromHash(sw);
82
+ sw = sw.replace(sentinel, cacheName);
83
+ sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
63
84
  }
64
- sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
65
85
  if (features.backgroundSync) {
66
86
  const authType = features.auth.enabled ? features.auth.type : undefined;
67
87
  sw += `\n\n${generateBackgroundSyncHandler(authType)}`;