@swoff/cli 0.3.13 → 0.3.14

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 (224) hide show
  1. package/README.md +28 -32
  2. package/dist/index.js +2 -13
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/cli/help.js +3 -8
  5. package/dist/lib/cli/help.js.map +1 -1
  6. package/dist/lib/commands/add.js +29 -87
  7. package/dist/lib/commands/add.js.map +1 -1
  8. package/dist/lib/commands/generate-guide.js +16 -30
  9. package/dist/lib/commands/generate-guide.js.map +1 -1
  10. package/dist/lib/commands/generate.js +31 -6
  11. package/dist/lib/commands/generate.js.map +1 -1
  12. package/dist/lib/commands/init.js +188 -11
  13. package/dist/lib/commands/init.js.map +1 -1
  14. package/dist/lib/commands/validate.js +2 -4
  15. package/dist/lib/commands/validate.js.map +1 -1
  16. package/dist/lib/config/loader.js +34 -25
  17. package/dist/lib/config/loader.js.map +1 -1
  18. package/dist/lib/config/validator.js +202 -96
  19. package/dist/lib/config/validator.js.map +1 -1
  20. package/dist/lib/generators/file-generators/api-config.js +1 -1
  21. package/dist/lib/generators/file-generators/api-config.js.map +1 -1
  22. package/dist/lib/generators/file-generators/auth-adapter.js +13 -0
  23. package/dist/lib/generators/file-generators/auth-adapter.js.map +1 -0
  24. package/dist/lib/generators/file-generators/auth-check.js +40 -0
  25. package/dist/lib/generators/file-generators/auth-check.js.map +1 -0
  26. package/dist/lib/generators/file-generators/auth-store.js +2 -5
  27. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  28. package/dist/lib/generators/file-generators/background-sync.js +1 -1
  29. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  30. package/dist/lib/generators/file-generators/cache.js +2 -1
  31. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  32. package/dist/lib/generators/file-generators/client-injector-bundle.js +15 -0
  33. package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -0
  34. package/dist/lib/generators/file-generators/client-injector.js +2 -1
  35. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  36. package/dist/lib/generators/file-generators/connectivity-manager.js +12 -0
  37. package/dist/lib/generators/file-generators/connectivity-manager.js.map +1 -0
  38. package/dist/lib/generators/file-generators/connectivity.js +12 -0
  39. package/dist/lib/generators/file-generators/connectivity.js.map +1 -0
  40. package/dist/lib/generators/file-generators/context.js +7 -0
  41. package/dist/lib/generators/file-generators/context.js.map +1 -1
  42. package/dist/lib/generators/file-generators/fetch-wrapper.js +1 -1
  43. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  44. package/dist/lib/generators/file-generators/generate-framework-adapters.js +29 -20
  45. package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
  46. package/dist/lib/generators/file-generators/gql-wrapper.js +2 -2
  47. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  48. package/dist/lib/generators/file-generators/guide-generator.js +51 -1
  49. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  50. package/dist/lib/generators/file-generators/invalidation-tags.js +2 -3
  51. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  52. package/dist/lib/generators/file-generators/mutation-queue.js +2 -2
  53. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  54. package/dist/lib/generators/file-generators/mutation-state.js +1 -1
  55. package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
  56. package/dist/lib/generators/file-generators/notification-generator.js +6 -0
  57. package/dist/lib/generators/file-generators/notification-generator.js.map +1 -0
  58. package/dist/lib/generators/file-generators/open-db.js +6 -0
  59. package/dist/lib/generators/file-generators/open-db.js.map +1 -0
  60. package/dist/lib/generators/file-generators/push.js +1 -2
  61. package/dist/lib/generators/file-generators/push.js.map +1 -1
  62. package/dist/lib/generators/file-generators/pwa-install.js +2 -6
  63. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  64. package/dist/lib/generators/file-generators/server-push.js +1 -1
  65. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  66. package/dist/lib/generators/file-generators/storage-generator.js +6 -0
  67. package/dist/lib/generators/file-generators/storage-generator.js.map +1 -0
  68. package/dist/lib/generators/file-generators/storage-notify.js +6 -0
  69. package/dist/lib/generators/file-generators/storage-notify.js.map +1 -0
  70. package/dist/lib/generators/file-generators/storage.js +6 -0
  71. package/dist/lib/generators/file-generators/storage.js.map +1 -0
  72. package/dist/lib/generators/file-generators/sw-generator-build.js +96 -45
  73. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  74. package/dist/lib/generators/file-generators/sw-injector.js +0 -4
  75. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  76. package/dist/lib/generators/file-generators/sw-template.js +8 -9
  77. package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
  78. package/dist/lib/generators/file-generators/sw-version-gen.js +32 -0
  79. package/dist/lib/generators/file-generators/sw-version-gen.js.map +1 -0
  80. package/dist/lib/generators/file-generators/swoff-api-bundle.js +51 -0
  81. package/dist/lib/generators/file-generators/swoff-api-bundle.js.map +1 -0
  82. package/dist/lib/generators/sw-build-utils.js +69 -0
  83. package/dist/lib/generators/sw-build-utils.js.map +1 -0
  84. package/dist/lib/generators/sw-generator.js +23 -34
  85. package/dist/lib/generators/sw-generator.js.map +1 -1
  86. package/dist/lib/generators/sw-sections/activate-handler.js +74 -23
  87. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  88. package/dist/lib/generators/sw-sections/assemble-sw.js +15 -94
  89. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  90. package/dist/lib/generators/sw-sections/background-sync-handler.js +74 -72
  91. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  92. package/dist/lib/generators/sw-sections/batch-refresh-queue.js +102 -0
  93. package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -0
  94. package/dist/lib/generators/sw-sections/config-header.js +2 -3
  95. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  96. package/dist/lib/generators/sw-sections/default-template.js +59 -6
  97. package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
  98. package/dist/lib/generators/sw-sections/fetch-handler.js +621 -495
  99. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  100. package/dist/lib/generators/sw-sections/install-handler.js +40 -23
  101. package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
  102. package/dist/lib/generators/sw-sections/message-handler.js +25 -8
  103. package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
  104. package/dist/lib/generators/sw-sections/server-push-handler.js +10 -14
  105. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  106. package/dist/lib/generators/sw-sections/shared.js +68 -0
  107. package/dist/lib/generators/sw-sections/shared.js.map +1 -0
  108. package/dist/lib/generators/sw-sections/sw-push.js +2 -6
  109. package/dist/lib/generators/sw-sections/sw-push.js.map +1 -1
  110. package/dist/lib/generators/sw-sections/tag-management.js +55 -86
  111. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  112. package/dist/lib/generators/swoff-files-generator.js +183 -59
  113. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  114. package/dist/lib/shared/config-types.js +67 -63
  115. package/dist/lib/shared/config-types.js.map +1 -1
  116. package/dist/lib/shared/feature-registry.js +198 -0
  117. package/dist/lib/shared/feature-registry.js.map +1 -0
  118. package/dist/lib/utils/detect-framework.js +52 -9
  119. package/dist/lib/utils/detect-framework.js.map +1 -1
  120. package/dist/runtime/api-config.js +2 -2
  121. package/dist/runtime/api-config.js.map +1 -1
  122. package/dist/runtime/auth-adapter.js +179 -0
  123. package/dist/runtime/auth-adapter.js.map +1 -0
  124. package/dist/runtime/auth-state.js +16 -6
  125. package/dist/runtime/auth-state.js.map +1 -1
  126. package/dist/runtime/auth-store.js +219 -213
  127. package/dist/runtime/auth-store.js.map +1 -1
  128. package/dist/runtime/auth-user.js +5 -19
  129. package/dist/runtime/auth-user.js.map +1 -1
  130. package/dist/runtime/background-sync.js +5 -2
  131. package/dist/runtime/background-sync.js.map +1 -1
  132. package/dist/runtime/cache.js +91 -3
  133. package/dist/runtime/cache.js.map +1 -1
  134. package/dist/runtime/client-injector-bundle.js +314 -0
  135. package/dist/runtime/client-injector-bundle.js.map +1 -0
  136. package/dist/runtime/client-injector.js +173 -30
  137. package/dist/runtime/client-injector.js.map +1 -1
  138. package/dist/runtime/connectivity-manager.js +83 -0
  139. package/dist/runtime/connectivity-manager.js.map +1 -0
  140. package/dist/runtime/connectivity.js +83 -0
  141. package/dist/runtime/connectivity.js.map +1 -0
  142. package/dist/runtime/fetch-wrapper.js +26 -59
  143. package/dist/runtime/fetch-wrapper.js.map +1 -1
  144. package/dist/runtime/gql-wrapper.js +15 -9
  145. package/dist/runtime/gql-wrapper.js.map +1 -1
  146. package/dist/runtime/invalidation-tags.js +12 -89
  147. package/dist/runtime/invalidation-tags.js.map +1 -1
  148. package/dist/runtime/mutation-queue.js +83 -31
  149. package/dist/runtime/mutation-queue.js.map +1 -1
  150. package/dist/runtime/mutation-state.js +3 -3
  151. package/dist/runtime/mutation-state.js.map +1 -1
  152. package/dist/runtime/notification.js +65 -0
  153. package/dist/runtime/notification.js.map +1 -0
  154. package/dist/runtime/open-db.js +41 -0
  155. package/dist/runtime/open-db.js.map +1 -0
  156. package/dist/runtime/push.js +15 -27
  157. package/dist/runtime/push.js.map +1 -1
  158. package/dist/runtime/pwa-injector.js +3 -32
  159. package/dist/runtime/pwa-injector.js.map +1 -1
  160. package/dist/runtime/pwa-prompt.js +35 -3
  161. package/dist/runtime/pwa-prompt.js.map +1 -1
  162. package/dist/runtime/reset.js +39 -42
  163. package/dist/runtime/reset.js.map +1 -1
  164. package/dist/runtime/server-push.js +23 -13
  165. package/dist/runtime/server-push.js.map +1 -1
  166. package/dist/runtime/storage.js +32 -0
  167. package/dist/runtime/storage.js.map +1 -0
  168. package/dist/runtime/sw-injector.js +19 -178
  169. package/dist/runtime/sw-injector.js.map +1 -1
  170. package/dist/runtime/sw-version.js +13 -0
  171. package/dist/runtime/sw-version.js.map +1 -0
  172. package/dist/runtime/swoff-api-bundle.js +1491 -0
  173. package/dist/runtime/swoff-api-bundle.js.map +1 -0
  174. package/dist/runtime/type-definitions.js +8 -7
  175. package/dist/runtime/type-definitions.js.map +1 -1
  176. package/dist/runtime/utils.js +4 -0
  177. package/dist/runtime/utils.js.map +1 -1
  178. package/dist/swoff-nkdhhmw8. +18 -0
  179. package/dist/swoff.js +5 -0
  180. package/package.json +1 -1
  181. package/templates/react/useAuth.jsx +7 -4
  182. package/templates/react/useAuth.tsx +14 -14
  183. package/templates/react/useBackgroundSync.jsx +5 -4
  184. package/templates/react/useBackgroundSync.tsx +5 -4
  185. package/templates/react/useCachedFetch.jsx +8 -0
  186. package/templates/react/useCachedFetch.tsx +8 -0
  187. package/templates/react/useMutation.jsx +60 -56
  188. package/templates/react/useMutation.tsx +65 -64
  189. package/templates/react/useMutationQueue.jsx +5 -1
  190. package/templates/react/useMutationQueue.tsx +8 -2
  191. package/templates/react/useMutationState.jsx +3 -4
  192. package/templates/react/useMutationState.tsx +4 -8
  193. package/templates/react/useNetworkStatus.jsx +57 -17
  194. package/templates/react/useNetworkStatus.tsx +42 -20
  195. package/templates/react/useOfflineAnalytics.jsx +42 -0
  196. package/templates/react/useOfflineAnalytics.tsx +50 -0
  197. package/templates/react/usePwaInstall.jsx +26 -0
  198. package/templates/react/usePwaInstall.tsx +26 -0
  199. package/templates/react/useSWUpdate.jsx +7 -52
  200. package/templates/react/useSWUpdate.tsx +12 -51
  201. package/templates/react/useStorageEstimate.jsx +56 -0
  202. package/templates/react/useStorageEstimate.tsx +67 -0
  203. package/dist/__tests__/assemble-sw.test.js +0 -145
  204. package/dist/__tests__/assemble-sw.test.js.map +0 -1
  205. package/dist/__tests__/cli-integration.test.js +0 -231
  206. package/dist/__tests__/cli-integration.test.js.map +0 -1
  207. package/dist/__tests__/config-types.test.js +0 -124
  208. package/dist/__tests__/config-types.test.js.map +0 -1
  209. package/dist/__tests__/detect-language.test.js +0 -51
  210. package/dist/__tests__/detect-language.test.js.map +0 -1
  211. package/dist/__tests__/file-generators.test.js +0 -228
  212. package/dist/__tests__/file-generators.test.js.map +0 -1
  213. package/dist/__tests__/glob-matcher.test.js +0 -86
  214. package/dist/__tests__/glob-matcher.test.js.map +0 -1
  215. package/dist/__tests__/invalidation-tags.test.js +0 -283
  216. package/dist/__tests__/invalidation-tags.test.js.map +0 -1
  217. package/dist/__tests__/loader.test.js +0 -77
  218. package/dist/__tests__/loader.test.js.map +0 -1
  219. package/dist/__tests__/validator.test.js +0 -340
  220. package/dist/__tests__/validator.test.js.map +0 -1
  221. package/templates/react/useCacheInvalidation.jsx +0 -19
  222. package/templates/react/useCacheInvalidation.tsx +0 -52
  223. package/templates/react/useIsFetching.jsx +0 -32
  224. package/templates/react/useIsFetching.tsx +0 -32
