@swoff/cli 0.3.8 → 0.3.10

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 (228) hide show
  1. package/README.md +47 -33
  2. package/dist/__tests__/assemble-sw.test.js +14 -53
  3. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  4. package/dist/__tests__/cli-integration.test.js +119 -11
  5. package/dist/__tests__/cli-integration.test.js.map +1 -1
  6. package/dist/__tests__/config-types.test.js +44 -17
  7. package/dist/__tests__/config-types.test.js.map +1 -1
  8. package/dist/__tests__/file-generators.test.js +3 -3
  9. package/dist/__tests__/file-generators.test.js.map +1 -1
  10. package/dist/__tests__/glob-matcher.test.js +86 -0
  11. package/dist/__tests__/glob-matcher.test.js.map +1 -0
  12. package/dist/__tests__/invalidation-tags.test.js +207 -23
  13. package/dist/__tests__/invalidation-tags.test.js.map +1 -1
  14. package/dist/__tests__/loader.test.js +16 -12
  15. package/dist/__tests__/loader.test.js.map +1 -1
  16. package/dist/__tests__/validator.test.js +140 -100
  17. package/dist/__tests__/validator.test.js.map +1 -1
  18. package/dist/index.js +39 -34
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/cli/help.js +12 -12
  21. package/dist/lib/cli/help.js.map +1 -1
  22. package/dist/lib/commands/add.js +84 -36
  23. package/dist/lib/commands/add.js.map +1 -1
  24. package/dist/lib/commands/assets.js +37 -0
  25. package/dist/lib/commands/assets.js.map +1 -0
  26. package/dist/lib/commands/clean.js +32 -30
  27. package/dist/lib/commands/clean.js.map +1 -1
  28. package/dist/lib/commands/generate-assets.js +44 -0
  29. package/dist/lib/commands/generate-assets.js.map +1 -0
  30. package/dist/lib/commands/generate-guide.js +22 -29
  31. package/dist/lib/commands/generate-guide.js.map +1 -1
  32. package/dist/lib/commands/generate.js +17 -24
  33. package/dist/lib/commands/generate.js.map +1 -1
  34. package/dist/lib/commands/info.js +41 -48
  35. package/dist/lib/commands/info.js.map +1 -1
  36. package/dist/lib/commands/init.js +27 -13
  37. package/dist/lib/commands/init.js.map +1 -1
  38. package/dist/lib/commands/validate.js +4 -4
  39. package/dist/lib/commands/validate.js.map +1 -1
  40. package/dist/lib/config/loader.js +25 -23
  41. package/dist/lib/config/loader.js.map +1 -1
  42. package/dist/lib/config/validator.js +169 -91
  43. package/dist/lib/config/validator.js.map +1 -1
  44. package/dist/lib/generators/asset-generator/dependency-resolver.js +76 -0
  45. package/dist/lib/generators/asset-generator/dependency-resolver.js.map +1 -0
  46. package/dist/lib/generators/asset-generator/generate.js +111 -0
  47. package/dist/lib/generators/asset-generator/generate.js.map +1 -0
  48. package/dist/lib/generators/asset-generator/guide.js +43 -0
  49. package/dist/lib/generators/asset-generator/guide.js.map +1 -0
  50. package/dist/lib/generators/asset-generator/html-patcher.js +65 -0
  51. package/dist/lib/generators/asset-generator/html-patcher.js.map +1 -0
  52. package/dist/lib/generators/asset-generator/ico-encoder.js +29 -0
  53. package/dist/lib/generators/asset-generator/ico-encoder.js.map +1 -0
  54. package/dist/lib/generators/asset-generator/manifest-patcher.js +23 -0
  55. package/dist/lib/generators/asset-generator/manifest-patcher.js.map +1 -0
  56. package/dist/lib/generators/asset-generator/maskable.js +15 -0
  57. package/dist/lib/generators/asset-generator/maskable.js.map +1 -0
  58. package/dist/lib/generators/asset-generator/rasterize.js +32 -0
  59. package/dist/lib/generators/asset-generator/rasterize.js.map +1 -0
  60. package/dist/lib/generators/asset-generator/sizes.js +25 -0
  61. package/dist/lib/generators/asset-generator/sizes.js.map +1 -0
  62. package/dist/lib/generators/file-generators/api-config.js +6 -0
  63. package/dist/lib/generators/file-generators/api-config.js.map +1 -0
  64. package/dist/lib/generators/file-generators/auth-state.js +2 -34
  65. package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
  66. package/dist/lib/generators/file-generators/auth-store.js +2 -250
  67. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  68. package/dist/lib/generators/file-generators/auth-user.js +3 -81
  69. package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
  70. package/dist/lib/generators/file-generators/background-sync.js +2 -57
  71. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  72. package/dist/lib/generators/file-generators/cache.js +2 -38
  73. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  74. package/dist/lib/generators/file-generators/client-injector.js +2 -91
  75. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  76. package/dist/lib/generators/file-generators/fetch-state.js +6 -0
  77. package/dist/lib/generators/file-generators/fetch-state.js.map +1 -0
  78. package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -267
  79. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  80. package/dist/lib/generators/file-generators/generate-framework-adapters.js +56 -0
  81. package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -0
  82. package/dist/lib/generators/file-generators/generate-hooks.js +4 -3
  83. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  84. package/dist/lib/generators/file-generators/gql-wrapper.js +3 -145
  85. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  86. package/dist/lib/generators/file-generators/guide-generator.js +24 -630
  87. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  88. package/dist/lib/generators/file-generators/invalidation-tags.js +18 -86
  89. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  90. package/dist/lib/generators/file-generators/manifest.js +15 -5
  91. package/dist/lib/generators/file-generators/manifest.js.map +1 -1
  92. package/dist/lib/generators/file-generators/mutation-queue.js +3 -285
  93. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  94. package/dist/lib/generators/file-generators/mutation-state.js +2 -117
  95. package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
  96. package/dist/lib/generators/file-generators/push.js +6 -139
  97. package/dist/lib/generators/file-generators/push.js.map +1 -1
  98. package/dist/lib/generators/file-generators/pwa-install.js +11 -84
  99. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  100. package/dist/lib/generators/file-generators/quick-readme.js +8 -125
  101. package/dist/lib/generators/file-generators/quick-readme.js.map +1 -1
  102. package/dist/lib/generators/file-generators/reset.js +6 -0
  103. package/dist/lib/generators/file-generators/reset.js.map +1 -0
  104. package/dist/lib/generators/file-generators/server-push.js +3 -124
  105. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  106. package/dist/lib/generators/file-generators/sw-generator-build.js +8 -9
  107. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  108. package/dist/lib/generators/file-generators/sw-injector.js +10 -230
  109. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  110. package/dist/lib/generators/file-generators/type-definitions.js +2 -114
  111. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  112. package/dist/lib/generators/sw-generator.js +10 -20
  113. package/dist/lib/generators/sw-generator.js.map +1 -1
  114. package/dist/lib/generators/sw-sections/activate-handler.js +25 -3
  115. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  116. package/dist/lib/generators/sw-sections/assemble-sw.js +20 -16
  117. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  118. package/dist/lib/generators/sw-sections/background-sync-handler.js +68 -19
  119. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  120. package/dist/lib/generators/sw-sections/config-header.js +2 -2
  121. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  122. package/dist/lib/generators/sw-sections/fetch-handler.js +506 -144
  123. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  124. package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +1 -0
  125. package/dist/lib/generators/sw-sections/message-handler.js +55 -15
  126. package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
  127. package/dist/lib/generators/sw-sections/server-push-handler.js +12 -0
  128. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  129. package/dist/lib/generators/sw-sections/sw-push.js +67 -0
  130. package/dist/lib/generators/sw-sections/sw-push.js.map +1 -0
  131. package/dist/lib/generators/sw-sections/tag-management.js +65 -23
  132. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  133. package/dist/lib/generators/swoff-files-generator.js +66 -45
  134. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  135. package/dist/lib/shared/config-types.js +182 -71
  136. package/dist/lib/shared/config-types.js.map +1 -1
  137. package/dist/lib/shared/glob-matcher.js +77 -0
  138. package/dist/lib/shared/glob-matcher.js.map +1 -0
  139. package/dist/lib/utils/tty-status.js +21 -0
  140. package/dist/lib/utils/tty-status.js.map +1 -0
  141. package/dist/runtime/api-config.js +18 -0
  142. package/dist/runtime/api-config.js.map +1 -0
  143. package/dist/runtime/auth-state.js +35 -0
  144. package/dist/runtime/auth-state.js.map +1 -0
  145. package/dist/runtime/auth-store.js +273 -0
  146. package/dist/runtime/auth-store.js.map +1 -0
  147. package/dist/runtime/auth-user.js +168 -0
  148. package/dist/runtime/auth-user.js.map +1 -0
  149. package/dist/runtime/background-sync.js +59 -0
  150. package/dist/runtime/background-sync.js.map +1 -0
  151. package/dist/runtime/cache.js +30 -0
  152. package/dist/runtime/cache.js.map +1 -0
  153. package/dist/runtime/client-injector.js +136 -0
  154. package/dist/runtime/client-injector.js.map +1 -0
  155. package/dist/runtime/fetch-state.js +42 -0
  156. package/dist/runtime/fetch-state.js.map +1 -0
  157. package/dist/runtime/fetch-wrapper.js +355 -0
  158. package/dist/runtime/fetch-wrapper.js.map +1 -0
  159. package/dist/runtime/gql-wrapper.js +135 -0
  160. package/dist/runtime/gql-wrapper.js.map +1 -0
  161. package/dist/runtime/invalidation-tags.js +260 -0
  162. package/dist/runtime/invalidation-tags.js.map +1 -0
  163. package/dist/runtime/mutation-queue.js +304 -0
  164. package/dist/runtime/mutation-queue.js.map +1 -0
  165. package/dist/runtime/mutation-state.js +117 -0
  166. package/dist/runtime/mutation-state.js.map +1 -0
  167. package/dist/runtime/push.js +138 -0
  168. package/dist/runtime/push.js.map +1 -0
  169. package/dist/runtime/pwa-index.js +11 -0
  170. package/dist/runtime/pwa-index.js.map +1 -0
  171. package/dist/runtime/pwa-injector.js +44 -0
  172. package/dist/runtime/pwa-injector.js.map +1 -0
  173. package/dist/runtime/pwa-install.js +83 -0
  174. package/dist/runtime/pwa-install.js.map +1 -0
  175. package/dist/runtime/pwa-prompt.js +54 -0
  176. package/dist/runtime/pwa-prompt.js.map +1 -0
  177. package/dist/runtime/reset.js +148 -0
  178. package/dist/runtime/reset.js.map +1 -0
  179. package/dist/runtime/server-push.js +148 -0
  180. package/dist/runtime/server-push.js.map +1 -0
  181. package/dist/runtime/sw-injector.js +236 -0
  182. package/dist/runtime/sw-injector.js.map +1 -0
  183. package/dist/runtime/type-definitions.js +138 -0
  184. package/dist/runtime/type-definitions.js.map +1 -0
  185. package/dist/runtime/utils.js +27 -0
  186. package/dist/runtime/utils.js.map +1 -0
  187. package/package.json +3 -1
  188. package/templates/react/useAuth.jsx +106 -0
  189. package/templates/react/useAuth.tsx +118 -0
  190. package/templates/{hooks → react}/useBackgroundSync.jsx +6 -4
  191. package/templates/{hooks → react}/useBackgroundSync.tsx +6 -4
  192. package/templates/{hooks → react}/useCacheInvalidation.jsx +2 -2
  193. package/templates/react/useCacheInvalidation.tsx +52 -0
  194. package/templates/react/useCachedFetch.jsx +140 -0
  195. package/templates/react/useCachedFetch.tsx +160 -0
  196. package/templates/react/useIsFetching.jsx +32 -0
  197. package/templates/react/useIsFetching.tsx +32 -0
  198. package/templates/react/useMutation.jsx +141 -0
  199. package/templates/react/useMutation.tsx +180 -0
  200. package/templates/react/useMutationQueue.jsx +69 -0
  201. package/templates/react/useMutationQueue.tsx +83 -0
  202. package/templates/react/useMutationState.jsx +13 -0
  203. package/templates/react/useMutationState.tsx +25 -0
  204. package/templates/react/useNetworkStatus.jsx +61 -0
  205. package/templates/react/useNetworkStatus.tsx +76 -0
  206. package/templates/react/usePrefetch.jsx +32 -0
  207. package/templates/react/usePrefetch.tsx +42 -0
  208. package/templates/{hooks → react}/usePushSubscription.jsx +7 -7
  209. package/templates/{hooks → react}/usePushSubscription.tsx +7 -7
  210. package/templates/{hooks → react}/useSWUpdate.jsx +3 -3
  211. package/templates/{hooks → react}/useSWUpdate.tsx +4 -4
  212. package/templates/react/useSwoffReset.jsx +32 -0
  213. package/templates/react/useSwoffReset.tsx +39 -0
  214. package/templates/hooks/useAuth.jsx +0 -30
  215. package/templates/hooks/useAuth.tsx +0 -30
  216. package/templates/hooks/useCacheInvalidation.tsx +0 -19
  217. package/templates/hooks/useCachedFetch.jsx +0 -94
  218. package/templates/hooks/useCachedFetch.tsx +0 -112
  219. package/templates/hooks/useMutation.jsx +0 -76
  220. package/templates/hooks/useMutation.tsx +0 -97
  221. package/templates/hooks/useMutationQueue.jsx +0 -42
  222. package/templates/hooks/useMutationQueue.tsx +0 -49
  223. package/templates/hooks/useMutationState.jsx +0 -27
  224. package/templates/hooks/useMutationState.tsx +0 -41
  225. package/templates/hooks/useNetworkStatus.jsx +0 -19
  226. package/templates/hooks/useNetworkStatus.tsx +0 -19
  227. package/templates/hooks/usePrefetch.jsx +0 -10
  228. package/templates/hooks/usePrefetch.tsx +0 -25
