@swoff/cli 0.3.7 → 0.3.8
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 +43 -610
- package/dist/__tests__/assemble-sw.test.js +4 -4
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +7 -4
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +50 -1
- 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/commands/add.js +5 -4
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +3 -3
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/info.js +29 -6
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/config/validator.js +85 -4
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +1 -1
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +55 -14
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-hooks.js +5 -1
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
- package/dist/lib/generators/file-generators/gql-wrapper.js +152 -0
- package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -0
- package/dist/lib/generators/file-generators/guide-generator.js +214 -10
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +117 -49
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js +126 -0
- package/dist/lib/generators/file-generators/mutation-state.js.map +1 -0
- package/dist/lib/generators/file-generators/quick-readme.js +59 -9
- package/dist/lib/generators/file-generators/quick-readme.js.map +1 -1
- package/dist/lib/generators/file-generators/server-push.js +136 -0
- package/dist/lib/generators/file-generators/server-push.js.map +1 -0
- package/dist/lib/generators/file-generators/type-definitions.js +28 -0
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +6 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-sync-handler.js +22 -8
- 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/default-template.js +2 -1
- package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +137 -98
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/server-push-handler.js +114 -0
- package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -0
- package/dist/lib/generators/sw-sections/tag-management.js +6 -5
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +7 -1
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +40 -2
- package/dist/lib/shared/config-types.js.map +1 -1
- package/package.json +1 -1
- package/templates/hooks/useCachedFetch.jsx +43 -4
- package/templates/hooks/useCachedFetch.tsx +49 -6
- package/templates/hooks/useMutation.jsx +76 -0
- package/templates/hooks/useMutation.tsx +97 -0
- package/templates/hooks/useMutationQueue.jsx +19 -11
- package/templates/hooks/useMutationQueue.tsx +30 -15
- package/templates/hooks/useMutationState.jsx +27 -0
- package/templates/hooks/useMutationState.tsx +41 -0
- package/templates/hooks/usePrefetch.jsx +10 -0
- package/templates/hooks/usePrefetch.tsx +25 -0
|
@@ -6,65 +6,45 @@
|
|
|
6
6
|
* 2. URL pattern match — from features.serviceWorker.strategies in swoff.config.json
|
|
7
7
|
* 3. defaultStrategy — fallback from features.serviceWorker.defaultStrategy
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* "explicit-only" — only requests with X-SW-Cache-Strategy header go through strategy;
|
|
12
|
-
* plain fetch() calls pass through the SW unmodified
|
|
9
|
+
* Each tier also carries: staleTime, maxCacheEntries, maxCacheAge
|
|
10
|
+
* Per-request values override route values, which override globals.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* With staleTime enabled, every strategy gains a "fresh" window:
|
|
13
|
+
* - cache-first: returns cached immediately, background refresh if stale
|
|
14
|
+
* - network-first: skips network if fresh, tries network if stale
|
|
15
|
+
* - stale-while-revalidate: skips refresh if fresh, refreshes if stale
|
|
16
|
+
* - cache-only: background refresh if stale (best effort)
|
|
17
|
+
* - network-only: unaffected (never caches)
|
|
16
18
|
*/
|
|
17
19
|
export function generateFetchHandler(swConfig, tagInvalidation) {
|
|
18
|
-
const { defaultStrategy, strategies, cacheStrategy = "all", maxCacheEntries, maxCacheAge, navigationPreload, navigationMode, spaEntry } = swConfig;
|
|
19
|
-
const hasPerRouteLimits = Object.values(strategies || {}).some((s) => typeof s === "object" && (s.maxCacheEntries || s.maxCacheAge));
|
|
20
|
-
const hasTrim = (maxCacheEntries ?? 0) > 0 || (maxCacheAge ?? 0) > 0 || hasPerRouteLimits;
|
|
20
|
+
const { defaultStrategy, strategies, cacheStrategy = "all", staleTime, maxCacheEntries, maxCacheAge, navigationPreload, navigationMode, spaEntry } = swConfig;
|
|
21
21
|
const navMode = navigationMode ?? "spa";
|
|
22
22
|
const spaPath = spaEntry ?? "/index.html";
|
|
23
23
|
const staleVersionCode = tagInvalidation ? `
|
|
24
24
|
cleanStaleVersions();
|
|
25
|
-
if (staleVersions.has(request
|
|
26
|
-
event.waitUntil(refreshCache(request).then(() => staleVersions.delete(request
|
|
25
|
+
if (staleVersions.has(cacheKey(request))) {
|
|
26
|
+
event.waitUntil(refreshCache(request).then(() => staleVersions.delete(cacheKey(request))));
|
|
27
27
|
}` : "";
|
|
28
28
|
const tagCode = tagInvalidation ? `
|
|
29
29
|
const tagsHeader = request.headers.get("X-SW-Cache-Tags");
|
|
30
30
|
if (tagsHeader) {
|
|
31
|
-
const
|
|
31
|
+
const cacheKeyUrl = cacheKey(request);
|
|
32
|
+
const actualUrl = new URL(request.url).href;
|
|
32
33
|
const tags = tagsHeader.split(",").map((t) => t.trim());
|
|
33
|
-
await cacheTagUrl(
|
|
34
|
+
await cacheTagUrl(cacheKeyUrl, actualUrl, tags);
|
|
34
35
|
}` : "";
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
if (keys.length >= MAX_CACHE_ENTRIES) {
|
|
46
|
-
const toDelete = keys.slice(0, keys.length - MAX_CACHE_ENTRIES + 1);
|
|
47
|
-
await Promise.all(toDelete.map((key) => cache.delete(key)));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (MAX_CACHE_AGE > 0) {
|
|
52
|
-
const keys = await cache.keys();
|
|
53
|
-
const now = Date.now();
|
|
54
|
-
for (const request of keys) {
|
|
55
|
-
const response = await cache.match(request);
|
|
56
|
-
const dateHeader = response?.headers.get("date");
|
|
57
|
-
if (dateHeader) {
|
|
58
|
-
const age = now - new Date(dateHeader).getTime();
|
|
59
|
-
if (age > MAX_CACHE_AGE) {
|
|
60
|
-
await cache.delete(request);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
36
|
+
const trimDecl = `const GLOBAL_MAX_ENTRIES = ${maxCacheEntries ?? 0};
|
|
37
|
+
const GLOBAL_MAX_AGE = ${maxCacheAge ?? 0};
|
|
38
|
+
const GLOBAL_STALE_TIME = ${staleTime ?? 0};`;
|
|
39
|
+
return `${trimDecl}
|
|
40
|
+
// --- Cache Key ---
|
|
41
|
+
|
|
42
|
+
function cacheKey(request) {
|
|
43
|
+
const key = request.headers.get("X-SW-Cache-Key");
|
|
44
|
+
if (key) return new URL("/__swc/" + key, self.location.origin).href;
|
|
45
|
+
return new URL(request.url).href;
|
|
65
46
|
}
|
|
66
|
-
|
|
67
|
-
return `${trimFunction}
|
|
47
|
+
|
|
68
48
|
// --- Cache Helpers ---
|
|
69
49
|
|
|
70
50
|
async function fromPrecache(request) {
|
|
@@ -74,12 +54,19 @@ async function fromPrecache(request) {
|
|
|
74
54
|
|
|
75
55
|
async function fromRuntime(request) {
|
|
76
56
|
const cache = await caches.open(CACHE_NAME_RUNTIME);
|
|
77
|
-
return cache.match(
|
|
57
|
+
return cache.match(cacheKey(request));
|
|
78
58
|
}
|
|
79
59
|
|
|
80
60
|
async function storeRuntime(request, response) {
|
|
81
61
|
const cache = await caches.open(CACHE_NAME_RUNTIME);
|
|
82
|
-
|
|
62
|
+
const headers = new Headers(response.headers);
|
|
63
|
+
headers.set("X-SW-Cached-At", String(Date.now()));
|
|
64
|
+
const cloned = new Response(response.body, {
|
|
65
|
+
status: response.status,
|
|
66
|
+
statusText: response.statusText,
|
|
67
|
+
headers,
|
|
68
|
+
});
|
|
69
|
+
await cache.put(cacheKey(request), cloned);
|
|
83
70
|
}
|
|
84
71
|
|
|
85
72
|
async function cacheResponse(request, response) {
|
|
@@ -105,42 +92,71 @@ function markFromCache(response) {
|
|
|
105
92
|
});
|
|
106
93
|
}
|
|
107
94
|
|
|
108
|
-
|
|
95
|
+
function isStale(response, staleTimeSeconds) {
|
|
96
|
+
if (!staleTimeSeconds || staleTimeSeconds <= 0) return false;
|
|
97
|
+
const cachedAt = response.headers.get("X-SW-Cached-At");
|
|
98
|
+
if (!cachedAt) return false;
|
|
99
|
+
return Date.now() - Number(cachedAt) > staleTimeSeconds * 1000;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// --- Strategy Selection (3-tier config resolution) ---
|
|
109
103
|
|
|
110
104
|
function resolveStrategyEntry(entry) {
|
|
111
105
|
return typeof entry === "string" ? { strategy: entry } : entry;
|
|
112
106
|
}
|
|
113
107
|
|
|
114
|
-
function determineCacheStrategy(request, customStrategies,
|
|
108
|
+
function determineCacheStrategy(request, customStrategies, globalDefaults) {
|
|
115
109
|
const override = request.headers.get("X-SW-Strategy");
|
|
116
|
-
if (override)
|
|
110
|
+
if (override) {
|
|
111
|
+
return {
|
|
112
|
+
strategy: override,
|
|
113
|
+
staleTime: Number(request.headers.get("X-SW-Stale-Time")) || globalDefaults.staleTime,
|
|
114
|
+
maxCacheEntries: Number(request.headers.get("X-SW-Max-Entries")) || globalDefaults.maxCacheEntries,
|
|
115
|
+
maxCacheAge: Number(request.headers.get("X-SW-Max-Age")) || globalDefaults.maxCacheAge,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
117
118
|
const path = new URL(request.url).pathname;
|
|
118
119
|
for (const [pattern, entry] of Object.entries(customStrategies)) {
|
|
119
|
-
if (path.startsWith(pattern.replace("*", "")))
|
|
120
|
+
if (path.startsWith(pattern.replace("*", ""))) {
|
|
121
|
+
const resolved = resolveStrategyEntry(entry);
|
|
122
|
+
return {
|
|
123
|
+
strategy: resolved.strategy,
|
|
124
|
+
staleTime: resolved.staleTime ?? globalDefaults.staleTime,
|
|
125
|
+
maxCacheEntries: resolved.maxCacheEntries ?? globalDefaults.maxCacheEntries,
|
|
126
|
+
maxCacheAge: resolved.maxCacheAge ?? globalDefaults.maxCacheAge,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
120
129
|
}
|
|
121
|
-
return {
|
|
130
|
+
return {
|
|
131
|
+
strategy: globalDefaults.defaultStrategy,
|
|
132
|
+
staleTime: globalDefaults.staleTime,
|
|
133
|
+
maxCacheEntries: globalDefaults.maxCacheEntries,
|
|
134
|
+
maxCacheAge: globalDefaults.maxCacheAge,
|
|
135
|
+
};
|
|
122
136
|
}
|
|
123
137
|
|
|
124
138
|
function applyStrategy(event, request, config) {
|
|
125
|
-
const { strategy, maxCacheEntries, maxCacheAge } = config;
|
|
139
|
+
const { strategy, staleTime, maxCacheEntries, maxCacheAge } = config;
|
|
126
140
|
if (strategy === "stale-while-revalidate") {
|
|
127
|
-
event.respondWith(staleWhileRevalidate(event, request, maxCacheEntries, maxCacheAge));
|
|
141
|
+
event.respondWith(staleWhileRevalidate(event, request, staleTime, maxCacheEntries, maxCacheAge));
|
|
128
142
|
} else if (strategy === "network-first") {
|
|
129
|
-
event.respondWith(networkFirst(event, request, maxCacheEntries, maxCacheAge));
|
|
143
|
+
event.respondWith(networkFirst(event, request, staleTime, maxCacheEntries, maxCacheAge));
|
|
130
144
|
} else if (strategy === "cache-only") {
|
|
131
|
-
event.respondWith(cacheOnly(event, request, maxCacheEntries, maxCacheAge));
|
|
145
|
+
event.respondWith(cacheOnly(event, request, staleTime, maxCacheEntries, maxCacheAge));
|
|
132
146
|
} else if (strategy === "network-only") {
|
|
133
147
|
event.respondWith(networkOnly(event, request));
|
|
134
148
|
} else {
|
|
135
|
-
event.respondWith(cacheFirst(event, request, maxCacheEntries, maxCacheAge));
|
|
149
|
+
event.respondWith(cacheFirst(event, request, staleTime, maxCacheEntries, maxCacheAge));
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
|
|
139
153
|
self.addEventListener("fetch", (event) => {
|
|
140
154
|
const { request } = event;
|
|
141
|
-
if (request.method !== "GET" && request.method !== "HEAD")
|
|
155
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
156
|
+
if (!request.headers.get("X-SW-Cache-Key")) return;
|
|
157
|
+
}
|
|
142
158
|
${cacheStrategy === "explicit-only" ? `if (!request.headers.get("X-SW-Cache-Strategy")) return;` : ""}
|
|
143
|
-
applyStrategy(event, request, determineCacheStrategy(event.request, ${JSON.stringify(strategies)}, "${defaultStrategy}"));
|
|
159
|
+
applyStrategy(event, request, determineCacheStrategy(event.request, ${JSON.stringify(strategies)}, { defaultStrategy: "${defaultStrategy}", staleTime: GLOBAL_STALE_TIME, maxCacheEntries: GLOBAL_MAX_ENTRIES, maxCacheAge: GLOBAL_MAX_AGE }));
|
|
144
160
|
});
|
|
145
161
|
|
|
146
162
|
// --- Strategies ---
|
|
@@ -155,45 +171,17 @@ async function fetchWithPreload(event, request) {
|
|
|
155
171
|
}
|
|
156
172
|
` : ""}const _fetch = ${navigationPreload ? "fetchWithPreload" : `(event, request) => fetch(request)`};
|
|
157
173
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
${hasTrim ? `
|
|
162
|
-
async function trimRuntimeCache(cacheName, maxEntries, maxAge) {
|
|
163
|
-
const _maxEntries = maxEntries ?? GLOBAL_MAX_ENTRIES;
|
|
164
|
-
const _maxAge = maxAge ?? GLOBAL_MAX_AGE;
|
|
165
|
-
const cache = await caches.open(cacheName);
|
|
166
|
-
|
|
167
|
-
if (_maxEntries > 0) {
|
|
168
|
-
const keys = await cache.keys();
|
|
169
|
-
if (keys.length >= _maxEntries) {
|
|
170
|
-
const toDelete = keys.slice(0, keys.length - _maxEntries + 1);
|
|
171
|
-
await Promise.all(toDelete.map((key) => cache.delete(key)));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (_maxAge > 0) {
|
|
176
|
-
const keys = await cache.keys();
|
|
177
|
-
const now = Date.now();
|
|
178
|
-
for (const request of keys) {
|
|
179
|
-
const response = await cache.match(request);
|
|
180
|
-
const dateHeader = response?.headers.get("date");
|
|
181
|
-
if (dateHeader) {
|
|
182
|
-
const age = now - new Date(dateHeader).getTime();
|
|
183
|
-
if (age > _maxAge) {
|
|
184
|
-
await cache.delete(request);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
` : ""}function _trim(cacheName, maxEntries, maxAge) {
|
|
191
|
-
${hasTrim ? ` trimRuntimeCache(cacheName, maxEntries, maxAge);` : ""}
|
|
174
|
+
${generateTrimCode(maxCacheEntries, maxCacheAge)}
|
|
175
|
+
function _trim(cacheName, maxEntries, maxAge) {
|
|
176
|
+
${maxCacheEntries || maxCacheAge ? ` trimRuntimeCache(cacheName, maxEntries, maxAge);` : ""}
|
|
192
177
|
}
|
|
193
178
|
|
|
194
|
-
async function cacheFirst(event, request, maxEntries, maxAge) {
|
|
179
|
+
async function cacheFirst(event, request, staleTime, maxEntries, maxAge) {
|
|
195
180
|
const cached = await fromRuntime(request);
|
|
196
181
|
if (cached) {${staleVersionCode}
|
|
182
|
+
if (isStale(cached, staleTime)) {
|
|
183
|
+
event.waitUntil(refreshCache(request));
|
|
184
|
+
}
|
|
197
185
|
return markFromCache(cached);
|
|
198
186
|
}
|
|
199
187
|
|
|
@@ -215,7 +203,15 @@ async function cacheFirst(event, request, maxEntries, maxAge) {
|
|
|
215
203
|
return response;
|
|
216
204
|
}
|
|
217
205
|
|
|
218
|
-
async function networkFirst(event, request, maxEntries, maxAge) {
|
|
206
|
+
async function networkFirst(event, request, staleTime, maxEntries, maxAge) {
|
|
207
|
+
// If cached and fresh (within staleTime), skip network entirely
|
|
208
|
+
if (staleTime > 0) {
|
|
209
|
+
const cached = await fromRuntime(request);
|
|
210
|
+
if (cached && !isStale(cached, staleTime)) {
|
|
211
|
+
return markFromCache(cached);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
219
215
|
try {
|
|
220
216
|
const response = await _fetch(event, request);
|
|
221
217
|
if (response.ok) {
|
|
@@ -241,9 +237,13 @@ async function networkFirst(event, request, maxEntries, maxAge) {
|
|
|
241
237
|
}
|
|
242
238
|
}
|
|
243
239
|
|
|
244
|
-
async function staleWhileRevalidate(event, request, maxEntries, maxAge) {
|
|
240
|
+
async function staleWhileRevalidate(event, request, staleTime, maxEntries, maxAge) {
|
|
245
241
|
const cached = await fromRuntime(request);
|
|
246
242
|
if (cached) {
|
|
243
|
+
// Skip background refresh if still fresh
|
|
244
|
+
if (!isStale(cached, staleTime)) {
|
|
245
|
+
return markFromCache(cached);
|
|
246
|
+
}
|
|
247
247
|
event.waitUntil(refreshCache(request));
|
|
248
248
|
return markFromCache(cached);
|
|
249
249
|
}
|
|
@@ -264,7 +264,9 @@ async function staleWhileRevalidate(event, request, maxEntries, maxAge) {
|
|
|
264
264
|
|
|
265
265
|
async function refreshCache(request) {
|
|
266
266
|
try {
|
|
267
|
-
const
|
|
267
|
+
const key = request.headers.get("X-SW-Cache-Key");
|
|
268
|
+
const fetchRequest = key ? new Request(new URL(request.url).href, { method: request.method, headers: request.headers, body: request.method !== "GET" ? request.body : undefined }) : request;
|
|
269
|
+
const response = await fetch(fetchRequest);
|
|
268
270
|
if (response.ok) {
|
|
269
271
|
await storeRuntime(request, response);
|
|
270
272
|
}
|
|
@@ -273,9 +275,12 @@ async function refreshCache(request) {
|
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
277
|
|
|
276
|
-
async function cacheOnly(event, request, maxEntries, maxAge) {
|
|
278
|
+
async function cacheOnly(event, request, staleTime, maxEntries, maxAge) {
|
|
277
279
|
const cached = await fromRuntime(request);
|
|
278
280
|
if (cached) {${staleVersionCode}
|
|
281
|
+
if (isStale(cached, staleTime)) {
|
|
282
|
+
event.waitUntil(refreshCache(request));
|
|
283
|
+
}
|
|
279
284
|
return markFromCache(cached);
|
|
280
285
|
}
|
|
281
286
|
|
|
@@ -289,4 +294,38 @@ async function networkOnly(event, request) {
|
|
|
289
294
|
return _fetch(event, request);
|
|
290
295
|
}`;
|
|
291
296
|
}
|
|
297
|
+
function generateTrimCode(maxCacheEntries, maxCacheAge) {
|
|
298
|
+
if (!maxCacheEntries && !maxCacheAge)
|
|
299
|
+
return "";
|
|
300
|
+
return `
|
|
301
|
+
async function trimRuntimeCache(cacheName, maxEntries, maxAge) {
|
|
302
|
+
const _maxEntries = maxEntries ?? GLOBAL_MAX_ENTRIES;
|
|
303
|
+
const _maxAge = maxAge ?? GLOBAL_MAX_AGE;
|
|
304
|
+
const cache = await caches.open(cacheName);
|
|
305
|
+
|
|
306
|
+
if (_maxEntries > 0) {
|
|
307
|
+
const keys = await cache.keys();
|
|
308
|
+
if (keys.length >= _maxEntries) {
|
|
309
|
+
const toDelete = keys.slice(0, keys.length - _maxEntries + 1);
|
|
310
|
+
await Promise.all(toDelete.map((key) => cache.delete(key)));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (_maxAge > 0) {
|
|
315
|
+
const keys = await cache.keys();
|
|
316
|
+
const now = Date.now();
|
|
317
|
+
for (const request of keys) {
|
|
318
|
+
const response = await cache.match(request);
|
|
319
|
+
const dateHeader = response?.headers.get("date");
|
|
320
|
+
if (dateHeader) {
|
|
321
|
+
const age = now - new Date(dateHeader).getTime();
|
|
322
|
+
if (age > _maxAge) {
|
|
323
|
+
await cache.delete(request);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
`;
|
|
330
|
+
}
|
|
292
331
|
//# sourceMappingURL=fetch-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/fetch-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fetch-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/fetch-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,UAAU,oBAAoB,CAClC,QAA0V,EAC1V,eAAwB;IAExB,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,aAAa,GAAG,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;IAE9J,MAAM,OAAO,GAAG,cAAc,IAAI,KAAK,CAAC;IACxC,MAAM,OAAO,GAAG,QAAQ,IAAI,aAAa,CAAC;IAE1C,MAAM,gBAAgB,GAAG,eAAe,CAAC,CAAC,CAAC;;;;MAIvC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEV,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC;;;;;;;IAOhC,CAAC,CAAC,CAAC,EAAE,CAAC;IAER,MAAM,QAAQ,GAAG,8BAA8B,eAAe,IAAI,CAAC;yBAC5C,WAAW,IAAI,CAAC;4BACb,SAAS,IAAI,CAAC,GAAG,CAAC;IAE5C,OAAO,GAAG,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CAkCsB,OAAO;;;;;;0BAMvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+E7B,aAAa,KAAK,eAAe,CAAC,CAAC,CAAC,0DAA0D,CAAC,CAAC,CAAC,EAAE;wEAC/B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,yBAAyB,eAAe;;;;;EAKxI,iBAAiB,CAAC,CAAC,CAAC;;;;;;;;CAQrB,CAAC,CAAC,CAAC,EAAE,kBAAkB,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,oCAAoC;;EAEnG,gBAAgB,CAAC,eAAe,EAAE,WAAW,CAAC;;EAE9C,eAAe,IAAI,WAAW,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,EAAE;;;;;iBAK3E,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmGhB,gBAAgB;;;;;;;;;;;;;;;EAe/B,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,eAAmC,EAAE,WAA+B;IAC5F,IAAI,CAAC,eAAe,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAEhD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the SW push event connection handler.
|
|
3
|
+
* Opens an SSE (fetch+stream) or WebSocket connection from the SW,
|
|
4
|
+
* listens for "invalidate" events, and calls invalidateByTag().
|
|
5
|
+
*/
|
|
6
|
+
export function generateServerPushHandler(type, endpoint, reconnectDelayMs) {
|
|
7
|
+
if (type === "websocket") {
|
|
8
|
+
return `
|
|
9
|
+
// --- Server Push Events (WebSocket) ---
|
|
10
|
+
|
|
11
|
+
let pushWs = null;
|
|
12
|
+
let pushReconnectTimer = null;
|
|
13
|
+
|
|
14
|
+
async function connectPushEvents() {
|
|
15
|
+
try {
|
|
16
|
+
pushWs = new WebSocket("${endpoint}");
|
|
17
|
+
pushWs.onmessage = (event) => {
|
|
18
|
+
try {
|
|
19
|
+
const data = JSON.parse(event.data);
|
|
20
|
+
if (data.type === "invalidate" && data.tags) {
|
|
21
|
+
data.tags.forEach((tag) => invalidateByTag(tag));
|
|
22
|
+
}
|
|
23
|
+
} catch {}
|
|
24
|
+
};
|
|
25
|
+
pushWs.onclose = () => {
|
|
26
|
+
pushWs = null;
|
|
27
|
+
scheduleReconnect();
|
|
28
|
+
};
|
|
29
|
+
pushWs.onerror = () => {
|
|
30
|
+
pushWs?.close();
|
|
31
|
+
};
|
|
32
|
+
} catch {
|
|
33
|
+
scheduleReconnect();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function scheduleReconnect() {
|
|
38
|
+
if (pushReconnectTimer) clearTimeout(pushReconnectTimer);
|
|
39
|
+
pushReconnectTimer = setTimeout(connectPushEvents, ${reconnectDelayMs});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
self.addEventListener("activate", (event) => {
|
|
43
|
+
event.waitUntil(connectPushEvents());
|
|
44
|
+
});
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
// SSE: fetch + ReadableStream reader
|
|
48
|
+
return `
|
|
49
|
+
// --- Server Push Events (SSE) ---
|
|
50
|
+
|
|
51
|
+
let pushReconnectTimer = null;
|
|
52
|
+
let pushAbortController = null;
|
|
53
|
+
|
|
54
|
+
async function connectPushEvents() {
|
|
55
|
+
try {
|
|
56
|
+
pushAbortController = new AbortController();
|
|
57
|
+
const response = await fetch("${endpoint}", {
|
|
58
|
+
headers: { Accept: "text/event-stream" },
|
|
59
|
+
signal: pushAbortController.signal,
|
|
60
|
+
});
|
|
61
|
+
if (!response.ok || !response.body) {
|
|
62
|
+
scheduleReconnect();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const reader = response.body.getReader();
|
|
66
|
+
const decoder = new TextDecoder();
|
|
67
|
+
let buffer = "";
|
|
68
|
+
let eventType = "";
|
|
69
|
+
let dataStr = "";
|
|
70
|
+
|
|
71
|
+
while (true) {
|
|
72
|
+
const { done, value } = await reader.read();
|
|
73
|
+
if (done) break;
|
|
74
|
+
buffer += decoder.decode(value, { stream: true });
|
|
75
|
+
|
|
76
|
+
const lines = buffer.split("\\n");
|
|
77
|
+
buffer = lines.pop() || "";
|
|
78
|
+
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.startsWith("event: ")) {
|
|
81
|
+
eventType = line.slice(7).trim();
|
|
82
|
+
} else if (line.startsWith("data: ")) {
|
|
83
|
+
dataStr = line.slice(6);
|
|
84
|
+
} else if (line === "") {
|
|
85
|
+
if (eventType === "invalidate" && dataStr) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(dataStr);
|
|
88
|
+
if (parsed.tags) {
|
|
89
|
+
parsed.tags.forEach((tag) => invalidateByTag(tag));
|
|
90
|
+
}
|
|
91
|
+
} catch {}
|
|
92
|
+
}
|
|
93
|
+
eventType = "";
|
|
94
|
+
dataStr = "";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// Connection lost or aborted
|
|
100
|
+
}
|
|
101
|
+
scheduleReconnect();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function scheduleReconnect() {
|
|
105
|
+
if (pushReconnectTimer) clearTimeout(pushReconnectTimer);
|
|
106
|
+
pushReconnectTimer = setTimeout(connectPushEvents, ${reconnectDelayMs});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
self.addEventListener("activate", (event) => {
|
|
110
|
+
event.waitUntil(connectPushEvents());
|
|
111
|
+
});
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=server-push-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-push-handler.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/server-push-handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,yBAAyB,CACvC,IAAyB,EACzB,QAAgB,EAChB,gBAAwB;IAExB,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,OAAO;;;;;;;;8BAQmB,QAAQ;;;;;;;;;;;;;;;;;;;;;;;uDAuBiB,gBAAgB;;;;;;CAMtE,CAAC;IACA,CAAC;IAED,qCAAqC;IACrC,OAAO;;;;;;;;;oCAS2B,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAiDW,gBAAgB;;;;;;CAMtE,CAAC;AACF,CAAC"}
|
|
@@ -35,20 +35,21 @@ function openTagDB() {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
async function cacheTagUrl(url, tags) {
|
|
38
|
+
async function cacheTagUrl(url, actualUrl, tags) {
|
|
39
39
|
const db = await openTagDB();
|
|
40
40
|
const tx = db.transaction(TAG_STORE_NAME, "readwrite");
|
|
41
41
|
const store = tx.objectStore(TAG_STORE_NAME);
|
|
42
|
-
store.put({ url, tags });
|
|
42
|
+
store.put({ url, actualUrl, tags });
|
|
43
43
|
await new Promise((resolve, reject) => {
|
|
44
44
|
tx.oncomplete = () => resolve();
|
|
45
45
|
tx.onerror = () => reject(tx.error);
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
async function refetchAfterInvalidation(url) {
|
|
49
|
+
async function refetchAfterInvalidation(url, actualUrl) {
|
|
50
|
+
const fetchUrl = actualUrl || url;
|
|
50
51
|
try {
|
|
51
|
-
const response = await fetch(
|
|
52
|
+
const response = await fetch(fetchUrl);
|
|
52
53
|
if (response.ok) {
|
|
53
54
|
const runtimeCache = await caches.open(CACHE_NAME_RUNTIME);
|
|
54
55
|
await runtimeCache.put(url, response);
|
|
@@ -88,7 +89,7 @@ async function invalidateByTag(tag) {
|
|
|
88
89
|
|
|
89
90
|
// Background refetch each deleted URL; keep stale on failure
|
|
90
91
|
for (const entry of entries) {
|
|
91
|
-
refetchAfterInvalidation(entry.url);
|
|
92
|
+
refetchAfterInvalidation(entry.url, entry.actualUrl);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
const clients = await self.clients.matchAll();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag-management.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/tag-management.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,qBAAqB;IACnC,OAAO
|
|
1
|
+
{"version":3,"file":"tag-management.js","sourceRoot":"","sources":["../../../../src/lib/generators/sw-sections/tag-management.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4FP,CAAC;AACH,CAAC"}
|
|
@@ -25,10 +25,13 @@ import { generatePwaInstall } from "./file-generators/pwa-install.js";
|
|
|
25
25
|
import { generateManifest } from "./file-generators/manifest.js";
|
|
26
26
|
import { generateInvalidationTags } from "./file-generators/invalidation-tags.js";
|
|
27
27
|
import { generatePush } from "./file-generators/push.js";
|
|
28
|
+
import { generateMutationState } from "./file-generators/mutation-state.js";
|
|
29
|
+
import { generateServerPush } from "./file-generators/server-push.js";
|
|
28
30
|
import { generateAuthStore } from "./file-generators/auth-store.js";
|
|
29
31
|
import { generateAuthUser } from "./file-generators/auth-user.js";
|
|
30
32
|
import { generateAuthState } from "./file-generators/auth-state.js";
|
|
31
33
|
import { generateSwGeneratorBuild } from "./file-generators/sw-generator-build.js";
|
|
34
|
+
import { generateGqlWrapper } from "./file-generators/gql-wrapper.js";
|
|
32
35
|
import { generateTypeDefinitions } from "./file-generators/type-definitions.js";
|
|
33
36
|
import { generateHooks } from "./file-generators/generate-hooks.js";
|
|
34
37
|
import { generateGuide } from "./file-generators/guide-generator.js";
|
|
@@ -40,7 +43,8 @@ export function generateFiles(ctx, onFile) {
|
|
|
40
43
|
{ name: "client-injector", gen: () => generateClientInjector(ctx), enabled: true },
|
|
41
44
|
{ name: "fetch-wrapper", gen: () => generateFetchWrapper(ctx), enabled: true },
|
|
42
45
|
{ name: "cache", gen: () => generateCache(ctx), enabled: ctx.config.features.tagInvalidation },
|
|
43
|
-
{ name: "mutation-queue", gen: () => generateMutationQueue(ctx), enabled: ctx.config.features.mutationQueue },
|
|
46
|
+
{ name: "mutation-queue", gen: () => generateMutationQueue(ctx), enabled: ctx.config.features.mutationQueue.enabled },
|
|
47
|
+
{ name: "mutation-state", gen: () => generateMutationState(ctx), enabled: ctx.config.features.mutationQueue.enabled },
|
|
44
48
|
{ name: "background-sync", gen: () => generateBackgroundSync(ctx), enabled: ctx.config.features.backgroundSync },
|
|
45
49
|
{ name: "auth-store", gen: () => generateAuthStore(ctx), enabled: ctx.config.features.auth.enabled },
|
|
46
50
|
{ name: "auth-user", gen: () => generateAuthUser(ctx), enabled: ctx.config.features.auth.enabled },
|
|
@@ -50,7 +54,9 @@ export function generateFiles(ctx, onFile) {
|
|
|
50
54
|
{ name: "pwa-install", gen: () => generatePwaInstall(ctx), enabled: ctx.config.features.pwa.enabled },
|
|
51
55
|
{ name: "manifest.json", gen: () => generateManifest(ctx), enabled: ctx.config.features.pwa.enabled },
|
|
52
56
|
{ name: "invalidation-tags", gen: () => generateInvalidationTags(ctx), enabled: ctx.config.features.tagInvalidation },
|
|
57
|
+
{ name: "gql-wrapper", gen: () => generateGqlWrapper(ctx), enabled: ctx.config.features.graphql.enabled },
|
|
53
58
|
{ name: "push", gen: () => generatePush(ctx), enabled: ctx.config.features.pushNotifications?.enabled ?? false },
|
|
59
|
+
{ name: "server-push", gen: () => generateServerPush(ctx), enabled: ctx.config.features.serverPush.enabled },
|
|
54
60
|
{ name: "hooks", gen: () => generateHooks(ctx), enabled: ctx.config.framework === "react" },
|
|
55
61
|
{ name: "GUIDE.md", gen: () => generateGuide(ctx), enabled: true },
|
|
56
62
|
{ name: "README.md", gen: () => generateReadme(ctx), enabled: true },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swoff-files-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/swoff-files-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAG5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAQnE,MAAM,UAAU,aAAa,CAAC,GAAqB,EAAE,MAA+B;IAClF,MAAM,KAAK,GAAW;QACpB,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1E,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1E,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;QAClF,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;QAC9E,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE;QAE9F,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE;
|
|
1
|
+
{"version":3,"file":"swoff-files-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/swoff-files-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,11 @@ export const KNOWN_FEATURES = [
|
|
|
4
4
|
"auth",
|
|
5
5
|
"crossTabSync",
|
|
6
6
|
"tagInvalidation",
|
|
7
|
+
"graphql",
|
|
7
8
|
"pushNotifications",
|
|
9
|
+
"serverPush",
|
|
8
10
|
];
|
|
9
|
-
export const OBJECT_FEATURES = ["pwa", "serviceWorker", "auth", "pushNotifications"];
|
|
11
|
+
export const OBJECT_FEATURES = ["pwa", "serviceWorker", "auth", "pushNotifications", "graphql", "serverPush"];
|
|
10
12
|
export const VALID_STRATEGIES = [
|
|
11
13
|
"cache-first",
|
|
12
14
|
"network-first",
|
|
@@ -27,6 +29,20 @@ function normalizeSwVersion(ver) {
|
|
|
27
29
|
return { ...defaultVersionConfig, ...ver };
|
|
28
30
|
return defaultVersionConfig;
|
|
29
31
|
}
|
|
32
|
+
function normalizeGql(val) {
|
|
33
|
+
if (typeof val === "boolean")
|
|
34
|
+
return { ...defaultGql, enabled: val };
|
|
35
|
+
if (val && typeof val === "object")
|
|
36
|
+
return { ...defaultGql, ...val };
|
|
37
|
+
return defaultGql;
|
|
38
|
+
}
|
|
39
|
+
function normalizeMutationQueue(val) {
|
|
40
|
+
if (typeof val === "boolean")
|
|
41
|
+
return { ...defaultMutationQueue, enabled: val };
|
|
42
|
+
if (val && typeof val === "object")
|
|
43
|
+
return { ...defaultMutationQueue, ...val };
|
|
44
|
+
return defaultMutationQueue;
|
|
45
|
+
}
|
|
30
46
|
export function mergeConfigs(base, override) {
|
|
31
47
|
return {
|
|
32
48
|
...base,
|
|
@@ -41,6 +57,9 @@ export function mergeConfigs(base, override) {
|
|
|
41
57
|
version: normalizeSwVersion(override.features?.serviceWorker?.version),
|
|
42
58
|
},
|
|
43
59
|
auth: normalizeAuth(override.features?.auth),
|
|
60
|
+
mutationQueue: normalizeMutationQueue(override.features?.mutationQueue),
|
|
61
|
+
graphql: normalizeGql(override.features?.graphql),
|
|
62
|
+
serverPush: { ...defaultServerPush, ...override.features?.serverPush },
|
|
44
63
|
},
|
|
45
64
|
build: { ...base.build, ...override.build },
|
|
46
65
|
};
|
|
@@ -56,6 +75,23 @@ export const defaultVersionConfig = {
|
|
|
56
75
|
source: "from-package",
|
|
57
76
|
minSupportedVersion: "0.0.0",
|
|
58
77
|
};
|
|
78
|
+
export const defaultGql = {
|
|
79
|
+
enabled: false,
|
|
80
|
+
endpoint: "/graphql",
|
|
81
|
+
};
|
|
82
|
+
export const defaultMutationQueue = {
|
|
83
|
+
enabled: false,
|
|
84
|
+
batchSize: 1,
|
|
85
|
+
batchDelayMs: 0,
|
|
86
|
+
maxRetries: 5,
|
|
87
|
+
retryBackoffMs: 1000,
|
|
88
|
+
};
|
|
89
|
+
export const defaultServerPush = {
|
|
90
|
+
enabled: false,
|
|
91
|
+
type: "sse",
|
|
92
|
+
endpoint: "/api/events",
|
|
93
|
+
reconnectDelayMs: 5000,
|
|
94
|
+
};
|
|
59
95
|
export const defaultConfig = {
|
|
60
96
|
enabled: true,
|
|
61
97
|
features: {
|
|
@@ -75,12 +111,14 @@ export const defaultConfig = {
|
|
|
75
111
|
navigationMode: "spa",
|
|
76
112
|
spaEntry: "/index.html",
|
|
77
113
|
},
|
|
78
|
-
mutationQueue:
|
|
114
|
+
mutationQueue: { ...defaultMutationQueue },
|
|
79
115
|
backgroundSync: false,
|
|
80
116
|
auth: { ...defaultAuth },
|
|
81
117
|
crossTabSync: true,
|
|
82
118
|
tagInvalidation: true,
|
|
119
|
+
graphql: { ...defaultGql },
|
|
83
120
|
pushNotifications: { enabled: false },
|
|
121
|
+
serverPush: { ...defaultServerPush },
|
|
84
122
|
},
|
|
85
123
|
build: {
|
|
86
124
|
outputDir: "dist",
|