@swoff/cli 0.2.2 → 0.3.1

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 (62) hide show
  1. package/dist/__tests__/assemble-sw.test.js +2 -2
  2. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  3. package/dist/__tests__/cli-integration.test.js +11 -13
  4. package/dist/__tests__/cli-integration.test.js.map +1 -1
  5. package/dist/__tests__/config-types.test.js +24 -10
  6. package/dist/__tests__/config-types.test.js.map +1 -1
  7. package/dist/__tests__/file-generators.test.js +55 -21
  8. package/dist/__tests__/file-generators.test.js.map +1 -1
  9. package/dist/__tests__/loader.test.js +3 -5
  10. package/dist/__tests__/loader.test.js.map +1 -1
  11. package/dist/__tests__/validator.test.js +59 -67
  12. package/dist/__tests__/validator.test.js.map +1 -1
  13. package/dist/lib/cli/logger.js +2 -1
  14. package/dist/lib/cli/logger.js.map +1 -1
  15. package/dist/lib/commands/add.js +4 -6
  16. package/dist/lib/commands/add.js.map +1 -1
  17. package/dist/lib/commands/generate.js +3 -3
  18. package/dist/lib/commands/generate.js.map +1 -1
  19. package/dist/lib/commands/info.js +9 -9
  20. package/dist/lib/commands/info.js.map +1 -1
  21. package/dist/lib/commands/init.js +6 -7
  22. package/dist/lib/commands/init.js.map +1 -1
  23. package/dist/lib/config/validator.js +59 -44
  24. package/dist/lib/config/validator.js.map +1 -1
  25. package/dist/lib/generators/file-generators/background-sync.js +4 -3
  26. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  27. package/dist/lib/generators/file-generators/cache.js +6 -22
  28. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  29. package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -2
  30. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  31. package/dist/lib/generators/file-generators/indexeddb.js +5 -4
  32. package/dist/lib/generators/file-generators/indexeddb.js.map +1 -1
  33. package/dist/lib/generators/file-generators/invalidation-tags.js +4 -3
  34. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  35. package/dist/lib/generators/file-generators/manifest.js +15 -5
  36. package/dist/lib/generators/file-generators/manifest.js.map +1 -1
  37. package/dist/lib/generators/file-generators/mutation-queue.js +8 -7
  38. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  39. package/dist/lib/generators/file-generators/pwa-install.js +10 -54
  40. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  41. package/dist/lib/generators/file-generators/reconcile.js +4 -3
  42. package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
  43. package/dist/lib/generators/file-generators/store.js +4 -3
  44. package/dist/lib/generators/file-generators/store.js.map +1 -1
  45. package/dist/lib/generators/file-generators/sw-generator-build.js +1 -1
  46. package/dist/lib/generators/file-generators/sw-injector.js +52 -16
  47. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  48. package/dist/lib/generators/file-generators/sw-template.js +6 -10
  49. package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
  50. package/dist/lib/generators/sw-sections/activate-handler.js +13 -4
  51. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  52. package/dist/lib/generators/sw-sections/assemble-sw.js +2 -2
  53. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  54. package/dist/lib/generators/sw-sections/config-header.js +1 -1
  55. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  56. package/dist/lib/generators/sw-sections/fetch-handler.js +29 -46
  57. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  58. package/dist/lib/generators/swoff-files-generator.js +5 -5
  59. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  60. package/dist/lib/shared/config-types.js +19 -25
  61. package/dist/lib/shared/config-types.js.map +1 -1
  62. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"background-sync.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/background-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,oBAAoB,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"background-sync.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/background-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;;iEAMkD,GAAG;;;;;;;;;;;;yFAYqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC3F,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
