@swoff/cli 0.3.5 → 0.3.6
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/README.md +327 -25
- package/dist/__tests__/assemble-sw.test.js +15 -1
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +9 -8
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +6 -6
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +67 -59
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +12 -13
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +41 -21
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +3 -3
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +7 -1
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/clean.js +5 -0
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +12 -9
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +7 -3
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +135 -32
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/validate.js +3 -1
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/validator.js +25 -16
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-fetch.js +29 -34
- package/dist/lib/generators/file-generators/auth-fetch.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-state.js +8 -5
- package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-store.js +71 -40
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-user.js +27 -31
- package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +9 -5
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +7 -2
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +113 -0
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -6
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +170 -40
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-hooks.js +61 -3
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +333 -0
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/invalidation-tags.js +12 -10
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +34 -84
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-reconcile.js +57 -0
- package/dist/lib/generators/file-generators/mutation-reconcile.js.map +1 -0
- package/dist/lib/generators/file-generators/pwa-install.js +41 -9
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/quick-readme.js +78 -0
- package/dist/lib/generators/file-generators/quick-readme.js.map +1 -0
- package/dist/lib/generators/file-generators/reconcile.js +7 -12
- package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
- package/dist/lib/generators/file-generators/store.js +30 -20
- package/dist/lib/generators/file-generators/store.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +37 -16
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +81 -110
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +1 -1
- package/dist/lib/generators/file-generators/type-definitions.js +33 -10
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +19 -10
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +32 -12
- package/dist/lib/generators/sw-sections/assemble-sw.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 +25 -10
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +25 -5
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +7 -5
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +21 -6
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/build-script.js +35 -0
- package/dist/lib/utils/build-script.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates GUIDE.md — full integration guide for enabled features.
|
|
3
|
+
*/
|
|
4
|
+
import { writeFile } from "./context.js";
|
|
5
|
+
export function generateGuide(ctx) {
|
|
6
|
+
const { config } = ctx;
|
|
7
|
+
const ext = ctx.ext;
|
|
8
|
+
const lines = [];
|
|
9
|
+
const w = (s) => lines.push(s);
|
|
10
|
+
const wb = (s) => lines.push("", s);
|
|
11
|
+
w("# Swoff Integration Guide");
|
|
12
|
+
w("");
|
|
13
|
+
w("This guide explains every file and feature Swoff generated for your project.");
|
|
14
|
+
w("Each section answers: **What is it?**, **What files were created?**, **How to use it?**, **Where to edit?**");
|
|
15
|
+
w("");
|
|
16
|
+
// ── SW Registration ──
|
|
17
|
+
wb("## 📦 Service Worker Registration");
|
|
18
|
+
w("The service worker handles caching, offline support, background sync, and PWA installability.");
|
|
19
|
+
w("");
|
|
20
|
+
const hasSW = config.features.clientRegistration || config.features.pwa.enabled || config.features.crossTabSync;
|
|
21
|
+
if (hasSW) {
|
|
22
|
+
w("### `client-injector.ts` — Single entry point");
|
|
23
|
+
w("This is the **only file you need to import** at app startup to enable all Swoff features.");
|
|
24
|
+
w("```ts");
|
|
25
|
+
w(`import { initServiceWorker } from "./swoff/client-injector.${ext}";`);
|
|
26
|
+
w("initServiceWorker();");
|
|
27
|
+
w("```");
|
|
28
|
+
w("It wires together: SW registration, PWA install prompt, mutation queue online listener, and cross-tab sync.");
|
|
29
|
+
w("");
|
|
30
|
+
if (config.features.clientRegistration) {
|
|
31
|
+
w("### `sw/injector.ts` — SW registration logic");
|
|
32
|
+
w("Handles registering the service worker, checking for updates via version.json, and dispatching");
|
|
33
|
+
w("update-available / ready / error events on the window.");
|
|
34
|
+
w("");
|
|
35
|
+
w("**Functions:**");
|
|
36
|
+
w("- `initServiceWorker()` — registers the SW and checks for updates");
|
|
37
|
+
w("- `handleUpdateApproved(version)` — accepts a pending update and reloads on activation");
|
|
38
|
+
w("- `skipWaiting()` — activates a waiting SW without reloading");
|
|
39
|
+
w("");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// ── Fetch Wrapper ──
|
|
43
|
+
wb("## 🌐 fetchWithCache — API calls with caching");
|
|
44
|
+
w("A drop-in replacement for `fetch()` that communicates with the service worker about caching strategy.");
|
|
45
|
+
w("GET requests are cached by the SW for offline access; POST/PUT/DELETE pass through.");
|
|
46
|
+
w("");
|
|
47
|
+
w("### `fetch-wrapper.ts`");
|
|
48
|
+
w("```ts");
|
|
49
|
+
w(`import { fetchWithCache } from "./swoff/fetch-wrapper.${ext}";`);
|
|
50
|
+
w("");
|
|
51
|
+
w("// GET — cached for offline");
|
|
52
|
+
w('const todos = await fetchWithCache("/api/todos").then(r => r.json());');
|
|
53
|
+
w("");
|
|
54
|
+
w("// POST — passes through to server");
|
|
55
|
+
w("await fetchWithCache(\"/api/todos\", {");
|
|
56
|
+
w(' method: "POST",');
|
|
57
|
+
w(' body: JSON.stringify({ title: "New task" }),');
|
|
58
|
+
w("});");
|
|
59
|
+
w("```");
|
|
60
|
+
w("");
|
|
61
|
+
w("**Functions:**");
|
|
62
|
+
w("- `fetchWithCache(input, options?)` — main fetch wrapper. Use for all API calls.");
|
|
63
|
+
w("- `fetchWithCache(input, options?)` — unified fetch wrapper. Auto-queues writes when offline (disable with `queueOffline: false`).");
|
|
64
|
+
w("");
|
|
65
|
+
w("**Returns** `{ response: Response, fromCache: boolean }` — `fromCache` lets the UI show stale indicators when a stale-while-revalidate fallback is served.");
|
|
66
|
+
w("");
|
|
67
|
+
w("**Note:** There is no separate `authenticatedMutation` wrapper. For authenticated writes, just use `authenticatedFetch` (see Auth section below).");
|
|
68
|
+
w("");
|
|
69
|
+
if (ctx.frameworkName === "react") {
|
|
70
|
+
w("### React Hook: `useCachedFetch`");
|
|
71
|
+
w("Re-fetches automatically when the SW invalidates related cache tags.");
|
|
72
|
+
w("```tsx");
|
|
73
|
+
w(`import { useCachedFetch } from "./swoff/hooks/useCachedFetch.${ext}x";`);
|
|
74
|
+
w("");
|
|
75
|
+
w('const { data, error, loading, refetch } = useCachedFetch("/api/todos");');
|
|
76
|
+
w("```");
|
|
77
|
+
w("");
|
|
78
|
+
w("**Returns** `{ data: Response | null, error, loading, refetch }`");
|
|
79
|
+
w("");
|
|
80
|
+
w("The hook listens for `cache-invalidated` events (when tag invalidation is enabled) and automatically");
|
|
81
|
+
w("re-fetches if the event's tags match the URL. Call `refetch()` to manually refresh.");
|
|
82
|
+
w("");
|
|
83
|
+
}
|
|
84
|
+
// ── Mutation Queue ──
|
|
85
|
+
if (config.features.mutationQueue) {
|
|
86
|
+
wb("## 📝 Mutation Queue — offline writes that sync when back online");
|
|
87
|
+
w("When the user is offline and performs a write (POST/PUT/PATCH/DELETE), `queueMutation` stores it");
|
|
88
|
+
w("in IndexedDB. When the connection returns, `processMutationQueue` replays them in order.");
|
|
89
|
+
w("");
|
|
90
|
+
w("### `mutation-queue.ts`");
|
|
91
|
+
w("```ts");
|
|
92
|
+
w(`import { queueMutation, processMutationQueue, flushMutations, getPendingCount } from "./swoff/mutation-queue.${ext}";`);
|
|
93
|
+
w("");
|
|
94
|
+
w("// Queue an offline write");
|
|
95
|
+
w("await queueMutation({");
|
|
96
|
+
w(' method: "POST",');
|
|
97
|
+
w(' url: "/api/todos",');
|
|
98
|
+
w(' body: { title: "Grocery" },');
|
|
99
|
+
w(' tags: ["todos"],');
|
|
100
|
+
w("});");
|
|
101
|
+
w("");
|
|
102
|
+
w("// Flush after re-login (mutations queued while offline may fail with 401)");
|
|
103
|
+
w("await flushMutations();");
|
|
104
|
+
w("```");
|
|
105
|
+
w("");
|
|
106
|
+
w("**Functions:**");
|
|
107
|
+
w("- `queueMutation(mutation)` — store a write for later sync");
|
|
108
|
+
w("- `processMutationQueue()` — replay all queued writes. Runs automatically on `online` event.");
|
|
109
|
+
w("- `flushMutations()` — same as processMutationQueue. Call after re-login.");
|
|
110
|
+
w("- `getPendingCount()` — number of mutations waiting to sync.");
|
|
111
|
+
w("");
|
|
112
|
+
if (config.features.backgroundSync) {
|
|
113
|
+
w("### `background-sync.ts` — Sync even after tab close");
|
|
114
|
+
w("Uses the Background Sync API to register a sync event so mutations are processed even if the user");
|
|
115
|
+
w("closes the tab. Falls back to the `online` event listener in unsupported browsers (Firefox, Safari).");
|
|
116
|
+
w("```ts");
|
|
117
|
+
w(`import { syncWhenPossible } from "./swoff/background-sync.${ext}";`);
|
|
118
|
+
w("await syncWhenPossible({ method: \"POST\", url: \"/api/todos\", body: { ... } });");
|
|
119
|
+
w("```");
|
|
120
|
+
w("");
|
|
121
|
+
w("**Functions:**");
|
|
122
|
+
w("- `syncWhenPossible(mutation)` — queue and register background sync");
|
|
123
|
+
w("- `retrySync()` — re-register sync if mutations are still pending (called automatically)");
|
|
124
|
+
w("");
|
|
125
|
+
w("> ⚠️ Background Sync is Chrome/Edge only. Not supported in Firefox or Safari.");
|
|
126
|
+
w("");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// ── Auth ──
|
|
130
|
+
if (config.features.auth.enabled) {
|
|
131
|
+
wb("## 🔐 Auth — token management and authenticated requests");
|
|
132
|
+
w("Swoff's auth module manages authentication state with a **memory-only token** (never persisted to");
|
|
133
|
+
w("IndexedDB) and optional offline user info caching.");
|
|
134
|
+
w("");
|
|
135
|
+
const authType = config.features.auth.type;
|
|
136
|
+
w(`Auth type: **${authType}**`);
|
|
137
|
+
w("");
|
|
138
|
+
if (authType === "bearer") {
|
|
139
|
+
w("> ⚠️ The Bearer token lives **in memory only** and is cleared on page refresh.");
|
|
140
|
+
w("> Only `{ user, expiresAt }` is persisted to IndexedDB for offline user display.");
|
|
141
|
+
w("> After a page refresh, re-login is required. Use the `refreshPath` for token refresh.");
|
|
142
|
+
w("");
|
|
143
|
+
}
|
|
144
|
+
w("### `auth/store.ts` — Token and user persistence");
|
|
145
|
+
w("```ts");
|
|
146
|
+
w(`import { setAuth, getAuth, clearAuth, isAuthValid, createAuthFromResponse } from "./swoff/auth/store.${ext}";`);
|
|
147
|
+
w("");
|
|
148
|
+
w("// After successful login, store auth data");
|
|
149
|
+
w("await setAuth({ token, user, expiresAt });");
|
|
150
|
+
w("");
|
|
151
|
+
w("// Check if still authenticated");
|
|
152
|
+
w("const auth = await getAuth();");
|
|
153
|
+
w("if (!isAuthValid(auth)) { /* redirect to login */ }");
|
|
154
|
+
w("```");
|
|
155
|
+
w("");
|
|
156
|
+
w("**Where to edit:**");
|
|
157
|
+
w("- `createAuthFromResponse(response)` — **edit this** to match your backend's login response shape.");
|
|
158
|
+
w("");
|
|
159
|
+
w("**Functions:**");
|
|
160
|
+
w("- `setAuth(authData)` — store in memory + persist user to IndexedDB");
|
|
161
|
+
w("- `getAuth()` — get from memory (or IndexedDB after refresh)");
|
|
162
|
+
w("- `clearAuth()` — clear everything (call on logout/401)");
|
|
163
|
+
w("- `isAuthValid(auth)` — check expiry");
|
|
164
|
+
w("- `createAuthFromResponse(response)` — extract AuthData from login response. **Edit this.**");
|
|
165
|
+
w("");
|
|
166
|
+
w("### `auth/fetch.ts` — Authenticated API calls");
|
|
167
|
+
w("Wraps `fetchWithCache` with automatic auth headers and 401 handling.");
|
|
168
|
+
w("No separate `authenticatedMutation` needed — just use `authenticatedFetch` for both reads and writes.");
|
|
169
|
+
w("```ts");
|
|
170
|
+
w(`import { authenticatedFetch, ensureValidAuth } from "./swoff/auth/fetch.${ext}";`);
|
|
171
|
+
w("");
|
|
172
|
+
w("// Authenticated GET");
|
|
173
|
+
w('const user = await authenticatedFetch("/api/me").then(r => r.json());');
|
|
174
|
+
w("");
|
|
175
|
+
w("// Authenticated POST (mutation)");
|
|
176
|
+
w("await authenticatedFetch(\"/api/todos\", {");
|
|
177
|
+
w(' method: "POST",');
|
|
178
|
+
w(' body: JSON.stringify({ title: "New" }),');
|
|
179
|
+
w("});");
|
|
180
|
+
w("```");
|
|
181
|
+
w("");
|
|
182
|
+
w("**Functions:**");
|
|
183
|
+
w("- `authenticatedFetch(input, options)` — auth-aware fetch. Attaches token, bypasses cache for auth endpoints, dispatches `sw-auth-unauthorized` on 401.");
|
|
184
|
+
w("- `ensureValidAuth()` — check expiry and refresh token if needed (uses refreshPath from config).");
|
|
185
|
+
w("");
|
|
186
|
+
w("**Where to edit:**");
|
|
187
|
+
w("- The `isAuthUrl` function in `auth/fetch.ts` lists auth endpoints that bypass the SW cache. Edit this list if your backend uses different paths.");
|
|
188
|
+
w("- If your auth type is `custom`, edit the `withAuthHeaders` function.");
|
|
189
|
+
w("");
|
|
190
|
+
w("### `auth/user.ts` — User data caching");
|
|
191
|
+
w("```ts");
|
|
192
|
+
w(`import { fetchCurrentUser, getCachedUser, cacheUser, clearCachedUser } from "./swoff/auth/user.${ext}";`);
|
|
193
|
+
w("");
|
|
194
|
+
w("// Fetch and cache the current user");
|
|
195
|
+
w("const user = await fetchCurrentUser();");
|
|
196
|
+
w("```");
|
|
197
|
+
w("");
|
|
198
|
+
w("**Functions:**");
|
|
199
|
+
w("- `fetchCurrentUser()` — fetch from user endpoint and cache in IndexedDB");
|
|
200
|
+
w("- `getCachedUser()` — load user from IndexedDB (available offline)");
|
|
201
|
+
w("- `cacheUser(user)` — persist user object manually");
|
|
202
|
+
w("- `clearCachedUser()` — remove user from cache (call on logout)");
|
|
203
|
+
w("");
|
|
204
|
+
w("### `auth/state.ts` — Auth state detection");
|
|
205
|
+
w("Detects which of the 4 states the app is in: online+authenticated, online+unauthenticated, offline+authenticated, offline+unauthenticated.");
|
|
206
|
+
w("```ts");
|
|
207
|
+
w(`import { getAuthState } from "./swoff/auth/state.${ext}";`);
|
|
208
|
+
w('const { authenticated, user, online } = await getAuthState();');
|
|
209
|
+
w("```");
|
|
210
|
+
w("");
|
|
211
|
+
if (ctx.frameworkName === "react") {
|
|
212
|
+
w("### React Hooks");
|
|
213
|
+
w("- `useAuth()` — returns `{ authenticated, user, online }`, listens to online/offline/auth changes");
|
|
214
|
+
w("- `useCachedFetch(url, options?)` — fetches with auto-refetch on tag invalidation, see Fetch Wrapper section");
|
|
215
|
+
w("");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// ── Tag Invalidation ──
|
|
219
|
+
if (config.features.tagInvalidation) {
|
|
220
|
+
wb("## 🏷️ Tag Invalidation — keep cached data fresh");
|
|
221
|
+
w("When data changes on the server, cached responses in the SW become stale. Tag invalidation");
|
|
222
|
+
w("lets you mark related cache entries as stale so they're re-fetched on next request.");
|
|
223
|
+
w("");
|
|
224
|
+
w("### How it works");
|
|
225
|
+
w("1. When fetching, attach tags: `fetchWithCache(url, { tags: generateTags(url) })`");
|
|
226
|
+
w("2. After a mutation, invalidate: `await invalidateUrl(url)`");
|
|
227
|
+
w("3. The SW removes all cached responses that were tagged with the related tags");
|
|
228
|
+
w("");
|
|
229
|
+
w("### `invalidation-tags.ts` — Tag generation helpers");
|
|
230
|
+
w("```ts");
|
|
231
|
+
w(`import { generateTags, invalidateUrl } from "./swoff/invalidation-tags.${ext}";`);
|
|
232
|
+
w("");
|
|
233
|
+
w('// Tag reads');
|
|
234
|
+
w('const data = await fetchWithCache("/api/todos", { tags: generateTags("/api/todos") });');
|
|
235
|
+
w("");
|
|
236
|
+
w('// Invalidate after writing');
|
|
237
|
+
w('await invalidateUrl("/api/todos/42");');
|
|
238
|
+
w("```");
|
|
239
|
+
w("");
|
|
240
|
+
w("**Functions:**");
|
|
241
|
+
w("- `generateTags(url)` — extract tags from a URL path. e.g. `/api/todos/42` → `[\"todos\", \"todo:42\"]`");
|
|
242
|
+
w("- `generateTagsFromMethod(method, url)` — method-prefixed tags. e.g. `post-todos`");
|
|
243
|
+
w("- `invalidateUrl(url)` — extract tags and invalidate all matching cache entries");
|
|
244
|
+
w("- `invalidateByMethod(method, url)` — invalidate using method-prefixed tags");
|
|
245
|
+
w("");
|
|
246
|
+
w("### `cache.ts` — Low-level invalidation");
|
|
247
|
+
w("```ts");
|
|
248
|
+
w(`import { invalidateByTag, invalidateByTags } from "./swoff/cache.${ext}";`);
|
|
249
|
+
w("");
|
|
250
|
+
w('await invalidateByTag("todos");');
|
|
251
|
+
w('await invalidateByTags(["todos", "categories"]);');
|
|
252
|
+
w("```");
|
|
253
|
+
w("");
|
|
254
|
+
w("**Functions:**");
|
|
255
|
+
w("- `invalidateByTag(tag)` — invalidate a single tag. Dispatches `cache-invalidated` event.");
|
|
256
|
+
w("- `invalidateByTags(tags)` — invalidate multiple tags.");
|
|
257
|
+
w("");
|
|
258
|
+
}
|
|
259
|
+
// ── Cross-tab Sync ──
|
|
260
|
+
if (config.features.crossTabSync) {
|
|
261
|
+
wb("## 🔄 Cross-tab Sync — keep tabs in sync");
|
|
262
|
+
w("When the user opens your app in multiple browser tabs, changes in one tab (logout, mutation sync)");
|
|
263
|
+
w("are broadcast to all other tabs via the service worker.");
|
|
264
|
+
w("");
|
|
265
|
+
w("No separate imports needed — this is handled automatically by `client-injector.ts`.");
|
|
266
|
+
w("The service worker listens for invalidation events and forwards them to all clients.");
|
|
267
|
+
w("");
|
|
268
|
+
if (!config.features.tagInvalidation) {
|
|
269
|
+
w("> ⚠️ Cross-tab sync requires tag invalidation to be enabled for full functionality.");
|
|
270
|
+
w("");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// ── PWA ──
|
|
274
|
+
if (config.features.pwa.enabled) {
|
|
275
|
+
wb("## 📱 PWA — installable web app");
|
|
276
|
+
w("Swoff adds a beforeinstallprompt handler and install flow so users can install your app");
|
|
277
|
+
w("on their home screen.");
|
|
278
|
+
w("");
|
|
279
|
+
w("### `pwa/install.ts`");
|
|
280
|
+
w("```ts");
|
|
281
|
+
w(`import { setupPwaInstall, isInstallable, promptInstall } from "./swoff/pwa/install.${ext}";`);
|
|
282
|
+
w("");
|
|
283
|
+
w("setupPwaInstall(); // called automatically by client-injector.ts");
|
|
284
|
+
w("");
|
|
285
|
+
w("// Show install button when available");
|
|
286
|
+
w("if (isInstallable()) {");
|
|
287
|
+
w(' const { outcome } = await promptInstall();');
|
|
288
|
+
w("}");
|
|
289
|
+
w("```");
|
|
290
|
+
w("");
|
|
291
|
+
w("**Functions:**");
|
|
292
|
+
w("- `setupPwaInstall()` — listen for beforeinstallprompt/appinstalled events (called by client-injector)");
|
|
293
|
+
w("- `isInstallable()` — check if install prompt is available");
|
|
294
|
+
w("- `promptInstall()` — show the native install prompt");
|
|
295
|
+
w("");
|
|
296
|
+
w("### `manifest.json`");
|
|
297
|
+
w("Generated in `swoff/manifest.json`. If you want it exposed at the root, copy it to your `public/` directory.");
|
|
298
|
+
w("");
|
|
299
|
+
if (ctx.frameworkName === "react") {
|
|
300
|
+
w("### React Hooks");
|
|
301
|
+
w("- `usePWAUpdate()` — returns `{ updateStatus, progress, forceUpdate, acceptUpdate, dismissUpdate }`");
|
|
302
|
+
w("- `useSWProgress()` — returns `{ status, progress }` for download progress during SW update");
|
|
303
|
+
w("- `useCachedFetch(url, options?)` — fetches with auto-refetch on tag invalidation, see Fetch Wrapper section");
|
|
304
|
+
w("");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// ── Build Script ──
|
|
308
|
+
wb("## 🏗️ Build script");
|
|
309
|
+
w("The SW generator must run after every build to produce the final service worker file.");
|
|
310
|
+
w("Swoff has already added this to your `package.json` build script for you:");
|
|
311
|
+
w("```");
|
|
312
|
+
w('"build": "<your-build> && node swoff/sw/generator.js"');
|
|
313
|
+
w("```");
|
|
314
|
+
w("If you run `swoff clean`, this script suffix will be removed automatically.");
|
|
315
|
+
w("");
|
|
316
|
+
// ── Config file ──
|
|
317
|
+
wb("## ⚙️ swoff.config.json");
|
|
318
|
+
w("This is the configuration file that controls which features are enabled and how they behave.");
|
|
319
|
+
w("Re-run `npx @swoff/cli generate` after changing it.");
|
|
320
|
+
w("");
|
|
321
|
+
w("### Features you can toggle:");
|
|
322
|
+
w("- `mutationQueue` — offline write queue with IndexedDB");
|
|
323
|
+
w("- `backgroundSync` — Background Sync API (Chrome/Edge only)");
|
|
324
|
+
w("- `auth.enabled` — auth module (bearer/cookie/custom)");
|
|
325
|
+
w("- `crossTabSync` — broadcast changes across tabs");
|
|
326
|
+
w("- `tagInvalidation` — cache invalidation by tags");
|
|
327
|
+
w("- `clientRegistration` — SW registration with version management");
|
|
328
|
+
w("- `pwa.enabled` — PWA install prompt and manifest");
|
|
329
|
+
w("");
|
|
330
|
+
w("---");
|
|
331
|
+
writeFile(ctx, "GUIDE.md", lines.join("\n"));
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=guide-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guide-generator.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/guide-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateInvalidationTags(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
7
10
|
const code = `/**
|
|
8
11
|
* Swoff Invalidation Tags Helper
|
|
9
12
|
* URL-based tag generation from REST endpoints for automatic cache invalidation.
|
|
@@ -25,15 +28,15 @@ export function generateInvalidationTags(ctx) {
|
|
|
25
28
|
* await invalidateUrl("/api/todos/42");
|
|
26
29
|
*/
|
|
27
30
|
|
|
28
|
-
import {
|
|
31
|
+
import { invalidateByTags } from "./cache.${ext}";
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
/** Generate cache invalidation tags from a URL path. e.g. /api/todos/42 → ["todos", "todo:42"]. Used with fetchWithCache's tags option. */
|
|
34
|
+
export function generateTags(url${T("string | URL")})${R("string[]")}{
|
|
31
35
|
const parsed = typeof url === "string" ? new URL(url, window.location.origin) : url;
|
|
32
36
|
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
33
37
|
|
|
34
38
|
if (segments.length === 0) return ["root"];
|
|
35
39
|
|
|
36
|
-
// Skip common API prefixes
|
|
37
40
|
const skipPrefixes = ["api", "v1", "v2", "v3", "rest", "graphql", "gql"];
|
|
38
41
|
let startIdx = 0;
|
|
39
42
|
while (startIdx < segments.length && skipPrefixes.includes(segments[startIdx])) {
|
|
@@ -45,10 +48,8 @@ export function generateTags(url) {
|
|
|
45
48
|
|
|
46
49
|
const tags = [];
|
|
47
50
|
|
|
48
|
-
// Collection tag: /api/todos -> "todos"
|
|
49
51
|
tags.push(resourceSegments[0]);
|
|
50
52
|
|
|
51
|
-
// Resource tag: /api/todos/42 -> "todo:42"
|
|
52
53
|
if (resourceSegments.length >= 2 && !isNaN(Number(resourceSegments[1]))) {
|
|
53
54
|
const collection = resourceSegments[0];
|
|
54
55
|
const id = resourceSegments[1];
|
|
@@ -56,7 +57,6 @@ export function generateTags(url) {
|
|
|
56
57
|
tags.push(\`\${singular}:\${id}\`);
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
// Sub-resource tags: /api/todos/42/comments -> "comments"
|
|
60
60
|
for (let i = 2; i < resourceSegments.length; i++) {
|
|
61
61
|
if (isNaN(Number(resourceSegments[i]))) {
|
|
62
62
|
tags.push(resourceSegments[i]);
|
|
@@ -66,23 +66,25 @@ export function generateTags(url) {
|
|
|
66
66
|
return tags;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
/** Generate tags prefixed by HTTP method. e.g. POST /api/todos → ["post-todos"]. Useful for invalidating only mutation-tagged caches. */
|
|
70
|
+
export function generateTagsFromMethod(method${T("string")}, url${T("string | URL")})${R("string[]")}{
|
|
70
71
|
const tags = generateTags(url);
|
|
71
72
|
|
|
72
73
|
if (method === "GET" || method === "HEAD") {
|
|
73
74
|
return tags;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
// For mutations, add method prefix
|
|
77
77
|
return tags.map((tag) => \`\${method.toLowerCase()}-\${tag}\`);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
/** Invalidate cached responses related to a URL (extracts tags from the URL then calls invalidateByTags). */
|
|
81
|
+
export async function invalidateUrl(url${T("string | URL")})${R("Promise<void>")}{
|
|
81
82
|
const tags = generateTags(url);
|
|
82
83
|
await invalidateByTags(tags);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
/** Invalidate cached responses tagged with method-prefixed tags from a URL. */
|
|
87
|
+
export async function invalidateByMethod(method${T("string")}, url${T("string | URL")})${R("Promise<void>")}{
|
|
86
88
|
const tags = generateTagsFromMethod(method, url);
|
|
87
89
|
await invalidateByTags(tags);
|
|
88
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/invalidation-tags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;8EAK+D,GAAG;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/invalidation-tags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,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;;;;;8EAK+D,GAAG;;;;;;;;;;;;;;;;4CAgBrC,GAAG;;;kCAGb,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAoCrB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;;;;;;;;;;;yCAW3D,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;iDAM/B,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;CAI1G,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,qBAAqB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC"}
|