@b9g/cache 0.1.5 → 0.2.0-beta.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 +260 -136
- package/package.json +2 -5
- package/src/index.d.ts +10 -9
- package/src/index.js +19 -16
- package/src/memory.d.ts +2 -10
- package/src/memory.js +46 -41
- package/src/postmessage.d.ts +7 -2
- package/src/postmessage.js +80 -46
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **ServiceWorker Cache API**: Standard `
|
|
8
|
-
- **Multiple Backends**: Memory
|
|
7
|
+
- **ServiceWorker Cache API**: Standard `Cache` and `CacheStorage` interfaces from ServiceWorker spec
|
|
8
|
+
- **Multiple Backends**: Memory cache with LRU eviction, PostMessage coordination for workers
|
|
9
9
|
- **Universal**: Same API works in browsers, Node.js, Bun, and edge platforms
|
|
10
10
|
- **Request/Response Caching**: Full HTTP semantics with Request/Response objects
|
|
11
|
-
- **
|
|
11
|
+
- **Factory Pattern**: Flexible cache creation with factory functions
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -18,104 +18,208 @@ npm install @b9g/cache
|
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
### Using with Shovel (Recommended)
|
|
22
|
+
|
|
23
|
+
Configure cache providers in via the `shovel` key in package.json or `shovel.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"caches": {
|
|
28
|
+
"pages": {"provider": "memory"},
|
|
29
|
+
"api": {"provider": "memory", "maxEntries": 5000}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
const caches = new CacheStorage();
|
|
34
|
+
Shovel provides `self.caches` as a global following the ServiceWorker CacheStorage API. Access it directly in your handlers and middleware:
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
```typescript
|
|
37
|
+
import {Router} from '@b9g/router';
|
|
38
|
+
|
|
39
|
+
const router = new Router();
|
|
40
|
+
|
|
41
|
+
// Cache middleware using generator API
|
|
42
|
+
router.use(async function* (request, _context) {
|
|
43
|
+
if (request.method !== 'GET' || !self.caches) {
|
|
44
|
+
return yield request; // Skip caching
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Open cache
|
|
48
|
+
const cache = await self.caches.open('pages-v1');
|
|
49
|
+
|
|
50
|
+
// Check cache
|
|
51
|
+
const cached = await cache.match(request);
|
|
52
|
+
if (cached) {
|
|
53
|
+
return cached; // Cache hit
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Cache miss - get response from handler
|
|
57
|
+
const response = yield request;
|
|
58
|
+
|
|
59
|
+
// Store in cache
|
|
60
|
+
if (response.ok) {
|
|
61
|
+
await cache.put(request, response.clone());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return response;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
router.route('/posts/:id')
|
|
68
|
+
.get(async (request, context) => {
|
|
69
|
+
const post = await getPost(context.params.id);
|
|
70
|
+
return Response.json(post, {
|
|
71
|
+
headers: {'Cache-Control': 'max-age=300'},
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Standalone Usage
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
import {CustomCacheStorage} from '@b9g/cache';
|
|
80
|
+
import {MemoryCache} from '@b9g/cache/memory';
|
|
81
|
+
|
|
82
|
+
// Create cache storage with factory
|
|
83
|
+
const caches = new CustomCacheStorage((name) => {
|
|
84
|
+
return new MemoryCache(name, {maxEntries: 1000});
|
|
85
|
+
});
|
|
30
86
|
|
|
31
87
|
// Open and use caches
|
|
32
88
|
const apiCache = await caches.open('api');
|
|
33
89
|
|
|
34
90
|
// Store response
|
|
35
91
|
const request = new Request('https://api.example.com/posts/1');
|
|
36
|
-
const response = new Response(JSON.stringify({
|
|
92
|
+
const response = new Response(JSON.stringify({id: 1, title: 'Hello'}), {
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
'Cache-Control': 'max-age=300',
|
|
96
|
+
}
|
|
97
|
+
});
|
|
37
98
|
await apiCache.put(request, response);
|
|
38
99
|
|
|
39
100
|
// Retrieve response
|
|
40
101
|
const cached = await apiCache.match(request);
|
|
41
|
-
console.log(await cached.json()); // {
|
|
102
|
+
console.log(await cached.json()); // {id: 1, title: 'Hello'}
|
|
42
103
|
```
|
|
43
104
|
|
|
44
|
-
## Cache
|
|
105
|
+
## Cache Providers
|
|
45
106
|
|
|
46
|
-
|
|
107
|
+
Shovel supports multiple cache providers that can be configured in `shovel.json`:
|
|
47
108
|
|
|
48
|
-
|
|
109
|
+
### Built-in Providers
|
|
49
110
|
|
|
50
|
-
|
|
51
|
-
|
|
111
|
+
- **`memory`** - In-memory cache with LRU eviction (default)
|
|
112
|
+
- **`redis`** - Redis-backed cache (requires `@b9g/cache-redis`)
|
|
113
|
+
- **`cloudflare`** - Uses Cloudflare Workers native Cache API (only works with the Cloudflare platform)
|
|
52
114
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
115
|
+
You can also use custom providers by specifying a module path:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"caches": {
|
|
120
|
+
"pages": {"provider": "memory"},
|
|
121
|
+
"sessions": {"provider": "redis", "url": "REDIS_URL"},
|
|
122
|
+
"custom": {"provider": "./my-cache-provider.js"}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
58
125
|
```
|
|
59
126
|
|
|
60
|
-
|
|
127
|
+
Pattern matching is supported for cache names:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"caches": {
|
|
132
|
+
"api-*": {"provider": "memory", "maxEntries": 5000},
|
|
133
|
+
"page-*": {"provider": "memory", "maxEntries": 100}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
61
137
|
|
|
62
|
-
|
|
138
|
+
## Cache Implementations
|
|
139
|
+
|
|
140
|
+
### MemoryCache
|
|
141
|
+
|
|
142
|
+
In-memory cache with LRU eviction and HTTP Cache-Control header support:
|
|
63
143
|
|
|
64
144
|
```javascript
|
|
65
|
-
import {
|
|
145
|
+
import {MemoryCache} from '@b9g/cache/memory';
|
|
66
146
|
|
|
67
|
-
const cache = new
|
|
68
|
-
|
|
69
|
-
compression: true,
|
|
70
|
-
indexing: true
|
|
147
|
+
const cache = new MemoryCache(name, {
|
|
148
|
+
maxEntries: 1000, // Maximum number of entries (LRU eviction)
|
|
71
149
|
});
|
|
150
|
+
|
|
151
|
+
// Cache respects Cache-Control headers
|
|
152
|
+
await cache.put(request, new Response(data, {
|
|
153
|
+
headers: {'Cache-Control': 'max-age=300'},
|
|
154
|
+
}));
|
|
155
|
+
|
|
156
|
+
// After 300 seconds, match() returns undefined
|
|
72
157
|
```
|
|
73
158
|
|
|
74
|
-
|
|
159
|
+
### PostMessageCache
|
|
160
|
+
|
|
161
|
+
Worker-side cache that coordinates with main thread via PostMessage:
|
|
75
162
|
|
|
76
163
|
```javascript
|
|
77
|
-
import {
|
|
164
|
+
import {PostMessageCache} from '@b9g/cache/postmessage';
|
|
78
165
|
|
|
79
|
-
|
|
166
|
+
// In worker thread - forwards operations to main thread
|
|
167
|
+
const cache = new PostMessageCache({
|
|
168
|
+
name: 'shared',
|
|
169
|
+
timeout: 30000, // Optional, defaults to 30000ms
|
|
170
|
+
});
|
|
80
171
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
172
|
+
// Operations are synchronized with main thread's MemoryCache
|
|
173
|
+
await cache.put(request, response);
|
|
174
|
+
```
|
|
85
175
|
|
|
86
|
-
|
|
87
|
-
new FilesystemCache('pages', { directory: './dist/pages' })
|
|
88
|
-
);
|
|
176
|
+
## CustomCacheStorage
|
|
89
177
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
178
|
+
Create cache storage with a factory function:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
import {CustomCacheStorage} from '@b9g/cache';
|
|
182
|
+
import {MemoryCache} from '@b9g/cache/memory';
|
|
93
183
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
184
|
+
const caches = new CustomCacheStorage((name) => {
|
|
185
|
+
// Different caches can have different configurations
|
|
186
|
+
if (name === 'api') {
|
|
187
|
+
return new MemoryCache(name, {maxEntries: 5000});
|
|
188
|
+
}
|
|
189
|
+
if (name === 'pages') {
|
|
190
|
+
return new MemoryCache(name, {maxEntries: 100});
|
|
191
|
+
}
|
|
192
|
+
return new MemoryCache();
|
|
193
|
+
});
|
|
97
194
|
```
|
|
98
195
|
|
|
99
196
|
## Exports
|
|
100
197
|
|
|
101
|
-
###
|
|
198
|
+
### Main (`@b9g/cache`)
|
|
102
199
|
|
|
103
|
-
- `Cache` - Abstract base class
|
|
104
|
-
- `CustomCacheStorage` - CacheStorage implementation with factory
|
|
200
|
+
- `Cache` - Abstract base class implementing `globalThis.Cache`
|
|
201
|
+
- `CustomCacheStorage` - CacheStorage implementation with factory pattern
|
|
202
|
+
- `generateCacheKey(request, options?)` - Generate cache key from Request
|
|
203
|
+
- `toRequest(request)` - Convert RequestInfo or URL to Request
|
|
204
|
+
- `CacheQueryOptions` - Type for cache query options
|
|
105
205
|
|
|
106
|
-
###
|
|
206
|
+
### Memory (`@b9g/cache/memory`)
|
|
107
207
|
|
|
108
|
-
- `
|
|
208
|
+
- `MemoryCache` - In-memory cache with LRU and Cache-Control support
|
|
209
|
+
- `MemoryCacheOptions` - Configuration type
|
|
109
210
|
|
|
110
|
-
###
|
|
211
|
+
### PostMessage (`@b9g/cache/postmessage`)
|
|
111
212
|
|
|
112
|
-
- `
|
|
113
|
-
- `
|
|
213
|
+
- `PostMessageCache` - Worker-side cache with main thread coordination
|
|
214
|
+
- `PostMessageCacheOptions` - Configuration type
|
|
215
|
+
- `handleCacheResponse(message)` - Message handler for worker coordination
|
|
114
216
|
|
|
115
217
|
## API Reference
|
|
116
218
|
|
|
117
219
|
### Standard Cache Methods
|
|
118
220
|
|
|
221
|
+
All cache implementations provide the standard Cache API:
|
|
222
|
+
|
|
119
223
|
```javascript
|
|
120
224
|
// Check for cached response
|
|
121
225
|
const response = await cache.match(request, options?);
|
|
@@ -140,10 +244,7 @@ const requests = await cache.keys(request?, options?);
|
|
|
140
244
|
### CacheStorage Methods
|
|
141
245
|
|
|
142
246
|
```javascript
|
|
143
|
-
//
|
|
144
|
-
caches.register(name, factory);
|
|
145
|
-
|
|
146
|
-
// Open named cache
|
|
247
|
+
// Open named cache (creates if doesn't exist)
|
|
147
248
|
const cache = await caches.open(name);
|
|
148
249
|
|
|
149
250
|
// Check if cache exists
|
|
@@ -154,6 +255,12 @@ const deleted = await caches.delete(name);
|
|
|
154
255
|
|
|
155
256
|
// List cache names
|
|
156
257
|
const names = await caches.keys();
|
|
258
|
+
|
|
259
|
+
// Match across all caches
|
|
260
|
+
const response = await caches.match(request, options?);
|
|
261
|
+
|
|
262
|
+
// Cleanup (disposes all caches)
|
|
263
|
+
await caches.dispose();
|
|
157
264
|
```
|
|
158
265
|
|
|
159
266
|
## Cache Options
|
|
@@ -162,29 +269,52 @@ const names = await caches.keys();
|
|
|
162
269
|
|
|
163
270
|
```javascript
|
|
164
271
|
const response = await cache.match(request, {
|
|
165
|
-
ignoreSearch: true, // Ignore query parameters
|
|
272
|
+
ignoreSearch: true, // Ignore query parameters in URL
|
|
166
273
|
ignoreMethod: false, // Consider HTTP method
|
|
167
|
-
ignoreVary: false, // Honor Vary header
|
|
168
|
-
cacheName: 'specific' // Target specific cache
|
|
274
|
+
ignoreVary: false, // Honor Vary header (default behavior)
|
|
169
275
|
});
|
|
170
276
|
```
|
|
171
277
|
|
|
172
|
-
|
|
278
|
+
**Vary Header Support:**
|
|
279
|
+
|
|
280
|
+
The cache respects the HTTP `Vary` header by default:
|
|
173
281
|
|
|
174
282
|
```javascript
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
283
|
+
// Cache a response that varies on Accept-Encoding
|
|
284
|
+
await cache.put(
|
|
285
|
+
new Request('https://api.example.com/data', {
|
|
286
|
+
headers: {'Accept-Encoding': 'gzip'},
|
|
287
|
+
}),
|
|
288
|
+
new Response(gzippedData, {
|
|
289
|
+
headers: {'Vary': 'Accept-Encoding'},
|
|
290
|
+
})
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Same URL with same Accept-Encoding: matches
|
|
294
|
+
await cache.match(new Request('https://api.example.com/data', {
|
|
295
|
+
headers: {'Accept-Encoding': 'gzip'},
|
|
296
|
+
})); // ✓ Returns cached response
|
|
297
|
+
|
|
298
|
+
// Same URL with different Accept-Encoding: no match
|
|
299
|
+
await cache.match(new Request('https://api.example.com/data', {
|
|
300
|
+
headers: {'Accept-Encoding': 'br'},
|
|
301
|
+
})); // ✗ Returns undefined
|
|
302
|
+
|
|
303
|
+
// Use ignoreVary to bypass Vary header checking
|
|
304
|
+
await cache.match(new Request('https://api.example.com/data', {
|
|
305
|
+
headers: {'Accept-Encoding': 'br'},
|
|
306
|
+
}), {ignoreVary: true}); // ✓ Returns cached response
|
|
307
|
+
```
|
|
181
308
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
309
|
+
Special cases:
|
|
310
|
+
- `Vary: *` means the response varies on everything and will never match (unless `ignoreVary: true`)
|
|
311
|
+
- Multiple headers: `Vary: Accept-Encoding, User-Agent` requires all specified headers to match
|
|
312
|
+
|
|
313
|
+
### MemoryCache Options
|
|
314
|
+
|
|
315
|
+
```javascript
|
|
316
|
+
new MemoryCache(name, {
|
|
317
|
+
maxEntries: 1000 // Maximum entries (LRU eviction when exceeded)
|
|
188
318
|
});
|
|
189
319
|
```
|
|
190
320
|
|
|
@@ -193,99 +323,93 @@ new FilesystemCache('name', {
|
|
|
193
323
|
### With Router
|
|
194
324
|
|
|
195
325
|
```javascript
|
|
196
|
-
import {
|
|
197
|
-
import {
|
|
326
|
+
import {Router} from '@b9g/router';
|
|
327
|
+
import {CustomCacheStorage} from '@b9g/cache';
|
|
328
|
+
import {MemoryCache} from '@b9g/cache/memory';
|
|
198
329
|
|
|
199
|
-
const caches = new
|
|
200
|
-
|
|
330
|
+
const caches = new CustomCacheStorage((name) =>
|
|
331
|
+
new MemoryCache(name, {maxEntries: 1000})
|
|
332
|
+
);
|
|
201
333
|
|
|
202
|
-
const router = new Router(
|
|
334
|
+
const router = new Router();
|
|
203
335
|
|
|
204
336
|
// Cache-aware middleware
|
|
205
|
-
router.use(async function* (request,
|
|
206
|
-
if (request.method
|
|
207
|
-
|
|
208
|
-
if (cached) return cached;
|
|
337
|
+
router.use(async function* (request, _context) {
|
|
338
|
+
if (request.method !== 'GET') {
|
|
339
|
+
return yield request;
|
|
209
340
|
}
|
|
210
|
-
|
|
341
|
+
|
|
342
|
+
const cache = await caches.open('api');
|
|
343
|
+
const cached = await cache.match(request);
|
|
344
|
+
if (cached) return cached;
|
|
345
|
+
|
|
211
346
|
const response = yield request;
|
|
212
|
-
|
|
213
|
-
if (
|
|
214
|
-
await
|
|
347
|
+
|
|
348
|
+
if (response.ok) {
|
|
349
|
+
await cache.put(request, response.clone());
|
|
215
350
|
}
|
|
216
|
-
|
|
351
|
+
|
|
217
352
|
return response;
|
|
218
353
|
});
|
|
219
354
|
|
|
220
|
-
router.route('/api/posts/:id'
|
|
355
|
+
router.route('/api/posts/:id')
|
|
221
356
|
.get(postHandler);
|
|
222
357
|
```
|
|
223
358
|
|
|
224
|
-
###
|
|
359
|
+
### Multi-Worker Setup
|
|
225
360
|
|
|
226
361
|
```javascript
|
|
227
|
-
|
|
362
|
+
// Main thread
|
|
363
|
+
import {CustomCacheStorage} from '@b9g/cache';
|
|
364
|
+
import {MemoryCache} from '@b9g/cache/memory';
|
|
228
365
|
|
|
229
|
-
const
|
|
230
|
-
|
|
366
|
+
const caches = new CustomCacheStorage((name) =>
|
|
367
|
+
new MemoryCache()
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
worker.on('message', (message) => {
|
|
371
|
+
if (message.type?.startsWith('cache:')) {
|
|
372
|
+
caches.handleMessage(worker, message);
|
|
373
|
+
}
|
|
231
374
|
});
|
|
232
375
|
|
|
233
|
-
//
|
|
234
|
-
|
|
376
|
+
// Worker thread
|
|
377
|
+
import {PostMessageCache} from '@b9g/cache/postmessage';
|
|
378
|
+
import {handleCacheResponse} from '@b9g/cache/postmessage';
|
|
235
379
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
380
|
+
const cache = new PostMessageCache('shared');
|
|
381
|
+
|
|
382
|
+
self.addEventListener('message', (event) => {
|
|
383
|
+
if (event.data.type === 'cache:response' || event.data.type === 'cache:error') {
|
|
384
|
+
handleCacheResponse(event.data);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
240
387
|
|
|
241
|
-
//
|
|
242
|
-
|
|
388
|
+
// Operations coordinate with main thread
|
|
389
|
+
await cache.put(request, response);
|
|
243
390
|
```
|
|
244
391
|
|
|
245
|
-
###
|
|
392
|
+
### HTTP Caching Semantics
|
|
246
393
|
|
|
247
394
|
```javascript
|
|
248
|
-
|
|
249
|
-
import { CacheStorage } from '@b9g/cache';
|
|
250
|
-
|
|
251
|
-
// Use native browser caches when available
|
|
252
|
-
const caches = new CacheStorage();
|
|
253
|
-
|
|
254
|
-
self.addEventListener('fetch', async event => {
|
|
255
|
-
const cache = await caches.open('runtime');
|
|
256
|
-
|
|
257
|
-
event.respondWith(
|
|
258
|
-
cache.match(event.request).then(response => {
|
|
259
|
-
if (response) return response;
|
|
260
|
-
|
|
261
|
-
return fetch(event.request).then(response => {
|
|
262
|
-
if (response.ok) {
|
|
263
|
-
cache.put(event.request, response.clone());
|
|
264
|
-
}
|
|
265
|
-
return response;
|
|
266
|
-
});
|
|
267
|
-
})
|
|
268
|
-
);
|
|
269
|
-
});
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Cache Coordination
|
|
395
|
+
import {MemoryCache} from '@b9g/cache/memory';
|
|
273
396
|
|
|
274
|
-
|
|
397
|
+
const cache = new MemoryCache();
|
|
275
398
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
channel: 'cache-coordination'
|
|
399
|
+
// Respect Cache-Control headers
|
|
400
|
+
const response = new Response(data, {
|
|
401
|
+
headers: {
|
|
402
|
+
'Cache-Control': 'max-age=3600', // Cache for 1 hour
|
|
403
|
+
'Vary': 'Accept-Encoding',
|
|
282
404
|
}
|
|
283
405
|
});
|
|
284
406
|
|
|
285
|
-
|
|
286
|
-
|
|
407
|
+
await cache.put(request, response);
|
|
408
|
+
|
|
409
|
+
// After 3600 seconds, entry expires automatically
|
|
410
|
+
const cached = await cache.match(request); // undefined after expiry
|
|
287
411
|
```
|
|
288
412
|
|
|
289
413
|
## License
|
|
290
414
|
|
|
291
|
-
MIT
|
|
415
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/cache",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-beta.0",
|
|
4
4
|
"description": "Universal Cache API for ServiceWorker applications. Provides standard CacheStorage and Cache interfaces across all JavaScript runtimes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -9,14 +9,11 @@
|
|
|
9
9
|
"cachestorage",
|
|
10
10
|
"web-standards",
|
|
11
11
|
"universal",
|
|
12
|
-
"memory",
|
|
13
|
-
"filesystem",
|
|
14
12
|
"shovel"
|
|
15
13
|
],
|
|
16
14
|
"dependencies": {},
|
|
17
15
|
"devDependencies": {
|
|
18
|
-
"@b9g/libuild": "^0.1.18"
|
|
19
|
-
"bun-types": "latest"
|
|
16
|
+
"@b9g/libuild": "^0.1.18"
|
|
20
17
|
},
|
|
21
18
|
"type": "module",
|
|
22
19
|
"types": "src/index.d.ts",
|
package/src/index.d.ts
CHANGED
|
@@ -25,8 +25,11 @@ export declare function toRequest(request: RequestInfo | URL): Request;
|
|
|
25
25
|
* Abstract Cache class implementing the Cache API interface
|
|
26
26
|
* Provides shared implementations for add() and addAll() while requiring
|
|
27
27
|
* concrete implementations to handle the core storage operations
|
|
28
|
+
*
|
|
29
|
+
* All cache implementations must follow the constructor signature:
|
|
30
|
+
* constructor(name: string, options?: CacheOptions)
|
|
28
31
|
*/
|
|
29
|
-
export declare abstract class Cache {
|
|
32
|
+
export declare abstract class Cache implements globalThis.Cache {
|
|
30
33
|
/**
|
|
31
34
|
* Returns a Promise that resolves to the response associated with the first matching request
|
|
32
35
|
*/
|
|
@@ -64,6 +67,11 @@ export declare abstract class Cache {
|
|
|
64
67
|
* Normalizes the request for consistent cache key generation
|
|
65
68
|
*/
|
|
66
69
|
export declare function generateCacheKey(request: RequestInfo | URL, options?: CacheQueryOptions): string;
|
|
70
|
+
/**
|
|
71
|
+
* Constructor type for Cache implementations
|
|
72
|
+
* All cache classes must accept name as first parameter and optional options as second
|
|
73
|
+
*/
|
|
74
|
+
export type CacheConstructor<T extends Cache = Cache, O = any> = new (name: string, options?: O) => T;
|
|
67
75
|
/**
|
|
68
76
|
* Factory function for creating Cache instances based on cache name
|
|
69
77
|
*/
|
|
@@ -72,7 +80,7 @@ export type CacheFactory = (name: string) => Cache | Promise<Cache>;
|
|
|
72
80
|
* CustomCacheStorage implements CacheStorage interface with a configurable factory
|
|
73
81
|
* The factory function receives the cache name and can return different cache types
|
|
74
82
|
*/
|
|
75
|
-
export declare class CustomCacheStorage {
|
|
83
|
+
export declare class CustomCacheStorage implements CacheStorage {
|
|
76
84
|
#private;
|
|
77
85
|
constructor(factory: CacheFactory);
|
|
78
86
|
/**
|
|
@@ -96,13 +104,6 @@ export declare class CustomCacheStorage {
|
|
|
96
104
|
* Returns a list of all opened cache names
|
|
97
105
|
*/
|
|
98
106
|
keys(): Promise<string[]>;
|
|
99
|
-
/**
|
|
100
|
-
* Get statistics about the cache storage
|
|
101
|
-
*/
|
|
102
|
-
getStats(): {
|
|
103
|
-
openInstances: number;
|
|
104
|
-
cacheNames: string[];
|
|
105
|
-
};
|
|
106
107
|
/**
|
|
107
108
|
* Dispose of all cache instances
|
|
108
109
|
* Calls dispose() on each cache if it exists (e.g., RedisCache needs to close connections)
|
package/src/index.js
CHANGED
|
@@ -110,15 +110,6 @@ var CustomCacheStorage = class {
|
|
|
110
110
|
async keys() {
|
|
111
111
|
return Array.from(this.#instances.keys());
|
|
112
112
|
}
|
|
113
|
-
/**
|
|
114
|
-
* Get statistics about the cache storage
|
|
115
|
-
*/
|
|
116
|
-
getStats() {
|
|
117
|
-
return {
|
|
118
|
-
openInstances: this.#instances.size,
|
|
119
|
-
cacheNames: Array.from(this.#instances.keys())
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
113
|
/**
|
|
123
114
|
* Dispose of all cache instances
|
|
124
115
|
* Calls dispose() on each cache if it exists (e.g., RedisCache needs to close connections)
|
|
@@ -141,16 +132,23 @@ var CustomCacheStorage = class {
|
|
|
141
132
|
try {
|
|
142
133
|
const cache = await this.open(cacheName);
|
|
143
134
|
let result;
|
|
135
|
+
const transfer = [];
|
|
144
136
|
switch (type) {
|
|
145
137
|
case "cache:match": {
|
|
146
138
|
const req = new Request(message.request.url, message.request);
|
|
147
139
|
const response = await cache.match(req, message.options);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
140
|
+
if (response) {
|
|
141
|
+
const body = await response.arrayBuffer();
|
|
142
|
+
transfer.push(body);
|
|
143
|
+
result = {
|
|
144
|
+
status: response.status,
|
|
145
|
+
statusText: response.statusText,
|
|
146
|
+
headers: Object.fromEntries(response.headers),
|
|
147
|
+
body
|
|
148
|
+
};
|
|
149
|
+
} else {
|
|
150
|
+
result = void 0;
|
|
151
|
+
}
|
|
154
152
|
break;
|
|
155
153
|
}
|
|
156
154
|
case "cache:put": {
|
|
@@ -180,7 +178,12 @@ var CustomCacheStorage = class {
|
|
|
180
178
|
result = true;
|
|
181
179
|
break;
|
|
182
180
|
}
|
|
183
|
-
|
|
181
|
+
const responseMessage = { type: "cache:response", requestID, result };
|
|
182
|
+
if (transfer.length > 0) {
|
|
183
|
+
worker.postMessage(responseMessage, transfer);
|
|
184
|
+
} else {
|
|
185
|
+
worker.postMessage(responseMessage);
|
|
186
|
+
}
|
|
184
187
|
} catch (error) {
|
|
185
188
|
worker.postMessage({
|
|
186
189
|
type: "cache:error",
|
package/src/memory.d.ts
CHANGED
|
@@ -9,10 +9,11 @@ export interface MemoryCacheOptions {
|
|
|
9
9
|
/**
|
|
10
10
|
* In-memory cache implementation using Map for storage
|
|
11
11
|
* Supports LRU eviction and TTL expiration
|
|
12
|
+
* Uses Map's insertion order for LRU tracking
|
|
12
13
|
*/
|
|
13
14
|
export declare class MemoryCache extends Cache {
|
|
14
15
|
#private;
|
|
15
|
-
constructor(
|
|
16
|
+
constructor(_name: string, options?: MemoryCacheOptions);
|
|
16
17
|
/**
|
|
17
18
|
* Find a cached response for the given request
|
|
18
19
|
*/
|
|
@@ -33,13 +34,4 @@ export declare class MemoryCache extends Cache {
|
|
|
33
34
|
* Clear all entries from the cache
|
|
34
35
|
*/
|
|
35
36
|
clear(): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* Get cache statistics
|
|
38
|
-
*/
|
|
39
|
-
getStats(): {
|
|
40
|
-
name: string;
|
|
41
|
-
size: number;
|
|
42
|
-
maxEntries: number;
|
|
43
|
-
hitRate: number;
|
|
44
|
-
};
|
|
45
37
|
}
|
package/src/memory.js
CHANGED
|
@@ -7,33 +7,31 @@ import {
|
|
|
7
7
|
} from "./index.js";
|
|
8
8
|
var MemoryCache = class extends Cache {
|
|
9
9
|
#storage;
|
|
10
|
-
#accessOrder;
|
|
11
|
-
#accessCounter;
|
|
12
|
-
#name;
|
|
13
10
|
#options;
|
|
14
|
-
constructor(
|
|
11
|
+
constructor(_name, options = {}) {
|
|
15
12
|
super();
|
|
16
13
|
this.#storage = /* @__PURE__ */ new Map();
|
|
17
|
-
this.#accessOrder = /* @__PURE__ */ new Map();
|
|
18
|
-
this.#accessCounter = 0;
|
|
19
|
-
this.#name = name;
|
|
20
14
|
this.#options = options;
|
|
21
15
|
}
|
|
22
16
|
/**
|
|
23
17
|
* Find a cached response for the given request
|
|
24
18
|
*/
|
|
25
19
|
async match(request, options) {
|
|
20
|
+
const req = toRequest(request);
|
|
26
21
|
if (options?.ignoreSearch) {
|
|
27
22
|
const filterKey = generateCacheKey(request, options);
|
|
28
23
|
for (const [key2, entry2] of this.#storage) {
|
|
29
24
|
if (this.#isExpired(entry2)) {
|
|
30
25
|
this.#storage.delete(key2);
|
|
31
|
-
this.#accessOrder.delete(key2);
|
|
32
26
|
continue;
|
|
33
27
|
}
|
|
34
28
|
const entryKey = generateCacheKey(entry2.request, options);
|
|
35
29
|
if (entryKey === filterKey) {
|
|
36
|
-
this.#
|
|
30
|
+
if (!options?.ignoreVary && !this.#matchesVary(req, entry2)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
this.#storage.delete(key2);
|
|
34
|
+
this.#storage.set(key2, entry2);
|
|
37
35
|
return entry2.response.clone();
|
|
38
36
|
}
|
|
39
37
|
}
|
|
@@ -46,10 +44,13 @@ var MemoryCache = class extends Cache {
|
|
|
46
44
|
}
|
|
47
45
|
if (this.#isExpired(entry)) {
|
|
48
46
|
this.#storage.delete(key);
|
|
49
|
-
this.#accessOrder.delete(key);
|
|
50
47
|
return void 0;
|
|
51
48
|
}
|
|
52
|
-
this.#
|
|
49
|
+
if (!options?.ignoreVary && !this.#matchesVary(req, entry)) {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
this.#storage.delete(key);
|
|
53
|
+
this.#storage.set(key, entry);
|
|
53
54
|
return entry.response.clone();
|
|
54
55
|
}
|
|
55
56
|
/**
|
|
@@ -68,8 +69,10 @@ var MemoryCache = class extends Cache {
|
|
|
68
69
|
response: clonedResponse,
|
|
69
70
|
timestamp: Date.now()
|
|
70
71
|
};
|
|
72
|
+
if (this.#storage.has(key)) {
|
|
73
|
+
this.#storage.delete(key);
|
|
74
|
+
}
|
|
71
75
|
this.#storage.set(key, entry);
|
|
72
|
-
this.#accessOrder.set(key, ++this.#accessCounter);
|
|
73
76
|
this.#enforceMaxEntries();
|
|
74
77
|
}
|
|
75
78
|
/**
|
|
@@ -78,23 +81,18 @@ var MemoryCache = class extends Cache {
|
|
|
78
81
|
async delete(request, options) {
|
|
79
82
|
if (options?.ignoreSearch) {
|
|
80
83
|
const filterKey = generateCacheKey(request, options);
|
|
81
|
-
let
|
|
84
|
+
let deleted = false;
|
|
82
85
|
for (const [key2, entry] of this.#storage) {
|
|
83
86
|
const entryKey = generateCacheKey(entry.request, options);
|
|
84
87
|
if (entryKey === filterKey) {
|
|
85
88
|
this.#storage.delete(key2);
|
|
86
|
-
|
|
87
|
-
deleted2 = true;
|
|
89
|
+
deleted = true;
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
|
-
return
|
|
92
|
+
return deleted;
|
|
91
93
|
}
|
|
92
94
|
const key = generateCacheKey(request, options);
|
|
93
|
-
|
|
94
|
-
if (deleted) {
|
|
95
|
-
this.#accessOrder.delete(key);
|
|
96
|
-
}
|
|
97
|
-
return deleted;
|
|
95
|
+
return this.#storage.delete(key);
|
|
98
96
|
}
|
|
99
97
|
/**
|
|
100
98
|
* Get all stored requests, optionally filtered by a request pattern
|
|
@@ -122,20 +120,6 @@ var MemoryCache = class extends Cache {
|
|
|
122
120
|
*/
|
|
123
121
|
async clear() {
|
|
124
122
|
this.#storage.clear();
|
|
125
|
-
this.#accessOrder.clear();
|
|
126
|
-
this.#accessCounter = 0;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Get cache statistics
|
|
130
|
-
*/
|
|
131
|
-
getStats() {
|
|
132
|
-
return {
|
|
133
|
-
name: this.#name,
|
|
134
|
-
size: this.#storage.size,
|
|
135
|
-
maxEntries: this.#options.maxEntries,
|
|
136
|
-
hitRate: 0
|
|
137
|
-
// Could be implemented with additional tracking
|
|
138
|
-
};
|
|
139
123
|
}
|
|
140
124
|
/**
|
|
141
125
|
* Check if a cache entry has expired based on Cache-Control header
|
|
@@ -152,21 +136,42 @@ var MemoryCache = class extends Cache {
|
|
|
152
136
|
const maxAge = parseInt(maxAgeMatch[1], 10) * 1e3;
|
|
153
137
|
return Date.now() - entry.timestamp > maxAge;
|
|
154
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if a request matches the Vary header of a cached entry
|
|
141
|
+
* Returns true if the request matches or if there's no Vary header
|
|
142
|
+
*/
|
|
143
|
+
#matchesVary(request, entry) {
|
|
144
|
+
const varyHeader = entry.response.headers.get("vary");
|
|
145
|
+
if (!varyHeader) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (varyHeader === "*") {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const varyHeaders = varyHeader.split(",").map((h) => h.trim().toLowerCase());
|
|
152
|
+
for (const headerName of varyHeaders) {
|
|
153
|
+
const requestValue = request.headers.get(headerName);
|
|
154
|
+
const cachedValue = entry.request.headers.get(headerName);
|
|
155
|
+
if (requestValue !== cachedValue) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
155
161
|
/**
|
|
156
162
|
* Enforce maximum entry limits using LRU eviction
|
|
163
|
+
* Removes oldest entries (first in Map iteration order)
|
|
157
164
|
*/
|
|
158
165
|
#enforceMaxEntries() {
|
|
159
166
|
if (!this.#options.maxEntries || this.#storage.size <= this.#options.maxEntries) {
|
|
160
167
|
return;
|
|
161
168
|
}
|
|
162
|
-
const entries = Array.from(this.#accessOrder.entries()).sort(
|
|
163
|
-
(a, b) => a[1] - b[1]
|
|
164
|
-
);
|
|
165
169
|
const toRemove = this.#storage.size - this.#options.maxEntries;
|
|
166
|
-
|
|
167
|
-
|
|
170
|
+
let removed = 0;
|
|
171
|
+
for (const key of this.#storage.keys()) {
|
|
172
|
+
if (removed >= toRemove) break;
|
|
168
173
|
this.#storage.delete(key);
|
|
169
|
-
|
|
174
|
+
removed++;
|
|
170
175
|
}
|
|
171
176
|
}
|
|
172
177
|
};
|
package/src/postmessage.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { Cache, type CacheQueryOptions } from "./index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle cache response/error messages from main thread.
|
|
4
|
+
* Called by worker.ts when receiving cache:response or cache:error messages.
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleCacheResponse(message: any): void;
|
|
2
7
|
/**
|
|
3
8
|
* Configuration options for PostMessageCache
|
|
4
9
|
*/
|
|
@@ -7,8 +12,8 @@ export interface PostMessageCacheOptions {
|
|
|
7
12
|
timeout?: number;
|
|
8
13
|
}
|
|
9
14
|
/**
|
|
10
|
-
* Worker-side cache that forwards operations to main thread via postMessage
|
|
11
|
-
*
|
|
15
|
+
* Worker-side cache that forwards operations to main thread via postMessage.
|
|
16
|
+
* Used for MemoryCache in multi-worker environments so all workers share state.
|
|
12
17
|
*/
|
|
13
18
|
export declare class PostMessageCache extends Cache {
|
|
14
19
|
#private;
|
package/src/postmessage.js
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
/// <reference types="./postmessage.d.ts" />
|
|
2
2
|
// src/postmessage.ts
|
|
3
3
|
import { Cache } from "./index.js";
|
|
4
|
-
function getParentPort() {
|
|
5
|
-
return typeof self !== "undefined" ? self : null;
|
|
6
|
-
}
|
|
7
|
-
var messageHandlerSetup = false;
|
|
8
4
|
var pendingRequestsRegistry = /* @__PURE__ */ new Map();
|
|
9
|
-
function setupMessageHandler() {
|
|
10
|
-
if (messageHandlerSetup)
|
|
11
|
-
return;
|
|
12
|
-
messageHandlerSetup = true;
|
|
13
|
-
const parentPort = getParentPort();
|
|
14
|
-
if (parentPort && parentPort.addEventListener) {
|
|
15
|
-
parentPort.addEventListener("message", (event) => {
|
|
16
|
-
const message = event.data;
|
|
17
|
-
if (message.type === "cache:response" || message.type === "cache:error") {
|
|
18
|
-
handleCacheResponse(message);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
5
|
function handleCacheResponse(message) {
|
|
24
6
|
const pending = pendingRequestsRegistry.get(message.requestID);
|
|
25
7
|
if (pending) {
|
|
@@ -39,22 +21,30 @@ var PostMessageCache = class extends Cache {
|
|
|
39
21
|
super();
|
|
40
22
|
this.#name = name;
|
|
41
23
|
this.#timeout = options.timeout ?? 3e4;
|
|
42
|
-
setupMessageHandler();
|
|
43
24
|
}
|
|
44
|
-
async #sendRequest(type, data) {
|
|
45
|
-
|
|
46
|
-
if (!parentPort) {
|
|
25
|
+
async #sendRequest(type, data, transfer) {
|
|
26
|
+
if (typeof self === "undefined") {
|
|
47
27
|
throw new Error("PostMessageCache can only be used in worker threads");
|
|
48
28
|
}
|
|
29
|
+
if (globalRequestID >= Number.MAX_SAFE_INTEGER) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Congratulations! You've made 9 quadrillion cache requests. Please restart your server and tell us about your workload."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
49
34
|
const requestID = ++globalRequestID;
|
|
50
35
|
return new Promise((resolve, reject) => {
|
|
51
36
|
pendingRequestsRegistry.set(requestID, { resolve, reject });
|
|
52
|
-
|
|
37
|
+
const message = {
|
|
53
38
|
type,
|
|
54
39
|
requestID,
|
|
55
40
|
cacheName: this.#name,
|
|
56
41
|
...data
|
|
57
|
-
}
|
|
42
|
+
};
|
|
43
|
+
if (transfer && transfer.length > 0) {
|
|
44
|
+
self.postMessage(message, transfer);
|
|
45
|
+
} else {
|
|
46
|
+
self.postMessage(message);
|
|
47
|
+
}
|
|
58
48
|
setTimeout(() => {
|
|
59
49
|
if (pendingRequestsRegistry.has(requestID)) {
|
|
60
50
|
pendingRequestsRegistry.delete(requestID);
|
|
@@ -64,16 +54,26 @@ var PostMessageCache = class extends Cache {
|
|
|
64
54
|
});
|
|
65
55
|
}
|
|
66
56
|
async match(request, options) {
|
|
57
|
+
let requestBody;
|
|
58
|
+
const transfer = [];
|
|
59
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
60
|
+
requestBody = await request.arrayBuffer();
|
|
61
|
+
transfer.push(requestBody);
|
|
62
|
+
}
|
|
67
63
|
const serializedRequest = {
|
|
68
64
|
url: request.url,
|
|
69
65
|
method: request.method,
|
|
70
66
|
headers: Object.fromEntries(request.headers),
|
|
71
|
-
body:
|
|
67
|
+
body: requestBody
|
|
72
68
|
};
|
|
73
|
-
const response = await this.#sendRequest(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
const response = await this.#sendRequest(
|
|
70
|
+
"cache:match",
|
|
71
|
+
{
|
|
72
|
+
request: serializedRequest,
|
|
73
|
+
options
|
|
74
|
+
},
|
|
75
|
+
transfer
|
|
76
|
+
);
|
|
77
77
|
if (!response) {
|
|
78
78
|
return void 0;
|
|
79
79
|
}
|
|
@@ -84,49 +84,82 @@ var PostMessageCache = class extends Cache {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
async put(request, response) {
|
|
87
|
+
const transfer = [];
|
|
88
|
+
let requestBody;
|
|
89
|
+
let responseBody;
|
|
90
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
91
|
+
requestBody = await request.clone().arrayBuffer();
|
|
92
|
+
transfer.push(requestBody);
|
|
93
|
+
}
|
|
94
|
+
responseBody = await response.clone().arrayBuffer();
|
|
95
|
+
transfer.push(responseBody);
|
|
87
96
|
const serializedRequest = {
|
|
88
97
|
url: request.url,
|
|
89
98
|
method: request.method,
|
|
90
99
|
headers: Object.fromEntries(request.headers),
|
|
91
|
-
body:
|
|
100
|
+
body: requestBody
|
|
92
101
|
};
|
|
93
102
|
const serializedResponse = {
|
|
94
103
|
status: response.status,
|
|
95
104
|
statusText: response.statusText,
|
|
96
105
|
headers: Object.fromEntries(response.headers),
|
|
97
|
-
body:
|
|
106
|
+
body: responseBody
|
|
98
107
|
};
|
|
99
|
-
await this.#sendRequest(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
await this.#sendRequest(
|
|
109
|
+
"cache:put",
|
|
110
|
+
{
|
|
111
|
+
request: serializedRequest,
|
|
112
|
+
response: serializedResponse
|
|
113
|
+
},
|
|
114
|
+
transfer
|
|
115
|
+
);
|
|
103
116
|
}
|
|
104
117
|
async delete(request, options) {
|
|
118
|
+
let requestBody;
|
|
119
|
+
const transfer = [];
|
|
120
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
121
|
+
requestBody = await request.arrayBuffer();
|
|
122
|
+
transfer.push(requestBody);
|
|
123
|
+
}
|
|
105
124
|
const serializedRequest = {
|
|
106
125
|
url: request.url,
|
|
107
126
|
method: request.method,
|
|
108
127
|
headers: Object.fromEntries(request.headers),
|
|
109
|
-
body:
|
|
128
|
+
body: requestBody
|
|
110
129
|
};
|
|
111
|
-
return await this.#sendRequest(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
return await this.#sendRequest(
|
|
131
|
+
"cache:delete",
|
|
132
|
+
{
|
|
133
|
+
request: serializedRequest,
|
|
134
|
+
options
|
|
135
|
+
},
|
|
136
|
+
transfer
|
|
137
|
+
);
|
|
115
138
|
}
|
|
116
139
|
async keys(request, options) {
|
|
117
140
|
let serializedRequest;
|
|
141
|
+
const transfer = [];
|
|
118
142
|
if (request) {
|
|
143
|
+
let requestBody;
|
|
144
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
145
|
+
requestBody = await request.arrayBuffer();
|
|
146
|
+
transfer.push(requestBody);
|
|
147
|
+
}
|
|
119
148
|
serializedRequest = {
|
|
120
149
|
url: request.url,
|
|
121
150
|
method: request.method,
|
|
122
151
|
headers: Object.fromEntries(request.headers),
|
|
123
|
-
body:
|
|
152
|
+
body: requestBody
|
|
124
153
|
};
|
|
125
154
|
}
|
|
126
|
-
const keys = await this.#sendRequest(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
155
|
+
const keys = await this.#sendRequest(
|
|
156
|
+
"cache:keys",
|
|
157
|
+
{
|
|
158
|
+
request: serializedRequest,
|
|
159
|
+
options
|
|
160
|
+
},
|
|
161
|
+
transfer
|
|
162
|
+
);
|
|
130
163
|
return keys.map(
|
|
131
164
|
(req) => new Request(req.url, {
|
|
132
165
|
method: req.method,
|
|
@@ -140,5 +173,6 @@ var PostMessageCache = class extends Cache {
|
|
|
140
173
|
}
|
|
141
174
|
};
|
|
142
175
|
export {
|
|
143
|
-
PostMessageCache
|
|
176
|
+
PostMessageCache,
|
|
177
|
+
handleCacheResponse
|
|
144
178
|
};
|