@b9g/cache 0.1.1 → 0.1.2
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 +275 -0
- package/package.json +1 -1
- package/src/index.d.ts +8 -0
- package/src/index.js +6 -0
- package/src/memory.d.ts +5 -2
- package/src/memory.js +3 -3
- package/src/postmessage.js +7 -6
package/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# @b9g/cache
|
|
2
|
+
|
|
3
|
+
Universal Cache API implementation providing CacheStorage and Cache interfaces across all JavaScript runtimes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Standard APIs**: Implements the Cache and CacheStorage APIs from service workers
|
|
8
|
+
- **Multiple Backends**: Memory, filesystem, Redis, KV store implementations
|
|
9
|
+
- **Universal**: Same API works in browsers, Node.js, Bun, and edge platforms
|
|
10
|
+
- **Request/Response**: HTTP-semantic caching with full Request/Response support
|
|
11
|
+
- **Registry Pattern**: Named cache management with factory registration
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @b9g/cache
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import { CacheStorage, MemoryCache } from '@b9g/cache';
|
|
23
|
+
|
|
24
|
+
// Create cache storage
|
|
25
|
+
const caches = new CacheStorage();
|
|
26
|
+
|
|
27
|
+
// Register cache implementations
|
|
28
|
+
caches.register('api', () => new MemoryCache('api'));
|
|
29
|
+
caches.register('pages', () => new MemoryCache('pages'));
|
|
30
|
+
|
|
31
|
+
// Open and use caches
|
|
32
|
+
const apiCache = await caches.open('api');
|
|
33
|
+
|
|
34
|
+
// Store response
|
|
35
|
+
const request = new Request('https://api.example.com/posts/1');
|
|
36
|
+
const response = new Response(JSON.stringify({ id: 1, title: 'Hello' }));
|
|
37
|
+
await apiCache.put(request, response);
|
|
38
|
+
|
|
39
|
+
// Retrieve response
|
|
40
|
+
const cached = await apiCache.match(request);
|
|
41
|
+
console.log(await cached.json()); // { id: 1, title: 'Hello' }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Cache Implementations
|
|
45
|
+
|
|
46
|
+
### MemoryCache
|
|
47
|
+
|
|
48
|
+
In-memory cache with TTL and size limits:
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import { MemoryCache } from '@b9g/cache';
|
|
52
|
+
|
|
53
|
+
const cache = new MemoryCache('my-cache', {
|
|
54
|
+
maxEntries: 1000, // Maximum number of entries
|
|
55
|
+
ttl: 300, // Time to live in seconds
|
|
56
|
+
maxSize: 50 * 1024 * 1024 // Maximum total size in bytes
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### FilesystemCache
|
|
61
|
+
|
|
62
|
+
File-based cache for SSG and persistent storage:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
import { FilesystemCache } from '@b9g/cache';
|
|
66
|
+
|
|
67
|
+
const cache = new FilesystemCache('pages', {
|
|
68
|
+
directory: './dist/cache',
|
|
69
|
+
compression: true,
|
|
70
|
+
indexing: true
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## CacheStorage Registry
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import { CacheStorage, MemoryCache, FilesystemCache } from '@b9g/cache';
|
|
78
|
+
|
|
79
|
+
const caches = new CacheStorage();
|
|
80
|
+
|
|
81
|
+
// Register different implementations
|
|
82
|
+
caches.register('api', () =>
|
|
83
|
+
new MemoryCache('api', { ttl: 300 })
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
caches.register('pages', () =>
|
|
87
|
+
new FilesystemCache('pages', { directory: './dist/pages' })
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
caches.register('assets', () =>
|
|
91
|
+
new MemoryCache('assets', { maxEntries: 10000 })
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Use with router
|
|
95
|
+
import { Router } from '@b9g/router';
|
|
96
|
+
const router = new Router({ caches });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Cache API Reference
|
|
100
|
+
|
|
101
|
+
### Standard Cache Methods
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// Check for cached response
|
|
105
|
+
const response = await cache.match(request, options?);
|
|
106
|
+
|
|
107
|
+
// Get all matching responses
|
|
108
|
+
const responses = await cache.matchAll(request?, options?);
|
|
109
|
+
|
|
110
|
+
// Store request/response pair
|
|
111
|
+
await cache.put(request, response);
|
|
112
|
+
|
|
113
|
+
// Fetch and store
|
|
114
|
+
await cache.add(request);
|
|
115
|
+
await cache.addAll(requests);
|
|
116
|
+
|
|
117
|
+
// Remove cached entry
|
|
118
|
+
const deleted = await cache.delete(request, options?);
|
|
119
|
+
|
|
120
|
+
// List cached requests
|
|
121
|
+
const requests = await cache.keys(request?, options?);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### CacheStorage Methods
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// Register cache factory
|
|
128
|
+
caches.register(name, factory);
|
|
129
|
+
|
|
130
|
+
// Open named cache
|
|
131
|
+
const cache = await caches.open(name);
|
|
132
|
+
|
|
133
|
+
// Check if cache exists
|
|
134
|
+
const exists = await caches.has(name);
|
|
135
|
+
|
|
136
|
+
// Delete named cache
|
|
137
|
+
const deleted = await caches.delete(name);
|
|
138
|
+
|
|
139
|
+
// List cache names
|
|
140
|
+
const names = await caches.keys();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Cache Options
|
|
144
|
+
|
|
145
|
+
### Query Options
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const response = await cache.match(request, {
|
|
149
|
+
ignoreSearch: true, // Ignore query parameters
|
|
150
|
+
ignoreMethod: false, // Consider HTTP method
|
|
151
|
+
ignoreVary: false, // Honor Vary header
|
|
152
|
+
cacheName: 'specific' // Target specific cache
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Storage Options
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
// Memory cache options
|
|
160
|
+
new MemoryCache('name', {
|
|
161
|
+
maxEntries: 1000,
|
|
162
|
+
ttl: 300,
|
|
163
|
+
maxSize: 50 * 1024 * 1024
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Filesystem cache options
|
|
167
|
+
new FilesystemCache('name', {
|
|
168
|
+
directory: './cache',
|
|
169
|
+
compression: true,
|
|
170
|
+
indexing: true,
|
|
171
|
+
fsync: false
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Integration Examples
|
|
176
|
+
|
|
177
|
+
### With Router
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
import { Router } from '@b9g/router';
|
|
181
|
+
import { CacheStorage, MemoryCache } from '@b9g/cache';
|
|
182
|
+
|
|
183
|
+
const caches = new CacheStorage();
|
|
184
|
+
caches.register('api', () => new MemoryCache('api'));
|
|
185
|
+
|
|
186
|
+
const router = new Router({ caches });
|
|
187
|
+
|
|
188
|
+
// Cache-aware middleware
|
|
189
|
+
router.use(async (request, context, next) => {
|
|
190
|
+
if (request.method === 'GET' && context.cache) {
|
|
191
|
+
const cached = await context.cache.match(request);
|
|
192
|
+
if (cached) return cached;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const response = await next();
|
|
196
|
+
|
|
197
|
+
if (request.method === 'GET' && context.cache && response.ok) {
|
|
198
|
+
await context.cache.put(request, response.clone());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return response;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
router.route('/api/posts/:id', { cache: { name: 'api' } })
|
|
205
|
+
.get(postHandler);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Static Site Generation
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
import { FilesystemCache } from '@b9g/cache';
|
|
212
|
+
|
|
213
|
+
const cache = new FilesystemCache('pages', {
|
|
214
|
+
directory: './dist'
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Pre-populate cache at build time
|
|
218
|
+
const paths = ['/about', '/contact', '/blog/post-1'];
|
|
219
|
+
|
|
220
|
+
for (const path of paths) {
|
|
221
|
+
const request = new Request(`https://example.com${path}`);
|
|
222
|
+
await cache.add(request); // Fetches through your router
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// At runtime, serve from cache
|
|
226
|
+
const response = await cache.match(request);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Service Worker
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
// In service worker context
|
|
233
|
+
import { CacheStorage } from '@b9g/cache';
|
|
234
|
+
|
|
235
|
+
// Use native browser caches when available
|
|
236
|
+
const caches = new CacheStorage();
|
|
237
|
+
|
|
238
|
+
self.addEventListener('fetch', async event => {
|
|
239
|
+
const cache = await caches.open('runtime');
|
|
240
|
+
|
|
241
|
+
event.respondWith(
|
|
242
|
+
cache.match(event.request).then(response => {
|
|
243
|
+
if (response) return response;
|
|
244
|
+
|
|
245
|
+
return fetch(event.request).then(response => {
|
|
246
|
+
if (response.ok) {
|
|
247
|
+
cache.put(event.request, response.clone());
|
|
248
|
+
}
|
|
249
|
+
return response;
|
|
250
|
+
});
|
|
251
|
+
})
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Cache Coordination
|
|
257
|
+
|
|
258
|
+
For multi-worker setups, caches coordinate through PostMessage:
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
// Worker thread cache coordination
|
|
262
|
+
const cache = new MemoryCache('shared', {
|
|
263
|
+
coordination: {
|
|
264
|
+
type: 'postmessage',
|
|
265
|
+
channel: 'cache-coordination'
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Operations are coordinated across workers
|
|
270
|
+
await cache.put(request, response); // Synced to all workers
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -7,3 +7,11 @@ export { Cache, type CacheQueryOptions, generateCacheKey, cloneResponse } from "
|
|
|
7
7
|
export { CustomCacheStorage, type CacheFactory } from "./cache-storage.js";
|
|
8
8
|
export { MemoryCache, MemoryCacheManager, type MemoryCacheOptions } from "./memory.js";
|
|
9
9
|
export { PostMessageCache, type PostMessageCacheOptions } from "./postmessage.js";
|
|
10
|
+
import { MemoryCache, type MemoryCacheOptions } from "./memory.js";
|
|
11
|
+
/**
|
|
12
|
+
* Platform adapter factory function
|
|
13
|
+
* Creates a MemoryCache instance with the given configuration
|
|
14
|
+
*/
|
|
15
|
+
export declare function createCache(config?: MemoryCacheOptions & {
|
|
16
|
+
name?: string;
|
|
17
|
+
}): MemoryCache;
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,11 @@ import { Cache, generateCacheKey, cloneResponse } from "./cache.js";
|
|
|
4
4
|
import { CustomCacheStorage } from "./cache-storage.js";
|
|
5
5
|
import { MemoryCache, MemoryCacheManager } from "./memory.js";
|
|
6
6
|
import { PostMessageCache } from "./postmessage.js";
|
|
7
|
+
import { MemoryCache as MemoryCache2 } from "./memory.js";
|
|
8
|
+
function createCache(config = {}) {
|
|
9
|
+
const name = config.name || "default";
|
|
10
|
+
return new MemoryCache2(name, config);
|
|
11
|
+
}
|
|
7
12
|
export {
|
|
8
13
|
Cache,
|
|
9
14
|
CustomCacheStorage,
|
|
@@ -11,5 +16,6 @@ export {
|
|
|
11
16
|
MemoryCacheManager,
|
|
12
17
|
PostMessageCache,
|
|
13
18
|
cloneResponse,
|
|
19
|
+
createCache,
|
|
14
20
|
generateCacheKey
|
|
15
21
|
};
|
package/src/memory.d.ts
CHANGED
|
@@ -75,7 +75,10 @@ export declare class MemoryCache extends Cache {
|
|
|
75
75
|
* Only MemoryCache needs coordination since it stores data in process memory.
|
|
76
76
|
* Other cache types can be used directly by workers without coordination.
|
|
77
77
|
*/
|
|
78
|
-
|
|
78
|
+
interface WorkerLike {
|
|
79
|
+
postMessage(value: any): void;
|
|
80
|
+
on(event: string, listener: (data: any) => void): void;
|
|
81
|
+
}
|
|
79
82
|
interface CacheMessage {
|
|
80
83
|
type: string;
|
|
81
84
|
requestId: string;
|
|
@@ -101,7 +104,7 @@ export declare class MemoryCacheManager {
|
|
|
101
104
|
/**
|
|
102
105
|
* Handle memory cache-related message from a Worker
|
|
103
106
|
*/
|
|
104
|
-
handleMessage(worker:
|
|
107
|
+
handleMessage(worker: WorkerLike, message: CacheMessage): Promise<void>;
|
|
105
108
|
/**
|
|
106
109
|
* Get or create a MemoryCache instance
|
|
107
110
|
*/
|
package/src/memory.js
CHANGED
|
@@ -41,7 +41,7 @@ var MemoryCache = class extends Cache {
|
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
const clonedRequest = request.clone();
|
|
44
|
-
const clonedResponse = cloneResponse(response);
|
|
44
|
+
const clonedResponse = await cloneResponse(response);
|
|
45
45
|
const entry = {
|
|
46
46
|
request: clonedRequest,
|
|
47
47
|
response: clonedResponse,
|
|
@@ -217,7 +217,7 @@ var MemoryCacheManager = class {
|
|
|
217
217
|
return {
|
|
218
218
|
status: response.status,
|
|
219
219
|
statusText: response.statusText,
|
|
220
|
-
headers: Object.fromEntries(response.headers
|
|
220
|
+
headers: Object.fromEntries(response.headers),
|
|
221
221
|
body: await response.text()
|
|
222
222
|
};
|
|
223
223
|
}
|
|
@@ -266,7 +266,7 @@ var MemoryCacheManager = class {
|
|
|
266
266
|
return keys.map((r) => ({
|
|
267
267
|
url: r.url,
|
|
268
268
|
method: r.method,
|
|
269
|
-
headers: Object.fromEntries(r.headers
|
|
269
|
+
headers: Object.fromEntries(r.headers),
|
|
270
270
|
body: void 0
|
|
271
271
|
// Keys typically don't need body
|
|
272
272
|
}));
|
package/src/postmessage.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/// <reference types="./postmessage.d.ts" />
|
|
2
2
|
// src/postmessage.ts
|
|
3
3
|
import { Cache } from "./cache.js";
|
|
4
|
-
|
|
4
|
+
var isMainThread = typeof self === "undefined";
|
|
5
|
+
var parentPort = typeof self !== "undefined" ? self : null;
|
|
5
6
|
var PostMessageCache = class extends Cache {
|
|
6
7
|
constructor(name, options = {}) {
|
|
7
8
|
super();
|
|
@@ -60,7 +61,7 @@ var PostMessageCache = class extends Cache {
|
|
|
60
61
|
const serializedRequest = {
|
|
61
62
|
url: request.url,
|
|
62
63
|
method: request.method,
|
|
63
|
-
headers: Object.fromEntries(request.headers
|
|
64
|
+
headers: Object.fromEntries(request.headers),
|
|
64
65
|
body: request.method !== "GET" && request.method !== "HEAD" ? await request.text() : void 0
|
|
65
66
|
};
|
|
66
67
|
const response = await this.sendRequest("cache:match", {
|
|
@@ -80,13 +81,13 @@ var PostMessageCache = class extends Cache {
|
|
|
80
81
|
const serializedRequest = {
|
|
81
82
|
url: request.url,
|
|
82
83
|
method: request.method,
|
|
83
|
-
headers: Object.fromEntries(request.headers
|
|
84
|
+
headers: Object.fromEntries(request.headers),
|
|
84
85
|
body: request.method !== "GET" && request.method !== "HEAD" ? await request.clone().text() : void 0
|
|
85
86
|
};
|
|
86
87
|
const serializedResponse = {
|
|
87
88
|
status: response.status,
|
|
88
89
|
statusText: response.statusText,
|
|
89
|
-
headers: Object.fromEntries(response.headers
|
|
90
|
+
headers: Object.fromEntries(response.headers),
|
|
90
91
|
body: await response.clone().text()
|
|
91
92
|
};
|
|
92
93
|
await this.sendRequest("cache:put", {
|
|
@@ -98,7 +99,7 @@ var PostMessageCache = class extends Cache {
|
|
|
98
99
|
const serializedRequest = {
|
|
99
100
|
url: request.url,
|
|
100
101
|
method: request.method,
|
|
101
|
-
headers: Object.fromEntries(request.headers
|
|
102
|
+
headers: Object.fromEntries(request.headers),
|
|
102
103
|
body: request.method !== "GET" && request.method !== "HEAD" ? await request.text() : void 0
|
|
103
104
|
};
|
|
104
105
|
return await this.sendRequest("cache:delete", {
|
|
@@ -112,7 +113,7 @@ var PostMessageCache = class extends Cache {
|
|
|
112
113
|
serializedRequest = {
|
|
113
114
|
url: request.url,
|
|
114
115
|
method: request.method,
|
|
115
|
-
headers: Object.fromEntries(request.headers
|
|
116
|
+
headers: Object.fromEntries(request.headers),
|
|
116
117
|
body: request.method !== "GET" && request.method !== "HEAD" ? await request.text() : void 0
|
|
117
118
|
};
|
|
118
119
|
}
|