@@ -1,17 +1,15 @@
1
1
  /**
2
- * Generates cache.js - cache tag invalidation and cross-tab sync.
2
+ * Generates cache.{js|ts} - cache tag invalidation utilities.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateCache(ctx) {
6
+ const ext = ctx.ext;
6
7
  const code = `/**
7
- * Swoff Cache Invalidation & Cross-Tab Sync
8
- * Framework-agnostic cache tag invalidation and cross-tab synchronization.
8
+ * Swoff Cache Invalidation
9
+ * Framework-agnostic cache tag invalidation.
9
10
  *
10
11
  * Usage:
11
- * import { invalidateByTag, initCrossTabSync } from './swoff/cache.js';
12
- *
13
- * // Call once during app init
14
- * initCrossTabSync();
12
+ * import { invalidateByTag } from './swoff/cache.${ext}';
15
13
  *
16
14
  * // After a mutation, invalidate related cache
17
15
  * await invalidateByTag("todos");
@@ -35,21 +33,7 @@ export async function invalidateByTags(tags) {
35
33
  await invalidateByTag(tag);
36
34
  }
37
35
  }
38
-
39
- export function initCrossTabSync() {
40
- if (!navigator.serviceWorker) return;
41
-
42
- navigator.serviceWorker.addEventListener("message", (event) => {
43
- if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
44
- window.dispatchEvent(
45
- new CustomEvent("cache-invalidated", {
46
- detail: { tags: [event.data.tag] },
47
- })
48
- );
49
- }
50
- });
51
- }
52
36
  `;
53
- writeFile(ctx, "cache.js", code);
37
+ writeFile(ctx, `cache.${ext}`, code);
54
38
  }
55
39
  //# sourceMappingURL=cache.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Cd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAEpB,MAAM,IAAI,GAAG;;;;;sDAKuC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CAwBxD,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
@@ -3,12 +3,13 @@
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateFetchWrapper(ctx) {
6
+ const ext = ctx.ext;
6
7
  const code = `/**
7
8
  * Swoff Fetch Wrapper
8
9
  * Framework-agnostic fetch with cache strategy, tags, and query deduplication.
9
10
  *
10
11
  * Usage:
11
- * import { fetchWithCache } from './swoff/fetch-wrapper.js';
12
+ * import { fetchWithCache } from './swoff/fetch-wrapper.${ext}';
12
13
  *
13
14
  * // GET - cached with tag
14
15
  * const todos = await fetchWithCache("/api/todos", { tags: ["todos"] }).then(r => r.json());
@@ -75,6 +76,6 @@ export async function fetchWithCacheOrQueue(input, options = {}) {
75
76
  return fetchWithCache(input, options);
76
77
  }
77
78
  `;
78
- writeFile(ctx, "fetch-wrapper.js", code);
79
+ writeFile(ctx, `fetch-wrapper.${ext}`, code);
79
80
  }
80
81
  //# sourceMappingURL=fetch-wrapper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-wrapper.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,oBAAoB,CAAC,GAAqB;IACxD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuEd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-wrapper.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,oBAAoB,CAAC,GAAqB;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;6DAK8C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkE/D,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC"}
@@ -3,8 +3,9 @@
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateIndexedDB(ctx) {
6
- const dbName = ctx.config.database?.name || "app-db";
7
- const stores = ctx.config.database?.stores || [];
6
+ const ext = ctx.ext;
7
+ const dbName = ctx.config.features.indexeddb?.name || "app-db";
8
+ const stores = ctx.config.features.indexeddb?.stores || [];
8
9
  const storesCode = stores.length > 0
9
10
  ? stores
10
11
  .map((store, i) => ` if (oldVersion < ${i + 1}) {\n db.createObjectStore("${store}", { keyPath: "id" });\n }`)
@@ -19,7 +20,7 @@ export function generateIndexedDB(ctx) {
19
20
  * Database initialization with schema migrations.
20
21
  *
21
22
  * Usage:
22
- * import { openDB } from './swoff/indexeddb.js';
23
+ * import { openDB } from './swoff/indexeddb.${ext}';
23
24
  *
24
25
  * const db = await openDB();
25
26
  */
@@ -68,6 +69,6 @@ export async function monitorStorage() {
68
69
  };
69
70
  }
