@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.
- package/dist/__tests__/assemble-sw.test.js +2 -2
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +11 -13
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +24 -10
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +55 -21
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +3 -5
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +59 -67
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/lib/cli/logger.js +2 -1
- package/dist/lib/cli/logger.js.map +1 -1
- package/dist/lib/commands/add.js +4 -6
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/generate.js +3 -3
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +9 -9
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/init.js +6 -7
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/config/validator.js +59 -44
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +4 -3
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +6 -22
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -2
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/indexeddb.js +5 -4
- package/dist/lib/generators/file-generators/indexeddb.js.map +1 -1
- package/dist/lib/generators/file-generators/invalidation-tags.js +4 -3
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/manifest.js +15 -5
- package/dist/lib/generators/file-generators/manifest.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +8 -7
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/pwa-install.js +10 -54
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/reconcile.js +4 -3
- package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
- package/dist/lib/generators/file-generators/store.js +4 -3
- package/dist/lib/generators/file-generators/store.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +52 -16
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +6 -10
- package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +13 -4
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +2 -2
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/config-header.js +1 -1
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +29 -46
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +5 -5
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +19 -25
- package/dist/lib/shared/config-types.js.map +1 -1
- 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
|
|
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
|
|
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
|
|
8
|
-
* Framework-agnostic cache tag invalidation
|
|
8
|
+
* Swoff Cache Invalidation
|
|
9
|
+
* Framework-agnostic cache tag invalidation.
|
|
9
10
|
*
|
|
10
11
|
* Usage:
|
|
11
|
-
* import { invalidateByTag
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
7
|
-
const
|
|
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
|
|
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,
|
|
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;
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
6
|
+
const ext = ctx.ext;
|
|
7
7
|
const code = `/**
|
|
8
|
-
* Swoff PWA Install Prompt
|
|
9
|
-
*
|
|
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
|
|
13
|
+
* import { isInstallable, promptInstall } from './swoff/pwa-install.${ext}';
|
|
13
14
|
*
|
|
14
|
-
*
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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;
|
|
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
|
|
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
|
|
11
|
-
* Framework-agnostic
|
|
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
|
|
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
|
|
91
|
-
if (
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
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"}
|