@swoff/cli 0.3.9 → 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 (216) hide show
  1. package/dist/__tests__/assemble-sw.test.js +8 -17
  2. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  3. package/dist/__tests__/cli-integration.test.js +2 -2
  4. package/dist/__tests__/cli-integration.test.js.map +1 -1
  5. package/dist/__tests__/config-types.test.js +18 -8
  6. package/dist/__tests__/config-types.test.js.map +1 -1
  7. package/dist/__tests__/file-generators.test.js +3 -3
  8. package/dist/__tests__/file-generators.test.js.map +1 -1
  9. package/dist/__tests__/loader.test.js +5 -1
  10. package/dist/__tests__/loader.test.js.map +1 -1
  11. package/dist/__tests__/validator.test.js +6 -13
  12. package/dist/__tests__/validator.test.js.map +1 -1
  13. package/dist/index.js +39 -35
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/cli/help.js +13 -8
  16. package/dist/lib/cli/help.js.map +1 -1
  17. package/dist/lib/commands/add.js +38 -8
  18. package/dist/lib/commands/add.js.map +1 -1
  19. package/dist/lib/commands/assets.js +37 -0
  20. package/dist/lib/commands/assets.js.map +1 -0
  21. package/dist/lib/commands/clean.js +33 -17
  22. package/dist/lib/commands/clean.js.map +1 -1
  23. package/dist/lib/commands/generate-assets.js +44 -0
  24. package/dist/lib/commands/generate-assets.js.map +1 -0
  25. package/dist/lib/commands/generate-guide.js +22 -29
  26. package/dist/lib/commands/generate-guide.js.map +1 -1
  27. package/dist/lib/commands/generate.js +16 -9
  28. package/dist/lib/commands/generate.js.map +1 -1
  29. package/dist/lib/commands/info.js +36 -45
  30. package/dist/lib/commands/info.js.map +1 -1
  31. package/dist/lib/commands/init.js +25 -3
  32. package/dist/lib/commands/init.js.map +1 -1
  33. package/dist/lib/commands/validate.js +1 -1
  34. package/dist/lib/commands/validate.js.map +1 -1
  35. package/dist/lib/config/loader.js +25 -20
  36. package/dist/lib/config/loader.js.map +1 -1
  37. package/dist/lib/config/validator.js +20 -11
  38. package/dist/lib/config/validator.js.map +1 -1
  39. package/dist/lib/generators/asset-generator/dependency-resolver.js +76 -0
  40. package/dist/lib/generators/asset-generator/dependency-resolver.js.map +1 -0
  41. package/dist/lib/generators/asset-generator/generate.js +111 -0
  42. package/dist/lib/generators/asset-generator/generate.js.map +1 -0
  43. package/dist/lib/generators/asset-generator/guide.js +43 -0
  44. package/dist/lib/generators/asset-generator/guide.js.map +1 -0
  45. package/dist/lib/generators/asset-generator/html-patcher.js +65 -0
  46. package/dist/lib/generators/asset-generator/html-patcher.js.map +1 -0
  47. package/dist/lib/generators/asset-generator/ico-encoder.js +29 -0
  48. package/dist/lib/generators/asset-generator/ico-encoder.js.map +1 -0
  49. package/dist/lib/generators/asset-generator/manifest-patcher.js +23 -0
  50. package/dist/lib/generators/asset-generator/manifest-patcher.js.map +1 -0
  51. package/dist/lib/generators/asset-generator/maskable.js +15 -0
  52. package/dist/lib/generators/asset-generator/maskable.js.map +1 -0
  53. package/dist/lib/generators/asset-generator/rasterize.js +32 -0
  54. package/dist/lib/generators/asset-generator/rasterize.js.map +1 -0
  55. package/dist/lib/generators/asset-generator/sizes.js +25 -0
  56. package/dist/lib/generators/asset-generator/sizes.js.map +1 -0
  57. package/dist/lib/generators/file-generators/api-config.js +6 -0
  58. package/dist/lib/generators/file-generators/api-config.js.map +1 -0
  59. package/dist/lib/generators/file-generators/auth-state.js +2 -34
  60. package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
  61. package/dist/lib/generators/file-generators/auth-store.js +2 -250
  62. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  63. package/dist/lib/generators/file-generators/auth-user.js +3 -81
  64. package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
  65. package/dist/lib/generators/file-generators/background-sync.js +2 -57
  66. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  67. package/dist/lib/generators/file-generators/cache.js +2 -30
  68. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  69. package/dist/lib/generators/file-generators/client-injector.js +2 -130
  70. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  71. package/dist/lib/generators/file-generators/fetch-state.js +6 -0
  72. package/dist/lib/generators/file-generators/fetch-state.js.map +1 -0
  73. package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -295
  74. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  75. package/dist/lib/generators/file-generators/generate-framework-adapters.js +56 -0
  76. package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -0
  77. package/dist/lib/generators/file-generators/generate-hooks.js +4 -3
  78. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  79. package/dist/lib/generators/file-generators/gql-wrapper.js +3 -132
  80. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  81. package/dist/lib/generators/file-generators/guide-generator.js +29 -1
  82. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  83. package/dist/lib/generators/file-generators/invalidation-tags.js +3 -258
  84. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  85. package/dist/lib/generators/file-generators/manifest.js +13 -3
  86. package/dist/lib/generators/file-generators/manifest.js.map +1 -1
  87. package/dist/lib/generators/file-generators/mutation-queue.js +3 -305
  88. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  89. package/dist/lib/generators/file-generators/mutation-state.js +2 -117
  90. package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
  91. package/dist/lib/generators/file-generators/push.js +6 -138
  92. package/dist/lib/generators/file-generators/push.js.map +1 -1
  93. package/dist/lib/generators/file-generators/pwa-install.js +11 -84
  94. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  95. package/dist/lib/generators/file-generators/reset.js +6 -0
  96. package/dist/lib/generators/file-generators/reset.js.map +1 -0
  97. package/dist/lib/generators/file-generators/server-push.js +3 -134
  98. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  99. package/dist/lib/generators/file-generators/sw-generator-build.js +4 -4
  100. package/dist/lib/generators/file-generators/sw-injector.js +9 -230
  101. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  102. package/dist/lib/generators/file-generators/type-definitions.js +2 -111
  103. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  104. package/dist/lib/generators/sw-generator.js +5 -5
  105. package/dist/lib/generators/sw-generator.js.map +1 -1
  106. package/dist/lib/generators/sw-sections/activate-handler.js +25 -3
  107. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  108. package/dist/lib/generators/sw-sections/assemble-sw.js +13 -12
  109. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  110. package/dist/lib/generators/sw-sections/background-sync-handler.js +14 -7
  111. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  112. package/dist/lib/generators/sw-sections/config-header.js +1 -1
  113. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  114. package/dist/lib/generators/sw-sections/fetch-handler.js +165 -104
  115. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  116. package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +1 -0
  117. package/dist/lib/generators/sw-sections/server-push-handler.js +12 -0
  118. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  119. package/dist/lib/generators/sw-sections/tag-management.js +6 -4
  120. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  121. package/dist/lib/generators/swoff-files-generator.js +66 -35
  122. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  123. package/dist/lib/shared/config-types.js +89 -28
  124. package/dist/lib/shared/config-types.js.map +1 -1
  125. package/dist/runtime/api-config.js +18 -0
  126. package/dist/runtime/api-config.js.map +1 -0
  127. package/dist/runtime/auth-state.js +35 -0
  128. package/dist/runtime/auth-state.js.map +1 -0
  129. package/dist/runtime/auth-store.js +273 -0
  130. package/dist/runtime/auth-store.js.map +1 -0
  131. package/dist/runtime/auth-user.js +168 -0
  132. package/dist/runtime/auth-user.js.map +1 -0
  133. package/dist/runtime/background-sync.js +59 -0
  134. package/dist/runtime/background-sync.js.map +1 -0
  135. package/dist/runtime/cache.js +30 -0
  136. package/dist/runtime/cache.js.map +1 -0
  137. package/dist/runtime/client-injector.js +136 -0
  138. package/dist/runtime/client-injector.js.map +1 -0
  139. package/dist/runtime/fetch-state.js +42 -0
  140. package/dist/runtime/fetch-state.js.map +1 -0
  141. package/dist/runtime/fetch-wrapper.js +355 -0
  142. package/dist/runtime/fetch-wrapper.js.map +1 -0
  143. package/dist/runtime/gql-wrapper.js +135 -0
  144. package/dist/runtime/gql-wrapper.js.map +1 -0
  145. package/dist/runtime/invalidation-tags.js +260 -0
  146. package/dist/runtime/invalidation-tags.js.map +1 -0
  147. package/dist/runtime/mutation-queue.js +304 -0
  148. package/dist/runtime/mutation-queue.js.map +1 -0
  149. package/dist/runtime/mutation-state.js +117 -0
  150. package/dist/runtime/mutation-state.js.map +1 -0
  151. package/dist/runtime/push.js +138 -0
  152. package/dist/runtime/push.js.map +1 -0
  153. package/dist/runtime/pwa-index.js +11 -0
  154. package/dist/runtime/pwa-index.js.map +1 -0
  155. package/dist/runtime/pwa-injector.js +44 -0
  156. package/dist/runtime/pwa-injector.js.map +1 -0
  157. package/dist/runtime/pwa-install.js +83 -0
  158. package/dist/runtime/pwa-install.js.map +1 -0
  159. package/dist/runtime/pwa-prompt.js +54 -0
  160. package/dist/runtime/pwa-prompt.js.map +1 -0
  161. package/dist/runtime/reset.js +148 -0
  162. package/dist/runtime/reset.js.map +1 -0
  163. package/dist/runtime/server-push.js +148 -0
  164. package/dist/runtime/server-push.js.map +1 -0
  165. package/dist/runtime/sw-injector.js +236 -0
  166. package/dist/runtime/sw-injector.js.map +1 -0
  167. package/dist/runtime/type-definitions.js +138 -0
  168. package/dist/runtime/type-definitions.js.map +1 -0
  169. package/dist/runtime/utils.js +27 -0
  170. package/dist/runtime/utils.js.map +1 -0
  171. package/package.json +3 -2
  172. package/templates/react/useAuth.jsx +106 -0
  173. package/templates/react/useAuth.tsx +118 -0
  174. package/templates/{hooks → react}/useBackgroundSync.jsx +6 -4
  175. package/templates/{hooks → react}/useBackgroundSync.tsx +6 -4
  176. package/templates/{hooks → react}/useCacheInvalidation.jsx +2 -2
  177. package/templates/react/useCacheInvalidation.tsx +52 -0
  178. package/templates/react/useCachedFetch.jsx +140 -0
  179. package/templates/react/useCachedFetch.tsx +160 -0
  180. package/templates/react/useIsFetching.jsx +32 -0
  181. package/templates/react/useIsFetching.tsx +32 -0
  182. package/templates/react/useMutation.jsx +141 -0
  183. package/templates/react/useMutation.tsx +180 -0
  184. package/templates/react/useMutationQueue.jsx +69 -0
  185. package/templates/react/useMutationQueue.tsx +83 -0
  186. package/templates/react/useNetworkStatus.jsx +61 -0
  187. package/templates/react/useNetworkStatus.tsx +76 -0
  188. package/templates/react/usePrefetch.jsx +32 -0
  189. package/templates/react/usePrefetch.tsx +42 -0
  190. package/templates/{hooks → react}/usePushSubscription.jsx +4 -4
  191. package/templates/{hooks → react}/usePushSubscription.tsx +4 -4
  192. package/templates/{hooks → react}/useSWUpdate.jsx +3 -3
  193. package/templates/{hooks → react}/useSWUpdate.tsx +4 -4
  194. package/templates/react/useSwoffReset.jsx +32 -0
  195. package/templates/react/useSwoffReset.tsx +39 -0
  196. package/docs/API.md +0 -565
  197. package/docs/ARCHITECTURE.md +0 -374
  198. package/docs/CLI.md +0 -148
  199. package/docs/COMPARISON.md +0 -192
  200. package/docs/CONFIG.md +0 -277
  201. package/docs/ECOSYSTEM.md +0 -33
  202. package/templates/hooks/useAuth.jsx +0 -30
  203. package/templates/hooks/useAuth.tsx +0 -30
  204. package/templates/hooks/useCacheInvalidation.tsx +0 -19
  205. package/templates/hooks/useCachedFetch.jsx +0 -69
  206. package/templates/hooks/useCachedFetch.tsx +0 -81
  207. package/templates/hooks/useMutation.jsx +0 -76
  208. package/templates/hooks/useMutation.tsx +0 -109
  209. package/templates/hooks/useMutationQueue.jsx +0 -43
  210. package/templates/hooks/useMutationQueue.tsx +0 -50
  211. package/templates/hooks/useNetworkStatus.jsx +0 -19
  212. package/templates/hooks/useNetworkStatus.tsx +0 -19
  213. package/templates/hooks/usePrefetch.jsx +0 -10
  214. package/templates/hooks/usePrefetch.tsx +0 -25
  215. /package/templates/{hooks → react}/useMutationState.jsx +0 -0
  216. /package/templates/{hooks → react}/useMutationState.tsx +0 -0
