@swoff/cli 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +610 -24
- package/bin/swoff +8 -14
- package/dist/__tests__/assemble-sw.test.js +17 -3
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +9 -9
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +10 -9
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +65 -68
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +12 -14
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +41 -22
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +3 -3
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +11 -5
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/clean.js +5 -0
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +43 -13
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +7 -3
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +149 -32
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/init.js +6 -2
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +3 -1
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/validator.js +25 -16
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-fetch.js +29 -34
- package/dist/lib/generators/file-generators/auth-fetch.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-state.js +8 -5
- package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-store.js +181 -41
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-user.js +28 -32
- package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +9 -5
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +8 -5
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +101 -0
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -6
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +196 -41
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-hooks.js +23 -151
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +450 -0
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/invalidation-tags.js +12 -10
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +65 -87
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-reconcile.js +57 -0
- package/dist/lib/generators/file-generators/mutation-reconcile.js.map +1 -0
- package/dist/lib/generators/file-generators/push.js +143 -0
- package/dist/lib/generators/file-generators/push.js.map +1 -0
- package/dist/lib/generators/file-generators/pwa-install.js +41 -9
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/quick-readme.js +88 -0
- package/dist/lib/generators/file-generators/quick-readme.js.map +1 -0
- package/dist/lib/generators/file-generators/reconcile.js +7 -12
- package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
- package/dist/lib/generators/file-generators/store.js +30 -20
- package/dist/lib/generators/file-generators/store.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +38 -17
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +82 -111
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-push.js +67 -0
- package/dist/lib/generators/file-generators/sw-push.js.map +1 -0
- package/dist/lib/generators/file-generators/sw-template.js +3 -1
- package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
- package/dist/lib/generators/file-generators/type-definitions.js +45 -22
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +27 -19
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +6 -2
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +36 -12
- 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/default-template.js +1 -30
- package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +176 -90
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/install-handler.js +12 -10
- package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +36 -5
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +14 -12
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +26 -9
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/build-script.js +35 -0
- package/dist/lib/utils/build-script.js.map +1 -0
- package/package.json +3 -2
- package/templates/hooks/useAuth.jsx +30 -0
- package/templates/hooks/useAuth.tsx +30 -0
- package/templates/hooks/useBackgroundSync.jsx +31 -0
- package/templates/hooks/useBackgroundSync.tsx +31 -0
- package/templates/hooks/useCacheInvalidation.jsx +19 -0
- package/templates/hooks/useCacheInvalidation.tsx +19 -0
- package/templates/hooks/useCachedFetch.jsx +55 -0
- package/templates/hooks/useCachedFetch.tsx +69 -0
- package/templates/hooks/useMutationQueue.jsx +34 -0
- package/templates/hooks/useMutationQueue.tsx +34 -0
- package/templates/hooks/useNetworkStatus.jsx +19 -0
- package/templates/hooks/useNetworkStatus.tsx +19 -0
- package/templates/hooks/usePushSubscription.jsx +67 -0
- package/templates/hooks/usePushSubscription.tsx +67 -0
- package/templates/hooks/useSWUpdate.jsx +76 -0
- package/templates/hooks/useSWUpdate.tsx +76 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quick-readme.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/quick-readme.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateReconcile(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
10
|
+
const AS = (type) => (ts ? ` as ${type}` : "");
|
|
7
11
|
const code = `/**
|
|
8
12
|
* Swoff ID Reconciliation
|
|
9
13
|
* Update local records with server data after mutation sync.
|
|
@@ -16,7 +20,7 @@ export function generateReconcile(ctx) {
|
|
|
16
20
|
|
|
17
21
|
import { getRecord, putRecord, deleteRecord } from './store.${ext}';
|
|
18
22
|
|
|
19
|
-
export async function reconcileRecord(storeName, tempId, serverData)
|
|
23
|
+
export async function reconcileRecord(storeName${T("string")}, tempId${T("string")}, serverData${T("Record<string, unknown>")})${R("Promise<void>")}{
|
|
20
24
|
const existing = await getRecord(storeName, tempId);
|
|
21
25
|
if (!existing) return;
|
|
22
26
|
|
|
@@ -34,20 +38,11 @@ export async function reconcileRecord(storeName, tempId, serverData) {
|
|
|
34
38
|
await deleteRecord(storeName, tempId);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
await reconcileReferences(storeName, tempId, serverData.id);
|
|
41
|
+
await reconcileReferences(storeName, tempId, serverData.id${AS("string | number")});
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
export async function reconcileReferences(storeName, oldId, newId)
|
|
44
|
+
export async function reconcileReferences(storeName${T("string")}, oldId${T("string")}, newId${T("string | number")})${R("Promise<void>")}{
|
|
41
45
|
// Override this for your app's schema.
|
|
42
|
-
// Example: update foreign-key references in related stores.
|
|
43
|
-
//
|
|
44
|
-
// const txns = await getAllRecords('transactions');
|
|
45
|
-
// for (const txn of txns) {
|
|
46
|
-
// if (txn.todoId === oldId) {
|
|
47
|
-
// txn.todoId = newId;
|
|
48
|
-
// await putRecord('transactions', txn);
|
|
49
|
-
// }
|
|
50
|
-
// }
|
|
51
46
|
}
|
|
52
47
|
`;
|
|
53
48
|
writeFile(ctx, `reconcile.${ext}`, code);
|
|
@@ -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,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;0DAK2C,GAAG;;;;;8DAKC,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,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,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;IACtD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEvD,MAAM,IAAI,GAAG;;;;;0DAK2C,GAAG;;;;;8DAKC,GAAG;;iDAEhB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;8DAkBrF,EAAE,CAAC,iBAAiB,CAAC;;;qDAG9B,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;CAGxI,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateStore(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
10
|
+
const PT = (type) => (ts ? `<${type}>` : "");
|
|
11
|
+
const AS = (type) => (ts ? ` as ${type}` : "");
|
|
7
12
|
const dbName = "app-db";
|
|
8
13
|
const code = `/**
|
|
9
14
|
* Swoff IndexedDB Store
|
|
@@ -19,55 +24,60 @@ export function generateStore(ctx) {
|
|
|
19
24
|
|
|
20
25
|
const DB_NAME = "${dbName}";
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
/** Open the app's IndexedDB database. Creates it if it doesn't exist. */
|
|
28
|
+
export function openAppDB()${R("Promise<IDBDatabase>")}{
|
|
29
|
+
return new Promise${PT("IDBDatabase")}((resolve, reject) => {
|
|
24
30
|
const request = indexedDB.open(DB_NAME);
|
|
25
|
-
request.onsuccess = (e) => resolve(e.target.result);
|
|
26
|
-
request.onerror = (e) => reject(e.target.error);
|
|
31
|
+
request.onsuccess = (e) => resolve((e.target${AS("IDBOpenDBRequest")}).result);
|
|
32
|
+
request.onerror = (e) => reject((e.target${AS("IDBRequest")}).error);
|
|
27
33
|
});
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
/** Get a record by ID from an IndexedDB store. Returns undefined if not found. */
|
|
37
|
+
export async function getRecord(storeName${T("string")}, id${T("IDBValidKey")})${R("Promise<Record<string, unknown> | undefined>")}{
|
|
31
38
|
const db = await openAppDB();
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
39
|
+
return new Promise${PT("Record<string, unknown> | undefined")}((resolve, reject) => {
|
|
33
40
|
const tx = db.transaction(storeName, "readonly");
|
|
34
41
|
const store = tx.objectStore(storeName);
|
|
35
42
|
const request = store.get(id);
|
|
36
|
-
request.onsuccess = () => resolve(request.result);
|
|
37
|
-
request.onerror = () => reject(request.error);
|
|
43
|
+
request.onsuccess = () => resolve((request${AS("IDBRequest<Record<string, unknown> | undefined>")}).result);
|
|
44
|
+
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
38
45
|
});
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
|
|
48
|
+
/** Store a record in an IndexedDB store (insert or update). Returns the record's key. */
|
|
49
|
+
export async function putRecord(storeName${T("string")}, record${T("Record<string, unknown>")})${R("Promise<IDBValidKey>")}{
|
|
42
50
|
const db = await openAppDB();
|
|
43
|
-
return new Promise((resolve, reject) => {
|
|
51
|
+
return new Promise${PT("IDBValidKey")}((resolve, reject) => {
|
|
44
52
|
const tx = db.transaction(storeName, "readwrite");
|
|
45
53
|
const store = tx.objectStore(storeName);
|
|
46
54
|
const request = store.put(record);
|
|
47
|
-
tx.oncomplete = () => resolve(request.result);
|
|
48
|
-
tx.onerror = () => reject(tx.error);
|
|
55
|
+
tx.oncomplete = () => resolve((request${AS("IDBRequest<IDBValidKey>")}).result);
|
|
56
|
+
tx.onerror = () => reject((tx${AS("IDBTransaction")}).error);
|
|
49
57
|
});
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
/** Delete a record by ID from an IndexedDB store. */
|
|
61
|
+
export async function deleteRecord(storeName${T("string")}, id${T("IDBValidKey")})${R("Promise<void>")}{
|
|
53
62
|
const db = await openAppDB();
|
|
54
|
-
return new Promise((resolve, reject) => {
|
|
63
|
+
return new Promise${PT("void")}((resolve, reject) => {
|
|
55
64
|
const tx = db.transaction(storeName, "readwrite");
|
|
56
65
|
const store = tx.objectStore(storeName);
|
|
57
|
-
|
|
66
|
+
store.delete(id);
|
|
58
67
|
tx.oncomplete = () => resolve();
|
|
59
|
-
tx.onerror = () => reject(tx.error);
|
|
68
|
+
tx.onerror = () => reject((tx${AS("IDBTransaction")}).error);
|
|
60
69
|
});
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
/** Get all records from an IndexedDB store. */
|
|
73
|
+
export async function getAllRecords(storeName${T("string")})${R("Promise<Record<string, unknown>[]>")}{
|
|
64
74
|
const db = await openAppDB();
|
|
65
|
-
return new Promise((resolve, reject) => {
|
|
75
|
+
return new Promise${PT("Record<string, unknown>[]")}((resolve, reject) => {
|
|
66
76
|
const tx = db.transaction(storeName, "readonly");
|
|
67
77
|
const store = tx.objectStore(storeName);
|
|
68
78
|
const request = store.getAll();
|
|
69
|
-
request.onsuccess = () => resolve(request.result);
|
|
70
|
-
request.onerror = () => reject(request.error);
|
|
79
|
+
request.onsuccess = () => resolve((request${AS("IDBRequest<Record<string, unknown>[]>")}).result);
|
|
80
|
+
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
71
81
|
});
|
|
72
82
|
}
|
|
73
83
|
`;
|
|
@@ -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,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,MAAM,GAAG,QAAQ,CAAC;IAExB,MAAM,IAAI,GAAG;;;;;oFAKqE,GAAG;;;;;;;mBAOpE,MAAM
|
|
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,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,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;IACtD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,QAAQ,CAAC;IAExB,MAAM,IAAI,GAAG;;;;;oFAKqE,GAAG;;;;;;;mBAOpE,MAAM;;;6BAGI,CAAC,CAAC,sBAAsB,CAAC;sBAChC,EAAE,CAAC,aAAa,CAAC;;kDAEW,EAAE,CAAC,kBAAkB,CAAC;+CACzB,EAAE,CAAC,YAAY,CAAC;;;;;2CAKpB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,8CAA8C,CAAC;;sBAE5G,EAAE,CAAC,qCAAqC,CAAC;;;;gDAIf,EAAE,CAAC,iDAAiD,CAAC;6CACxD,EAAE,CAAC,YAAY,CAAC;;;;;2CAKlB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,sBAAsB,CAAC;;sBAEpG,EAAE,CAAC,aAAa,CAAC;;;;4CAIK,EAAE,CAAC,yBAAyB,CAAC;mCACtC,EAAE,CAAC,gBAAgB,CAAC;;;;;8CAKT,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;sBAEhF,EAAE,CAAC,MAAM,CAAC;;;;;mCAKG,EAAE,CAAC,gBAAgB,CAAC;;;;;+CAKR,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oCAAoC,CAAC;;sBAE/E,EAAE,CAAC,2BAA2B,CAAC;;;;gDAIL,EAAE,CAAC,uCAAuC,CAAC;6CAC9C,EAAE,CAAC,YAAY,CAAC;;;CAG5D,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -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
|
|
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
|
|
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
|
-
const projectRoot =
|
|
21
|
+
const projectRoot = process.cwd();
|
|
21
22
|
|
|
22
23
|
const pkgPath = join(projectRoot, 'package.json');
|
|
23
|
-
const templatePath = join(__dirname, '
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6
|
+
const autoUpdate = ctx.config.features.serviceWorker.autoUpdate;
|
|
7
7
|
const autoActivate = ctx.config.features.serviceWorker.autoActivate;
|
|
8
|
-
const
|
|
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
|
|
13
|
-
const
|
|
14
|
-
const
|
|
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
|
|
15
|
+
* Framework-agnostic SW registration with versioned URLs and update flow.
|
|
17
16
|
*
|
|
18
17
|
* Usage:
|
|
19
|
-
* import { initServiceWorker } from './swoff/sw
|
|
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,52 +24,48 @@ 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
|
|
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
|
-
|
|
51
|
-
` : ""}
|
|
52
|
-
const AUTO_REGISTER = ${autoRegister};
|
|
37
|
+
const AUTO_UPDATE = ${autoUpdate};
|
|
53
38
|
const AUTO_ACTIVATE = ${autoActivate};
|
|
54
39
|
|
|
55
|
-
|
|
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
|
-
const response = await fetch("/version.json");
|
|
51
|
+
const response = await fetch("/version.json?t=" + Date.now());
|
|
59
52
|
if (!response.ok) {
|
|
60
53
|
throw new Error("Failed to fetch version.json");
|
|
61
54
|
}
|
|
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
|
-
|
|
88
|
-
|
|
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
|
|
106
|
+
semverCompare(currentVersion, manifest.minSupportedVersion || "0.0.0") < 0;
|
|
119
107
|
|
|
120
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
${
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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,
|
|
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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates SW push notification event handlers.
|
|
3
|
+
* Appended to the SW template via the // [[PUSH_HANDLERS]] placeholder.
|
|
4
|
+
*/
|
|
5
|
+
export function generateSwPushHandlers() {
|
|
6
|
+
return `
|
|
7
|
+
// --- Push Notification Handlers ---
|
|
8
|
+
|
|
9
|
+
self.addEventListener("push", (event) => {
|
|
10
|
+
let data;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
data = event.data ? event.data.json() : {};
|
|
14
|
+
} catch {
|
|
15
|
+
data = { title: "New Update", body: event.data?.text() || "" };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const options = {
|
|
19
|
+
body: data.body || "",
|
|
20
|
+
icon: data.icon || "/icon-192.png",
|
|
21
|
+
badge: data.badge || "",
|
|
22
|
+
image: data.image || undefined,
|
|
23
|
+
vibrate: data.vibrate || [200, 100, 200],
|
|
24
|
+
data: {
|
|
25
|
+
url: data.url || "/",
|
|
26
|
+
...(data.data || {}),
|
|
27
|
+
},
|
|
28
|
+
actions: data.actions || [],
|
|
29
|
+
tag: data.tag || undefined,
|
|
30
|
+
requireInteraction: data.requireInteraction || false,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
event.waitUntil(self.registration.showNotification(data.title || "Update", options));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
self.addEventListener("notificationclick", (event) => {
|
|
37
|
+
event.notification.close();
|
|
38
|
+
|
|
39
|
+
const url = event.notification.data?.url || "/";
|
|
40
|
+
const action = event.action;
|
|
41
|
+
|
|
42
|
+
event.waitUntil(
|
|
43
|
+
(async () => {
|
|
44
|
+
if (action) {
|
|
45
|
+
// Handle action clicks (e.g., "reply", "dismiss")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const clients = await self.clients.matchAll({ type: "window" });
|
|
49
|
+
|
|
50
|
+
for (const client of clients) {
|
|
51
|
+
const clientUrl = new URL(client.url);
|
|
52
|
+
const targetUrl = new URL(url, self.location.origin);
|
|
53
|
+
|
|
54
|
+
if (clientUrl.pathname === targetUrl.pathname && "focus" in client) {
|
|
55
|
+
return client.focus();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (clients.openWindow) {
|
|
60
|
+
return clients.openWindow(url);
|
|
61
|
+
}
|
|
62
|
+
})(),
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=sw-push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sw-push.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-push.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2DR,CAAC;AACF,CAAC"}
|