swimple 0.3.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 CHANGED
@@ -163,17 +163,28 @@ The `customFetch` function has the same signature as the native `fetch` function
163
163
 
164
164
  ### Example 5: Enable Logging
165
165
 
166
- You can enable logging to debug cache behavior. When enabled, the library logs cache hits, misses, header usage, invalidation, and cleanup events to the console.
166
+ You can enable logging to debug cache behavior. The library supports three logging levels:
167
+
168
+ - `"none"` (default): No logging
169
+ - `"minimal"`: console.info logs cache hits and cache invalidation only
170
+ - `"verbose"`: console.debug logs all events including cache misses, header usage, and cleanup
167
171
 
168
172
  ```javascript
169
173
  const handleRequest = createHandleRequest({
170
174
  cacheName: "api-cache-v1",
171
175
  scope: ["/api/"],
172
- enableLogging: true
176
+ loggingLevel: "verbose" // or "minimal" for less verbose output
173
177
  });
174
178
  ```
175
179
 
176
- When logging is enabled, you'll see console output like:
180
+ With `loggingLevel: "minimal"`, you'll see:
181
+
182
+ ```
183
+ [swimple] Cache hit: https://example.com/api/users
184
+ [swimple] Cache invalidated: https://example.com/api/users/123
185
+ ```
186
+
187
+ With `loggingLevel: "verbose"`, you'll see all events:
177
188
 
