swimple 0.4.0 → 0.5.0
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 +59 -0
- package/headers.js +50 -0
- package/helpers.js +17 -8
- package/index.js +10 -6
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -416,6 +416,65 @@ fetch("/api/logout", {
|
|
|
416
416
|
|
|
417
417
|
**Note:** While this header is typically used on mutation requests (POST/PATCH/PUT/DELETE), if used on a GET request, it will still clear the cache and attempt to fetch from network. If the network fails, the error will propagate to the caller.
|
|
418
418
|
|
|
419
|
+
## Header Constants
|
|
420
|
+
|
|
421
|
+
For convenience, swimple exports header name constants that you can use in your non-service-worker code (e.g., client-side JavaScript) to avoid hard-coding header names. This helps prevent typos and makes refactoring easier.
|
|
422
|
+
|
|
423
|
+
### Importing Header Constants
|
|
424
|
+
|
|
425
|
+
#### Node.js / npm Import
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
import {
|
|
429
|
+
CACHE_STRATEGY_HEADER,
|
|
430
|
+
CACHE_TTL_HEADER,
|
|
431
|
+
CACHE_STALE_TTL_HEADER,
|
|
432
|
+
CACHE_INVALIDATE_HEADER,
|
|
433
|
+
CACHE_CLEAR_HEADER
|
|
434
|
+
} from "swimple/headers";
|
|
435
|
+
|
|
436
|
+
// Use in your fetch calls
|
|
437
|
+
fetch("/api/users", {
|
|
438
|
+
headers: {
|
|
439
|
+
[CACHE_STRATEGY_HEADER]: "network-first",
|
|
440
|
+
[CACHE_TTL_HEADER]: "600"
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
#### CDN Import
|
|
446
|
+
|
|
447
|
+
```javascript
|
|
448
|
+
import {
|
|
449
|
+
CACHE_STRATEGY_HEADER,
|
|
450
|
+
CACHE_TTL_HEADER,
|
|
451
|
+
CACHE_STALE_TTL_HEADER,
|
|
452
|
+
CACHE_INVALIDATE_HEADER,
|
|
453
|
+
CACHE_CLEAR_HEADER
|
|
454
|
+
} from "https://cdn.jsdelivr.net/npm/swimple@1.0.0/headers.js";
|
|
455
|
+
|
|
456
|
+
// Use in your fetch calls
|
|
457
|
+
const headers = new Headers();
|
|
458
|
+
headers.set(CACHE_TTL_HEADER, "300");
|
|
459
|
+
headers.append(CACHE_INVALIDATE_HEADER, "/api/users");
|
|
460
|
+
headers.append(CACHE_INVALIDATE_HEADER, "/api/posts");
|
|
461
|
+
|
|
462
|
+
fetch("/api/users/123", {
|
|
463
|
+
method: "PATCH",
|
|
464
|
+
headers
|
|
465
|
+
});
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Available Constants
|
|
469
|
+
|
|
470
|
+
- `CACHE_STRATEGY_HEADER` - `"X-SW-Cache-Strategy"`
|
|
471
|
+
- `CACHE_TTL_HEADER` - `"X-SW-Cache-TTL-Seconds"`
|
|
472
|
+
- `CACHE_STALE_TTL_HEADER` - `"X-SW-Cache-Stale-TTL-Seconds"`
|
|
473
|
+
- `CACHE_INVALIDATE_HEADER` - `"X-SW-Cache-Invalidate"`
|
|
474
|
+
- `CACHE_CLEAR_HEADER` - `"X-SW-Cache-Clear"`
|
|
475
|
+
|
|
476
|
+
**Note:** There is also an internal `CACHE_TIMESTAMP_HEADER` constant (`"x-sw-cache-timestamp"`), but this is used internally by the library and should not be set manually.
|
|
477
|
+
|
|
419
478
|
## Caching Behavior
|
|
420
479
|
|
|
421
480
|
### Automatic Caching (Default)
|
package/headers.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Header constants used by swimple for cache control.
|
|
5
|
+
* These constants can be imported in non-service-worker code to set headers on requests.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Node.js / npm import
|
|
9
|
+
* import { CACHE_STRATEGY_HEADER, CACHE_TTL_HEADER } from "swimple/headers";
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // CDN import
|
|
13
|
+
* import { CACHE_STRATEGY_HEADER, CACHE_TTL_HEADER } from "https://cdn.jsdelivr.net/npm/swimple@1.0.0/headers.js";
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Header name for overriding the caching strategy for a specific request.
|
|
18
|
+
* Values: "cache-first", "network-first", or "stale-while-revalidate"
|
|
19
|
+
*/
|
|
20
|
+
export const CACHE_STRATEGY_HEADER = "X-SW-Cache-Strategy";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Header name for setting the time-to-live (in seconds) for a cached response.
|
|
24
|
+
* Set to "0" to completely opt out of caching for a specific request.
|
|
25
|
+
*/
|
|
26
|
+
export const CACHE_TTL_HEADER = "X-SW-Cache-TTL-Seconds";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Header name for setting the stale time-to-live (in seconds) for a cached response.
|
|
30
|
+
* Used by cache-first (when offline), network-first (when offline), and stale-while-revalidate strategies.
|
|
31
|
+
*/
|
|
32
|
+
export const CACHE_STALE_TTL_HEADER = "X-SW-Cache-Stale-TTL-Seconds";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Header name for explicitly invalidating specific cache entries.
|
|
36
|
+
* Can be set multiple times to invalidate multiple paths.
|
|
37
|
+
*/
|
|
38
|
+
export const CACHE_INVALIDATE_HEADER = "X-SW-Cache-Invalidate";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Header name for clearing the entire cache.
|
|
42
|
+
* Any value works - the header's presence triggers cache clearing.
|
|
43
|
+
*/
|
|
44
|
+
export const CACHE_CLEAR_HEADER = "X-SW-Cache-Clear";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Internal header name used to store the cache timestamp in cached responses.
|
|
48
|
+
* This header is set automatically by the library and should not be set manually.
|
|
49
|
+
*/
|
|
50
|
+
export const CACHE_TIMESTAMP_HEADER = "x-sw-cache-timestamp";
|
package/helpers.js
CHANGED
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
/** @import { CacheStrategy, HandleRequestConfig, LoggingLevel } from "./types" */
|
|
10
10
|
|
|
11
|
+
import {
|
|
12
|
+
CACHE_TIMESTAMP_HEADER,
|
|
13
|
+
CACHE_STRATEGY_HEADER,
|
|
14
|
+
CACHE_TTL_HEADER,
|
|
15
|
+
CACHE_STALE_TTL_HEADER
|
|
16
|
+
} from "./headers.js";
|
|
17
|
+
|
|
11
18
|
// Module-level logging state
|
|
12
19
|
/** @type {LoggingLevel} */
|
|
13
20
|
let loggingLevel = "none";
|
|
@@ -71,7 +78,7 @@ export function getAllHeaders(headers, name) {
|
|
|
71
78
|
* @returns {number | null} Timestamp in milliseconds since epoch, or null if not found
|
|
72
79
|
*/
|
|
73
80
|
export function getCacheTimestamp(response) {
|
|
74
|
-
const timestampHeader = getHeader(response.headers,
|
|
81
|
+
const timestampHeader = getHeader(response.headers, CACHE_TIMESTAMP_HEADER);
|
|
75
82
|
if (!timestampHeader) {
|
|
76
83
|
return null;
|
|
77
84
|
}
|
|
@@ -120,7 +127,7 @@ export function isStale(response, ttl, staleTTL) {
|
|
|
120
127
|
*/
|
|
121
128
|
export function addTimestamp(response) {
|
|
122
129
|
const headers = new Headers(response.headers);
|
|
123
|
-
headers.set(
|
|
130
|
+
headers.set(CACHE_TIMESTAMP_HEADER, Date.now().toString());
|
|
124
131
|
return new Response(response.body, {
|
|
125
132
|
status: response.status,
|
|
126
133
|
statusText: response.statusText,
|
|
@@ -159,14 +166,16 @@ export function getInferredInvalidationPaths(url) {
|
|
|
159
166
|
* @returns {CacheStrategy}
|
|
160
167
|
*/
|
|
161
168
|
export function getStrategy(headers, defaultStrategy, url = "") {
|
|
162
|
-
const strategyHeader = getHeader(headers,
|
|
169
|
+
const strategyHeader = getHeader(headers, CACHE_STRATEGY_HEADER);
|
|
163
170
|
if (
|
|
164
171
|
strategyHeader &&
|
|
165
172
|
["cache-first", "network-first", "stale-while-revalidate"].includes(
|
|
166
173
|
strategyHeader
|
|
167
174
|
)
|
|
168
175
|
) {
|
|
169
|
-
logVerbose(
|
|
176
|
+
logVerbose(
|
|
177
|
+
`${CACHE_STRATEGY_HEADER} header set: ${strategyHeader} (${url})`
|
|
178
|
+
);
|
|
170
179
|
return /** @type {CacheStrategy} */ (strategyHeader);
|
|
171
180
|
}
|
|
172
181
|
return defaultStrategy;
|
|
@@ -180,11 +189,11 @@ export function getStrategy(headers, defaultStrategy, url = "") {
|
|
|
180
189
|
* @returns {number | null} TTL in seconds, or null if caching is disabled
|
|
181
190
|
*/
|
|
182
191
|
export function getTTL(headers, defaultTTL, url = "") {
|
|
183
|
-
const ttlHeader = getHeader(headers,
|
|
192
|
+
const ttlHeader = getHeader(headers, CACHE_TTL_HEADER);
|
|
184
193
|
if (ttlHeader === null) {
|
|
185
194
|
return defaultTTL > 0 ? defaultTTL : null;
|
|
186
195
|
}
|
|
187
|
-
logVerbose(
|
|
196
|
+
logVerbose(`${CACHE_TTL_HEADER} header set: ${ttlHeader} (${url})`);
|
|
188
197
|
const ttl = parseInt(ttlHeader, 10);
|
|
189
198
|
if (isNaN(ttl) || ttl <= 0) {
|
|
190
199
|
return null;
|
|
@@ -200,12 +209,12 @@ export function getTTL(headers, defaultTTL, url = "") {
|
|
|
200
209
|
* @returns {number | null} Stale TTL in seconds, or null if stale caching is disabled
|
|
201
210
|
*/
|
|
202
211
|
export function getStaleTTL(headers, defaultStaleTTL, url = "") {
|
|
203
|
-
const staleTTLHeader = getHeader(headers,
|
|
212
|
+
const staleTTLHeader = getHeader(headers, CACHE_STALE_TTL_HEADER);
|
|
204
213
|
if (staleTTLHeader === null) {
|
|
205
214
|
return defaultStaleTTL > 0 ? defaultStaleTTL : null;
|
|
206
215
|
}
|
|
207
216
|
logVerbose(
|
|
208
|
-
|
|
217
|
+
`${CACHE_STALE_TTL_HEADER} header set: ${staleTTLHeader} (${url})`
|
|
209
218
|
);
|
|
210
219
|
const staleTTL = parseInt(staleTTLHeader, 10);
|
|
211
220
|
if (isNaN(staleTTL) || staleTTL <= 0) {
|
package/index.js
CHANGED
|
@@ -28,6 +28,11 @@ import {
|
|
|
28
28
|
logInfo,
|
|
29
29
|
logVerbose
|
|
30
30
|
} from "./helpers.js";
|
|
31
|
+
import {
|
|
32
|
+
CACHE_CLEAR_HEADER,
|
|
33
|
+
CACHE_INVALIDATE_HEADER,
|
|
34
|
+
CACHE_TTL_HEADER
|
|
35
|
+
} from "./headers.js";
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Creates a request handler function for service worker fetch events.
|
|
@@ -75,8 +80,8 @@ export function createHandleRequest(config) {
|
|
|
75
80
|
const headers = request.headers;
|
|
76
81
|
|
|
77
82
|
// Handle cache clearing - header presence (any value) triggers cache clear
|
|
78
|
-
if (getHeader(headers,
|
|
79
|
-
logVerbose(
|
|
83
|
+
if (getHeader(headers, CACHE_CLEAR_HEADER) !== null) {
|
|
84
|
+
logVerbose(`${CACHE_CLEAR_HEADER} header set: ${url}`);
|
|
80
85
|
return (async () => {
|
|
81
86
|
await clearCache(cacheName);
|
|
82
87
|
return customFetch(request);
|
|
@@ -85,12 +90,12 @@ export function createHandleRequest(config) {
|
|
|
85
90
|
|
|
86
91
|
// Handle invalidation for mutations
|
|
87
92
|
// Check for explicit invalidation headers first (works even if inferInvalidation is false)
|
|
88
|
-
const invalidateHeaders = getAllHeaders(headers,
|
|
93
|
+
const invalidateHeaders = getAllHeaders(headers, CACHE_INVALIDATE_HEADER);
|
|
89
94
|
const isMutation = ["POST", "PATCH", "PUT", "DELETE"].includes(method);
|
|
90
95
|
|
|
91
96
|
if (invalidateHeaders.length > 0) {
|
|
92
97
|
invalidateHeaders.forEach((path) => {
|
|
93
|
-
logVerbose(
|
|
98
|
+
logVerbose(`${CACHE_INVALIDATE_HEADER} header set: ${path} (${url})`);
|
|
94
99
|
});
|
|
95
100
|
}
|
|
96
101
|
|
|
@@ -153,8 +158,7 @@ export function createHandleRequest(config) {
|
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
// Check if request matches scope and should be cached
|
|
156
|
-
const hasExplicitTTLHeader =
|
|
157
|
-
getHeader(headers, "X-SW-Cache-TTL-Seconds") !== null;
|
|
161
|
+
const hasExplicitTTLHeader = getHeader(headers, CACHE_TTL_HEADER) !== null;
|
|
158
162
|
const ttl = getTTL(headers, defaultTTLSeconds, url);
|
|
159
163
|
|
|
160
164
|
// If scope doesn't match and there's no explicit TTL header, don't handle the request
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swimple",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A simple service worker library for request caching",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./index.js"
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./headers": "./headers.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"test": "npm run test:unit && npm run test:e2e && npm run test:ui",
|