@swoff/cli 0.3.13 → 0.3.15

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 (269) hide show
  1. package/README.md +28 -32
  2. package/dist/index.js +4 -15
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/cli/help.js +4 -9
  5. package/dist/lib/cli/help.js.map +1 -1
  6. package/dist/lib/commands/clean.js +8 -17
  7. package/dist/lib/commands/clean.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 +33 -7
  11. package/dist/lib/commands/generate.js.map +1 -1
  12. package/dist/lib/commands/init.js +259 -23
  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/minimal-config.js +64 -0
  19. package/dist/lib/config/minimal-config.js.map +1 -0
  20. package/dist/lib/config/validator.js +219 -110
  21. package/dist/lib/config/validator.js.map +1 -1
  22. package/dist/lib/generators/file-generators/api-config.js +1 -1
  23. package/dist/lib/generators/file-generators/api-config.js.map +1 -1
  24. package/dist/lib/generators/file-generators/auth-adapter.js +13 -0
  25. package/dist/lib/generators/file-generators/auth-adapter.js.map +1 -0
  26. package/dist/lib/generators/file-generators/auth-check.js +40 -0
  27. package/dist/lib/generators/file-generators/auth-check.js.map +1 -0
  28. package/dist/lib/generators/file-generators/auth-store.js +2 -5
  29. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  30. package/dist/lib/generators/file-generators/background-sync.js +1 -1
  31. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  32. package/dist/lib/generators/file-generators/cache.js +2 -1
  33. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  34. package/dist/lib/generators/file-generators/client-injector-bundle.js +15 -0
  35. package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -0
  36. package/dist/lib/generators/file-generators/client-injector.js +2 -1
  37. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  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/open-db.js +6 -0
  57. package/dist/lib/generators/file-generators/open-db.js.map +1 -0
  58. package/dist/lib/generators/file-generators/push.js +1 -2
  59. package/dist/lib/generators/file-generators/push.js.map +1 -1
  60. package/dist/lib/generators/file-generators/pwa-install.js +2 -6
  61. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  62. package/dist/lib/generators/file-generators/server-push.js +1 -1
  63. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  64. package/dist/lib/generators/file-generators/storage.js +6 -0
  65. package/dist/lib/generators/file-generators/storage.js.map +1 -0
  66. package/dist/lib/generators/file-generators/sw-generator-build.js +105 -48
  67. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  68. package/dist/lib/generators/file-generators/sw-injector.js +0 -4
  69. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  70. package/dist/lib/generators/file-generators/sw-template.js +8 -9
  71. package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
  72. package/dist/lib/generators/file-generators/swoff-api-bundle.js +51 -0
  73. package/dist/lib/generators/file-generators/swoff-api-bundle.js.map +1 -0
  74. package/dist/lib/generators/sw-build-utils.js +69 -0
  75. package/dist/lib/generators/sw-build-utils.js.map +1 -0
  76. package/dist/lib/generators/sw-generator.js +23 -34
  77. package/dist/lib/generators/sw-generator.js.map +1 -1
  78. package/dist/lib/generators/sw-sections/activate-handler.js +77 -23
  79. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  80. package/dist/lib/generators/sw-sections/assemble-sw.js +20 -95
  81. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  82. package/dist/lib/generators/sw-sections/background-precache.js +126 -0
  83. package/dist/lib/generators/sw-sections/background-precache.js.map +1 -0
  84. package/dist/lib/generators/sw-sections/background-sync-handler.js +74 -72
  85. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  86. package/dist/lib/generators/sw-sections/batch-refresh-queue.js +102 -0
  87. package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -0
  88. package/dist/lib/generators/sw-sections/config-header.js +2 -3
  89. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  90. package/dist/lib/generators/sw-sections/default-template.js +64 -7
  91. package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
  92. package/dist/lib/generators/sw-sections/fetch-handler.js +621 -495
  93. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  94. package/dist/lib/generators/sw-sections/install-handler.js +41 -23
  95. package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
  96. package/dist/lib/generators/sw-sections/message-handler.js +34 -8
  97. package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
  98. package/dist/lib/generators/sw-sections/server-push-handler.js +10 -14
  99. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  100. package/dist/lib/generators/sw-sections/shared.js +70 -0
  101. package/dist/lib/generators/sw-sections/shared.js.map +1 -0
  102. package/dist/lib/generators/sw-sections/sw-push.js +2 -6
  103. package/dist/lib/generators/sw-sections/sw-push.js.map +1 -1
  104. package/dist/lib/generators/sw-sections/tag-management.js +55 -86
  105. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  106. package/dist/lib/generators/swoff-files-generator.js +183 -59
  107. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  108. package/dist/lib/shared/config-types.js +84 -79
  109. package/dist/lib/shared/config-types.js.map +1 -1
  110. package/dist/lib/shared/feature-registry.js +198 -0
  111. package/dist/lib/shared/feature-registry.js.map +1 -0
  112. package/dist/lib/utils/detect-framework.js +52 -9
  113. package/dist/lib/utils/detect-framework.js.map +1 -1
  114. package/dist/runtime/api-config.js +2 -2
  115. package/dist/runtime/api-config.js.map +1 -1
  116. package/dist/runtime/auth-adapter.js +179 -0
  117. package/dist/runtime/auth-adapter.js.map +1 -0
  118. package/dist/runtime/auth-state.js +16 -6
  119. package/dist/runtime/auth-state.js.map +1 -1
  120. package/dist/runtime/auth-store.js +219 -213
  121. package/dist/runtime/auth-store.js.map +1 -1
  122. package/dist/runtime/background-sync.js +5 -2
  123. package/dist/runtime/background-sync.js.map +1 -1
  124. package/dist/runtime/cache.js +91 -3
  125. package/dist/runtime/cache.js.map +1 -1
  126. package/dist/runtime/client-injector-bundle.js +314 -0
  127. package/dist/runtime/client-injector-bundle.js.map +1 -0
  128. package/dist/runtime/client-injector.js +188 -30
  129. package/dist/runtime/client-injector.js.map +1 -1
  130. package/dist/runtime/connectivity.js +83 -0
  131. package/dist/runtime/connectivity.js.map +1 -0
  132. package/dist/runtime/fetch-wrapper.js +26 -59
  133. package/dist/runtime/fetch-wrapper.js.map +1 -1
  134. package/dist/runtime/gql-wrapper.js +15 -9
  135. package/dist/runtime/gql-wrapper.js.map +1 -1
  136. package/dist/runtime/invalidation-tags.js +12 -89
  137. package/dist/runtime/invalidation-tags.js.map +1 -1
  138. package/dist/runtime/mutation-queue.js +83 -31
  139. package/dist/runtime/mutation-queue.js.map +1 -1
  140. package/dist/runtime/mutation-state.js +3 -3
  141. package/dist/runtime/mutation-state.js.map +1 -1
  142. package/dist/runtime/open-db.js +41 -0
  143. package/dist/runtime/open-db.js.map +1 -0
  144. package/dist/runtime/push.js +15 -27
  145. package/dist/runtime/push.js.map +1 -1
  146. package/dist/runtime/pwa-prompt.js +35 -3
  147. package/dist/runtime/pwa-prompt.js.map +1 -1
  148. package/dist/runtime/reset.js +39 -42
  149. package/dist/runtime/reset.js.map +1 -1
  150. package/dist/runtime/server-push.js +23 -13
  151. package/dist/runtime/server-push.js.map +1 -1
  152. package/dist/runtime/storage.js +32 -0
  153. package/dist/runtime/storage.js.map +1 -0
  154. package/dist/runtime/sw-injector.js +19 -178
  155. package/dist/runtime/sw-injector.js.map +1 -1
  156. package/dist/runtime/swoff-api-bundle.js +1491 -0
  157. package/dist/runtime/swoff-api-bundle.js.map +1 -0
  158. package/dist/runtime/type-definitions.js +8 -7
  159. package/dist/runtime/type-definitions.js.map +1 -1
  160. package/dist/runtime/utils.js +4 -0
  161. package/dist/runtime/utils.js.map +1 -1
  162. package/package.json +9 -3
  163. package/templates/react/useAuth.jsx +7 -4
  164. package/templates/react/useAuth.tsx +14 -14
  165. package/templates/react/useBackgroundSync.jsx +5 -4
  166. package/templates/react/useBackgroundSync.tsx +5 -4
  167. package/templates/react/useCachedFetch.jsx +8 -0
  168. package/templates/react/useCachedFetch.tsx +8 -0
  169. package/templates/react/useMutation.jsx +60 -56
  170. package/templates/react/useMutation.tsx +65 -64
  171. package/templates/react/useMutationQueue.jsx +5 -1
  172. package/templates/react/useMutationQueue.tsx +8 -2
  173. package/templates/react/useMutationState.jsx +3 -4
  174. package/templates/react/useMutationState.tsx +4 -8
  175. package/templates/react/useNetworkStatus.jsx +57 -17
  176. package/templates/react/useNetworkStatus.tsx +42 -20
  177. package/templates/react/useOfflineAnalytics.jsx +42 -0
  178. package/templates/react/useOfflineAnalytics.tsx +50 -0
  179. package/templates/react/usePwaInstall.jsx +26 -0
  180. package/templates/react/usePwaInstall.tsx +26 -0
  181. package/templates/react/useSWUpdate.jsx +7 -52
  182. package/templates/react/useSWUpdate.tsx +12 -51
  183. package/templates/react/useStorageEstimate.jsx +56 -0
  184. package/templates/react/useStorageEstimate.tsx +67 -0
  185. package/dist/__tests__/assemble-sw.test.js +0 -145
  186. package/dist/__tests__/assemble-sw.test.js.map +0 -1
  187. package/dist/__tests__/cli-integration.test.js +0 -231
  188. package/dist/__tests__/cli-integration.test.js.map +0 -1
  189. package/dist/__tests__/config-types.test.js +0 -124
  190. package/dist/__tests__/config-types.test.js.map +0 -1
  191. package/dist/__tests__/detect-language.test.js +0 -51
  192. package/dist/__tests__/detect-language.test.js.map +0 -1
  193. package/dist/__tests__/file-generators.test.js +0 -228
  194. package/dist/__tests__/file-generators.test.js.map +0 -1
  195. package/dist/__tests__/glob-matcher.test.js +0 -86
  196. package/dist/__tests__/glob-matcher.test.js.map +0 -1
  197. package/dist/__tests__/invalidation-tags.test.js +0 -283
  198. package/dist/__tests__/invalidation-tags.test.js.map +0 -1
  199. package/dist/__tests__/loader.test.js +0 -77
  200. package/dist/__tests__/loader.test.js.map +0 -1
  201. package/dist/__tests__/validator.test.js +0 -340
  202. package/dist/__tests__/validator.test.js.map +0 -1
  203. package/dist/lib/commands/add.js +0 -120
  204. package/dist/lib/commands/add.js.map +0 -1
  205. package/dist/lib/commands/assets.js +0 -45
  206. package/dist/lib/commands/assets.js.map +0 -1
  207. package/dist/lib/commands/generate-assets.js +0 -44
  208. package/dist/lib/commands/generate-assets.js.map +0 -1
  209. package/dist/lib/commands/info.js +0 -183
  210. package/dist/lib/commands/info.js.map +0 -1
  211. package/dist/lib/generators/asset-generator/dependency-resolver.js +0 -85
  212. package/dist/lib/generators/asset-generator/dependency-resolver.js.map +0 -1
  213. package/dist/lib/generators/asset-generator/generate.js +0 -111
  214. package/dist/lib/generators/asset-generator/generate.js.map +0 -1
  215. package/dist/lib/generators/asset-generator/guide.js +0 -43
  216. package/dist/lib/generators/asset-generator/guide.js.map +0 -1
  217. package/dist/lib/generators/asset-generator/html-patcher.js +0 -65
  218. package/dist/lib/generators/asset-generator/html-patcher.js.map +0 -1
  219. package/dist/lib/generators/asset-generator/ico-encoder.js +0 -29
  220. package/dist/lib/generators/asset-generator/ico-encoder.js.map +0 -1
  221. package/dist/lib/generators/asset-generator/manifest-patcher.js +0 -23
  222. package/dist/lib/generators/asset-generator/manifest-patcher.js.map +0 -1
  223. package/dist/lib/generators/asset-generator/maskable.js +0 -15
  224. package/dist/lib/generators/asset-generator/maskable.js.map +0 -1
  225. package/dist/lib/generators/asset-generator/rasterize.js +0 -32
  226. package/dist/lib/generators/asset-generator/rasterize.js.map +0 -1
  227. package/dist/lib/generators/asset-generator/sizes.js +0 -25
  228. package/dist/lib/generators/asset-generator/sizes.js.map +0 -1
  229. package/dist/lib/generators/file-generators/auth-fetch.js +0 -151
  230. package/dist/lib/generators/file-generators/auth-fetch.js.map +0 -1
  231. package/dist/lib/generators/file-generators/auth-user.js +0 -13
  232. package/dist/lib/generators/file-generators/auth-user.js.map +0 -1
  233. package/dist/lib/generators/file-generators/fetch-state.js +0 -6
  234. package/dist/lib/generators/file-generators/fetch-state.js.map +0 -1
  235. package/dist/lib/generators/file-generators/generate-hooks.js +0 -44
  236. package/dist/lib/generators/file-generators/generate-hooks.js.map +0 -1
  237. package/dist/lib/generators/file-generators/indexeddb.js +0 -74
  238. package/dist/lib/generators/file-generators/indexeddb.js.map +0 -1
  239. package/dist/lib/generators/file-generators/manifest.js +0 -46
  240. package/dist/lib/generators/file-generators/manifest.js.map +0 -1
  241. package/dist/lib/generators/file-generators/mutation-reconcile.js +0 -57
  242. package/dist/lib/generators/file-generators/mutation-reconcile.js.map +0 -1
  243. package/dist/lib/generators/file-generators/quick-readme.js +0 -21
  244. package/dist/lib/generators/file-generators/quick-readme.js.map +0 -1
  245. package/dist/lib/generators/file-generators/reconcile.js +0 -50
  246. package/dist/lib/generators/file-generators/reconcile.js.map +0 -1
  247. package/dist/lib/generators/file-generators/store.js +0 -86
  248. package/dist/lib/generators/file-generators/store.js.map +0 -1
  249. package/dist/lib/generators/file-generators/sw-push.js +0 -67
  250. package/dist/lib/generators/file-generators/sw-push.js.map +0 -1
  251. package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +0 -1
  252. package/dist/lib/utils/build-script.js +0 -35
  253. package/dist/lib/utils/build-script.js.map +0 -1
  254. package/dist/lib/utils/tty-status.js +0 -21
  255. package/dist/lib/utils/tty-status.js.map +0 -1
  256. package/dist/runtime/auth-user.js +0 -168
  257. package/dist/runtime/auth-user.js.map +0 -1
  258. package/dist/runtime/fetch-state.js +0 -42
  259. package/dist/runtime/fetch-state.js.map +0 -1
  260. package/dist/runtime/pwa-index.js +0 -11
  261. package/dist/runtime/pwa-index.js.map +0 -1
  262. package/dist/runtime/pwa-injector.js +0 -44
  263. package/dist/runtime/pwa-injector.js.map +0 -1
  264. package/dist/runtime/pwa-install.js +0 -83
  265. package/dist/runtime/pwa-install.js.map +0 -1
  266. package/templates/react/useCacheInvalidation.jsx +0 -19
  267. package/templates/react/useCacheInvalidation.tsx +0 -52
  268. package/templates/react/useIsFetching.jsx +0 -32
  269. package/templates/react/useIsFetching.tsx +0 -32