package/docs/API.md DELETED
@@ -1,565 +0,0 @@
1
- # Generated Files API Reference
2
-
3
- Complete API documentation for every generated file in `swoff/`. Files are
4
- generated based on `swoff.config.json` — see [CLI.md](./CLI.md#generate) for
5
- generation commands and [CONFIG.md](./CONFIG.md) for the full config schema.
6
-
7
- ---
8
-
9
- ## `fetch-wrapper.ts`
10
-
11
- Unified fetch with caching, auth, offline queue, auto-invalidation, staleTime, prefetching, and
12
- per-request strategy override. This is the core networking primitive — use it for all API calls.
13
-
14
- ```ts
15
- import { fetchWithCache, prefetchCache } from "swoff/fetch-wrapper";
16
- ```
17
-
18
- ### `fetchWithCache<T>(input, options?)`
19
-
20
- | Param | Type | Description |
21
- | --------- | ----------------------- | ---------------------------- |
22
- | `input` | `RequestInfo` | URL string or Request object |
23
- | `options` | `FetchWithCacheOptions` | See below |
24
-
25
- **Returns:** `Promise<{ response: Response & { json(): Promise<T> }, fromCache: boolean }>`
26
-
27
- - `response`: the fetch Response (from cache or network). Typed `json()` in TS.
28
- - `fromCache`: `true` when served from SW cache
29
-
30
- ### Options
31
-
32
- All `RequestInit` fields are supported (`method`, `body`, `headers`, `credentials`, `signal`, etc.) plus:
33
-
34
- | Option | Type | Default | Description |
35
- | ---------------------- | ------------------------------------------------------------------------------------------------ | ----------------------- | -------------------------------------------------------- |
36
- | `tags` | `string[]` | auto-generated from URL | Cache invalidation tags for this request |
37
- | `auth` | `boolean` | `false` | Attach auth token via `withAuthHeaders()` |
38
- | `queueOffline` | `boolean` | `true` | When offline, queue writes to IndexedDB |
39
- | `invalidate` | `'auto' \| string[] \| false` | `'auto'` | Auto-invalidate cache tags after successful mutation |
40
- | `type` | `'read' \| 'mutation'` | auto-detected | Override read/mutation detection |
41
- | `strategy` | `'cache-first' \| 'network-first' \| 'stale-while-revalidate' \| 'cache-only' \| 'network-only' \| 'reactive'` | — | Override caching strategy per-request (highest priority) |
42
- | `staleTime` | `number` | — | Override stale time in seconds (reactive-only, per-request tier 1) |
43
- | `validateSuccess` | `(response: Response) => boolean \| Promise<boolean>` | `res.ok` | Custom mutation success check (e.g. when API returns 200 with `{ success: false }`) |
44
- | `invalidateUrl` | `string` | the request URL | Override the URL used for auto-invalidation tags. Useful when mutation URL differs from cache tag URL. |
45
- | `signal` | `AbortSignal` | — | AbortController signal for cancellation |
46
-
47
- ### Behavior
48
-
49
- - **Read vs mutation**: GET/HEAD → read (cached). POST/PUT/DELETE/PATCH → mutation (pass through).
50
- Override with `type: 'read'` or `type: 'mutation'`.
51
- - **Offline reads**: returns cached response if available; throws if not cached.
52
- **Abort check**: if `signal.aborted`, throws `AbortError` before cache lookup.
53
- - **Offline writes**: queues to IndexedDB (when `mutationQueue` enabled). Replays on `online` event.
54
- Disable per-request with `queueOffline: false`.
55
- - **Dedup**: in-flight GETs to the same URL return a single promise (cloned response).
56
- AbortController integration: cleaned up on completion or abort.
57
- - **Auto-tags**: when `tagInvalidation` enabled, tags derived from URL path for read requests.
58
- - **Auto-invalidate**: after a successful mutation, matching cache tags are invalidated. Mutation success is determined by `response.ok` by default, or `validateSuccess` if provided. The auto-invalidation target URL can be overridden with `invalidateUrl` (useful when mutation URL differs from the cache tag URL).
59
- - **Auth**: when `auth: true`, attaches auth headers. Dispatches `sw-auth-unauthorized` on 401.
60
- - **StaleTime**: 3-tier resolution (per-request / route pattern / global default). Only affects `reactive` strategy. Controls the fresh window before a background refresh is triggered.
61
- - **Background refresh retry**: failed refetches are retried with exponential backoff up to `features.refetchQueue.maxRetries` times (base delay `features.refetchQueue.retryDelayMs`).
62
-
63
- ### `prefetchCache(input, options?)`
64
-
65
- Fire-and-forget prefetch to warm the cache. Silently swallows errors.
66
-
67
- ```ts
68
- prefetchCache("/api/todos");
69
- prefetchCache("/api/todos");
70
- ```
71
-
72
- ---
73
-
74
- ## `mutation-queue.ts`
75
-
76
- Offline write queue backed by IndexedDB. Writes performed while offline are stored and replayed
77
- automatically when the connection returns.
78
-
79
- ```ts
80
- import {
81
- queueMutation,
82
- processMutationQueue,
83
- flushMutations,
84
- getPendingCount,
85
- getQueuePosition,
86
- getQueueItems,
87
- } from "swoff/mutation-queue";
88
- ```
89
-
90
- ### Functions
91
-
92
- | Function | Signature | Description |
93
- | ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
94
- | `queueMutation` | `(mutation: MutationQueueItem) => Promise<void>` | Store a write for later sync |
95
- | `processMutationQueue` | `() => Promise<{ succeeded: number, failed: number }>` | Replay all queued writes. Respects `batchSize`, `batchDelayMs`, `maxRetries`, `retryBackoffMs`. Runs automatically on `online` event. |
96
- | `flushMutations` | `() => Promise<void>` | Same as `processMutationQueue`. Call after re-login (queued mutations may have stale auth). |
97
- | `getPendingCount` | `() => Promise<number>` | Number of mutations waiting to sync |
98
- | `getQueuePosition` | `(id: string) => Promise<number>` | 0-based position of a mutation in the queue. Returns -1 if not found. |
99
- | `getQueueItems` | `() => Promise<MutationQueueItem[]>` | All pending queue items with their status, retry count, and metadata |
100
-
101
- ### `MutationQueueItem`
102
-
103
- ```ts
104
- {
105
- id: string;
106
- method: string;
107
- url: string;
108
- body?: unknown;
109
- headers?: Record<string, string>;
110
- tags?: string[];
111
- timestamp: number;
112
- retryCount: number;
113
- status?: "pending" | "processing" | "failed";
114
- }
115
- ```
116
-
117
- Generated when `features.mutationQueue.enabled` is `true`.
118
-
119
- ---
120
-
121
- ## `mutation-state.ts`
122
-
123
- Per-mutation state tracking. Each mutation operation gets an ID that can be used to track its
124
- status (loading, success, error) across the app.
125
-
126
- ```ts
127
- import {
128
- startMutation,
129
- trackMutation,
130
- getMutationState,
131
- clearMutationState,
132
- onMutationStateChange,
133
- } from "swoff/mutation-state";
134
- ```
135
-
136
- ### Functions
137
-
138
- | Function | Signature | Description |
139
- | ----------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
140
- | `startMutation` | `(id?: string) => string` | Create a new mutation with `"loading"` status. Returns the mutation ID. |
141
- | `trackMutation` | `(id: string, status: MutationStatus, data?: unknown, error?: unknown) => void` | Update a mutation's status. Dispatches a state change event. |
142
- | `getMutationState` | `(id: string) => MutationState \| undefined` | Get the current state of a mutation |
143
- | `clearMutationState` | `(id: string) => void` | Remove a mutation from tracking |
144
- | `onMutationStateChange` | `(cb: (state: MutationState) => void) => () => void` | Subscribe to all mutation state changes. Returns an unsubscribe function. |
145
-
146
- ### Use with `useMutation`
147
-
148
- The `useMutation` React hook wraps `startMutation` + `trackMutation` and exposes
149
- `{ mutate, isLoading, isError, isSuccess, data, error, reset }`.
150
-
151
- ---
152
-
153
- ## `server-push.ts`
154
-
155
- Client-side connection manager for real-time cache invalidation via SSE or WebSocket.
156
- The service worker maintains the primary connection; this module provides a fallback and
157
- status events for the UI.
158
-
159
- ```ts
160
- import {
161
- startPushEvents,
162
- stopPushEvents,
163
- isPushConnected,
164
- } from "swoff/server-push";
165
- ```
166
-
167
- ### Functions
168
-
169
- | Function | Description |
170
- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
171
- | `startPushEvents()` | Connect to the push endpoint and begin listening for invalidation events. Auto-reconnects on connection loss with exponential backoff (initial delay from config, capped at 30s). |
172
- | `stopPushEvents()` | Disconnect from the push endpoint |
173
- | `isPushConnected()` | Returns `boolean` — whether the connection is currently active |
174
-
175
- ### Events
176
-
177
- The module dispatches:
178
-
179
- - `push-events-status` — `CustomEvent<{ connected: boolean }>` — connection status changes
180
- - `cache-invalidated` — `CustomEvent<{ tags: string[] }>` — when the server sends an invalidation
181
-
182
- ### Server format
183
-
184
- **SSE** (default):
185
-
186
- ```
187
- event: invalidate
188
- data: {"tags": ["todos", "categories"]}
189
-
190
- ```
191
-
192
- **WebSocket:**
193
-
194
- ```json
195
- { "event": "invalidate", "tags": ["todos", "categories"] }
196
- ```
197
-
198
- Generated when `features.serverPush.enabled` is `true`.
199
-
200
- ---
201
-
202
- ## `cache.ts`
203
-
204
- Low-level cache invalidation. Sends invalidation messages to the SW; the SW
205
- removes matching entries from the runtime cache and confirms back to the
206
- client-injector, which dispatches `cache-invalidated` on the window.
207
-
208
- ```ts
209
- import { invalidateByTag, invalidateByTags } from "swoff/cache";
210
- ```
211
-
212
- ### Functions
213
-
214
- | Function | Signature | Description |
215
- | ------------------ | ----------------------------------- | ----------------------------------------------------------------------------------------- |
216
- | `invalidateByTag` | `(tag: string) => Promise<void>` | Send `INVALIDATE_TAG` to the SW; the SW removes matching cache entries and confirms via `TAG_INVALIDATED` (client-injector dispatches `cache-invalidated` on the window). |
217
- | `invalidateByTags` | `(tags: string[]) => Promise<void>` | Invalidate multiple tags at once. Cascading is expanded by callers before calling this function. |
218
-
219
- Generated when `features.tagInvalidation` is `true`.
220
-
221
- ---
222
-
223
- ## `invalidation-tags.ts`
224
-
225
- Tag generation helpers. Tags are derived from URL paths and used to mark cache entries
226
- for targeted invalidation after mutations.
227
-
228
- ```ts
229
- import {
230
- generateTags,
231
- generateTagsFromMethod,
232
- invalidateUrl,
233
- invalidateByMethod,
234
- expandCascading,
235
- getUrlsForTag,
236
- getTagsForUrl,
237
- invalidateMatching,
238
- } from "swoff/invalidation-tags";
239
- ```
240
-
241
- ### Functions
242
-
243
- | Function | Signature | Description |
244
- | ------------------------ | ------------------------------------------------ | -------------------------------------------------------------------- |
245
- | `generateTags` | `(url: string) => string[]` | Extract tags from URL path. `/api/todos/42` → `["todos", "todo:42"]` |
246
- | `generateTagsFromMethod` | `(method: string, url: string) => string[]` | Method-prefixed tags. `POST /api/todos` → `["post-todos"]` |
247
- | `invalidateUrl` | `(url: string) => Promise<void>` | Extract tags from URL and invalidate all matching cache entries |
248
- | `invalidateByMethod` | `(method: string, url: string) => Promise<void>` | Invalidate using method-prefixed tags |
249
- | `expandCascading` | `(tags: string[]) => string[]` | Expand tags with cascading dependencies, deduplicated |
250
- | `getUrlsForTag` | `(tag: string) => Promise<{ url, actualUrl }[]>` | Get all URLs cached under a given tag (uses MessageChannel) |
251
- | `getTagsForUrl` | `(url: string) => Promise<string[]>` | Get all tags associated with a given URL |
252
- | `invalidateMatching` | `(glob: string) => Promise<void>` | Invalidate all cached responses whose URL matches a glob pattern |
253
-
254
- Generated when `features.tagInvalidation` is `true`.
255
-
256
- ---
257
-
258
- ## `gql-wrapper.ts`
259
-
260
- GraphQL wrapper with body-hash caching. Brings Swoff's caching, auth, offline queue, and
261
- tag-based invalidation to GraphQL APIs.
262
-
263
- ```ts
264
- import { fetchWithGql, queryGql, mutateGql } from "swoff/gql-wrapper";
265
- ```
266
-
267
- ### Functions
268
-
269
- | Function | Signature | Description |
270
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
271
- | `fetchWithGql<T>` | `(query: string, options?: GqlOptions) => Promise<{ data: T, fromCache: boolean }>` | Core function. Hashes query + variables, sets `X-SW-Cache-Key`, delegates to `fetchWithCache`. |
272
- | `queryGql<T>` | `(query: string, variables?: Record<string, unknown>, options?: GqlOptions) => Promise<{ data: T, fromCache: boolean }>` | Shorthand for reads — sets `type: "read"` |
273
- | `mutateGql<T>` | `(mutation: string, variables?: Record<string, unknown>, options?: GqlOptions) => Promise<{ data: T }>` | Shorthand for writes — sets `type: "mutation"`, auto-invalidates related tags |
274
-
275
- ### How it works
276
-
277
- 1. **Body hashing**: `query` + `variables` are JSON-stringified and SHA-256 hashed via `crypto.subtle.digest()`. First 16 hex chars become `X-SW-Cache-Key: gql:<hash>`.
278
- 2. **Virtual cache URL**: The SW treats the hash as the cache key instead of the actual URL. Different queries to the same endpoint don't collide.
279
- 3. **Auto-tags**: Operation names like `getTodos` → `["todos"]`; `createTodo` → `["todos", "todo"]`.
280
- 4. **Offline queue**: Mutations queue in IndexedDB; reads return cached data.
281
-
282
- ### `GqlOptions`
283
-
284
- All `fetchWithCache` options are available, plus:
285
-
286
- | Option | Type | Default | Description |
287
- | ---------------------- | ----------------------------- | -------------- | ------------------------------------------------ |
288
- | `variables` | `Record<string, unknown>` | `undefined` | GraphQL variables |
289
- | `tags` | `string[]` | auto-generated | Override invalidation tags |
290
- | `auth` | `boolean` | `false` | Attach auth token |
291
- | `queueOffline` | `boolean` | `true` | Queue mutations when offline |
292
- | `invalidate` | `'auto' \| string[] \| false` | `'auto'` | Auto-invalidate after mutation |
293
-
294
- Generated when `features.graphql.enabled` is `true`.
295
-
296
- ---
297
-
298
- ## Auth module
299
-
300
- Generated when `features.auth.enabled` is `true`. Three files in `swoff/auth/`.
301
-
302
- ### `auth/store.ts`
303
-
304
- Token and user persistence. Token is memory-only (never persisted to disk).
305
-
306
- ```ts
307
- import {
308
- setAuth,
309
- getAuth,
310
- clearAuth,
311
- isAuthValid,
312
- createAuthFromResponse,
313
- ensureValidAuth,
314
- withAuthHeaders,
315
- } from "swoff/auth/store";
316
- ```
317
-
318
- | Function | Returns | Description |
319
- | ---------------------------------- | --------------------------- | ------------------------------------------------------------ |
320
- | `setAuth(authData)` | `Promise<void>` | Store auth in memory, persist user to IndexedDB |
321
- | `getAuth()` | `Promise<AuthData \| null>` | Get auth from memory (IndexedDB fallback after page refresh) |
322
- | `clearAuth()` | `Promise<void>` | Clear memory + IndexedDB. Call on logout/401. |
323
- | `isAuthValid(auth)` | `boolean` | Check existence + `expiresAt` expiry |
324
- | `createAuthFromResponse(response)` | `AuthData` | **Edit this** to match your backend's login response shape |
325
- | `ensureValidAuth()` | `Promise<AuthData \| null>` | Check expiry, refresh via `refreshPath` if needed |
326
- | `withAuthHeaders(headers, auth)` | `void` | Inject auth headers (bearer, cookie, or custom) |
327
-
328
- ### `auth/user.ts`
329
-
330
- User data caching for offline display.
331
-
332
- ```ts
333
- import {
334
- fetchCurrentUser,
335
- getCachedUser,
336
- cacheUser,
337
- clearCachedUser,
338
- } from "swoff/auth/user";
339
- ```
340
-
341
- | Function | Description |
342
- | -------------------- | ------------------------------------------------ |
343
- | `fetchCurrentUser()` | Fetch from `userEndpoint` and cache in IndexedDB |
344
- | `getCachedUser()` | Load user from IndexedDB (available offline) |
345
- | `cacheUser(user)` | Manually persist user data |
346
- | `clearCachedUser()` | Remove user from cache |
347
-
348
- ### `auth/state.ts`
349
-
350
- Detects which of 4 states the app is in.
351
-
352
- ```ts
353
- import { getAuthState } from "swoff/auth/state";
354
- ```
355
-
356
- | Function | Returns | Description |
357
- | ---------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
358
- | `getAuthState()` | `Promise<{ authenticated: boolean, user: Record<string, unknown> \| null, online: boolean }>` | Detect state: online+auth, online+unauthed, offline+auth, offline+unauthed |
359
-
360
- ---
361
-
362
- ## `pwa/install.ts`
363
-
364
- Install prompt handling and manifest generation.
365
-
366
- ```ts
367
- import {
368
- setupPwaInstall,
369
- isInstallable,
370
- promptInstall,
371
- } from "swoff/pwa/install";
372
- ```
373
-
374
- | Function | Description |
375
- | ------------------- | ------------------------------------------------------------------------------------------------------- |
376
- | `setupPwaInstall()` | Listen for `beforeinstallprompt` / `appinstalled` events. Called automatically by `client-injector.ts`. |
377
- | `isInstallable()` | Check if install prompt is available (returns `boolean`) |
378
- | `promptInstall()` | Show the native install prompt. Returns `{ outcome: string }`. |
379
-
380
- Generated when `features.pwa.enabled` is `true`.
381
-
382
- ---
383
-
384
- ## `push.ts`
385
-
386
- Push notification subscription management with IndexedDB persistence.
387
-
388
- ```ts
389
- import {
390
- subscribeToPush,
391
- unsubscribeFromPush,
392
- isSubscribed,
393
- getPushSubscription,
394
- requestNotificationPermission,
395
- } from "swoff/push";
396
- ```
397
-
398
- | Function | Description |
399
- | --------------------------------- | -------------------------------------------------------------------------------------- |
400
- | `subscribeToPush()` | Request permission and subscribe. Returns `PushSubscription \| null` (null if denied). |
401
- | `unsubscribeFromPush()` | Unsubscribe and clear stored subscription |
402
- | `isSubscribed()` | Check subscription status (returns `Promise<boolean>`) |
403
- | `getPushSubscription()` | Get current subscription object (returns `Promise<PushSubscription \| null>`) |
404
- | `requestNotificationPermission()` | Request permission only. Returns `Promise<boolean>`. |
405
-
406
- Generated when `features.pushNotifications.enabled` is `true`.
407
-
408
- ---
409
-
410
- ## `background-sync.ts`
411
-
412
- Background Sync API registration for processing mutations even after tab close.
413
-
414
- ```ts
415
- import { syncWhenPossible, retrySync } from "swoff/background-sync";
416
- ```
417
-
418
- | Function | Description |
419
- | ---------------------------- | ---------------------------------------------------------------------- |
420
- | `syncWhenPossible(mutation)` | Queue and register a background sync event |
421
- | `retrySync()` | Re-register sync if mutations are still pending (called automatically) |
422
-
423
- Fallback: uses the `online` event listener in unsupported browsers (Firefox, Safari).
424
- Generated when `features.backgroundSync` is `true`.
425
-
426
- ---
427
-
428
- ## React hooks
429
-
430
- Generated when `features.framework` is `"react"`. All hooks live in `swoff/hooks/`.
431
-
432
- ### `useCachedFetch<T>(url, options?)`
433
-
434
- ```ts
435
- const { data, error, loading, refetch } = useCachedFetch<Todo[]>("/api/todos");
436
- const { data: posts } = useCachedFetch<Post[]>("/api/posts", {
437
- enabled: !!user,
438
- });
439
- ```
440
-
441
- Returns `{ data: T \| null, error: unknown, loading: boolean, refetch: () => void }`.
442
-
443
- - Auto-refetches on `cache-invalidated` events matching the URL
444
- - **Dependent queries**: pass `enabled: false` or a nullable URL to skip until a condition is met
445
- - Stale data is automatically refreshed in the background by the SW (batched & rate-limited) when using the `reactive` strategy. On `online` event, reactive entries with `refetchOnReconnect` are recovered.
446
-
447
- ### `useMutation()`
448
-
449
- ```ts
450
- const { mutate, isLoading, isError, isSuccess, data, error, reset } =
451
- useMutation({
452
- onSuccess: (data) => console.log(data),
453
- onError: (err) => console.error(err),
454
- });
455
- mutate("/api/todos", {
456
- method: "POST",
457
- body: JSON.stringify({ title: "New" }),
458
- });
459
- ```
460
-
461
- ### `usePrefetch()`
462
-
463
- ```ts
464
- const prefetch = usePrefetch();
465
- <a onMouseEnter={() => prefetch("/api/todos")}>Todos</a>
466
- ```
467
-
468
- Returns a stable `prefetch(input, options?)` callback.
469
-
470
- ### `useMutationState(id)`
471
-
472
- ```ts
473
- const mutation = useMutationState(mutationId);
474
- if (mutation?.status === "error") {
475
- /* show error */
476
- }
477
- ```
478
-
479
- Returns `MutationState | null`. Subscribes to a specific mutation's state changes.
480
- Pass `null` or empty string to disable.
481
-
482
- ### `useMutationQueue()`
483
-
484
- ```ts
485
- const { pending, items, lastSync } = useMutationQueue();
486
- ```
487
-
488
- | Field | Type | Description |
489
- | ---------- | ----------------------------------------------- | ------------------------------------------------------- |
490
- | `pending` | `number` | Count of mutations waiting to sync |
491
- | `items` | `MutationQueueItem[]` | All pending items with status, retry count, URL, method |
492
- | `lastSync` | `{ succeeded: number, failed: number } \| null` | Result of the last sync attempt |
493
-
494
- ### `useNetworkStatus()`
495
-
496
- ```ts
497
- const online = useNetworkStatus();
498
- ```
499
-
500
- Returns `boolean` — `true` when navigator is online.
501
-
502
- ### `useAuth()`
503
-
504
- ```ts
505
- const { authenticated, user, online } = useAuth();
506
- ```
507
-
508
- Reactive auth + connectivity state. Listens to online/offline/auth changes.
509
-
510
- ### `useSWUpdate()`
511
-
512
- ```ts
513
- const {
514
- updateStatus,
515
- currentVersion,
516
- availableVersion,
517
- forceUpdate,
518
- error,
519
- acceptUpdate,
520
- dismissUpdate,
521
- } = useSWUpdate();
522
- ```
523
-
524
- `updateStatus` is one of: `"idle"`, `"available"`, `"downloading"`, `"ready"`.
525
-
526
- ### `useSWProgress()`
527
-
528
- ```ts
529
- const { status, progress } = useSWProgress();
530
- ```
531
-
532
- `progress` = `{ percent: number, downloaded: number, total: number }` during SW download.
533
-
534
- ### `usePushSubscription()`
535
-
536
- ```ts
537
- const {
538
- subscribed,
539
- subscription,
540
- permission,
541
- loading,
542
- subscribe,
543
- unsubscribe,
544
- } = usePushSubscription();
545
- ```
546
-
547
- Generated when `pushNotifications.enabled`.
548
-
549
- ### `useBackgroundSync()`
550
-
551
- ```ts
552
- const { supported, registered, lastSync, triggerSync } = useBackgroundSync();
553
- ```
554
-
555
- Generated when `backgroundSync` is `true`.
556
-
557
- ### `useCacheInvalidation()`
558
-
559
- ```ts
560
- const { invalidateByTag, invalidateByTags, invalidateUrl } =
561
- useCacheInvalidation();
562
- ```
563
-
564
- Stable `useCallback`-wrapped versions of cache invalidation functions.
565
- Generated when `tagInvalidation` is `true`.