@swoff/cli 0.3.9 → 0.3.11
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.
- package/dist/__tests__/assemble-sw.test.js +8 -17
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +2 -2
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +18 -8
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +3 -3
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +5 -1
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +6 -13
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/index.js +41 -35
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +13 -8
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +38 -8
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/assets.js +38 -0
- package/dist/lib/commands/assets.js.map +1 -0
- package/dist/lib/commands/clean.js +33 -17
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-assets.js +44 -0
- package/dist/lib/commands/generate-assets.js.map +1 -0
- package/dist/lib/commands/generate-guide.js +22 -29
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +16 -9
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +36 -45
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/init.js +25 -3
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +1 -1
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/loader.js +25 -20
- package/dist/lib/config/loader.js.map +1 -1
- package/dist/lib/config/validator.js +20 -11
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/asset-generator/dependency-resolver.js +76 -0
- package/dist/lib/generators/asset-generator/dependency-resolver.js.map +1 -0
- package/dist/lib/generators/asset-generator/generate.js +111 -0
- package/dist/lib/generators/asset-generator/generate.js.map +1 -0
- package/dist/lib/generators/asset-generator/guide.js +43 -0
- package/dist/lib/generators/asset-generator/guide.js.map +1 -0
- package/dist/lib/generators/asset-generator/html-patcher.js +65 -0
- package/dist/lib/generators/asset-generator/html-patcher.js.map +1 -0
- package/dist/lib/generators/asset-generator/ico-encoder.js +29 -0
- package/dist/lib/generators/asset-generator/ico-encoder.js.map +1 -0
- package/dist/lib/generators/asset-generator/manifest-patcher.js +23 -0
- package/dist/lib/generators/asset-generator/manifest-patcher.js.map +1 -0
- package/dist/lib/generators/asset-generator/maskable.js +15 -0
- package/dist/lib/generators/asset-generator/maskable.js.map +1 -0
- package/dist/lib/generators/asset-generator/rasterize.js +32 -0
- package/dist/lib/generators/asset-generator/rasterize.js.map +1 -0
- package/dist/lib/generators/asset-generator/sizes.js +25 -0
- package/dist/lib/generators/asset-generator/sizes.js.map +1 -0
- package/dist/lib/generators/file-generators/api-config.js +6 -0
- package/dist/lib/generators/file-generators/api-config.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-state.js +2 -34
- package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-store.js +2 -250
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-user.js +3 -81
- package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +2 -57
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +2 -30
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +2 -130
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-state.js +6 -0
- package/dist/lib/generators/file-generators/fetch-state.js.map +1 -0
- package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -295
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +56 -0
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -0
- package/dist/lib/generators/file-generators/generate-hooks.js +4 -3
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
- package/dist/lib/generators/file-generators/gql-wrapper.js +3 -132
- package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +29 -1
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
- package/dist/lib/generators/file-generators/invalidation-tags.js +3 -258
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/manifest.js +13 -3
- package/dist/lib/generators/file-generators/manifest.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +3 -305
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js +2 -117
- package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
- package/dist/lib/generators/file-generators/push.js +6 -138
- package/dist/lib/generators/file-generators/push.js.map +1 -1
- package/dist/lib/generators/file-generators/pwa-install.js +11 -84
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/reset.js +6 -0
- package/dist/lib/generators/file-generators/reset.js.map +1 -0
- package/dist/lib/generators/file-generators/server-push.js +3 -134
- package/dist/lib/generators/file-generators/server-push.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +4 -4
- package/dist/lib/generators/file-generators/sw-injector.js +9 -230
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/type-definitions.js +2 -111
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +5 -5
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +25 -3
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +13 -12
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-sync-handler.js +14 -7
- package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/config-header.js +1 -1
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +165 -104
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +1 -0
- package/dist/lib/generators/sw-sections/server-push-handler.js +12 -0
- package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +6 -4
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +66 -35
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +89 -28
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/runtime/api-config.js +18 -0
- package/dist/runtime/api-config.js.map +1 -0
- package/dist/runtime/auth-state.js +35 -0
- package/dist/runtime/auth-state.js.map +1 -0
- package/dist/runtime/auth-store.js +273 -0
- package/dist/runtime/auth-store.js.map +1 -0
- package/dist/runtime/auth-user.js +168 -0
- package/dist/runtime/auth-user.js.map +1 -0
- package/dist/runtime/background-sync.js +59 -0
- package/dist/runtime/background-sync.js.map +1 -0
- package/dist/runtime/cache.js +30 -0
- package/dist/runtime/cache.js.map +1 -0
- package/dist/runtime/client-injector.js +136 -0
- package/dist/runtime/client-injector.js.map +1 -0
- package/dist/runtime/fetch-state.js +42 -0
- package/dist/runtime/fetch-state.js.map +1 -0
- package/dist/runtime/fetch-wrapper.js +355 -0
- package/dist/runtime/fetch-wrapper.js.map +1 -0
- package/dist/runtime/gql-wrapper.js +135 -0
- package/dist/runtime/gql-wrapper.js.map +1 -0
- package/dist/runtime/invalidation-tags.js +260 -0
- package/dist/runtime/invalidation-tags.js.map +1 -0
- package/dist/runtime/mutation-queue.js +304 -0
- package/dist/runtime/mutation-queue.js.map +1 -0
- package/dist/runtime/mutation-state.js +117 -0
- package/dist/runtime/mutation-state.js.map +1 -0
- package/dist/runtime/push.js +138 -0
- package/dist/runtime/push.js.map +1 -0
- package/dist/runtime/pwa-index.js +11 -0
- package/dist/runtime/pwa-index.js.map +1 -0
- package/dist/runtime/pwa-injector.js +44 -0
- package/dist/runtime/pwa-injector.js.map +1 -0
- package/dist/runtime/pwa-install.js +83 -0
- package/dist/runtime/pwa-install.js.map +1 -0
- package/dist/runtime/pwa-prompt.js +54 -0
- package/dist/runtime/pwa-prompt.js.map +1 -0
- package/dist/runtime/reset.js +148 -0
- package/dist/runtime/reset.js.map +1 -0
- package/dist/runtime/server-push.js +148 -0
- package/dist/runtime/server-push.js.map +1 -0
- package/dist/runtime/sw-injector.js +236 -0
- package/dist/runtime/sw-injector.js.map +1 -0
- package/dist/runtime/type-definitions.js +138 -0
- package/dist/runtime/type-definitions.js.map +1 -0
- package/dist/runtime/utils.js +27 -0
- package/dist/runtime/utils.js.map +1 -0
- package/package.json +3 -2
- package/templates/react/useAuth.jsx +106 -0
- package/templates/react/useAuth.tsx +118 -0
- package/templates/{hooks → react}/useBackgroundSync.jsx +6 -4
- package/templates/{hooks → react}/useBackgroundSync.tsx +6 -4
- package/templates/{hooks → react}/useCacheInvalidation.jsx +2 -2
- package/templates/react/useCacheInvalidation.tsx +52 -0
- package/templates/react/useCachedFetch.jsx +140 -0
- package/templates/react/useCachedFetch.tsx +160 -0
- package/templates/react/useIsFetching.jsx +32 -0
- package/templates/react/useIsFetching.tsx +32 -0
- package/templates/react/useMutation.jsx +141 -0
- package/templates/react/useMutation.tsx +180 -0
- package/templates/react/useMutationQueue.jsx +69 -0
- package/templates/react/useMutationQueue.tsx +83 -0
- package/templates/react/useNetworkStatus.jsx +61 -0
- package/templates/react/useNetworkStatus.tsx +76 -0
- package/templates/react/usePrefetch.jsx +32 -0
- package/templates/react/usePrefetch.tsx +42 -0
- package/templates/{hooks → react}/usePushSubscription.jsx +4 -4
- package/templates/{hooks → react}/usePushSubscription.tsx +4 -4
- package/templates/{hooks → react}/useSWUpdate.jsx +3 -3
- package/templates/{hooks → react}/useSWUpdate.tsx +4 -4
- package/templates/react/useSwoffReset.jsx +32 -0
- package/templates/react/useSwoffReset.tsx +39 -0
- package/docs/API.md +0 -565
- package/docs/ARCHITECTURE.md +0 -374
- package/docs/CLI.md +0 -148
- package/docs/COMPARISON.md +0 -192
- package/docs/CONFIG.md +0 -277
- package/docs/ECOSYSTEM.md +0 -33
- package/templates/hooks/useAuth.jsx +0 -30
- package/templates/hooks/useAuth.tsx +0 -30
- package/templates/hooks/useCacheInvalidation.tsx +0 -19
- package/templates/hooks/useCachedFetch.jsx +0 -69
- package/templates/hooks/useCachedFetch.tsx +0 -81
- package/templates/hooks/useMutation.jsx +0 -76
- package/templates/hooks/useMutation.tsx +0 -109
- package/templates/hooks/useMutationQueue.jsx +0 -43
- package/templates/hooks/useMutationQueue.tsx +0 -50
- package/templates/hooks/useNetworkStatus.jsx +0 -19
- package/templates/hooks/useNetworkStatus.tsx +0 -19
- package/templates/hooks/usePrefetch.jsx +0 -10
- package/templates/hooks/usePrefetch.tsx +0 -25
- /package/templates/{hooks → react}/useMutationState.jsx +0 -0
- /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`.
|