@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/cache",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Universal Cache API implementation",
5
5
  "keywords": [
6
6
  "cache",
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
- import type { Worker } from "worker_threads";
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: Worker, message: CacheMessage): Promise<void>;
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.entries()),
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.entries()),
269
+ headers: Object.fromEntries(r.headers),
270
270
  body: void 0
271
271
  // Keys typically don't need body
272
272
  }));
@@ -1,7 +1,8 @@
1
1
  /// <reference types="./postmessage.d.ts" />
2
2
  // src/postmessage.ts
3
3
  import { Cache } from "./cache.js";
4
- import { parentPort, isMainThread } from "worker_threads";
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.entries()),
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.entries()),
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.entries()),
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.entries()),
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.entries()),
116
+ headers: Object.fromEntries(request.headers),
116
117
  body: request.method !== "GET" && request.method !== "HEAD" ? await request.text() : void 0
117
118
  };
118
119
  }