Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 36ba609

Browse files
committed
Add support for namespaced caches
1 parent 50860cb commit 36ba609

File tree

6 files changed

+97
-7
lines changed

6 files changed

+97
-7
lines changed

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
# 🚧 Changelog
22

3+
## 1.1.0
4+
5+
### Features
6+
7+
- Added support for namespaced caches with `caches.open`. See
8+
[✨ Cache](https://miniflare.dev/cache.html) for more details.
9+
310
## 1.0.1
411

5-
- Fix
12+
### Fixes
13+
14+
- Fixed
615
`/usr/bin/env: 'node --experimental-vm-modules': No such file or directory`
716
error when running the CLI in Linux. See
817
[💻 Using the CLI](https://miniflare.dev/cli.html#usage) for more details.
@@ -94,6 +103,8 @@
94103

95104
## 0.1.1
96105

106+
### Fixes
107+
97108
- Depend on `@mrbbot/node-fetch` from npm instead of GitHub, closes
98109
[issue #2](https://github.com/mrbbot/miniflare/issues/2)
99110

docs/cache.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# ✨ Cache
22

33
- [Cache Reference](https://developers.cloudflare.com/workers/runtime-apis/cache)
4+
- [How the Cache workers](https://developers.cloudflare.com/workers/learning/how-the-cache-works#cache-api)
5+
(note that cache using `fetch` is unsupported)
46

57
## Default Cache
68

@@ -12,6 +14,15 @@ addEventListener("fetch", (e) => {
1214
});
1315
```
1416

17+
## Named Caches
18+
19+
You can access a namespaced cache using `open`. Note that you cannot name your
20+
cache `default`, trying to do so will throw an error:
21+
22+
```js
23+
await caches.open("cache_name");
24+
```
25+
1526
## Persistence
1627

1728
By default, cached data is stored in memory. It will persist between reloads,
@@ -36,12 +47,15 @@ const mf = new Miniflare({
3647
});
3748
```
3849

50+
Each namespace will get its own directory within the cache persistence
51+
directory.
52+
3953
## Manipulating Outside Workers
4054

4155
For testing, it can be useful to put/match data from cache outside a worker. You
4256
can do this with the `getCache` method:
4357

44-
```js{23-31}
58+
```js{23-33}
4559
import { Miniflare, Response } from "miniflare";
4660
4761
const mf = new Miniflare({
@@ -64,7 +78,8 @@ const mf = new Miniflare({
6478
let res = await mf.dispatchFetch("http://localhost:8787/put");
6579
console.log(await res.text()); // 1
6680
67-
const cache = await mf.getCache();
81+
const cache = await mf.getCache(); // Gets the default cache
82+
const namedCache = await mf.getCached("cache_name"); // Gets a namespaced cache
6883
const cachedRes = await cache.match("https://miniflare.dev/");
6984
console.log(await cachedRes.text()); // 1
7085

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "miniflare",
3-
"version": "1.0.1",
3+
"version": "1.1.0",
44
"description": "Fun, full-featured, fully-local simulator for Cloudflare Workers",
55
"keywords": [
66
"cloudflare",

src/modules/cache.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import path from "path";
2+
import { MiniflareError } from "../error";
23
import { Cache } from "../kv";
34
import { KVStorageFactory } from "../kv/helpers";
45
import { Log } from "../log";
56
import { ProcessedOptions } from "../options";
67
import { Context, Module } from "./module";
78

89
const defaultPersistRoot = path.resolve(".mf", "cache");
10+
const defaultCacheName = "default";
911

1012
export class CacheModule extends Module {
1113
constructor(
@@ -15,12 +17,24 @@ export class CacheModule extends Module {
1517
super(log);
1618
}
1719

18-
getCache(name = "default", persist?: boolean | string): Cache {
20+
getCache(name = defaultCacheName, persist?: boolean | string): Cache {
1921
return new Cache(this.storageFactory.getStorage(name, persist));
2022
}
2123

2224
buildSandbox(options: ProcessedOptions): Context {
2325
const defaultCache = this.getCache(undefined, options.cachePersist);
24-
return { caches: { default: defaultCache } };
26+
return {
27+
caches: {
28+
default: defaultCache,
29+
open: async (name: string) => {
30+
if (name === defaultCacheName) {
31+
throw new MiniflareError(
32+
`\"${defaultCacheName}\" is a reserved cache name`
33+
);
34+
}
35+
return this.getCache(name, options.cachePersist);
36+
},
37+
},
38+
};
2539
}
2640
}

src/modules/standards.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ export class StandardsModule extends Module {
236236
return new Response(null, { webSocket: worker });
237237
}
238238

239+
// TODO: (low priority) support cache using fetch:
240+
// https://developers.cloudflare.com/workers/learning/how-the-cache-works#fetch
241+
// https://developers.cloudflare.com/workers/examples/cache-using-fetch
242+
239243
return originalFetch(request);
240244
}
241245

test/modules/cache.spec.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { existsSync, promises as fs } from "fs";
22
import path from "path";
33
import test from "ava";
4-
import { Cache, CachedResponse, NoOpLog, Response } from "../../src";
4+
import {
5+
Cache,
6+
CachedResponse,
7+
MiniflareError,
8+
NoOpLog,
9+
Response,
10+
} from "../../src";
511
import { KVStorageFactory } from "../../src/kv/helpers";
612
import { CacheModule } from "../../src/modules/cache";
713
import { runInWorker, useTmp } from "../helpers";
@@ -217,3 +223,43 @@ test("buildSandbox: can delete from default cache", async (t) => {
217223
existsSync(path.join(tmp, "default", "http___localhost_8787_test.json"))
218224
);
219225
});
226+
test("buildSandbox: namespaced cache is separate from default cache", async (t) => {
227+
const tmp = await useTmp(t);
228+
const cached = await runInWorker({ cachePersist: tmp }, async () => {
229+
const sandbox = self as any;
230+
231+
// Store something at the same URLs in default and other caches
232+
const defaultCache = sandbox.caches.default as Cache;
233+
const otherCache = (await sandbox.caches.open("other")) as Cache;
234+
235+
await defaultCache.put(
236+
"http://localhost:8787/test",
237+
new sandbox.Response("default", {
238+
headers: { "Cache-Control": "max-age=3600" },
239+
})
240+
);
241+
await otherCache.put(
242+
"http://localhost:8787/test",
243+
new sandbox.Response("other", {
244+
headers: { "Cache-Control": "max-age=3600" },
245+
})
246+
);
247+
248+
const defaultCached = await defaultCache.match(
249+
"http://localhost:8787/test"
250+
);
251+
const otherCached = await otherCache.match("http://localhost:8787/test");
252+
253+
return [await defaultCached?.text(), await otherCached?.text()];
254+
});
255+
t.deepEqual(cached, ["default", "other"]);
256+
});
257+
test("buildSandbox: trying to open default cache throws", async (t) => {
258+
const tmp = await useTmp(t);
259+
const module = new CacheModule(new NoOpLog(), new KVStorageFactory(tmp));
260+
const { caches } = module.buildSandbox({});
261+
await t.throwsAsync(caches.open("default"), {
262+
instanceOf: MiniflareError,
263+
message: '"default" is a reserved cache name',
264+
});
265+
});

0 commit comments

Comments
 (0)