@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.
Files changed (121) hide show
  1. package/README.md +610 -24
  2. package/bin/swoff +8 -14
  3. package/dist/__tests__/assemble-sw.test.js +17 -3
  4. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  5. package/dist/__tests__/cli-integration.test.js +9 -9
  6. package/dist/__tests__/cli-integration.test.js.map +1 -1
  7. package/dist/__tests__/config-types.test.js +10 -9
  8. package/dist/__tests__/config-types.test.js.map +1 -1
  9. package/dist/__tests__/file-generators.test.js +65 -68
  10. package/dist/__tests__/file-generators.test.js.map +1 -1
  11. package/dist/__tests__/loader.test.js +12 -14
  12. package/dist/__tests__/loader.test.js.map +1 -1
  13. package/dist/__tests__/validator.test.js +41 -22
  14. package/dist/__tests__/validator.test.js.map +1 -1
  15. package/dist/index.js +2 -2
  16. package/dist/index.js.map +1 -1
  17. package/dist/lib/cli/help.js +3 -3
  18. package/dist/lib/cli/help.js.map +1 -1
  19. package/dist/lib/commands/add.js +11 -5
  20. package/dist/lib/commands/add.js.map +1 -1
  21. package/dist/lib/commands/clean.js +5 -0
  22. package/dist/lib/commands/clean.js.map +1 -1
  23. package/dist/lib/commands/generate-guide.js +43 -13
  24. package/dist/lib/commands/generate-guide.js.map +1 -1
  25. package/dist/lib/commands/generate.js +7 -3
  26. package/dist/lib/commands/generate.js.map +1 -1
  27. package/dist/lib/commands/info.js +149 -32
  28. package/dist/lib/commands/info.js.map +1 -1
  29. package/dist/lib/commands/init.js +6 -2
  30. package/dist/lib/commands/init.js.map +1 -1
  31. package/dist/lib/commands/validate.js +3 -1
  32. package/dist/lib/commands/validate.js.map +1 -1
  33. package/dist/lib/config/validator.js +25 -16
  34. package/dist/lib/config/validator.js.map +1 -1
  35. package/dist/lib/generators/file-generators/auth-fetch.js +29 -34
  36. package/dist/lib/generators/file-generators/auth-fetch.js.map +1 -1
  37. package/dist/lib/generators/file-generators/auth-state.js +8 -5
  38. package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
  39. package/dist/lib/generators/file-generators/auth-store.js +181 -41
  40. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  41. package/dist/lib/generators/file-generators/auth-user.js +28 -32
  42. package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
  43. package/dist/lib/generators/file-generators/background-sync.js +9 -5
  44. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  45. package/dist/lib/generators/file-generators/cache.js +8 -5
  46. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  47. package/dist/lib/generators/file-generators/client-injector.js +101 -0
  48. package/dist/lib/generators/file-generators/client-injector.js.map +1 -0
  49. package/dist/lib/generators/file-generators/context.js +7 -6
  50. package/dist/lib/generators/file-generators/context.js.map +1 -1
  51. package/dist/lib/generators/file-generators/fetch-wrapper.js +196 -41
  52. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  53. package/dist/lib/generators/file-generators/generate-hooks.js +23 -151
  54. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  55. package/dist/lib/generators/file-generators/guide-generator.js +450 -0
  56. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -0
  57. package/dist/lib/generators/file-generators/invalidation-tags.js +12 -10
  58. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  59. package/dist/lib/generators/file-generators/mutation-queue.js +65 -87
  60. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  61. package/dist/lib/generators/file-generators/mutation-reconcile.js +57 -0
  62. package/dist/lib/generators/file-generators/mutation-reconcile.js.map +1 -0
  63. package/dist/lib/generators/file-generators/push.js +143 -0
  64. package/dist/lib/generators/file-generators/push.js.map +1 -0
  65. package/dist/lib/generators/file-generators/pwa-install.js +41 -9
  66. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  67. package/dist/lib/generators/file-generators/quick-readme.js +88 -0
  68. package/dist/lib/generators/file-generators/quick-readme.js.map +1 -0
  69. package/dist/lib/generators/file-generators/reconcile.js +7 -12
  70. package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
  71. package/dist/lib/generators/file-generators/store.js +30 -20
  72. package/dist/lib/generators/file-generators/store.js.map +1 -1
  73. package/dist/lib/generators/file-generators/sw-generator-build.js +38 -17
  74. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  75. package/dist/lib/generators/file-generators/sw-injector.js +82 -111
  76. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  77. package/dist/lib/generators/file-generators/sw-push.js +67 -0
  78. package/dist/lib/generators/file-generators/sw-push.js.map +1 -0
  79. package/dist/lib/generators/file-generators/sw-template.js +3 -1
  80. package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
  81. package/dist/lib/generators/file-generators/type-definitions.js +45 -22
  82. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  83. package/dist/lib/generators/sw-generator.js +27 -19
  84. package/dist/lib/generators/sw-generator.js.map +1 -1
  85. package/dist/lib/generators/sw-sections/activate-handler.js +6 -2
  86. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  87. package/dist/lib/generators/sw-sections/assemble-sw.js +36 -12
  88. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  89. package/dist/lib/generators/sw-sections/config-header.js +1 -1
  90. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  91. package/dist/lib/generators/sw-sections/default-template.js +1 -30
  92. package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
  93. package/dist/lib/generators/sw-sections/fetch-handler.js +176 -90
  94. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  95. package/dist/lib/generators/sw-sections/install-handler.js +12 -10
  96. package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
  97. package/dist/lib/generators/sw-sections/tag-management.js +36 -5
  98. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  99. package/dist/lib/generators/swoff-files-generator.js +14 -12
  100. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  101. package/dist/lib/shared/config-types.js +26 -9
  102. package/dist/lib/shared/config-types.js.map +1 -1
  103. package/dist/lib/utils/build-script.js +35 -0
  104. package/dist/lib/utils/build-script.js.map +1 -0
  105. package/package.json +3 -2
  106. package/templates/hooks/useAuth.jsx +30 -0
  107. package/templates/hooks/useAuth.tsx +30 -0
  108. package/templates/hooks/useBackgroundSync.jsx +31 -0
  109. package/templates/hooks/useBackgroundSync.tsx +31 -0
  110. package/templates/hooks/useCacheInvalidation.jsx +19 -0
  111. package/templates/hooks/useCacheInvalidation.tsx +19 -0
  112. package/templates/hooks/useCachedFetch.jsx +55 -0
  113. package/templates/hooks/useCachedFetch.tsx +69 -0
  114. package/templates/hooks/useMutationQueue.jsx +34 -0
  115. package/templates/hooks/useMutationQueue.tsx +34 -0
  116. package/templates/hooks/useNetworkStatus.jsx +19 -0
  117. package/templates/hooks/useNetworkStatus.tsx +19 -0
  118. package/templates/hooks/usePushSubscription.jsx +67 -0
  119. package/templates/hooks/usePushSubscription.tsx +67 -0
  120. package/templates/hooks/useSWUpdate.jsx +76 -0
  121. 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":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,cAAc,CAAC,GAAqB;IAClD,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEvC,CAAC,CAAC,2BAA2B,CAAC,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC,CAAC;IACN,CAAC,CAAC,wEAAwE,CAAC,CAAC;IAC5E,CAAC,CAAC,EAAE,CAAC,CAAC;IAEN,MAAM,iBAAiB,GAAG,IAAI,CAAC;IAE/B,IAAI,iBAAiB,EAAE,CAAC;QACtB,CAAC,CAAC,gBAAgB,CAAC,CAAC;QACpB,CAAC,CAAC,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,8DAA8D,GAAG,IAAI,CAAC,CAAC;QACzE,CAAC,CAAC,sBAAsB,CAAC,CAAC;QAC1B,CAAC,CAAC,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,EAAE,CAAC,CAAC;IACR,CAAC;IAED,CAAC,CAAC,2CAA2C,CAAC,CAAC;IAC/C,CAAC,CAAC,mFAAmF,CAAC,CAAC;IACvF,CAAC,CAAC,+EAA+E,CAAC,CAAC;IACnF,CAAC,CAAC,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,yDAAyD,GAAG,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC,qEAAqE,CAAC,CAAC;IACzE,CAAC,CAAC,KAAK,CAAC,CAAC;IACT,CAAC,CAAC,EAAE,CAAC,CAAC;IAEN,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,CAAC,CAAC,4BAA4B,CAAC,CAAC;QAChC,CAAC,CAAC,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,yDAAyD,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC,CAAC,mFAAmF,CAAC,CAAC;QACvF,CAAC,CAAC,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,EAAE,CAAC,CAAC;IACR,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAClC,CAAC,CAAC,kDAAkD,CAAC,CAAC;QACtD,CAAC,CAAC,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,0EAA0E,GAAG,IAAI,CAAC,CAAC;QACrF,CAAC,CAAC,8EAA8E,CAAC,CAAC;QAClF,CAAC,CAAC,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,EAAE,CAAC,CAAC;QAEN,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,CAAC,CAAC,4CAA4C,CAAC,CAAC;YAChD,CAAC,CAAC,OAAO,CAAC,CAAC;YACX,CAAC,CAAC,0DAA0D,GAAG,IAAI,CAAC,CAAC;YACrE,CAAC,CAAC,yBAAyB,CAAC,CAAC;YAC7B,CAAC,CAAC,KAAK,CAAC,CAAC;YACT,CAAC,CAAC,EAAE,CAAC,CAAC;QACR,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,EAAE,CAAC;QAC/C,CAAC,CAAC,uBAAuB,CAAC,CAAC;QAC3B,CAAC,CAAC,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,oFAAoF,GAAG,IAAI,CAAC,CAAC;QAC/F,CAAC,CAAC,6DAA6D,CAAC,CAAC;QACjE,CAAC,CAAC,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,EAAE,CAAC,CAAC;IACR,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;QACpC,CAAC,CAAC,uBAAuB,CAAC,CAAC;QAC3B,CAAC,CAAC,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,0EAA0E,GAAG,IAAI,CAAC,CAAC;QACrF,CAAC,CAAC,wFAAwF,CAAC,CAAC;QAC5F,CAAC,CAAC,yDAAyD,CAAC,CAAC;QAC7D,CAAC,CAAC,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,EAAE,CAAC,CAAC;IACR,CAAC;IAED,IAAI,GAAG,CAAC,aAAa,KAAK,OAAO,EAAE,CAAC;QAClC,CAAC,CAAC,oDAAoD,CAAC,CAAC;QACxD,CAAC,CAAC,QAAQ,CAAC,CAAC;QACZ,CAAC,CAAC,gEAAgE,GAAG,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,yEAAyE,CAAC,CAAC;QAC7E,CAAC,CAAC,KAAK,CAAC,CAAC;QACT,CAAC,CAAC,EAAE,CAAC,CAAC;IACR,CAAC;IAED,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACrB,CAAC,CAAC,+DAA+D,CAAC,CAAC;IACnE,CAAC,CAAC,sDAAsD,CAAC,CAAC;IAC1D,CAAC,CAAC,SAAS,CAAC,CAAC;IACb,CAAC,CAAC,uDAAuD,CAAC,CAAC;IAC3D,CAAC,CAAC,KAAK,CAAC,CAAC;IAET,SAAS,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChD,CAAC"}
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmChE,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,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,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
- export function openAppDB() {
23
- return new Promise((resolve, reject) => {
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
- export async function getRecord(storeName, id) {
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
- export async function putRecord(storeName, record) {
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
- export async function deleteRecord(storeName, id) {
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
- const request = store.delete(id);
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
- export async function getAllRecords(storeName) {
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDxB,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACvC,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,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-template.js and generates versioned SW output.
9
+ * Reads swoff/sw/template.js and generates versioned SW output.
10
10
  *
11
11
  * Add to package.json:
12
- * "build": "your-build && node swoff/sw-generator.js"
12
+ * "build": "your-build && node swoff/sw/generator.js"
13
13
  */
14
14
 
15
15
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
16
16
  import { join, dirname, relative } from 'path';
17
+ import { createHash } from 'crypto';
17
18
  import { fileURLToPath } from 'url';
18
19
 
19
20
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const projectRoot = join(__dirname, '..');
21
+ const projectRoot = process.cwd();
21
22
 
22
23
  const pkgPath = join(projectRoot, 'package.json');
23
- const templatePath = join(__dirname, 'sw-template.js');
24
+ const templatePath = join(__dirname, 'template.js');
24
25
  const configPath = join(projectRoot, 'swoff.config.json');
25
26
 
26
27
  if (!existsSync(configPath)) {
@@ -30,7 +31,7 @@ if (!existsSync(configPath)) {
30
31
  }
31
32
 
32
33
  if (!existsSync(templatePath)) {
33
- console.error('Error: swoff/sw-template.js not found');
34
+ console.error('Error: swoff/sw/template.js not found');
34
35
  process.exit(1);
35
36
  }
36
37
 
@@ -38,7 +39,12 @@ const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, 'utf8')) : {
38
39
  const config = JSON.parse(readFileSync(configPath, 'utf8'));
39
40
  const template = readFileSync(templatePath, 'utf8');
40
41
 
41
- const version = config.version === 'from-package' ? pkg.version || '1.0.0' : config.version;
42
+ const swConfig = config.features?.serviceWorker || {};
43
+ const versionConfig = swConfig.version || {};
44
+ const versionEnabled = versionConfig.enabled !== false;
45
+ const version = versionConfig.source === 'manual' && versionConfig.value
46
+ ? versionConfig.value
47
+ : pkg.version || '1.0.0';
42
48
  const outputDir = config.build?.outputDir || 'dist';
43
49
  const swFilename = config.build?.swFilename || 'sw';
44
50
 
@@ -61,8 +67,12 @@ function collectAssets(dir) {
61
67
  return assets;
62
68
  }
63
69
 
70
+ function generateCacheNameHash(swContent) {
71
+ return 'sw-cache-' + createHash('sha256').update(swContent).digest('hex').slice(0, 12);
72
+ }
73
+
64
74
  const allAssets = collectAssets(outDir);
65
- const swFile = \`\${swFilename}-v\${version}.js\`;
75
+ const swFile = versionEnabled ? \`\${swFilename}-v\${version}.js\` : \`\${swFilename}.js\`;
66
76
  const filtered = allAssets.filter(a => !a.endsWith(swFile) && a !== '/version.json');
67
77
  const fallback = ['/index.html'];
68
78
  if (config.features?.pwa?.enabled) fallback.push('/manifest.json');
@@ -70,19 +80,30 @@ const combined = [...new Set([...fallback, ...filtered])];
70
80
  const assetsToCache = combined.map(url => ({ url, options: {} }));
71
81
 
72
82
  let sw = template;
73
- sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = 'sw-v\${version}'\`);
83
+ if (versionEnabled) {
84
+ sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = 'sw-v\${version}'\`);
85
+ } else {
86
+ const sentinel = 'SW_CACHE_SENTINEL';
87
+ sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = '\${sentinel}'\`);
88
+ }
74
89
  sw = sw.replace('// [[ASSETS_LIST]]', \`ASSETS_TO_CACHE = \${JSON.stringify(assetsToCache, null, 2)}\`);
75
90
  sw = sw.replace('// [[AUTO_SKIP_WAITING]]', \`const AUTO_SKIP_WAITING = \${config.features?.serviceWorker?.autoActivate || false};\`);
76
91
 
77
- writeFileSync(join(outDir, swFile), sw);
78
- writeFileSync(join(outDir, 'version.json'), JSON.stringify({
79
- version,
80
- minSupportedVersion: config.minSupportedVersion || '0.0.0',
81
- generatedAt: new Date().toISOString(),
82
- }, null, 2));
83
-
84
- console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
92
+ if (!versionEnabled) {
93
+ const cacheName = generateCacheNameHash(sw);
94
+ sw = sw.replace(sentinel, cacheName);
95
+ writeFileSync(join(outDir, swFile), sw);
96
+ console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
97
+ } else {
98
+ writeFileSync(join(outDir, swFile), sw);
99
+ writeFileSync(join(outDir, 'version.json'), JSON.stringify({
100
+ version,
101
+ minSupportedVersion: versionConfig.minSupportedVersion || '0.0.0',
102
+ generatedAt: new Date().toISOString(),
103
+ }, null, 2));
104
+ console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
105
+ }
85
106
  `;
86
- writeFile(ctx, "sw-generator.js", code);
107
+ writeFile(ctx, "sw/generator.js", code);
87
108
  }
88
109
  //# sourceMappingURL=sw-generator-build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sw-generator-build.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-generator-build.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+Ed,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"sw-generator-build.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-generator-build.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoGd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
@@ -1,24 +1,21 @@
1
1
  /**
2
- * Generates sw-injector.{js|ts} - client-side SW registration + PWA + cross-tab sync.
2
+ * Generates sw/injector.{js|ts} - core SW registration logic only.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
5
  export function generateSwInjector(ctx) {
6
- const autoRegister = ctx.config.features.serviceWorker.autoRegister;
6
+ const autoUpdate = ctx.config.features.serviceWorker.autoUpdate;
7
7
  const autoActivate = ctx.config.features.serviceWorker.autoActivate;
8
- const pwaEnabled = ctx.config.features.pwa.enabled;
9
- const preventDefaultInstall = ctx.config.features.pwa.preventDefaultInstall;
10
- const crossTabSync = ctx.config.features.crossTabSync;
8
+ const versionEnabled = ctx.config.features.serviceWorker.version.enabled;
11
9
  const ext = ctx.ext;
12
- const mutationQueueEnabled = ctx.config.features.mutationQueue;
13
- const authEnabled = ctx.config.features.auth.enabled;
14
- const code = `/**
10
+ const ts = ext === "ts";
11
+ const T = (type) => (ts ? `: ${type}` : "");
12
+ const R = (type) => (ts ? `: ${type} ` : " ");
13
+ const versionedCode = `/**
15
14
  * Swoff SW Injector
16
- * Framework-agnostic SW registration, PWA install support, and cross-tab sync.
15
+ * Framework-agnostic SW registration with versioned URLs and update flow.
17
16
  *
18
17
  * Usage:
19
- * import { initServiceWorker } from './swoff/sw-injector.${ext}';
20
- *
21
- * // Call in your app entry point (e.g., main.tsx, app.js):
18
+ * import { initServiceWorker } from './swoff/sw/injector.${ext}';
22
19
  * initServiceWorker();
23
20
  *
24
21
  * Window events:
@@ -27,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 from version.json
33
+ * window.swMinSupportedVersion - Minimum supported version
45
34
  * window.swReady - SW is active
46
35
  * window.swError - Registration failed
47
- * window.deferredInstallPrompt - Captured BeforeInstallPromptEvent
48
- * window.pwaInstallable - Whether PWA can be installed
49
36
  */
50
- ${mutationQueueEnabled ? `import { processMutationQueue } from "./mutation-queue.${ext}";
51
- ` : ""}
52
- const AUTO_REGISTER = ${autoRegister};
37
+ const AUTO_UPDATE = ${autoUpdate};
53
38
  const AUTO_ACTIVATE = ${autoActivate};
54
39
 
55
- // --- SW Registration ---
40
+ function semverCompare(a${T("string")}, b${T("string")})${R("number")}{
41
+ const pa = a.split('.').map(Number);
42
+ const pb = b.split('.').map(Number);
43
+ for (let i = 0; i < 3; i++) {
44
+ if (pa[i] > pb[i]) return 1;
45
+ if (pa[i] < pb[i]) return -1;
46
+ }
47
+ return 0;
48
+ }
56
49
 
57
50
  async function checkForUpdate() {
58
- 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
- function shouldRegister() {
88
- return true;
89
- }
90
-
91
- export async function initServiceWorker() {
80
+ /** Register the SW with version checking and update flow. Checks version.json, handles updates, and dispatches sw-ready/sw-error events. */
81
+ export async function initServiceWorker()${R("Promise<void>")}{
92
82
  if (!("serviceWorker" in navigator)) {
93
83
  console.warn("Service Workers not supported");
94
84
  return;
95
85
  }
96
86
 
97
- if (!shouldRegister()) return;
98
-
99
87
  try {
100
88
  const manifest = await checkForUpdate();
101
89
  const currentVersion = localStorage.getItem("swRegisteredVersion");
@@ -105,7 +93,7 @@ export async function initServiceWorker() {
105
93
  if (currentVersion === manifest.version) {
106
94
  const registration = await navigator.serviceWorker.getRegistration();
107
95
  if (registration && registration.active) {
108
- window.currentSWVersion = currentVersion;
96
+ window.currentSWVersion = currentVersion ?? undefined;
109
97
  window.dispatchEvent(new CustomEvent("sw-version-detected"));
110
98
  await waitForController();
111
99
  if (!window.swReady) {
@@ -115,9 +103,9 @@ export async function initServiceWorker() {
115
103
  } else if (currentVersion && currentVersion !== manifest.version) {
116
104
  window.swAvailableVersion = manifest.version;
117
105
  window.swUpdateRequired =
118
- currentVersion < (manifest.minSupportedVersion || "0.0.0");
106
+ semverCompare(currentVersion, manifest.minSupportedVersion || "0.0.0") < 0;
119
107
 
120
- if (AUTO_REGISTER) {
108
+ if (AUTO_UPDATE) {
121
109
  const registration = await navigator.serviceWorker.getRegistration();
122
110
  if (registration && registration.waiting) {
123
111
  if (AUTO_ACTIVATE) {
@@ -163,7 +151,9 @@ export async function initServiceWorker() {
163
151
  }
164
152
  return;
165
153
  }
166
- } catch {}
154
+ } catch {
155
+ // Handle the case where no existing active registration is found
156
+ }
167
157
 
168
158
  console.error("Service Worker initialization failed:", error);
169
159
  window.swError = true;
@@ -171,7 +161,8 @@ export async function initServiceWorker() {
171
161
  }
172
162
  }
173
163
 
174
- export async function handleUpdateApproved(newVersion) {
164
+ /** Accept a pending SW update. Reloads the page once the new SW takes control. */
165
+ export async function handleUpdateApproved(newVersion${T("string")})${R("Promise<void>")}{
175
166
  const registration = await navigator.serviceWorker.getRegistration();
176
167
  if (registration && registration.waiting) {
177
168
  registration.waiting.postMessage({ type: "SKIP_WAITING" });
@@ -186,81 +177,61 @@ export async function handleUpdateApproved(newVersion) {
186
177
  }
187
178
  }
188
179
 
189
- export async function skipWaiting() {
180
+ /** Activate a waiting SW without reloading. Useful when you handle the transition yourself. */
181
+ export async function skipWaiting()${R("Promise<void>")}{
190
182
  const registration = await navigator.serviceWorker.ready;
191
183
  if (registration.waiting) {
192
184
  registration.waiting.postMessage({ type: "SKIP_WAITING" });
193
185
  }
194
186
  }
187
+ `;
188
+ const simpleCode = `/**
189
+ * Swoff SW Injector (Simple Mode)
190
+ * Fixed SW URL with hash-based cache name — no versioned URLs or update events.
191
+ *
192
+ * Usage:
193
+ * import { initServiceWorker } from './swoff/sw/injector.${ext}';
194
+ * initServiceWorker();
195
+ *
196
+ * Window events:
197
+ * sw-ready - SW active and controlling page
198
+ * sw-error - SW registration failed
199
+ *
200
+ * Window properties:
201
+ * window.currentSWVersion - "simple"
202
+ * window.swReady - SW is active
203
+ * window.swError - Registration failed
204
+ */
205
+ async function waitForController()${R("Promise<void>")}{
206
+ return new Promise<void>((resolve) => {
207
+ if (navigator.serviceWorker.controller) {
208
+ resolve();
209
+ } else {
210
+ navigator.serviceWorker.addEventListener("controllerchange", () => resolve(), { once: true });
211
+ }
212
+ });
213
+ }
195
214
 
196
- // --- PWA Install Support ---
197
- ${pwaEnabled ? `
198
- window.addEventListener("beforeinstallprompt", (e) => {
199
- window.deferredInstallPrompt = e;
200
- window.pwaInstallable = true;
201
-
202
- if (${preventDefaultInstall}) {
203
- e.preventDefault();
215
+ /** Register the SW with a fixed URL (non-versioned mode). Dispatches sw-ready/sw-error. */
216
+ export async function initServiceWorker()${R("Promise<void>")}{
217
+ if (!("serviceWorker" in navigator)) {
218
+ console.warn("Service Workers not supported");
219
+ return;
220
+ }
221
+ try {
222
+ const registration = await navigator.serviceWorker.register("/sw.js");
223
+ window.currentSWVersion = "simple";
224
+ window.dispatchEvent(new CustomEvent("sw-version-detected"));
225
+ await waitForController();
226
+ window.dispatchEvent(new CustomEvent("sw-ready"));
227
+ } catch (error) {
228
+ console.error("Service Worker registration failed:", error);
229
+ window.swError = true;
230
+ window.dispatchEvent(new CustomEvent("sw-error"));
204
231
  }
205
-
206
- window.dispatchEvent(
207
- new CustomEvent("pwa-installable", {
208
- detail: { isInstallable: true },
209
- })
210
- );
211
- });
212
-
213
- window.addEventListener("appinstalled", () => {
214
- window.deferredInstallPrompt = null;
215
- window.pwaInstallable = false;
216
-
217
- window.dispatchEvent(
218
- new CustomEvent("pwa-installed", {
219
- detail: { outcome: "accepted" },
220
- })
221
- );
222
- });
223
- ` : ""}
224
- ${mutationQueueEnabled ? `
225
- // --- Mutation Queue Listener ---
226
- window.addEventListener("online", processMutationQueue);
227
- ` : ""}
228
- // --- SW Message Listener ---
229
- if (typeof window !== "undefined" && "serviceWorker" in navigator) {
230
- navigator.serviceWorker.addEventListener("message", (event) => {
231
- if (event.data.type === "SW_PROGRESS") {
232
- const { percent, downloaded, total } = event.data;
233
- window.dispatchEvent(
234
- new CustomEvent("sw-progress", {
235
- detail: { percent, downloaded, total },
236
- })
237
- );
238
- }
239
- if (event.data.type === "BACKGROUND_SYNC_COMPLETE") {
240
- const { succeeded, failed, tags } = event.data.detail;
241
- window.dispatchEvent(
242
- new CustomEvent("mutation-sync-complete", {
243
- detail: { succeeded, failed },
244
- })
245
- );
246
- if (tags && tags.length > 0) {
247
- window.dispatchEvent(
248
- new CustomEvent("cache-invalidated", { detail: { tags } })
249
- );
250
- }
251
- window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
252
- }
253
- ${crossTabSync ? `
254
- if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
255
- window.dispatchEvent(
256
- new CustomEvent("cache-invalidated", {
257
- detail: { tags: [event.data.tag] },
258
- })
259
- );
260
- }
261
- ` : ""} });
262
232
  }
263
233
  `;
264
- writeFile(ctx, `sw-injector.${ext}`, code);
234
+ const code = versionEnabled ? versionedCode : simpleCode;
235
+ writeFile(ctx, `sw/injector.${ext}`, code);
265
236
  }
266
237
  //# sourceMappingURL=sw-injector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;IACnD,MAAM,qBAAqB,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC5E,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAEpB,MAAM,oBAAoB,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;IAE/D,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;IAErD,MAAM,IAAI,GAAG;;;;;8DAK+C,GAAG;;;;;;;;;;;;;;;;;+EAiBc,WAAW,CAAC,CAAC,CAAC;kFACX,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;EAarF,oBAAoB,CAAC,CAAC,CAAC,0DAA0D,GAAG;CACrF,CAAC,CAAC,CAAC,EAAE;wBACkB,YAAY;wBACZ,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgJlC,UAAU,CAAC,CAAC,CAAC;;;;;QAKP,qBAAqB;;;;;;;;;;;;;;;;;;;;;CAqB5B,CAAC,CAAC,CAAC,EAAE;EACJ,oBAAoB,CAAC,CAAC,CAAC;;;CAGxB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BJ,YAAY,CAAC,CAAC,CAAC;;;;;;;;CAQhB,CAAC,CAAC,CAAC,EAAE;;CAEL,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;IAChE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC;IACzE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,aAAa,GAAG;;;;;8DAKsC,GAAG;;;;;;;;;;;;;;;;;;;sBAmB3C,UAAU;wBACR,YAAY;;0BAEV,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;oCAkBjC,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;gDAUN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oCAAoC,CAAC;;;;;;;;;;;;;2CAa3D,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAoFN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;qCAgBnD,CAAC,CAAC,eAAe,CAAC;;;;;;CAMtD,CAAC;IAEA,MAAM,UAAU,GAAG;;;;;8DAKyC,GAAG;;;;;;;;;;;;oCAY7B,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;2CAWX,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;CAiB5D,CAAC;IAEA,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC;IAEzD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
@@ -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"}