70
71
  `;
71
- writeFile(ctx, "indexeddb.js", code);
72
+ writeFile(ctx, `indexeddb.${ext}`, code);
72
73
  }
73
74
  //# sourceMappingURL=indexeddb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/indexeddb.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,IAAI,QAAQ,CAAC;IACrD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;IAEjD,MAAM,UAAU,GACd,MAAM,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,MAAM;aACH,GAAG,CACF,CAAC,KAAa,EAAE,CAAS,EAAE,EAAE,CAC3B,0BAA0B,CAAC,GAAG,CAAC,sCAAsC,KAAK,iCAAiC,CAC9G;aACA,IAAI,CAAC,MAAM,CAAC;QACjB,CAAC,CAAC;;;;WAIG,CAAC;IAEV,MAAM,IAAI,GAAG;;;;;;;;;;mBAUI,MAAM;;;;;;;;;;;EAWvB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCX,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"indexeddb.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/indexeddb.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,IAAI,QAAQ,CAAC;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;IAE3D,MAAM,UAAU,GACd,MAAM,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,MAAM;aACH,GAAG,CACF,CAAC,KAAa,EAAE,CAAS,EAAE,EAAE,CAC3B,0BAA0B,CAAC,GAAG,CAAC,sCAAsC,KAAK,iCAAiC,CAC9G;aACA,IAAI,CAAC,MAAM,CAAC;QACjB,CAAC,CAAC;;;;WAIG,CAAC;IAEV,MAAM,IAAI,GAAG;;;;;iDAKkC,GAAG;;;;;mBAKjC,MAAM;;;;;;;;;;;EAWvB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCX,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
@@ -3,12 +3,13 @@
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateInvalidationTags(ctx) {
6
+ const ext = ctx.ext;
6
7
  const code = `/**
7
8
  * Swoff Invalidation Tags Helper
8
9
  * URL-based tag generation from REST endpoints for automatic cache invalidation.
9
10
  *
10
11
  * Usage:
11
- * import { generateTags, invalidateUrl } from './swoff/invalidation-tags.js';
12
+ * import { generateTags, invalidateUrl } from './swoff/invalidation-tags.${ext}';
12
13
  *
13
14
  * // Generate tags from URL
14
15
  * generateTags("/api/todos"); // ["todos"]
@@ -24,7 +25,7 @@ export function generateInvalidationTags(ctx) {
24
25
  * await invalidateUrl("/api/todos/42");
25
26
  */
26
27
 
27
- import { invalidateByTag, invalidateByTags } from "./cache.js";
28
+ import { invalidateByTag, invalidateByTags } from "./cache.${ext}";
28
29
 