@@ -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"}
@@ -1,103 +1,21 @@
1
- import { T, R } from "./utils.js";
1
+ import { R } from "./utils.js";
2
2
  export function generateSwInjectorCode(ctx) {
3
- const { ext, ts, autoUpdate, autoActivate, versionEnabled, swFilename } = ctx;
4
- if (!versionEnabled) {
5
- return `/**
6
- * Swoff SW Injector (Simple Mode)
7
- * Fixed SW URL with hash-based cache name — no versioned URLs or update events.
8
- *
9
- * Usage:
10
- * import { initServiceWorker } from './swoff/sw/injector.${ext}';
11
- * initServiceWorker();
12
- *
13
- * Window events:
14
- * sw-ready - SW active and controlling page
15
- * sw-error - SW registration failed
16
- *
17
- * Window properties:
18
- * window.currentSWVersion - "simple"
19
- * window.swReady - SW is active
20
- * window.swError - Registration failed
21
- */
22
- async function waitForController()${R(ts, "Promise<void>")}{
23
- return new Promise<void>((resolve) => {
24
- if (navigator.serviceWorker.controller) {
25
- resolve();
26
- } else {
27
- const timeout = setTimeout(() => resolve(), 30000);
28
- navigator.serviceWorker.addEventListener("controllerchange", () => {
29
- clearTimeout(timeout);
30
- resolve();
31
- }, { once: true });
32
- }
33
- });
34
- }
35
-
36
- /** Register the SW with a fixed URL (non-versioned mode). Dispatches sw-ready/sw-error. */
37
- export async function initServiceWorker()${R(ts, "Promise<void>")}{
38
- if (!("serviceWorker" in navigator)) {
39
- console.warn("Service Workers not supported");
40
- return;
41
- }
42
- try {
43
- const registration = await navigator.serviceWorker.register("/${swFilename}.js");
44
- window.currentSWVersion = "simple";
45
- window.dispatchEvent(new CustomEvent("sw-version-detected"));
46
- await waitForController();
47
- window.dispatchEvent(new CustomEvent("sw-ready"));
48
- } catch (error) {
49
- console.error("Service Worker registration failed:", error);
50
- window.swError = true;
51
- window.dispatchEvent(new CustomEvent("sw-error"));
52
- }
53
- }
54
- `;
55
- }
3
+ const { ext, ts, autoActivate, swFilename } = ctx;
56
4
  return `/**
57
5
  * Swoff SW Injector
58
- * Framework-agnostic SW registration with versioned URLs and update flow.
6
+ * Registers the service worker and tracks installation progress.
59
7
  *
60
8
  * Usage:
61
9
  * import { initServiceWorker } from './swoff/sw/injector.${ext}';
62
10
  * initServiceWorker();
63
11
  *
64
12
  * Window events:
65
- * sw-version-detected - Version info available on window
66
- * sw-update-available - New version ready for user consent (detail: { version })
67
13
  * sw-progress - Download progress (detail: { percent, downloaded, total })
68
14
  * sw-ready - SW active and controlling page
69
15
  * sw-error - SW registration failed
70
- *
71
- * Window properties:
72
- * window.latestSWVersion - Latest version from version.json
73
- * window.currentSWVersion - Active SW version
74
- * window.swAvailableVersion - Pending update version
75
- * window.swUpdateRequired - Forced update needed (version < minSupportedVersion)
76
- * window.swMinSupportedVersion - Minimum supported version
77
- * window.swReady - SW is active
78
- * window.swError - Registration failed
79
16
  */
80
- const AUTO_UPDATE = ${autoUpdate};
81
17
  const AUTO_ACTIVATE = ${autoActivate};
82
18
 
83
- function semverCompare(a${T(ts, "string")}, b${T(ts, "string")})${R(ts, "number")}{
84
- const pa = a.split('.').map(Number);
85
- const pb = b.split('.').map(Number);
86
- for (let i = 0; i < 3; i++) {
87
- if (pa[i] > pb[i]) return 1;
88
- if (pa[i] < pb[i]) return -1;
89
- }
90
- return 0;
91
- }
92
-
93
- async function checkForUpdate() {
94
- const response = await fetch("/version.json?t=" + Date.now());
95
- if (!response.ok) {
96
- throw new Error("Failed to fetch version.json");
97
- }
98
- return response.json();
99
- }
100
-
101
19
  async function waitForController()${R(ts, "Promise<void>")}{
102
20
  return new Promise<void>((resolve) => {
103
21
  if (navigator.serviceWorker.controller) {
@@ -112,19 +30,7 @@ async function waitForController()${R(ts, "Promise<void>")}{
112
30
  });
113
31
  }
114
32
 
115
- async function doRegisterServiceWorker(version${T(ts, "string")})${R(ts, "Promise<ServiceWorkerRegistration>")}{
116
- const swUrl = \`/sw-v\${version}.js\`;
117
- const registration = await navigator.serviceWorker.register(swUrl);
118
- localStorage.setItem("swRegisteredVersion", version);
119
- window.currentSWVersion = version;
120
- window.swRegisteredVersion = version;
121
- window.dispatchEvent(new CustomEvent("sw-version-detected"));
122
- await waitForController();
123
- window.dispatchEvent(new CustomEvent("sw-ready"));
124
- return registration;
125
- }
126
-
127
- /** Register the SW with version checking and update flow. Checks version.json, handles updates, and dispatches sw-ready/sw-error events. */
33
+ /** Register the SW. The browser detects updates by comparing SW file bytes. */
128
34
  export async function initServiceWorker()${R(ts, "Promise<void>")}{
129
35
  if (!("serviceWorker" in navigator)) {
130
36
  console.warn("Service Workers not supported");
@@ -132,98 +38,33 @@ export async function initServiceWorker()${R(ts, "Promise<void>")}{
132
38
  }
133
39
 
134
40
  try {
135
- const manifest = await checkForUpdate();
136
- const currentVersion = localStorage.getItem("swRegisteredVersion");
137
- window.latestSWVersion = manifest.version;
138
- window.swMinSupportedVersion = manifest.minSupportedVersion || "0.0.0";
139
-
140
- if (currentVersion === manifest.version) {
141
- const registration = await navigator.serviceWorker.getRegistration();
142
- if (registration && registration.active) {
143
- window.currentSWVersion = currentVersion ?? undefined;
144
- window.dispatchEvent(new CustomEvent("sw-version-detected"));
145
- await waitForController();
146
- if (!window.swReady) {
147
- window.dispatchEvent(new CustomEvent("sw-ready"));
148
- }
149
- }
150
- } else if (currentVersion && currentVersion !== manifest.version) {
151
- window.swAvailableVersion = manifest.version;
152
- window.swUpdateRequired =
153
- semverCompare(currentVersion, manifest.minSupportedVersion || "0.0.0") < 0;
41
+ const registration = await navigator.serviceWorker.register("/${swFilename}.js");
154
42
 
155
- if (AUTO_UPDATE) {
156
- const registration = await navigator.serviceWorker.getRegistration();
157
- if (registration && registration.waiting) {
43
+ var swReadyDispatched = false;
44
+ if (registration.installing) {
45
+ const installingWorker = registration.installing;
46
+ installingWorker.addEventListener("statechange", () => {
47
+ if (installingWorker.state === "installed") {
158
48
  if (AUTO_ACTIVATE) {
159
- registration.waiting.postMessage({ type: "SKIP_WAITING" });
160
- } else {
161
- window.dispatchEvent(
162
- new CustomEvent("sw-update-available", {
163
- detail: { version: manifest.version },
164
- })
165
- );
166
- }
167
- } else {
168
- const newReg = await doRegisterServiceWorker(manifest.version);
169
- if (AUTO_ACTIVATE && newReg.waiting) {
170
- newReg.waiting.postMessage({ type: "SKIP_WAITING" });
171
- } else {
172
- window.dispatchEvent(
173
- new CustomEvent("sw-update-available", {
174
- detail: { version: manifest.version },
175
- })
176
- );
49
+ registration.waiting?.postMessage({ type: "SKIP_WAITING" });
177
50
  }
178
- }
179
- } else {
180
- window.dispatchEvent(
181
- new CustomEvent("sw-update-available", {
182
- detail: { version: manifest.version },
183
- })
184
- );
185
- }
186
- } else {
187
- await doRegisterServiceWorker(manifest.version);
188
- }
189
- } catch (error) {
190
- try {
191
- const existing = await navigator.serviceWorker.getRegistration();
192
- if (existing && existing.active) {
193
- window.currentSWVersion = localStorage.getItem("swRegisteredVersion") || "unknown";
194
- window.dispatchEvent(new CustomEvent("sw-version-detected"));
195
- await waitForController();
196
- if (!window.swReady) {
51
+ swReadyDispatched = true;
197
52
  window.dispatchEvent(new CustomEvent("sw-ready"));
198
53
  }
199
- return;
200
- }
201
- } catch {
202
- // Handle the case where no existing active registration is found
54
+ });
203
55
  }
204
56
 
205
- console.error("Service Worker initialization failed:", error);
57
+ await waitForController();
58
+ if (!swReadyDispatched) {
59
+ window.dispatchEvent(new CustomEvent("sw-ready"));
60
+ }
61
+ } catch (error) {
62
+ console.error("Service Worker registration failed:", error);
206
63
  window.swError = true;
207
64
  window.dispatchEvent(new CustomEvent("sw-error"));
208
65
  }
209
66
  }
210
67
 
211
- /** Accept a pending SW update. Reloads the page once the new SW takes control. */
212
- export async function handleUpdateApproved(newVersion${T(ts, "string")})${R(ts, "Promise<void>")}{
213
- const registration = await navigator.serviceWorker.getRegistration();
214
- if (registration && registration.waiting) {
215
- registration.waiting.postMessage({ type: "SKIP_WAITING" });
216
- registration.addEventListener("controllerchange", () => {
217
- window.location.reload();
218
- });
219
- } else {
220
- await doRegisterServiceWorker(newVersion);
221
- navigator.serviceWorker.addEventListener("controllerchange", () => {
222
- window.location.reload();
223
- });
224
- }
225
- }
226
-
227
68
  /** Activate a waiting SW without reloading. Useful when you handle the transition yourself. */
228
69
  export async function skipWaiting()${R(ts, "Promise<void>")}{
229
70
  const registration = await navigator.serviceWorker.ready;
@@ -1 +1 @@
1
- {"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../src/runtime/sw-injector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,UAAU,sBAAsB,CACpC,GAAiH;IAEjH,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC;IAE9E,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO;;;;;8DAKmD,GAAG;;;;;;;;;;;;oCAY7B,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;2CAef,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;oEAMG,UAAU;;;;;;;;;;;CAW7E,CAAC;IACA,CAAC;IAED,OAAO;;;;;8DAKqD,GAAG;;;;;;;;;;;;;;;;;;;sBAmB3C,UAAU;wBACR,YAAY;;0BAEV,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;;;;;;;;;;;;;;;;;;oCAkB7C,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;gDAcV,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,oCAAoC,CAAC;;;;;;;;;;;;;2CAanE,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAoFV,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;;qCAgB3D,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;CAM1D,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../src/runtime/sw-injector.ts"],"names":[],"mappings":"AACA,OAAO,EAAK,CAAC,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,UAAU,sBAAsB,CACpC,GAAmE;IAEnE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC;IAElD,OAAO;;;;;8DAKqD,GAAG;;;;;;;;wBAQzC,YAAY;;oCAEA,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;2CAef,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;oEAOG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCA4BzC,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;CAM1D,CAAC;AACF,CAAC"}