@@ -2,90 +2,12 @@
2
2
  * Generates auth-user.ts/js — fetch, cache, and invalidate current user.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
+ import { generateAuthUserCode } from "../../../runtime/auth-user.js";
5
6
  export function generateAuthUser(ctx) {
6
7
  const ext = ctx.ext;
7
8
  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}` : "");
12
- const userEndpoint = ctx.config.features.auth.userEndpoint;
13
- const code = `/**
14
- * Current User — fetch, cache, and invalidate the current user for offline access.
15
- *
16
- * Usage:
17
- * import { fetchCurrentUser, cacheUser, getCachedUser, clearCachedUser } from "./auth/user.${ext}";
18
- *
19
- * await fetchCurrentUser(); // Fetch from server & cache
20
- * const user = await getCachedUser(); // Get cached (offline-capable)
21
- * await clearCachedUser(); // Clear on logout
22
- */
23
-
24
- import { fetchWithCache } from "../fetch-wrapper.${ext}";
25
-
26
- const DB_NAME = "swoff-auth-user";
27
- const STORE_NAME = "current-user";
28
-
29
- function openAuthDB()${R("Promise<IDBDatabase>")}{
30
- return new Promise${PT("IDBDatabase")}((resolve, reject) => {
31
- const request = indexedDB.open(DB_NAME, 1);
32
- request.onupgradeneeded = (e) => {
33
- const db = (e.target${AS("IDBOpenDBRequest")}).result;
34
- if (!db.objectStoreNames.contains(STORE_NAME)) {
35
- db.createObjectStore(STORE_NAME, { keyPath: "key" });
36
- }
37
- };
38
- request.onsuccess = (e) => resolve((e.target${AS("IDBOpenDBRequest")}).result);
39
- request.onerror = (e) => reject((e.target${AS("IDBRequest")}).error);
40
- });
41
- }
42
-
43
- /** Fetch current user from the user endpoint and cache the result in IndexedDB. */
44
- export async function fetchCurrentUser()${R("Promise<Record<string, unknown>>")}{
45
- const { response } = await fetchWithCache("${userEndpoint}", { auth: true });
46
- if (!response.ok) throw new Error("Failed to fetch user");
47
-
48
- const user = await response.json();
49
- await cacheUser(user);
50
- return user;
51
- }
52
-
53
- /** Persist user data to IndexedDB for offline access. */
54
- export async function cacheUser(user${T("Record<string, unknown>")})${R("Promise<void>")}{
55
- const db = await openAuthDB();
56
- return new Promise<void>((resolve, reject) => {
57
- const tx = db.transaction(STORE_NAME, "readwrite");
58
- const store = tx.objectStore(STORE_NAME);
59
- store.put({ key: "user", value: user });
60
- tx.oncomplete = () => resolve();
61
- tx.onerror = () => reject(tx.error);
62
- });
63
- }
64
-
65
- /** Load user data from IndexedDB cache (no token — only user object survives refresh). */
66
- export async function getCachedUser()${R("Promise<Record<string, unknown> | null>")}{
67
- const db = await openAuthDB();
68
- return new Promise${PT("Record<string, unknown> | null")}((resolve, reject) => {
69
- const tx = db.transaction(STORE_NAME, "readonly");
70
- const store = tx.objectStore(STORE_NAME);
71
- const request = store.get("user");
72
- request.onsuccess = () => resolve((request${AS("IDBRequest")}).result?.value ?? null);
73
- request.onerror = () => reject((request${AS("IDBRequest")}).error);
74
- });
75
- }
76
-
77
- /** Remove user data from IndexedDB cache. Call on logout. */
78
- export async function clearCachedUser()${R("Promise<void>")}{
79
- const db = await openAuthDB();
80
- return new Promise${PT("void")}((resolve, reject) => {
81
- const tx = db.transaction(STORE_NAME, "readwrite");
82
- const store = tx.objectStore(STORE_NAME);
83
- store.delete("user");
84
- tx.oncomplete = () => resolve();
85
- tx.onerror = () => reject((tx${AS("IDBTransaction")}).error);
86
- });
87
- }
88
- `;
9
+ const authConfig = ctx.config.features.auth;
10
+ const code = generateAuthUserCode({ ts, ext }, authConfig.refreshPath, authConfig.userEndpoint, authConfig.type);
89
11
  writeFile(ctx, `auth/user.${ext}`, code);
90
12
  }
91
13
  //# sourceMappingURL=auth-user.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-user.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,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,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;IAE3D,MAAM,IAAI,GAAG;;;;gGAIiF,GAAG;;;;;;;mDAOhD,GAAG;;;;;uBAK/B,CAAC,CAAC,sBAAsB,CAAC;sBAC1B,EAAE,CAAC,aAAa,CAAC;;;4BAGX,EAAE,CAAC,kBAAkB,CAAC;;;;;kDAKA,EAAE,CAAC,kBAAkB,CAAC;+CACzB,EAAE,CAAC,YAAY,CAAC;;;;;0CAKrB,CAAC,CAAC,kCAAkC,CAAC;+CAChC,YAAY;;;;;;;;;sCASrB,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;uCAYjD,CAAC,CAAC,yCAAyC,CAAC;;sBAE7D,EAAE,CAAC,gCAAgC,CAAC;;;;gDAIV,EAAE,CAAC,YAAY,CAAC;6CACnB,EAAE,CAAC,YAAY,CAAC;;;;;yCAKpB,CAAC,CAAC,eAAe,CAAC;;sBAErC,EAAE,CAAC,MAAM,CAAC;;;;;mCAKG,EAAE,CAAC,gBAAgB,CAAC;;;CAGtD,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"auth-user.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAE5C,MAAM,IAAI,GAAG,oBAAoB,CAC/B,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,UAAU,CAAC,WAAW,EACtB,UAAU,CAAC,YAAY,EACvB,UAAU,CAAC,IAAI,CAChB,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
@@ -2,63 +2,8 @@
2
2
  * Generates background-sync.js - Background Sync API registration.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
+ import { generateBackgroundSyncCode } from "../../../runtime/background-sync.js";
5
6
  export function generateBackgroundSync(ctx) {
6
- const ext = ctx.ext;
7
- const ts = ext === "ts";
8
- const T = (type) => (ts ? `: ${type}` : "");
9
- const R = (type) => (ts ? `: ${type} ` : " ");
10
- const code = `/**
11
- * Swoff Background Sync
12
- * Register sync events for processing mutation queue after tab close.
13
- * Falls back to online event listener in unsupported browsers.
14
- *
15
- * Usage:
16
- * import { syncWhenPossible } from './swoff/background-sync.${ext}';
17
- *
18
- * await syncWhenPossible({
19
- * method: "POST",
20
- * url: "/api/todos",
21
- * body: { title: "Grocery" },
22
- * tags: ["todos"],
23
- * tags: ["todos"],
24
- * });
25
- */
26
-
27
- import { queueMutation, processMutationQueue, getPendingCount } from "./mutation-queue.${ext}";
28
-
29
- const SYNC_TAG = "sync-mutations";
30
-
31
- async function registerSync()${R("Promise<void>")}{
32
- if (!("serviceWorker" in navigator) || !("SyncManager" in window)) {
33
- window.addEventListener("online", processMutationQueue, { once: true });
34
- return;
35
- }
36
-
37
- try {
38
- const registration = await navigator.serviceWorker.ready;
39
- await registration.sync.register(SYNC_TAG);
40
- } catch {
41
- window.addEventListener("online", processMutationQueue, { once: true });
42
- }
43
- }
44
-
45
- /** Queue a mutation and register a background sync event so it runs even after tab close. */
46
- export async function syncWhenPossible(mutation${T("object")})${R("Promise<void>")}{
47
- await queueMutation(mutation);
48
- await registerSync();
49
- }
50
-
51
- /** Re-register a background sync if mutations are still pending. Called automatically after each sync cycle. */
52
- export async function retrySync()${R("Promise<void>")}{
53
- if (!("serviceWorker" in navigator) || !("SyncManager" in window)) return;
54
- const count = await getPendingCount();
55
- if (count > 0) {
56
- await registerSync();
57
- }
58
- }
59
-
60
- window.addEventListener("mutation-sync-complete", retrySync);
61
- `;
62
- writeFile(ctx, `background-sync.${ext}`, code);
7
+ writeFile(ctx, `offline/sync.${ctx.ext}`, generateBackgroundSyncCode({ ts: ctx.ext === "ts", ext: ctx.ext }));
63
8
  }
64
9
  //# sourceMappingURL=background-sync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"background-sync.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/background-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,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;IAEtD,MAAM,IAAI,GAAG;;;;;;iEAMkD,GAAG;;;;;;;;;;;yFAWqB,GAAG;;;;+BAI7D,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;iDAeA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;mCAM/C,CAAC,CAAC,eAAe,CAAC;;;;;;;;;CASpD,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"background-sync.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/background-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,SAAS,CAAC,GAAG,EAAE,gBAAgB,GAAG,CAAC,GAAG,EAAE,EAAE,0BAA0B,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAChH,CAAC"}
@@ -1,42 +1,6 @@
1
- /**
2
- * Generates cache.{js|ts} - cache tag invalidation utilities.
3
- */
4
1
  import { writeFile } from "./context.js";
2
+ import { generateCacheCode } from "../../../runtime/cache.js";
5
3
  export function generateCache(ctx) {
6
- const ext = ctx.ext;
7
- const ts = ext === "ts";
8
- const T = (type) => (ts ? `: ${type}` : "");
9
- const R = (type) => (ts ? `: ${type} ` : " ");
10
- const code = `/**
11
- * Swoff Cache Invalidation
12
- * Framework-agnostic cache tag invalidation.
13
- *
14
- * Usage:
15
- * import { invalidateByTag } from './swoff/cache.${ext}';
16
- *
17
- * // After a mutation, invalidate related cache
18
- * await invalidateByTag("todos");
19
- */
20
-
21
- /** Invalidate all cached responses tagged with the given tag. Dispatches cache-invalidated event. */
22
- export async function invalidateByTag(tag${T("string")})${R("Promise<void>")}{
23
- if (!navigator.serviceWorker?.controller) return;
24
-
25
- navigator.serviceWorker.controller.postMessage({
26
- type: "INVALIDATE_TAG",
27
- tag,
28
- });
29
-
30
- window.dispatchEvent(
31
- new CustomEvent("cache-invalidated", { detail: { tags: [tag] } })
32
- );
33
- }
34
-
35
- /** Invalidate all cached responses matching any of the given tags. */
36
- export async function invalidateByTags(tags${T("string[]")})${R("Promise<void>")}{
37
- await Promise.all(tags.map((tag) => invalidateByTag(tag)));
38
- }
39
- `;
40
- writeFile(ctx, `cache.${ext}`, code);
4
+ writeFile(ctx, `cache/index.${ctx.ext}`, generateCacheCode({ ts: ctx.ext === "ts", ext: ctx.ext }));
41
5
  }
42
6
  //# sourceMappingURL=cache.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,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;IAEtD,MAAM,IAAI,GAAG;;;;;sDAKuC,GAAG;;;;;;;2CAOd,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;6CAc/B,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;CAG/E,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACtG,CAAC"}
@@ -2,100 +2,11 @@
2
2
  * Generates client-injector.{js|ts} - orchestrator that wires feature modules together.
3
3
  */
4
4
  import { writeFile } from "./context.js";
5
+ import { generateClientInjectorCode } from "../../../runtime/client-injector.js";
5
6
  export function generateClientInjector(ctx) {
6
7
  const ext = ctx.ext;
7
8
  const ts = ext === "ts";
8
- const pwaEnabled = ctx.config.features.pwa.enabled;
9
- const mutationQueueEnabled = ctx.config.features.mutationQueue.enabled;
10
- const crossTabSync = ctx.config.features.crossTabSync;
11
- const pwaImport = pwaEnabled
12
- ? `import { setupPwaInstall } from "./pwa/install.${ext}";
13
- `
14
- : "";
15
- const pwaCall = pwaEnabled ? `setupPwaInstall();\n` : "";
16
- const mutationImport = mutationQueueEnabled
17
- ? `import { processMutationQueue } from "./mutation-queue.${ext}";
18
- `
19
- : "";
20
- const mutationOnlineListener = mutationQueueEnabled
21
- ? `
22
- // --- Mutation Queue Online Listener ---
23
- window.addEventListener("online", processMutationQueue);
24
- `
25
- : "";
26
- const crossTabHandler = crossTabSync
27
- ? `
28
- if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
29
- window.dispatchEvent(
30
- new CustomEvent("cache-invalidated", {
31
- detail: { tags: [event.data.tag] },
32
- })
33
- );
34
- }
35
- `
36
- : "";
37
- const swImport = `import { initServiceWorker as swInit } from "./sw/injector.${ext}";
38
- `;
39
- const code = `/**
40
- * Swoff Client Injector
41
- * Orchestrates SW registration, PWA install, and cross-tab sync.
42
- * Single entry point for all Swoff client-side features.
43
- *
44
- * Usage:
45
- * import { initServiceWorker } from './swoff/client-injector.${ext}';
46
- * initServiceWorker();
47
- *
48
- * Window events (dispatched by this module):
49
- * sw-progress - Download progress (detail: { percent, downloaded, total })
50
- * sw-ready - SW active and controlling page
51
- * sw-error - SW registration failed
52
- * cache-invalidated - Cache entries cleared (detail: { tags })
53
- * mutation-sync-complete - Queued mutations synced (detail: { succeeded, failed })
54
- * mutation-queue-changed - Queue modified
55
- *
56
- * Window events (dispatched by feature modules):
57
- * pwa-installable - PWA can be installed (detail: { isInstallable: true })
58
- * pwa-installed - User accepted install (detail: { outcome: 'accepted' })
59
- * sw-auth-unauthorized - 401 response received
60
- * sw-auth-state-change - Login or logout (detail: { authenticated: boolean })
61
- * mutation-rollback - Mutation exhausted retries
62
- * sw-update-available - New version ready (detail: { version })
63
- * sw-version-detected - Version info available
64
- */
65
- ${pwaImport}${mutationImport}${swImport}
66
- ${pwaCall}${mutationOnlineListener}
67
- // --- SW Message Listener ---
68
- if (typeof window !== "undefined" && "serviceWorker" in navigator) {
69
- navigator.serviceWorker.addEventListener("message", (event) => {
70
- if (event.data.type === "SW_PROGRESS") {
71
- const { percent, downloaded, total } = event.data;
72
- window.dispatchEvent(
73
- new CustomEvent("sw-progress", {
74
- detail: { percent, downloaded, total },
75
- })
76
- );
77
- }
78
- if (event.data.type === "BACKGROUND_SYNC_COMPLETE") {
79
- const { succeeded, failed, tags } = event.data.detail;
80
- window.dispatchEvent(
81
- new CustomEvent("mutation-sync-complete", {
82
- detail: { succeeded, failed },
83
- })
84
- );
85
- if (tags && tags.length > 0) {
86
- window.dispatchEvent(
87
- new CustomEvent("cache-invalidated", { detail: { tags } })
88
- );
89
- }
90
- window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
91
- }${crossTabHandler} });
92
- }
93
-
94
- /** Initialize SW registration and all client-side features (PWA install, mutation queue, cross-tab sync). Call once at app startup. */
95
- export async function initServiceWorker()${ts ? ": Promise<void>" : " "}{
96
- await swInit();
97
- }
98
- `;
9
+ const code = generateClientInjectorCode({ ts, ext }, ctx.config.features.pwa.enabled, ctx.config.features.mutationQueue.enabled, ctx.config.features.serverPush?.enabled ?? false);
99
10
  writeFile(ctx, `client-injector.${ext}`, code);
100
11
  }
101
12
  //# sourceMappingURL=client-injector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/client-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;IACnD,MAAM,oBAAoB,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC;IACvE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;IAEtD,MAAM,SAAS,GAAG,UAAU;QAC1B,CAAC,CAAC,kDAAkD,GAAG;CAC1D;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzD,MAAM,cAAc,GAAG,oBAAoB;QACzC,CAAC,CAAC,0DAA0D,GAAG;CAClE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,sBAAsB,GAAG,oBAAoB;QACjD,CAAC,CAAC;;;CAGL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,eAAe,GAAG,YAAY;QAClC,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,8DAA8D,GAAG;CACnF,CAAC;IAEA,MAAM,IAAI,GAAG;;;;;;kEAMmD,GAAG;;;;;;;;;;;;;;;;;;;;EAoBnE,SAAS,GAAG,cAAc,GAAG,QAAQ;EACrC,OAAO,GAAG,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;OAyB3B,eAAe;;;;2CAIqB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG;;;CAGtE,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/client-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,IAAI,GAAG,0BAA0B,CACrC,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAC/B,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,EACzC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,IAAI,KAAK,CACjD,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { writeFile } from "./context.js";
2
+ import { generateFetchStateCode } from "../../../runtime/fetch-state.js";
3
+ export function generateFetchState(ctx) {
4
+ writeFile(ctx, `fetch/state.${ctx.ext}`, generateFetchStateCode({ ts: ctx.ext === "ts", ext: ctx.ext }));
5
+ }
6
+ //# sourceMappingURL=fetch-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-state.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAC3G,CAAC"}
@@ -3,275 +3,11 @@
3
3
  * staleTime, prefetching, and cancellation.
4
4
  */
5
5
  import { writeFile } from "./context.js";
6
+ import { generateFetchWrapperCode } from "../../../runtime/fetch-wrapper.js";
6
7
  export function generateFetchWrapper(ctx) {
7
8
  const ext = ctx.ext;
8
9
  const ts = ext === "ts";
9
- const T = (type) => (ts ? `: ${type}` : "");
10
- const R = (type) => (ts ? `: ${type} ` : " ");
11
- const G = (type) => (ts ? `<${type}>` : "");
12
- const tagInvalidation = ctx.config.features.tagInvalidation;
13
- const authEnabled = ctx.config.features.auth.enabled;
14
- const mutationQueue = ctx.config.features.mutationQueue.enabled;
15
- const importLines = [
16
- tagInvalidation
17
- ? `import { generateTags } from "./invalidation-tags.${ext}";`
18
- : "",
19
- tagInvalidation ? `import { invalidateByTags } from "./cache.${ext}";` : "",
20
- authEnabled
21
- ? `import { getAuth, clearAuth, withAuthHeaders, isAuthUrl, AUTH_WITH_CREDENTIALS } from "./auth/store.${ext}";`
22
- : "",
23
- mutationQueue
24
- ? `import { queueMutation } from "./mutation-queue.${ext}";`
25
- : "",
26
- ]
27
- .filter(Boolean)
28
- .join("\n");
29
- const interfaceBlock = ts
30
- ? `
31
- export interface FetchWithCacheResult${G("T")} {
32
- response${T("Response & { json(): Promise<T> }")};
33
- fromCache${T("boolean")};
34
- }
35
-
36
- export interface ResolvedFetchConfig {
37
- staleTime${T("number")};
38
- refetchOnWindowFocus${T("boolean")};
39
- refetchOnReconnect${T("boolean")};
40
- refetchInterval${T("number")};
41
- }
42
- `
43
- : "";
44
- const optionsInterface = ts
45
- ? `
46
- export interface FetchWithCacheOptions extends RequestInit {
47
- tags?: string[];
48
- staleWhileRevalidate?: boolean;
49
- auth?: boolean;
50
- queueOffline?: boolean;
51
- invalidate?: 'auto' | string[] | false;
52
- type?: 'read' | 'mutation';
53
- strategy?: 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'cache-only' | 'network-only';
54
- staleTime?: number;
55
- }
56
- `
57
- : "";
58
- const authBlock = authEnabled
59
- ? `
60
- if (options.auth) {
61
- const auth = await getAuth();
62
- withAuthHeaders(headers, auth);
63
- }`
64
- : "";
65
- const authUrlsBlock = authEnabled
66
- ? `
67
- // Auth endpoints bypass SW cache
68
- if (options.auth && isAuthUrl(url) && !headers.has("X-SW-Cache-Strategy")) {
69
- headers.set("X-SW-Cache-Strategy", "mutation");
70
- }`
71
- : "";
72
- const authCredentialsBlock = authEnabled
73
- ? `
74
- if (AUTH_WITH_CREDENTIALS) {
75
- fetchOptions.credentials = "include";
76
- }`
77
- : "";
78
- const auth401Block = authEnabled
79
- ? `
80
- if (options.auth && response.status === 401) {
81
- await clearAuth();
82
- window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
83
- }`
84
- : "";
85
- const offlineReadBlock = ` if (isRead) {
86
- if (options.signal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
87
- const cached = await caches.match(input);
88
- if (cached) return { response: cached, fromCache: true };
89
- throw new Error("Offline: no cached data available");
90
- }`;
91
- const offlineWriteBlock = mutationQueue
92
- ? ` // Write offline — auto-queue
93
- if (options.queueOffline !== false) {
94
- const headerObj: Record<string, string> = {};
95
- headers.forEach((value, key) => {
96
- headerObj[key] = value;
97
- });
98
- await queueMutation({
99
- method,
100
- url,
101
- body: options.body,
102
- headers: headerObj,
103
- tags: options.tags || [],
104
- timestamp: Date.now(),
105
- });
106
- return {
107
- response: new Response(null, { status: 202, statusText: "Queued" }),
108
- fromCache: false,
109
- };
110
- }
111
- throw new Error("Offline: write operation failed");`
112
- : ` throw new Error("Offline: write operation failed");`;
113
- const autoInvalidateBlock = tagInvalidation
114
- ? `
115
- // Auto-invalidate after mutation success
116
- if (!isRead && response.ok) {
117
- const invalidateSetting = options.invalidate !== false ? (options.invalidate || 'auto') : false;
118
- if (invalidateSetting !== false) {
119
- const tagsToInvalidate${T("string[]")} = Array.isArray(invalidateSetting)
120
- ? invalidateSetting
121
- : (options.tags || generateTags(url));
122
- if (tagsToInvalidate.length > 0) {
123
- await invalidateByTags(tagsToInvalidate);
124
- }
125
- }
126
- }`
127
- : "";
128
- const autoTagsBlock = tagInvalidation
129
- ? `
130
- // Auto-generate tags from URL if not provided
131
- if (!options.tags && isRead) {
132
- const urlTags = generateTags(url);
133
- if (urlTags.length > 0) {
134
- headers.set("X-SW-Cache-Tags", urlTags.join(","));
135
- }
136
- }`
137
- : "";
138
- const staleTimeHeaders = ` // Pass staleTime to SW if set at per-request level
139
- if (options.staleTime !== undefined) {
140
- headers.set("X-SW-Stale-Time", String(options.staleTime));
141
- }`;
142
- const code = `/**
143
- * Swoff Fetch Wrapper
144
- * Unified fetch with caching, auth, offline queue, auto-invalidation, staleTime, prefetching, and
145
- * per-request strategy override.
146
- *
147
- * Usage:
148
- * import { fetchWithCache, prefetchCache } from './swoff/fetch-wrapper.${ext}';
149
- *
150
- * // GET — cached with auto-generated tags
151
- * const { response } = await fetchWithCache("/api/todos");
152
- * const data = await response.json();
153
- *
154
- * // POST — mutation with auto-invalidation
155
- * await fetchWithCache("/api/todos", {
156
- * method: "POST",
157
- * body: JSON.stringify({ title: "New task" }),
158
- * });
159
- *
160
- * // Authenticated request (works with bearer, cookie, custom)
161
- * const { response: userRes } = await fetchWithCache("/api/me", { auth: true });
162
- *
163
- * // Custom tags + stale-while-revalidate
164
- * const { response: staleRes, fromCache } = await fetchWithCache("/api/data", {
165
- * tags: ["data"],
166
- * staleWhileRevalidate: true,
167
- * });
168
- *
169
- * // Set staleTime per-request (overrides config — SW handles bg refresh)
170
- * await fetchWithCache("/api/todos", { staleTime: 30 });
171
- *
172
- * // Prefetch (fire-and-forget warm the cache)
173
- * prefetchCache("/api/todos");
174
- *
175
- * // Override caching strategy per-request (highest priority)
176
- * await fetchWithCache("/api/checkout", {
177
- * method: "POST",
178
- * type: "read",
179
- * strategy: "network-only",
180
- * });
181
- *
182
- * // Support AbortController for cancellation
183
- * const controller = new AbortController();
184
- * setTimeout(() => controller.abort(), 5000);
185
- * const { response } = await fetchWithCache("/api/todos", { signal: controller.signal });
186
- *
187
- * // Offline: auto-queues writes (disable with queueOffline: false)
188
- * await fetchWithCache("/api/todos", {
189
- * method: "POST",
190
- * body: JSON.stringify({ title: "Offline task" }),
191
- * });
192
- * // When back online, processMutationQueue() replays them
193
- */
194
-
195
- ${importLines}
196
- ${interfaceBlock}${optionsInterface}
197
- const inFlightRequests = new Map${G("string, Promise<Response>")}();
198
-
199
- /** Fetch with caching, auth, offline queue, auto-invalidation, and per-request strategy override. Returns { response, fromCache }. Use { auth: true } for authenticated requests — works with bearer, cookie, and custom auth types. */
200
- export async function fetchWithCache${G("T")}(input${T("RequestInfo")}, options${T("RequestInit & { tags?: string[]; staleWhileRevalidate?: boolean; auth?: boolean; queueOffline?: boolean; invalidate?: 'auto' | string[] | false; type?: 'read' | 'mutation'; strategy?: 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'cache-only' | 'network-only'; staleTime?: number }")} = {})${R("Promise<FetchWithCacheResult<T>>")}{
201
- const method = (options.method || "GET").toUpperCase();
202
- const isRead = options.type === "read" || (options.type !== "mutation" && (method === "GET" || method === "HEAD"));
203
- const url = typeof input === "string" ? input : input.url;
204
-
205
- const headers = new Headers(options.headers);
206
-
207
- // Set cache strategy
208
- if (!headers.has("X-SW-Cache-Strategy")) {
209
- headers.set("X-SW-Cache-Strategy", isRead ? "read" : "mutation");
210
- }
211
- ${autoTagsBlock}
212
- // Custom tags override auto-generated
213
- if (options.tags && options.tags.length > 0) {
214
- headers.set("X-SW-Cache-Tags", options.tags.join(","));
215
- }
216
-
217
- if (options.staleWhileRevalidate && !options.strategy) {
218
- headers.set("X-SW-Strategy", "stale-while-revalidate");
219
- }
220
- if (options.strategy) {
221
- headers.set("X-SW-Strategy", options.strategy);
222
- }
223
- ${staleTimeHeaders}
224
- ${authBlock}${authUrlsBlock}
225
- const fetchOptions${T("RequestInit")} = { ...options, headers };
226
- ${authCredentialsBlock}
227
-
228
- // Offline handling
229
- if (!navigator.onLine) {
230
- ${offlineReadBlock}
231
- ${offlineWriteBlock}
232
- }
233
-
234
- // Check for abort before proceeding
235
- if (options.signal?.aborted) {
236
- throw new DOMException("The operation was aborted", "AbortError");
237
- }
238
-
239
- // Deduplicate in-flight GET requests
240
- let responsePromise${T("Promise<Response>")};
241
- if (isRead && inFlightRequests.has(url)) {
242
- responsePromise = inFlightRequests.get(url)!.then((r) => r.clone());
243
- } else {
244
- const abortHandler = () => {
245
- inFlightRequests.delete(url);
246
- };
247
- if (options.signal) {
248
- options.signal.addEventListener("abort", abortHandler, { once: true });
249
- }
250
- responsePromise = fetch(input, fetchOptions);
251
- if (isRead) {
252
- const cleanup = () => {
253
- inFlightRequests.delete(url);
254
- if (options.signal) {
255
- options.signal.removeEventListener("abort", abortHandler);
256
- }
257
- };
258
- inFlightRequests.set(url, responsePromise.finally(cleanup));
259
- }
260
- }
261
-
262
- const response = await responsePromise;
263
- ${autoInvalidateBlock}${auth401Block}
264
- const fromCache = response.headers.get("X-SW-From-Cache") === "true";
265
- return { response, fromCache };
266
- }
267
-
268
- /** Fire-and-forget prefetch: warms the cache for a URL without blocking. Useful for route prefetching or link hover prefetching. */
269
- export function prefetchCache(input${T("RequestInfo")}, options${T("RequestInit & { tags?: string[]; staleWhileRevalidate?: boolean; auth?: boolean; queueOffline?: boolean; invalidate?: 'auto' | string[] | false; type?: 'read' | 'mutation'; strategy?: 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'cache-only' | 'network-only'; staleTime?: number }")} = {})${R("void")}{
270
- fetchWithCache(input, { ...options }).catch(() => {
271
- // Prefetch failures are intentionally silent
272
- });
273
- }
274
- `;
275
- writeFile(ctx, `fetch-wrapper.${ext}`, code);
10
+ const code = generateFetchWrapperCode({ ts, ext }, ctx.config.features.auth.enabled, ctx.config.features.auth.userEndpoint, ctx.config.features.mutationQueue.enabled, ctx.config.features.serviceWorker.requestBatchWindowMs);
11
+ writeFile(ctx, `fetch/core.${ext}`, code);
276
12
  }
277
13
  //# sourceMappingURL=fetch-wrapper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-wrapper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-wrapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,UAAU,oBAAoB,CAAC,GAAqB;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,IAAI,GAAG,wBAAwB,CACnC,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAChC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EACrC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,EACzC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,oBAAoB,CACvD,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}