178
189
  ```
179
190
  [swimple] Cache hit: https://example.com/api/users
@@ -255,7 +266,7 @@ Creates a request handler function for your service worker fetch handler.
255
266
  | `inferInvalidation` | `boolean` | No | `true` | Automatically invalidate cache on POST/PATCH/PUT/DELETE requests. |
256
267
  | `customFetch` | `function` | No | `fetch` | Custom fetch function to use for network requests. Receives a `Request` object and must return a `Promise<Response>`. Useful for handling authentication errors (401/403) or adding custom headers to all requests. |
257
268
  | `maxCacheAgeSeconds` | `number` | No | `7200` | Maximum age (in seconds) before cache entries are automatically cleaned up. Entries older than this age are deleted. Defaults to 7200 seconds (2 hours, which is 2x the default stale TTL). Cache entries are cleaned up reactively (when accessed) and periodically (every 100 fetches). |
258
- | `enableLogging` | `boolean` | No | `false` | Enable logging for cache hits, misses, header usage, invalidation, and cleanup. When enabled, logs are written to the console with the `[swimple]` prefix. Useful for debugging cache behavior. |
269
+ | `loggingLevel` | `string` | No | `"none"` | Logging level: `"none"` (no logging), `"minimal"` (cache hits and invalidation only), or `"verbose"` (all logging including misses, header usage, and cleanup). When enabled, logs are written to the console with the `[swimple]` prefix. Useful for debugging cache behavior. |
259
270
 
260
271
  #### Returns
261
272
 
@@ -405,6 +416,65 @@ fetch("/api/logout", {
405
416
 
406
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.
407
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
+
408
478
  ## Caching Behavior
409
479
 
410
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
@@ -6,17 +6,25 @@
6
6
  /// <reference lib="esnext" />
7
7
  /// <reference lib="webworker" />
8
8
 
9
- /** @import { CacheStrategy, HandleRequestConfig } from "./types" */
9
+ /** @import { CacheStrategy, HandleRequestConfig, LoggingLevel } from "./types" */
10
+
11
+ import {
12
+ CACHE_TIMESTAMP_HEADER,
13
+ CACHE_STRATEGY_HEADER,
14
+ CACHE_TTL_HEADER,
15
+ CACHE_STALE_TTL_HEADER
16
+ } from "./headers.js";
10
17
 
11
18
  // Module-level logging state
12
- let loggingEnabled = false;
19
+ /** @type {LoggingLevel} */
20
+ let loggingLevel = "none";
13
21
 
14
22
  /**
15
- * Set whether logging is enabled
16
- * @param {boolean} enabled - Whether to enable logging
23
+ * Set the logging level
24
+ * @param {LoggingLevel} level - Logging level: "none", "minimal", or "verbose"
17
25
  */
18
- export function setLoggingEnabled(enabled) {
19
- loggingEnabled = enabled;
26
+ export function setLoggingLevel(level) {
27
+ loggingLevel = level;
20
28
  }
21
29
 
22
30
  /**
@@ -70,7 +78,7 @@ export function getAllHeaders(headers, name) {
70
78
  * @returns {number | null} Timestamp in milliseconds since epoch, or null if not found
71
79
  */
72
80
  export function getCacheTimestamp(response) {
73
- const timestampHeader = getHeader(response.headers, "x-sw-cache-timestamp");
81
+ const timestampHeader = getHeader(response.headers, CACHE_TIMESTAMP_HEADER);
74
82
  if (!timestampHeader) {
75
83
  return null;
76
84
  }
@@ -119,7 +127,7 @@ export function isStale(response, ttl, staleTTL) {
119
127
  */
120
128
  export function addTimestamp(response) {
121
129
  const headers = new Headers(response.headers);
122
- headers.set("x-sw-cache-timestamp", Date.now().toString());
130
+ headers.set(CACHE_TIMESTAMP_HEADER, Date.now().toString());
123
131
  return new Response(response.body, {
124
132
  status: response.status,
125
133
  statusText: response.statusText,
@@ -158,14 +166,16 @@ export function getInferredInvalidationPaths(url) {
158
166
  * @returns {CacheStrategy}
159
167
  */
160
168
  export function getStrategy(headers, defaultStrategy, url = "") {
161
- const strategyHeader = getHeader(headers, "X-SW-Cache-Strategy");
169
+ const strategyHeader = getHeader(headers, CACHE_STRATEGY_HEADER);
162
170
  if (
163
171
  strategyHeader &&
164
172
  ["cache-first", "network-first", "stale-while-revalidate"].includes(
165
173
  strategyHeader
166
174
  )
167
175
  ) {
168
- log(`X-SW-Cache-Strategy header set: ${strategyHeader} (${url})`);
176
+ logVerbose(
177
+ `${CACHE_STRATEGY_HEADER} header set: ${strategyHeader} (${url})`
178
+ );
169
179
  return /** @type {CacheStrategy} */ (strategyHeader);
170
180
  }
171
181
  return defaultStrategy;
@@ -179,11 +189,11 @@ export function getStrategy(headers, defaultStrategy, url = "") {
179
189
  * @returns {number | null} TTL in seconds, or null if caching is disabled
180
190
  */
181
191
  export function getTTL(headers, defaultTTL, url = "") {
182
- const ttlHeader = getHeader(headers, "X-SW-Cache-TTL-Seconds");
192
+ const ttlHeader = getHeader(headers, CACHE_TTL_HEADER);
183
193
  if (ttlHeader === null) {
184
194
  return defaultTTL > 0 ? defaultTTL : null;
185
195
  }
186
- log(`X-SW-Cache-TTL-Seconds header set: ${ttlHeader} (${url})`);
196
+ logVerbose(`${CACHE_TTL_HEADER} header set: ${ttlHeader} (${url})`);
187
197
  const ttl = parseInt(ttlHeader, 10);
188
198
  if (isNaN(ttl) || ttl <= 0) {
189
199
  return null;
@@ -199,11 +209,13 @@ export function getTTL(headers, defaultTTL, url = "") {
199
209
  * @returns {number | null} Stale TTL in seconds, or null if stale caching is disabled
200
210
  */
201
211
  export function getStaleTTL(headers, defaultStaleTTL, url = "") {
202
- const staleTTLHeader = getHeader(headers, "X-SW-Cache-Stale-TTL-Seconds");
212
+ const staleTTLHeader = getHeader(headers, CACHE_STALE_TTL_HEADER);
203
213
  if (staleTTLHeader === null) {
204
214
  return defaultStaleTTL > 0 ? defaultStaleTTL : null;
205
215
  }
206
- log(`X-SW-Cache-Stale-TTL-Seconds header set: ${staleTTLHeader} (${url})`);
216
+ logVerbose(
217
+ `${CACHE_STALE_TTL_HEADER} header set: ${staleTTLHeader} (${url})`
218
+ );
207
219
  const staleTTL = parseInt(staleTTLHeader, 10);
208
220
  if (isNaN(staleTTL) || staleTTL <= 0) {
209
221
  return null;
@@ -235,8 +247,10 @@ export function matchesScope(url, scope, defaultTTLSeconds) {
235
247
  export async function invalidateCache(cacheName, urls) {
236
248
  const cache = await caches.open(cacheName);
237
249
  const deletePromises = urls.map(async (url) => {
238
- log(`Cache invalidated: ${url}`);
239
250
  const deleted = await cache.delete(url);
251
+ if (deleted) {
252
+ logInfo(`Cache invalidated: ${url}`);
253
+ }
240
254
  return deleted;
241
255
  });
242
256
  await Promise.allSettled(deletePromises);
@@ -292,21 +306,31 @@ export async function cleanupOldCacheEntries(cacheName, maxAgeSeconds) {
292
306
  await Promise.allSettled(cleanupPromises);
293
307
  if (cleanedUrls.length > 0) {
294
308
  cleanedUrls.forEach((url) => {
295
- log(`Cache entry cleaned up (maxAge): ${url}`);
309
+ logVerbose(`Cache entry cleaned up (maxAge): ${url}`);
296
310
  });
297
- log(
311
+ logVerbose(
298
312
  `Cleaned up ${cleanedUrls.length} cache entr${cleanedUrls.length === 1 ? "y" : "ies"} due to maxAge`
299
313
  );
300
314
  }
301
315
  }
302
316
 
303
317
  /**
304
- * Log a message if logging is enabled
318
+ * Log an informational message (minimal and verbose levels)
305
319
  * @param {string} message - Message to log
306
320
  */
307
- export function log(message) {
308
- if (loggingEnabled) {
309
- console.log(`[swimple] ${message}`);
321
+ export function logInfo(message) {
322
+ if (loggingLevel === "minimal" || loggingLevel === "verbose") {
323
+ console.info(`[swimple] ${message}`);
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Log a verbose message (verbose level only)
329
+ * @param {string} message - Message to log
330
+ */
331
+ export function logVerbose(message) {
332
+ if (loggingLevel === "verbose") {
333
+ console.debug(`[swimple] ${message}`);
310
334
  }
311
335
  }
312
336
 
@@ -345,9 +369,11 @@ export function validateConfig(config) {
345
369
  );
346
370
  }
347
371
  if (
348
- cfg.enableLogging !== undefined &&
349
- typeof cfg.enableLogging !== "boolean"
372
+ cfg.loggingLevel !== undefined &&
373
+ !["none", "minimal", "verbose"].includes(String(cfg.loggingLevel))
350
374
  ) {
351
- throw new Error("config.enableLogging must be a boolean if provided");
375
+ throw new Error(
376
+ 'config.loggingLevel must be one of: "none", "minimal", "verbose"'
377
+ );
352
378
  }
353
379
  }
package/index.js CHANGED
@@ -24,9 +24,15 @@ import {
24
24
  validateConfig,
25
25
  isOlderThanMaxAge,
26
26
  cleanupOldCacheEntries,
27
- setLoggingEnabled,
28
- log
27
+ setLoggingLevel,
28
+ logInfo,
29
+ logVerbose
29
30
  } from "./helpers.js";
31
+ import {
32
+ CACHE_CLEAR_HEADER,
33
+ CACHE_INVALIDATE_HEADER,
34
+ CACHE_TTL_HEADER
35
+ } from "./headers.js";
30
36
 
31
37
  /**
32
38
  * Creates a request handler function for service worker fetch events.
@@ -51,10 +57,10 @@ export function createHandleRequest(config) {
51
57
  const maxCacheAgeSeconds = config.maxCacheAgeSeconds ?? 7200;
52
58
  const inferInvalidation = config.inferInvalidation ?? true;
53
59
  const customFetch = config.customFetch || fetch;
54
- const enableLogging = config.enableLogging ?? false;
60
+ const loggingLevel = config.loggingLevel ?? "none";
55
61
 
56
62
  // Set module-level logging state
57
- setLoggingEnabled(enableLogging);
63
+ setLoggingLevel(loggingLevel);
58
64
 
59
65
  // Track fetch counter for periodic cleanup
60
66
  let fetchCounter = 0;
@@ -74,8 +80,8 @@ export function createHandleRequest(config) {
74
80
  const headers = request.headers;
75
81
 
76
82
  // Handle cache clearing - header presence (any value) triggers cache clear
77
- if (getHeader(headers, "X-SW-Cache-Clear") !== null) {
78
- log(`X-SW-Cache-Clear header set: ${url}`);
83
+ if (getHeader(headers, CACHE_CLEAR_HEADER) !== null) {
84
+ logVerbose(`${CACHE_CLEAR_HEADER} header set: ${url}`);
79
85
  return (async () => {
80
86
  await clearCache(cacheName);
81
87
  return customFetch(request);
@@ -84,12 +90,12 @@ export function createHandleRequest(config) {
84
90
 
85
91
  // Handle invalidation for mutations
86
92
  // Check for explicit invalidation headers first (works even if inferInvalidation is false)
87
- const invalidateHeaders = getAllHeaders(headers, "X-SW-Cache-Invalidate");
93
+ const invalidateHeaders = getAllHeaders(headers, CACHE_INVALIDATE_HEADER);
88
94
  const isMutation = ["POST", "PATCH", "PUT", "DELETE"].includes(method);
89
95
 
90
96
  if (invalidateHeaders.length > 0) {
91
97
  invalidateHeaders.forEach((path) => {
92
- log(`X-SW-Cache-Invalidate header set: ${path} (${url})`);
98
+ logVerbose(`${CACHE_INVALIDATE_HEADER} header set: ${path} (${url})`);
93
99
  });
94
100
  }
95
101
 
@@ -152,8 +158,7 @@ export function createHandleRequest(config) {
152
158
  }
153
159
 
154
160
  // Check if request matches scope and should be cached
155
- const hasExplicitTTLHeader =
156
- getHeader(headers, "X-SW-Cache-TTL-Seconds") !== null;
161
+ const hasExplicitTTLHeader = getHeader(headers, CACHE_TTL_HEADER) !== null;
157
162
  const ttl = getTTL(headers, defaultTTLSeconds, url);
158
163
 
159
164
  // If scope doesn't match and there's no explicit TTL header, don't handle the request
@@ -177,22 +182,22 @@ export function createHandleRequest(config) {
177
182
 
178
183
  if (cachedResponse) {
179
184
  if (isFresh(cachedResponse, ttl)) {
180
- log(`Cache hit: ${url}`);
185
+ logInfo(`Cache hit: ${url}`);
181
186
  return cachedResponse;
182
187
  }
183
188
 
184
189
  // Reactive cleanup: delete if older than maxCacheAgeSeconds
185
190
  if (isOlderThanMaxAge(cachedResponse, maxCacheAgeSeconds)) {
186
- log(`Cache entry cleaned up (maxAge): ${url}`);
191
+ logVerbose(`Cache entry cleaned up (maxAge): ${url}`);
187
192
  await cache.delete(request); // Fire-and-forget cleanup
188
193
  }
189
194
  }
190
195
 
191
196
  // No fresh cache, fetch from network
192
197
  if (!cachedResponse) {
193
- log(`Cache miss: ${url}`);
198
+ logVerbose(`Cache miss: ${url}`);
194
199
  } else if (!isStale(cachedResponse, ttl, staleTTL)) {
195
- log(`Cache miss (stale): ${url}`);
200
+ logVerbose(`Cache miss (stale): ${url}`);
196
201
  }
197
202
  let networkResponse;
198
203
  try {
@@ -200,7 +205,7 @@ export function createHandleRequest(config) {
200
205
  } catch (error) {
201
206
  // Network failed, return stale cache if available
202
207
  if (cachedResponse && isStale(cachedResponse, ttl, staleTTL)) {
203
- log(`Cache hit (stale, offline): ${url}`);
208
+ logInfo(`Cache hit (stale, offline): ${url}`);
204
209
  return cachedResponse;
205
210
  }
206
211
  throw error;
@@ -229,7 +234,7 @@ export function createHandleRequest(config) {
229
234
  if (cachedResponse) {
230
235
  // Reactive cleanup: delete if older than maxCacheAgeSeconds
231
236
  if (isOlderThanMaxAge(cachedResponse, maxCacheAgeSeconds)) {
232
- log(`Cache entry cleaned up (maxAge): ${url}`);
237
+ logVerbose(`Cache entry cleaned up (maxAge): ${url}`);
233
238
  cache.delete(request); // Fire-and-forget cleanup
234
239
  throw error;
235
240
  }
@@ -237,11 +242,11 @@ export function createHandleRequest(config) {
237
242
  isFresh(cachedResponse, ttl) ||
238
243
  isStale(cachedResponse, ttl, staleTTL)
239
244
  ) {
240
- log(`Cache hit (offline): ${url}`);
245
+ logInfo(`Cache hit (offline): ${url}`);
241
246
  return cachedResponse;
242
247
  }
243
248
  }
244
- log(`Cache miss (offline): ${url}`);
249
+ logVerbose(`Cache miss (offline): ${url}`);
245
250
  throw error;
246
251
  }
247
252
 
@@ -263,7 +268,7 @@ export function createHandleRequest(config) {
263
268
  if (cachedResponse) {
264
269
  // Reactive cleanup: delete if older than maxCacheAgeSeconds
265
270
  if (isOlderThanMaxAge(cachedResponse, maxCacheAgeSeconds)) {
266
- log(`Cache entry cleaned up (maxAge): ${url}`);
271
+ logVerbose(`Cache entry cleaned up (maxAge): ${url}`);
267
272
  cache.delete(request); // Fire-and-forget cleanup
268
273
  // Continue to fetch from network
269
274
  } else {
@@ -272,9 +277,9 @@ export function createHandleRequest(config) {
272
277
 
273
278
  if (fresh || stale) {
274
279
  if (fresh) {
275
- log(`Cache hit: ${url}`);
280
+ logInfo(`Cache hit: ${url}`);
276
281
  } else {
277
- log(`Cache hit (stale): ${url}`);
282
+ logInfo(`Cache hit (stale): ${url}`);
278
283
  }
279
284
  // Return cached response immediately
280
285
  // Update cache in background if stale
@@ -301,9 +306,9 @@ export function createHandleRequest(config) {
301
306
  // because we already know if there was a cached response it won't be
302
307
  // fresh or stale if we've reached this point
303
308
  if (!cachedResponse) {
304
- log(`Cache miss: ${url}`);
309
+ logVerbose(`Cache miss: ${url}`);
305
310
  } else {
306
- log(`Cache miss (too stale): ${url}`);
311
+ logVerbose(`Cache miss (too stale): ${url}`);
307
312
  }
308
313
  const networkResponse = await customFetch(request);
309
314
 
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "swimple",
3
- "version": "0.3.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",
package/types.d.ts CHANGED
@@ -9,6 +9,14 @@ export type CacheStrategy =
9
9
  | "network-first"
10
10
  | "stale-while-revalidate";
11
11
 
12
+ /**
13
+ * Logging level for cache operations.
14
+ * - `none`: No logging
15
+ * - `minimal`: Logs cache hits and cache invalidation only
16
+ * - `verbose`: Logs all events including cache misses, cleanup, and header usage
17
+ */
18
+ export type LoggingLevel = "none" | "minimal" | "verbose";
19
+
12
20
  export interface HandleRequestConfig {
13
21
  /** Name of the cache, used when calling `Cache.open(cacheName)` internally. Changing this name effectively clears the previous cache entries. */
14
22
  cacheName: string;
@@ -26,6 +34,6 @@ export interface HandleRequestConfig {
26
34
  customFetch?: typeof fetch;
27
35
  /** Maximum age (in seconds) before cache entries are automatically cleaned up. Entries older than this age are deleted. Defaults to 7200 seconds (2 hours, which is 2x the default stale TTL). Cache entries are cleaned up reactively (when accessed) and periodically (every 100 fetches). */
28
36
  maxCacheAgeSeconds?: number;
29
- /** Enable logging for cache hits, misses, header usage, invalidation, and cleanup. Defaults to false. */
30
- enableLogging?: boolean;
37
+ /** Logging level: "none" (no logging), "minimal" (cache hits and invalidation only), or "verbose" (all logging including misses, cleanup, and headers). Defaults to "none". */
38
+ loggingLevel?: LoggingLevel;
31
39
  }