@@ -1,13 +1,15 @@
1
- import { T, R, PT, AS } from "./utils.js";
1
+ import { T, R, AS } from "./utils.js";
2
2
  export function generatePushCode(ctx) {
3
3
  const { ext, ts } = ctx;
4
- const vapidKey = ctx.vapidKey;
5
4
  return `/**
6
5
  * Swoff Push Notifications
7
6
  * Push subscription management with IndexedDB persistence.
8
7
  *
9
8
  * Usage:
10
- * import { subscribeToPush, unsubscribeFromPush, isSubscribed } from './swoff/realtime/notifications.${ext}';
9
+ * import { subscribeToPush, unsubscribeFromPush, isSubscribed } from './swoff/push-notification/index.${ext}';
10
+ *
11
+ * // Replace with your VAPID public key:
12
+ * const VAPID_PUBLIC_KEY = "YOUR_VAPID_PUBLIC_KEY_HERE";
11
13
  *
12
14
  * // Enable (triggers permission prompt)
13
15
  * const subscription = await subscribeToPush();
@@ -24,28 +26,14 @@ export function generatePushCode(ctx) {
24
26
  * push-subscription-changed - Subscribed/unsubscribed (detail: { subscribed })
25
27
  */
26
28
 
29
+ import { openDB } from "../db.${ext}";
30
+
27
31
  const SUBSCRIPTION_DB = "swoff-push";
28
32
  const SUBSCRIPTION_STORE = "subscription";
29
- // Bump this when adding new indexes/stores for schema migration
30
- const DB_VERSION = 1;
31
- const VAPID_PUBLIC_KEY = ${JSON.stringify(vapidKey)};
33
+ const VAPID_PUBLIC_KEY = "";
32
34
 
33
35
  let permissionState${T(ts, "NotificationPermission | undefined")} = typeof Notification !== "undefined" ? Notification.permission : undefined;
34
36
 
35
- function openPushDB()${R(ts, "Promise<IDBDatabase>")}{
36
- return new Promise${PT(ts, "IDBDatabase")}((resolve, reject) => {
37
- const request = indexedDB.open(SUBSCRIPTION_DB, DB_VERSION);
38
- request.onupgradeneeded = (e) => {
39
- const db = (e.target${AS(ts, "IDBOpenDBRequest")}).result;
40
- if (!db.objectStoreNames.contains(SUBSCRIPTION_STORE)) {
41
- db.createObjectStore(SUBSCRIPTION_STORE, { keyPath: "id" });
42
- }
43
- };
44
- request.onsuccess = (e) => resolve((e.target${AS(ts, "IDBOpenDBRequest")}).result);
45
- request.onerror = (e) => reject((e.target${AS(ts, "IDBRequest")}).error);
46
- });
47
- }
48
-
49
37
  /** Request notification permission from the user. Returns true if granted. */
50
38
  export async function requestNotificationPermission()${R(ts, "Promise<boolean>")}{
51
39
  if (permissionState === "granted") return true;
@@ -69,7 +57,7 @@ export async function getPushSubscription()${R(ts, "Promise<PushSubscription | n
69
57
  }
70
58
  }
71
59
 
72
- /** Subscribe to push notifications. Returns the subscription or null if permission denied. Uses the VAPID public key from your swoff.config.json. */
60
+ /** Subscribe to push notifications. Returns the subscription or null if permission denied. Update VAPID_PUBLIC_KEY at the top with your key. */
73
61
  export async function subscribeToPush()${R(ts, "Promise<PushSubscription | null>")}{
74
62
  const granted = await requestNotificationPermission();
75
63
  if (!granted) return null;
@@ -80,7 +68,7 @@ export async function subscribeToPush()${R(ts, "Promise<PushSubscription | null>
80
68
  applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)${AS(ts, "BufferSource")},
81
69
  });
82
70
 
83
- const db = await openPushDB();
71
+ const db = await openDB(SUBSCRIPTION_DB, SUBSCRIPTION_STORE, "id");
84
72
  const tx = db.transaction(SUBSCRIPTION_STORE, "readwrite");
85
73
  tx.objectStore(SUBSCRIPTION_STORE).put({
86
74
  id: "current",
@@ -89,8 +77,8 @@ export async function subscribeToPush()${R(ts, "Promise<PushSubscription | null>
89
77
  subscribedAt: Date.now(),
90
78
  });
91
79
  await new Promise<void>((resolve, reject) => {
92
- tx.oncomplete = () => resolve();
93
- tx.onerror = () => reject(tx.error);
80
+ tx.oncomplete = () => { db.close(); resolve(); };
81
+ tx.onerror = () => { db.close(); reject(tx.error); };
94
82
  });
95
83
 
96
84
  window.dispatchEvent(
@@ -107,12 +95,12 @@ export async function unsubscribeFromPush()${R(ts, "Promise<void>")}{
107
95
 
108
96
  await subscription.unsubscribe();
109
97
 
110
- const db = await openPushDB();
98
+ const db = await openDB(SUBSCRIPTION_DB, SUBSCRIPTION_STORE, "id");
111
99
  const tx = db.transaction(SUBSCRIPTION_STORE, "readwrite");
112
100
  tx.objectStore(SUBSCRIPTION_STORE).delete("current");
113
101
  await new Promise<void>((resolve, reject) => {
114
- tx.oncomplete = () => resolve();
115
- tx.onerror = () => reject(tx.error);
102
+ tx.oncomplete = () => { db.close(); resolve(); };
103
+ tx.onerror = () => { db.close(); reject(tx.error); };
116
104
  });
117
105
 
118
106
  window.dispatchEvent(
@@ -1 +1 @@
1
- {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/runtime/push.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,gBAAgB,CAAC,GAA0C;IACzE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,OAAO;;;;;0GAKiG,GAAG;;;;;;;;;;;;;;;;;;;;;2BAqBlF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;;qBAE9B,CAAC,CAAC,EAAE,EAAE,oCAAoC,CAAC;;uBAEzC,CAAC,CAAC,EAAE,EAAE,sBAAsB,CAAC;sBAC9B,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC;;;4BAGf,EAAE,CAAC,EAAE,EAAE,kBAAkB,CAAC;;;;;kDAKJ,EAAE,CAAC,EAAE,EAAE,kBAAkB,CAAC;+CAC7B,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC;;;;;uDAKZ,CAAC,CAAC,EAAE,EAAE,kBAAkB,CAAC;;;;;;;;;;;;;6CAanC,CAAC,CAAC,EAAE,EAAE,kCAAkC,CAAC;;;;;;;;;;yCAU7C,CAAC,CAAC,EAAE,EAAE,kCAAkC,CAAC;;;;;;;mEAOf,EAAE,CAAC,EAAE,EAAE,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;6CAwB5C,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;sCAoB7B,CAAC,CAAC,EAAE,EAAE,kBAAkB,CAAC;;;;;;6CAMlB,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC;;;;;;CAMlF,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/runtime/push.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAM,EAAE,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAClD,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,OAAO;;;;;2GAKkG,GAAG;;;;;;;;;;;;;;;;;;;;gCAoB9E,GAAG;;;;;;qBAMd,CAAC,CAAC,EAAE,EAAE,oCAAoC,CAAC;;;uDAGT,CAAC,CAAC,EAAE,EAAE,kBAAkB,CAAC;;;;;;;;;;;;;6CAanC,CAAC,CAAC,EAAE,EAAE,kCAAkC,CAAC;;;;;;;;;;yCAU7C,CAAC,CAAC,EAAE,EAAE,kCAAkC,CAAC;;;;;;;mEAOf,EAAE,CAAC,EAAE,EAAE,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;6CAwB5C,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;sCAoB7B,CAAC,CAAC,EAAE,EAAE,kBAAkB,CAAC;;;;;;6CAMlB,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC;;;;;;CAMlF,CAAC;AACF,CAAC"}
@@ -1,44 +1,15 @@
1
- import { R } from "./utils.js";
2
1
  export function generatePwaInjectorCode(ctx) {
3
- const { ext, ts } = ctx;
4
- const preventLine = ctx.preventDefaultInstall ? " e.preventDefault();\n" : "";
2
+ const { ext } = ctx;
5
3
  return `/**
6
4
  * Swoff PWA Injector
7
- * Service worker installation event wiring for PWA installability.
5
+ * Re-exports setupPwaInstall from the prompt module.
8
6
  *
9
7
  * Usage:
10
8
  * import { setupPwaInstall } from './swoff/pwa/injector.${ext}';
11
9
  * setupPwaInstall();
12
- *
13
- * Window properties:
14
- * window.deferredInstallPrompt - The captured BeforeInstallPromptEvent
15
10
  */
16
11
 
17
- /** Set up beforeinstallprompt and appinstalled event listeners. Call once at app startup. */
18
- export function setupPwaInstall()${R(ts, "void")}{
19
- window.addEventListener("beforeinstallprompt", (e) => {
20
- window.deferredInstallPrompt = e as BeforeInstallPromptEvent;
21
- window.pwaInstallable = true;
22
-
23
- ${preventLine}
24
- window.dispatchEvent(
25
- new CustomEvent("pwa-installable", {
26
- detail: { isInstallable: true },
27
- })
28
- );
29
- });
30
-
31
- window.addEventListener("appinstalled", () => {
32
- window.deferredInstallPrompt = null;
33
- window.pwaInstallable = false;
34
-
35
- window.dispatchEvent(
36
- new CustomEvent("pwa-installed", {
37
- detail: { outcome: "accepted" },
38
- })
39
- );
40
- });
41
- }
12
+ export { setupPwaInstall } from "./prompt.${ext}";
42
13
  `;
43
14
  }
44
15
  //# sourceMappingURL=pwa-injector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pwa-injector.js","sourceRoot":"","sources":["../../src/runtime/pwa-injector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,uBAAuB,CAAC,GAAwD;IAC9F,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,MAAM,WAAW,GAAG,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE,CAAC;IAEjF,OAAO;;;;;6DAKoD,GAAG;;;;;;;;mCAQ7B,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;EAK9C,WAAW;;;;;;;;;;;;;;;;;;;CAmBZ,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"pwa-injector.js","sourceRoot":"","sources":["../../src/runtime/pwa-injector.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,GAAmB;IACzD,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAEpB,OAAO;;;;;6DAKoD,GAAG;;;;4CAIpB,GAAG;CAC9C,CAAC;AACF,CAAC"}
@@ -1,12 +1,17 @@
1
- import { R } from "./utils.js";
1
+ import { R, AS } from "./utils.js";
2
2
  export function generatePwaPromptCode(ctx) {
3
3
  const { ext, ts } = ctx;
4
+ const preventLine = ctx.preventDefaultInstall
5
+ ? " e.preventDefault();\n"
6
+ : "";
4
7
  return `/**
5
8
  * Swoff PWA Install Prompt
6
- * Installability check and manual prompt trigger.
9
+ * Installability check, manual prompt trigger, and event listener setup.
7
10
  *
8
11
  * Usage:
9
- * import { isInstallable, promptInstall } from './swoff/pwa/prompt.${ext}';
12
+ * import { setupPwaInstall, isInstallable, promptInstall } from './swoff/pwa/prompt.${ext}';
13
+ *
14
+ * setupPwaInstall(); // sets up beforeinstallprompt and appinstalled listeners
10
15
  *
11
16
  * if (isInstallable()) {
12
17
  * const result = await promptInstall();
@@ -16,8 +21,35 @@ export function generatePwaPromptCode(ctx) {
16
21
  * window.deferredInstallPrompt - The captured BeforeInstallPromptEvent
17
22
  */
18
23
 
24
+ /** Set up beforeinstallprompt and appinstalled event listeners. Call once at app startup. */
25
+ export function setupPwaInstall()${R(ts, "void")}{
26
+ window.addEventListener("beforeinstallprompt", (e) => {
27
+ window.deferredInstallPrompt = e${AS(ts, "BeforeInstallPromptEvent")};
28
+ window.pwaInstallable = true;
29
+
30
+ ${preventLine}
31
+ window.dispatchEvent(
32
+ new CustomEvent("pwa-installable", {
33
+ detail: { isInstallable: true },
34
+ })
35
+ );
36
+ });
37
+
38
+ window.addEventListener("appinstalled", () => {
39
+ window.deferredInstallPrompt = null;
40
+ window.pwaInstallable = false;
41
+
42
+ window.dispatchEvent(
43
+ new CustomEvent("pwa-installed", {
44
+ detail: { outcome: "accepted" },
45
+ })
46
+ );
47
+ });
48
+ }
49
+
19
50
  /** Check if the PWA install prompt is available (user has met install criteria). */
20
51
  export function isInstallable()${R(ts, "boolean")}{
52
+ if (typeof window === "undefined") return false;
21
53
  return !!window.deferredInstallPrompt;
22
54
  }
23
55
 
@@ -1 +1 @@
1
- {"version":3,"file":"pwa-prompt.js","sourceRoot":"","sources":["../../src/runtime/pwa-prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,qBAAqB,CAAC,GAAmB;IACvD,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAExB,OAAO;;;;;wEAK+D,GAAG;;;;;;;;;;;iCAW1C,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;;;;uCAKV,CAAC,CAAC,EAAE,EAAE,8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2B3E,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"pwa-prompt.js","sourceRoot":"","sources":["../../src/runtime/pwa-prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,UAAU,qBAAqB,CACnC,GAAyD;IAEzD,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,MAAM,WAAW,GAAG,GAAG,CAAC,qBAAqB;QAC3C,CAAC,CAAC,2BAA2B;QAC7B,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;yFAKgF,GAAG;;;;;;;;;;;;;mCAazD,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;sCAEV,EAAE,CAAC,EAAE,EAAE,0BAA0B,CAAC;;;EAGtE,WAAW;;;;;;;;;;;;;;;;;;;;;iCAqBoB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;;;;;uCAMV,CAAC,CAAC,EAAE,EAAE,8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2B3E,CAAC;AACF,CAAC"}
@@ -1,49 +1,51 @@
1
- import { T, R } from "./utils.js";
1
+ import { T, O, R } from "./utils.js";
2
2
  export function generateResetCode(ctx) {
3
3
  const { ext, ts } = ctx;
4
4
  return `/**
5
5
  * Swoff Reset
6
6
  * Nuclear option: wipe all swoff data (caches, IndexedDB, localStorage),
7
- * unregister the service worker, then re-register from scratch.
7
+ * then instruct the service worker to clear and re-precache all assets.
8
+ *
9
+ * Unlike the old approach of unregister + re-register (which often failed to
10
+ * trigger the install event for byte-identical scripts), this sends a
11
+ * RESET_CACHE message to the active SW, which handles cache clearing and
12
+ * re-precaching internally.
8
13
  *
9
14
  * Usage:
10
15
  * import { resetSwoff } from './swoff/reset.${ext}';
11
16
  *
12
17
  * // Full reset (default)
13
18
  * const result = await resetSwoff();
14
- * console.log(result.unregistered, result.reregistered, result.warnings);
19
+ * console.log(result.warnings);
15
20
  *
16
21
  * // Selective reset — keep caches but reset IDB + SW
17
22
  * await resetSwoff({ clearCache: false });
18
23
  *
19
24
  * Events dispatched:
20
25
  * swoff:reset-start — before any cleanup begins
21
- * swoff:reset-complete — after everything finishes (detail: { unregistered, reregistered, warnings })
26
+ * swoff:reset-complete — after everything finishes (detail: { warnings })
22
27
  */
23
28
 
24
- import { initServiceWorker } from "./sw/injector.${ext}";
25
-
26
29
  export interface ResetSwoffOptions {
27
30
  /** Clear all Cache Storage caches (default: true) */
28
- clearCache${T(ts, "boolean")};
31
+ clearCache${O(ts, "boolean")};
29
32
  /** Delete all swoff-* IndexedDB databases (default: true) */
30
- clearIdb${T(ts, "boolean")};
33
+ clearIdb${O(ts, "boolean")};
31
34
  /** Clear localStorage keys like swRegisteredVersion (default: true) */
32
- clearStorage${T(ts, "boolean")};
33
- /** Unregister SW then re-register (default: true) */
34
- unregisterSW${T(ts, "boolean")};
35
+ clearStorage${O(ts, "boolean")};
36
+ /** Reset SW caches (clear + re-precache) (default: true) */
37
+ resetSwCache${O(ts, "boolean")};
35
38
  }
36
39
 
37
40
  const DEFAULT_OPTIONS: ResetSwoffOptions = {
38
41
  clearCache: true,
39
42
  clearIdb: true,
40
43
  clearStorage: true,
41
- unregisterSW: true,
44
+ resetSwCache: true,
42
45
  };
43
46
 
44
47
  const KNOWN_DB_NAMES = [
45
48
  "swoff-auth",
46
- "swoff-auth-user",
47
49
  "swoff-queue",
48
50
  "swoff-cache-tags",
49
51
  "swoff-push",
@@ -74,14 +76,14 @@ async function deleteSwoffDatabases(warnings${T(ts, "string[]")})${R(ts, "Promis
74
76
  }
75
77
  }
76
78
 
77
- /** Nuclear reset: wipes all swoff-persisted data, unregisters the SW, then re-registers from scratch. Returns detailed results. */
78
- export async function resetSwoff(opts${T(ts, "Partial<ResetSwoffOptions>")} = {})${R(ts, "Promise<{ unregistered: boolean; reregistered: boolean; warnings: string[] }>")}{
79
+ /** Nuclear reset: wipes all swoff-persisted data and resets the SW cache. Returns detailed results. */
80
+ export async function resetSwoff(opts${T(ts, "ResetSwoffOptions")} = {})${R(ts, "Promise<{ warnings: string[] }>")}{
79
81
  const options${T(ts, "ResetSwoffOptions")} = { ...DEFAULT_OPTIONS, ...opts };
80
82
  const warnings${T(ts, "string[]")} = [];
81
83
 
82
84
  window.dispatchEvent(new CustomEvent("swoff:reset-start"));
83
85
 
84
- // 1. Clear Cache Storage
86
+ // 1. Clear Cache Storage (belt-and-suspenders — SW also clears them)
85
87
  if (options.clearCache) {
86
88
  try {
87
89
  if (typeof caches !== "undefined") {
@@ -107,39 +109,34 @@ export async function resetSwoff(opts${T(ts, "Partial<ResetSwoffOptions>")} = {}
107
109
  }
108
110
  }
109
111
 
110
- // 4. Unregister SW
111
- let unregistered = false;
112
- if (options.unregisterSW) {
112
+ // 4. Tell the SW to clear its caches and re-precache all assets
113
+ if (options.resetSwCache) {
113
114
  try {
114
- if ("serviceWorker" in navigator) {
115
- const reg = await navigator.serviceWorker.getRegistration();
116
- if (reg) {
117
- unregistered = await reg.unregister();
115
+ if ("serviceWorker" in navigator && navigator.serviceWorker.controller) {
116
+ const registration = await navigator.serviceWorker.ready;
117
+ if (registration.active) {
118
+ await new Promise<void>((resolve) => {
119
+ const channel = new MessageChannel();
120
+ const timer = setTimeout(() => {
121
+ channel.port1.close();
122
+ resolve();
123
+ }, 10000);
124
+ channel.port1.onmessage = (event) => {
125
+ if (event.data.type === "RESET_CACHE_COMPLETE") {
126
+ clearTimeout(timer);
127
+ resolve();
128
+ }
129
+ };
130
+ registration.active?.postMessage({ type: "RESET_CACHE" }, [channel.port2]);
131
+ });
118
132
  }
119
133
  }
120
134
  } catch (e) {
121
- warnings.push(\`Failed to unregister SW: \${e}\`);
122
- }
123
-
124
- // Always clear the version marker before re-registering so initServiceWorker
125
- // detects a fresh install rather than trying to reuse a stale registration
126
- try {
127
- localStorage.removeItem("swRegisteredVersion");
128
- } catch { /* ignore */ }
129
- }
130
-
131
- // 5. Re-register SW
132
- let reregistered = false;
133
- if (options.unregisterSW) {
134
- try {
135
- await initServiceWorker();
136
- reregistered = true;
137
- } catch (e) {
138
- warnings.push(\`Failed to re-register SW: \${e}\`);
135
+ warnings.push(\`Failed to reset SW cache: \${e}\`);
139
136
  }
140
137
  }
141
138
 
142
- const result = { unregistered, reregistered, warnings };
139
+ const result = { warnings };
143
140
  window.dispatchEvent(new CustomEvent("swoff:reset-complete", { detail: result }));
144
141
  return result;
145
142
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reset.js","sourceRoot":"","sources":["../../src/runtime/reset.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,UAAU,iBAAiB,CAAC,GAAmB;IACnD,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,OAAO;;;;;;iDAMwC,GAAG;;;;;;;;;;;;;;mDAcD,GAAG;;;;cAIxC,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;YAElB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;gBAEZ,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;gBAEhB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;;;;;;;;;;;;;;;;;;8CAmBc,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;uCAyBlD,CAAC,CAAC,EAAE,EAAE,4BAA4B,CAAE,SAAS,CAAC,CAAC,EAAE,EAAE,+EAA+E,CAAC;iBACzJ,CAAC,CAAC,EAAE,EAAE,mBAAmB,CAAC;kBACzB,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkElC,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"reset.js","sourceRoot":"","sources":["../../src/runtime/reset.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,UAAU,iBAAiB,CAAC,GAAmB;IACnD,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,OAAO;;;;;;;;;;;iDAWwC,GAAG;;;;;;;;;;;;;;;;cAgBtC,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;YAElB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;gBAEZ,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;gBAEhB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;;;;;;;;;;;;;;;;;8CAkBc,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;uCAyBlD,CAAC,CAAC,EAAE,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,iCAAiC,CAAC;iBACjG,CAAC,CAAC,EAAE,EAAE,mBAAmB,CAAC;kBACzB,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DlC,CAAC;AACF,CAAC"}
@@ -1,9 +1,9 @@
1
- import { T, R } from "./utils.js";
1
+ import { T, R, AS } from "./utils.js";
2
2
  export function generateServerPushCode(ctx, pushType, endpoint, reconnectDelayMs) {
3
3
  const { ext, ts } = ctx;
4
4
  const invalidate = `
5
5
  // Forward to SW for cache invalidation
6
- if (navigator.serviceWorker.controller) {
6
+ if (navigator.serviceWorker?.controller) {
7
7
  for (const tag of tags) {
8
8
  navigator.serviceWorker.controller.postMessage({ type: "INVALIDATE_TAG", tag });
9
9
  }
@@ -15,6 +15,8 @@ export function generateServerPushCode(ctx, pushType, endpoint, reconnectDelayMs
15
15
  credentials: "include",
16
16
  signal: options.signal,
17
17
  }).then(async (response) => {
18
+ currentFetchController = null;
19
+ if (!active) { resolve(); return; }
18
20
  if (!response.ok || !response.body) { resolve(); return; }
19
21
  notifyStatus(true);
20
22
  const reader = response.body.getReader();
@@ -51,14 +53,15 @@ export function generateServerPushCode(ctx, pushType, endpoint, reconnectDelayMs
51
53
  return new Promise((resolve) => {
52
54
  try {
53
55
  const ws = new WebSocket(API_BASE + "${endpoint}");
56
+ currentWs = ws;
54
57
  ws.onopen = () => notifyStatus(true);
55
58
  ws.onmessage = (event) => {
56
59
  try { const d = JSON.parse(event.data); if (d.type === "invalidate" && d.tags) handleInvalidation(d.tags); } catch {}
57
60
  };
58
- ws.onclose = () => { notifyStatus(false); resolve(); };
59
- ws.onerror = () => { ws.close(); resolve(); };
61
+ ws.onclose = () => { currentWs = null; notifyStatus(false); resolve(); };
62
+ ws.onerror = () => { currentWs = null; ws.close(); resolve(); };
60
63
  if (options.signal) {
61
- options.signal.addEventListener("abort", () => ws.close());
64
+ options.signal.addEventListener("abort", () => { ws.close(); currentWs = null; });
62
65
  }
63
66
  } catch { resolve(); }
64
67
  });`;
@@ -70,7 +73,7 @@ export function generateServerPushCode(ctx, pushType, endpoint, reconnectDelayMs
70
73
  * This client-side helper provides status tracking and a manual start/stop API.
71
74
  *
72
75
  * Usage:
73
- * import { startPushEvents, stopPushEvents, isPushConnected } from "./realtime/server-push.${ext}";
76
+ * import { startPushEvents, stopPushEvents, isPushConnected } from "./server-push/client.${ext}";
74
77
  * startPushEvents();
75
78
  *
76
79
  * Server sends:
@@ -87,6 +90,8 @@ type PushEventOptions= {
87
90
  let active${T(ts, "boolean")} = false;
88
91
  let swConnected${T(ts, "boolean")} = false;
89
92
  let reconnectTimer${T(ts, "ReturnType<typeof setTimeout> | null")} = null;
93
+ let currentWs${T(ts, "WebSocket | null")} = null;
94
+ let currentFetchController${T(ts, "AbortController | null")} = null;
90
95
 
91
96
  function handleInvalidation(tags${T(ts, "string[]")})${R(ts, "void")}{${invalidate}
92
97
  window.dispatchEvent(new CustomEvent("cache-invalidated", { detail: { tags } }));
@@ -97,7 +102,7 @@ function notifyStatus(connected${T(ts, "boolean")})${R(ts, "void")}{
97
102
  }
98
103
 
99
104
  // Listen for SSE status from the SW
100
- if (typeof navigator !== "undefined" && navigator.serviceWorker) {
105
+ if (typeof navigator !== "undefined" && "serviceWorker" in navigator) {
101
106
  navigator.serviceWorker.addEventListener("message", (event) => {
102
107
  if (event.data?.type === "SSE_STATUS") {
103
108
  swConnected = event.data.connected;
@@ -106,7 +111,10 @@ if (typeof navigator !== "undefined" && navigator.serviceWorker) {
106
111
  });
107
112
  }
108
113
 
109
- async function connect(options${T(ts, "PushEventOptions")} = {} as PushEventOptions)${R(ts, "Promise<void>")}{
114
+ async function connect()${R(ts, "Promise<void>")}{
115
+ const controller = new AbortController();
116
+ currentFetchController = controller;
117
+ const options = { signal: controller.signal }${AS(ts, "PushEventOptions")};
110
118
  ${pushType === "sse" ? sseConnect : wsConnect}
111
119
  }
112
120
 
@@ -115,14 +123,14 @@ export async function startPushEvents(){
115
123
  if (active) return;
116
124
 
117
125
  // If SW is already controlling the page, skip client-side connection — the SW handles push events.
118
- if (navigator.serviceWorker.controller) return;
126
+ if (navigator.serviceWorker?.controller) return;
119
127
 
120
128
  // Listen for SW activation — if SW takes over, disconnect the client fallback.
121
129
  const onControllerChange = () => { stopPushEvents(); };
122
- navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
130
+ navigator.serviceWorker?.addEventListener("controllerchange", onControllerChange);
123
131
 
124
132
  active = true;
125
- let delay = ${reconnectDelayMs};
133
+ let delay = Math.max(1000, ${reconnectDelayMs});
126
134
  while (active) {
127
135
  await connect();
128
136
  if (!active) break;
@@ -130,13 +138,15 @@ export async function startPushEvents(){
130
138
  delay = Math.min(delay * 1.5, 30000); // cap at 30s
131
139
  }
132
140
 
133
- navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange);
141
+ navigator.serviceWorker?.removeEventListener("controllerchange", onControllerChange);
134
142
  }
135
143
 
136
- /** Stop listening for push events. */
144
+ /** Stop listening for push events and close the active connection. */
137
145
  export function stopPushEvents(){
138
146
  active = false;
139
147
  if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
148
+ if (currentWs) { currentWs.close(); currentWs = null; }
149
+ if (currentFetchController) { currentFetchController.abort(); currentFetchController = null; }
140
150
  }
141
151
 
142
152
  /** Check if the push connection is currently established. */
@@ -1 +1 @@
1
- {"version":3,"file":"server-push.js","sourceRoot":"","sources":["../../src/runtime/server-push.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,UAAU,sBAAsB,CACpC,GAAmB,EACnB,QAAgB,EAChB,QAAgB,EAChB,gBAAwB;IAExB,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAExB,MAAM,UAAU,GAAG;;;;;;IAMjB,CAAC;IAEH,MAAM,UAAU,GAAG;;wBAEG,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAoC1B,CAAC;IAEL,MAAM,SAAS,GAAG;;;6CAGyB,QAAQ;;;;;;;;;;;MAW/C,CAAC;IAEL,OAAO;;;oCAG2B,QAAQ;;;;;gGAKoD,GAAG;;;;;;;;sCAQ7D,GAAG;;;UAG/B,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC;;;YAGlB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;iBACX,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;oBACb,CAAC,CAAC,EAAE,EAAE,sCAAsC,CAAC;;kCAE/B,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,UAAU;;;;iCAIjD,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;;;;;;;;;;gCAclC,CAAC,CAAC,EAAE,EAAE,kBAAkB,CAAC,6BAA6B,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;IACxG,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;;;;;;;;;;;;;;;gBAe/B,gBAAgB;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"server-push.js","sourceRoot":"","sources":["../../src/runtime/server-push.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,UAAU,sBAAsB,CACpC,GAAmB,EACnB,QAAgB,EAChB,QAAgB,EAChB,gBAAwB;IAExB,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAExB,MAAM,UAAU,GAAG;;;;;;IAMjB,CAAC;IAEH,MAAM,UAAU,GAAG;;wBAEG,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAsC1B,CAAC;IAEL,MAAM,SAAS,GAAG;;;6CAGyB,QAAQ;;;;;;;;;;;;MAY/C,CAAC;IAEL,OAAO;;;oCAG2B,QAAQ;;;;;8FAKkD,GAAG;;;;;;;;sCAQ3D,GAAG;;;UAG/B,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC;;;YAGlB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;iBACX,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;oBACb,CAAC,CAAC,EAAE,EAAE,sCAAsC,CAAC;eAClD,CAAC,CAAC,EAAE,EAAE,kBAAkB,CAAC;4BACZ,CAAC,CAAC,EAAE,EAAE,wBAAwB,CAAC;;kCAEzB,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,UAAU;;;;iCAIjD,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;;;;;;;;;;0BAcxC,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;iDAGC,EAAE,CAAC,EAAE,EAAE,kBAAkB,CAAC;IACvE,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;;;;;;;;;;;;;;;+BAehB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;CAuB9C,CAAC;AACF,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { T, R } from "./utils.js";
2
+ export function generateStorageCode(ctx) {
3
+ const { ext, ts } = ctx;
4
+ return `/**
5
+ * Swoff Storage
6
+ * Pure utility functions for storage estimation and formatting.
7
+ *
8
+ * Usage:
9
+ * import { getStorageEstimate, formatBytes } from "./swoff/storage.${ext}";
10
+ * const { usage, quota, percentUsed } = await getStorageEstimate();
11
+ */
12
+
13
+ /** Get raw storage estimate. */
14
+ export async function getStorageEstimate()${R(ts, "Promise<{ usage: number; quota: number; percentUsed: number }>")}{
15
+ if (!navigator.storage?.estimate) {
16
+ return { usage: 0, quota: 0, percentUsed: 0 };
17
+ }
18
+ const { usage = 0, quota = 0 } = await navigator.storage.estimate();
19
+ const percentUsed = quota > 0 ? Math.round((usage / quota) * 100) : 0;
20
+ return { usage, quota, percentUsed };
21
+ }
22
+
23
+ /** Format bytes to a human-readable string (e.g. "1.5 MB"). */
24
+ export function formatBytes(bytes${T(ts, "number")})${R(ts, "string")}{
25
+ if (bytes === 0) return "0 B";
26
+ const units = ["B", "KB", "MB", "GB"];
27
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
28
+ return \`\${(bytes / Math.pow(1024, i)).toFixed(1)} \${units[i]}\`;
29
+ }
30
+ `;
31
+ }
32
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/runtime/storage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACrD,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,OAAO;;;;;wEAK+D,GAAG;;;;;4CAK/B,CAAC,CAAC,EAAE,EAAE,gEAAgE,CAAC;;;;;;;;;;mCAUhF,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;;;;;;CAMpE,CAAC;AACF,CAAC"}