29
30
  export function generateTags(url) {
30
31
  const parsed = typeof url === "string" ? new URL(url, window.location.origin) : url;
@@ -86,6 +87,6 @@ export async function invalidateByMethod(method, url) {
86
87
  await invalidateByTags(tags);
87
88
  }
88
89
  `;
89
- writeFile(ctx, "invalidation-tags.js", code);
90
+ writeFile(ctx, `invalidation-tags.${ext}`, code);
90
91
  }
91
92
  //# sourceMappingURL=invalidation-tags.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/invalidation-tags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkFd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/invalidation-tags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;8EAK+D,GAAG;;;;;;;;;;;;;;;;6DAgBpB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6D/D,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,qBAAqB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC"}
@@ -1,12 +1,15 @@
1
1
  /**
2
2
  * Generates manifest.json - PWA web app manifest.
3
3
  */
4
- import { existsSync, mkdirSync, writeFileSync } from "fs";
4
+ import { existsSync, writeFileSync } from "fs";
5
5
  import { join } from "path";
6
6
  export function generateManifest(ctx) {
7
7
  const outputDir = join(ctx.projectRoot, "public");
8
8
  if (!existsSync(outputDir))
9
- mkdirSync(outputDir, { recursive: true });
9
+ return;
10
+ const manifestPath = join(outputDir, "manifest.json");
11
+ if (existsSync(manifestPath))
12
+ return;
10
13
  const manifest = {
11
14
  name: "Swoff App",
12
15
  short_name: "Swoff",
@@ -15,12 +18,19 @@ export function generateManifest(ctx) {
15
18
  display: "standalone",
16
19
  background_color: "#ffffff",
17
20
  theme_color: "#000000",
21
+ orientation: "portrait-primary",
22
+ scope: "/",
23
+ lang: "en-US",
24
+ categories: ["utilities", "web"],
25
+ prefer_related_applications: false,
26
+ display_override: ["window-controls-overlay", "standalone", "browser"],
18
27
  icons: [
19
- { src: "/icon-192.png", sizes: "192x192", type: "image/png" },
20
- { src: "/icon-512.png", sizes: "512x512", type: "image/png" },
28
+ { src: "/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any" },
29
+ { src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any maskable" },
21
30
  ],
31
+ screenshots: [],
22
32
  };
23
- writeFileSync(join(outputDir, "manifest.json"), JSON.stringify(manifest, null, 2));
33
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
24
34
  ctx.generatedFiles.push("public/manifest.json");
25
35
  }
26
36
  //# sourceMappingURL=manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/manifest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtE,MAAM,QAAQ,GAAG;QACf,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,OAAO;QACnB,WAAW,EAAE,+BAA+B;QAC5C,SAAS,EAAE,GAAG;QACd,OAAO,EAAE,YAAY;QACrB,gBAAgB,EAAE,SAAS;QAC3B,WAAW,EAAE,SAAS;QACtB,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE;YAC7D,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE;SAC9D;KACF,CAAC;IAEF,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACnF,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/manifest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO;IAEnC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACtD,IAAI,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO;IAErC,MAAM,QAAQ,GAAG;QACf,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,OAAO;QACnB,WAAW,EAAE,+BAA+B;QAC5C,SAAS,EAAE,GAAG;QACd,OAAO,EAAE,YAAY;QACrB,gBAAgB,EAAE,SAAS;QAC3B,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,GAAG;QACV,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;QAChC,2BAA2B,EAAE,KAAK;QAClC,gBAAgB,EAAE,CAAC,yBAAyB,EAAE,YAAY,EAAE,SAAS,CAAC;QACtE,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;YAC7E,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE;SACvF;QACD,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/D,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAClD,CAAC"}
@@ -3,12 +3,13 @@
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateMutationQueue(ctx) {
6
+ const ext = ctx.ext;
6
7
  const code = `/**
7
8
  * Swoff Mutation Queue
8
9
  * Queue offline writes and sync when connection returns.
9
10
  *
10
11
  * Usage:
11
- * import { queueMutation, processMutationQueue, getPendingCount } from './swoff/mutation-queue.js';
12
+ * import { queueMutation, processMutationQueue, getPendingCount } from './swoff/mutation-queue.${ext}';
12
13
  *
13
14
  * // Queue a mutation
14
15
  * await queueMutation({
@@ -116,7 +117,7 @@ export async function processMutationQueue() {
116
117
  }
117
118
 
118
119
  if (item.tags && item.tags.length > 0) {
119
- const { invalidateByTags } = await import("./cache.js");
120
+ const { invalidateByTags } = await import("./cache.${ext}");
120
121
  await invalidateByTags(item.tags);
121
122
  }
122
123
 
@@ -164,16 +165,16 @@ async function rollbackMutation(item) {
164
165
  if (!item.storeName) return;
165
166
 
166
167
  if (item.method === "POST" && item.tempId) {
167
- const { deleteRecord } = await import("./store.js");
168
+ const { deleteRecord } = await import("./store.${ext}");
168
169
  await deleteRecord(item.storeName, item.tempId);
169
170
  } else if (
170
171
  (item.method === "PUT" || item.method === "PATCH") &&
171
172
  item.previousData
172
173
  ) {
173
- const { putRecord } = await import("./store.js");
174
+ const { putRecord } = await import("./store.${ext}");
174
175
  await putRecord(item.storeName, { ...item.previousData, $synced: true });
175
176
  } else if (item.method === "DELETE" && item.tempId && item.previousData) {
176
- const { putRecord } = await import("./store.js");
177
+ const { putRecord } = await import("./store.${ext}");
177
178
  await putRecord(item.storeName, { ...item.previousData, $synced: true });
178
179
  }
179
180
 
@@ -190,7 +191,7 @@ async function rollbackMutation(item) {
190
191
  }
191
192
 
192
193
  async function reconcileRecord(storeName, tempId, serverData) {
193
- const { getRecord, putRecord, deleteRecord } = await import("./store.js");
194
+ const { getRecord, putRecord, deleteRecord } = await import("./store.${ext}");
194
195
  const existing = await getRecord(storeName, tempId);
195
196
  if (!existing) return;
196
197
 
@@ -221,6 +222,6 @@ export async function getPendingCount() {
221
222
 
222
223
  window.addEventListener("online", processMutationQueue);
223
224
  `;
224
- writeFile(ctx, "mutation-queue.js", code);
225
+ writeFile(ctx, `mutation-queue.${ext}`, code);
225
226
  }
226
227
  //# sourceMappingURL=mutation-queue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/mutation-queue.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,qBAAqB,CAAC,GAAqB;IACzD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyNd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/mutation-queue.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,qBAAqB,CAAC,GAAqB;IACzD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;oGAKqF,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+DA4GxC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qDAgDb,GAAG;;;;;;kDAMN,GAAG;;;kDAGH,GAAG;;;;;;;;;;;;;;;;;yEAiBoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B3E,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,kBAAkB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAChD,CAAC"}
@@ -1,70 +1,26 @@
1
1
  /**
2
- * Generates pwa-install.js - PWA install prompt handler.
2
+ * Generates pwa-install.{js|ts} - PWA install prompt utility.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generatePwaInstall(ctx) {
6
- const preventDefault = ctx.config.pwa?.preventDefaultInstall ?? false;
6
+ const ext = ctx.ext;
7
7
  const code = `/**
8
- * Swoff PWA Install Prompt Handler
9
- * Captures beforeinstallprompt event and provides manual install trigger.
8
+ * Swoff PWA Install Prompt
9
+ * Manual install trigger and installability check.
10
+ * Event listeners are registered in sw-injector at app entry.
10
11
  *
11
12
  * Usage:
12
- * import { isInstallable, promptInstall } from './swoff/pwa-install.js';
13
+ * import { isInstallable, promptInstall } from './swoff/pwa-install.${ext}';
13
14
  *
14
- * // Listen for installable event
15
- * window.addEventListener('pwa-installable', (e) => {
16
- * console.log('PWA can be installed:', e.detail.isInstallable);
17
- * // Show your custom install button
18
- * });
19
- *
20
- * // When user clicks install button
21
- * async function onInstallClick() {
15
+ * if (isInstallable()) {
22
16
  * const result = await promptInstall();
23
- * console.log('User choice:', result);
17
+ * console.log('User choice:', result.outcome);
24
18
  * }
25
19
  *
26
- * Window events:
27
- * pwa-installable - PWA can be installed (detail: { isInstallable: true })
28
- * pwa-installed - User accepted install (detail: { outcome: 'accepted' })
29
- * pwa-dismissed - User dismissed install (detail: { outcome: 'dismissed' })
30
- *
31
- * Window properties:
20
+ * Window properties (set by sw-injector):
32
21
  * window.deferredInstallPrompt - The captured BeforeInstallPromptEvent
33
- * window.pwaInstallable - Whether PWA can be installed
34
22
  */
35
23
 
36
- const PREVENT_DEFAULT_INSTALL = ${preventDefault};
37
-
38
- window.addEventListener("beforeinstallprompt", (e) => {
39
- // Always capture the event so we never lose it
40
- window.deferredInstallPrompt = e;
41
- window.pwaInstallable = true;
42
-
43
- if (PREVENT_DEFAULT_INSTALL) {
44
- // Suppress browser's native prompt
45
- e.preventDefault();
46
- }
47
- // When false, browser shows native prompt naturally
48
- // but we still capture the event for manual triggering
49
-
50
- window.dispatchEvent(
51
- new CustomEvent("pwa-installable", {
52
- detail: { isInstallable: true },
53
- })
54
- );
55
- });
56
-
57
- window.addEventListener("appinstalled", () => {
58
- window.deferredInstallPrompt = null;
59
- window.pwaInstallable = false;
60
-
61
- window.dispatchEvent(
62
- new CustomEvent("pwa-installed", {
63
- detail: { outcome: "accepted" },
64
- })
65
- );
66
- });
67
-
68
24
  export function isInstallable() {
69
25
  return !!window.deferredInstallPrompt;
70
26
  }
@@ -97,6 +53,6 @@ export async function promptInstall() {
97
53
  return choice;
98
54
  }
99
55
  `;
100
- writeFile(ctx, "pwa-install.js", code);
56
+ writeFile(ctx, `pwa-install.${ext}`, code);
101
57
  }
102
58
  //# sourceMappingURL=pwa-install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pwa-install.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/pwa-install.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,qBAAqB,IAAI,KAAK,CAAC;IAEtE,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCA6BmB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+D/C,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"pwa-install.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/pwa-install.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAEpB,MAAM,IAAI,GAAG;;;;;;yEAM0D,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C3E,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
@@ -3,17 +3,18 @@
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateReconcile(ctx) {
6
+ const ext = ctx.ext;
6
7
  const code = `/**
7
8
  * Swoff ID Reconciliation
8
9
  * Update local records with server data after mutation sync.
9
10
  *
10
11
  * Usage:
11
- * import { reconcileRecord } from './swoff/reconcile.js';
12
+ * import { reconcileRecord } from './swoff/reconcile.${ext}';
12
13
  *
13
14
  * await reconcileRecord('todos', 'temp_abc123', serverData);
14
15
  */
15
16
 
16
- import { getRecord, putRecord, deleteRecord } from './store.js';
17
+ import { getRecord, putRecord, deleteRecord } from './store.${ext}';
17
18
 
18
19
  export async function reconcileRecord(storeName, tempId, serverData) {
19
20
  const existing = await getRecord(storeName, tempId);
@@ -49,6 +50,6 @@ export async function reconcileReferences(storeName, oldId, newId) {
49
50
  // }
50
51
  }
51
52
  `;
52
- writeFile(ctx, "reconcile.js", code);
53
+ writeFile(ctx, `reconcile.${ext}`, code);
53
54
  }
54
55
  //# sourceMappingURL=reconcile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/reconcile.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Cd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/reconcile.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;0DAK2C,GAAG;;;;;8DAKC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmChE,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
@@ -3,13 +3,14 @@
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateStore(ctx) {
6
- const dbName = ctx.config.database?.name || "app-db";
6
+ const ext = ctx.ext;
7
+ const dbName = ctx.config.features.indexeddb?.name || "app-db";
7
8
  const code = `/**
8
9
  * Swoff IndexedDB Store
9
10
  * Generic CRUD operations for app's IndexedDB database.
10
11
  *
11
12
  * Usage:
12
- * import { getRecord, putRecord, deleteRecord, openAppDB } from './swoff/store.js';
13
+ * import { getRecord, putRecord, deleteRecord, openAppDB } from './swoff/store.${ext}';
13
14
  *
14
15
  * const record = await getRecord('todos', 'todo-123');
15
16
  * await putRecord('todos', { id: 'todo-123', title: 'New task', $synced: false });
@@ -70,6 +71,6 @@ export async function getAllRecords(storeName) {
70
71
  });
71
72
  }
72
73
  `;
73
- writeFile(ctx, "store.js", code);
74
+ writeFile(ctx, `store.${ext}`, code);
74
75
  }
75
76
  //# sourceMappingURL=store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,IAAI,QAAQ,CAAC;IAErD,MAAM,IAAI,GAAG;;;;;;;;;;;;mBAYI,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDxB,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,IAAI,QAAQ,CAAC;IAE/D,MAAM,IAAI,GAAG;;;;;oFAKqE,GAAG;;;;;;;mBAOpE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDxB,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
@@ -65,7 +65,7 @@ const allAssets = collectAssets(outDir);
65
65
  const swFile = \`\${swFilename}-v\${version}.js\`;
66
66
  const filtered = allAssets.filter(a => !a.endsWith(swFile) && a !== '/version.json');
67
67
  const fallback = ['/index.html'];
68
- if (config.features?.pwa) fallback.push('/manifest.json');
68
+ if (config.features?.pwa?.enabled) fallback.push('/manifest.json');
69
69
  const combined = [...new Set([...fallback, ...filtered])];
70
70
  const assetsToCache = combined.map(url => ({ url, options: {} }));
71
71
 
@@ -1,14 +1,17 @@
1
1
  /**
2
- * Generates sw-injector.{js|ts} - client-side SW registration with version checking.
2
+ * Generates sw-injector.{js|ts} - client-side SW registration + PWA + cross-tab sync.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateSwInjector(ctx) {
6
6
  const autoRegister = ctx.config.serviceWorker.autoRegister;
7
7
  const autoActivate = ctx.config.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
11
  const ext = ctx.ext;
9
12
  const code = `/**
10
- * Swoff Service Worker Injector
11
- * Framework-agnostic client registration with version checking.
13
+ * Swoff SW Injector
14
+ * Framework-agnostic SW registration, PWA install support, and cross-tab sync.
12
15
  *
13
16
  * Usage:
14
17
  * import { initServiceWorker } from './swoff/sw-injector.${ext}';
@@ -16,15 +19,15 @@ export function generateSwInjector(ctx) {
16
19
  * // Call in your app entry point (e.g., main.tsx, app.js):
17
20
  * initServiceWorker();
18
21
  *
19
- * // Or defer until after onboarding, but still call initServiceWorker():
20
- * // (edit the shouldRegister function below instead)
21
- *
22
22
  * Window events:
23
23
  * sw-version-detected - Version info available on window
24
24
  * sw-update-available - New version ready for user consent (detail: { version })
25
25
  * sw-progress - Download progress (detail: { percent, downloaded, total })
26
26
  * sw-ready - SW active and controlling page
27
27
  * sw-error - SW registration failed
28
+ * pwa-installable - PWA can be installed (detail: { isInstallable: true })
29
+ * pwa-installed - User accepted install (detail: { outcome: 'accepted' })
30
+ * cache-invalidated - Cache entries with given tags cleared (detail: { tags })
28
31
  *
29
32
  * Window properties:
30
33
  * window.latestSWVersion - Latest version from version.json
@@ -34,11 +37,15 @@ export function generateSwInjector(ctx) {
34
37
  * window.swMinSupportedVersion - Minimum supported version from version.json
35
38
  * window.swReady - SW is active
36
39
  * window.swError - Registration failed
40
+ * window.deferredInstallPrompt - Captured BeforeInstallPromptEvent
41
+ * window.pwaInstallable - Whether PWA can be installed
37
42
  */
38
43
 
39
44
  const AUTO_REGISTER = ${autoRegister};
40
45
  const AUTO_ACTIVATE = ${autoActivate};
41
46
 
47
+ // --- SW Registration ---
48
+
42
49
  async function checkForUpdate() {
43
50
  const response = await fetch("/version.json");
44
51
  if (!response.ok) {
@@ -59,11 +66,6 @@ async function doRegisterServiceWorker(version) {
59
66
  }
60
67
 
61
68
  function shouldRegister() {
62
- // Add custom preconditions here. Return false to prevent registration.
63
- // Examples:
64
- // if (!localStorage.getItem("onboarding-complete")) return false;
65
- // if (navigator.connection?.effectiveType === "slow-2g") return false;
66
-
67
69
  return true;
68
70
  }
69
71
 
@@ -128,7 +130,6 @@ export async function initServiceWorker() {
128
130
  await doRegisterServiceWorker(manifest.version);
129
131
  }
130
132
  } catch (error) {
131
- // Offline or version.json fetch failed — try using existing registration
132
133
  try {
133
134
  const existing = await navigator.serviceWorker.getRegistration();
134
135
  if (existing && existing.active) {
@@ -137,9 +138,7 @@ export async function initServiceWorker() {
137
138
  window.dispatchEvent(new CustomEvent("sw-ready"));
138
139
  return;
139
140
  }
140
- } catch {
141
- // Registration check also failed, nothing we can do
142
- }
141
+ } catch {}
143
142
 
144
143
  console.error("Service Worker initialization failed:", error);
145
144
  window.swError = true;
@@ -169,6 +168,35 @@ export async function skipWaiting() {
169
168
  }
170
169
  }
171
170
 
171
+ // --- PWA Install Support ---
172
+ ${pwaEnabled ? `
173
+ window.addEventListener("beforeinstallprompt", (e) => {
174
+ window.deferredInstallPrompt = e;
175
+ window.pwaInstallable = true;
176
+
177
+ if (${preventDefaultInstall}) {
178
+ e.preventDefault();
179
+ }
180
+
181
+ window.dispatchEvent(
182
+ new CustomEvent("pwa-installable", {
183
+ detail: { isInstallable: true },
184
+ })
185
+ );
186
+ });
187
+
188
+ window.addEventListener("appinstalled", () => {
189
+ window.deferredInstallPrompt = null;
190
+ window.pwaInstallable = false;
191
+
192
+ window.dispatchEvent(
193
+ new CustomEvent("pwa-installed", {
194
+ detail: { outcome: "accepted" },
195
+ })
196
+ );
197
+ });
198
+ ` : ""}
199
+ // --- SW Message Listener ---
172
200
  if (typeof window !== "undefined" && "serviceWorker" in navigator) {
173
201
  navigator.serviceWorker.addEventListener("message", (event) => {
174
202
  if (event.data.type === "SW_PROGRESS") {
@@ -193,7 +221,15 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
193
221
  }
194
222
  window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
195
223
  }
196
- });
224
+ ${crossTabSync ? `
225
+ if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
226
+ window.dispatchEvent(
227
+ new CustomEvent("cache-invalidated", {
228
+ detail: { tags: [event.data.tag] },
229
+ })
230
+ );
231
+ }
232
+ ` : ""} });
197
233
  }
198
234
  `;
199
235
  writeFile(ctx, `sw-injector.${ext}`, code);
@@ -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,aAAa,CAAC,YAAY,CAAC;IAC3D,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC;IAC3D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAEpB,MAAM,IAAI,GAAG;;;;;8DAK+C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;wBAyBzC,YAAY;wBACZ,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8JnC,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,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC;IAC3D,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC;IAC3D,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,IAAI,GAAG;;;;;8DAK+C,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA2BzC,YAAY;wBACZ,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+HlC,UAAU,CAAC,CAAC,CAAC;;;;;QAKP,qBAAqB;;;;;;;;;;;;;;;;;;;;;CAqB5B,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"}
@@ -87,23 +87,19 @@ self.addEventListener("fetch", (event) => {
87
87
  const byPath = await cache.match(url.pathname);
88
88
  if (byPath) return byPath;
89
89
 
90
- const byRequest = await runtimeCache.match(event.request);
91
- if (byRequest) return byRequest;
90
+ const cached = await runtimeCache.match(event.request);
91
+ if (cached) return cached;
92
92
 
93
93
  if (event.request.mode === "navigate") {
94
94
  const spa = await cache.match("/index.html");
95
95
  if (spa) return spa;
96
96
  }
97
97
 
98
- try {
99
- const response = await fetch(event.request);
100
- if (response.ok) {
101
- await runtimeCache.put(event.request, response.clone());
102
- }
103
- return response;
104
- } catch {
105
- return new Response("Offline: content not available", { status: 503 });
98
+ const response = await fetch(event.request);
99
+ if (response.ok) {
100
+ await runtimeCache.put(event.request, response.clone());
106
101
  }
102
+ return response;
107
103
  })(),
108
104
  );
109
105
  });
@@ -1 +1 @@
1
- {"version":3,"file":"sw-template.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-template.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsId,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"sw-template.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-template.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkId,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}