Skip to content

Commit fee440c

Browse files
authored
feat(ext/cache_storage): cache storage (#102)
* feat(ext/cache_storage): cache storage * chore: fmt
1 parent 7eb35db commit fee440c

File tree

8 files changed

+1052
-0
lines changed

8 files changed

+1052
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/cache_storage.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const cache = await caches.open("test-cache-simple");
2+
console.log("✓ Opened cache successfully");
3+
4+
const hasCache = await caches.has("test-cache-simple");
5+
console.log("✓ Cache exists:", hasCache);
6+
7+
await cache.add("https://api.example.com/simple");
8+
console.log("✓ Added URL to cache: https://api.example.com/simple");
9+
const matchResult = await cache.match("https://api.example.com/simple");
10+
console.log("✓ Cache match result:", matchResult !== undefined);
11+
console.log(" Match type:", typeof matchResult);
12+
console.log(" Match value:", matchResult);
13+
14+
await cache.add("https://api.example.com/data1");
15+
await cache.add("https://api.example.com/data2");
16+
await cache.add("https://api.example.com/data3");
17+
18+
const match1 = await cache.match("https://api.example.com/data1");
19+
const match2 = await cache.match("https://api.example.com/data2");
20+
const match3 = await cache.match("https://api.example.com/data3");
21+
const nonMatch = await cache.match("https://api.example.com/nonexistent");
22+
23+
console.log("✓ Match data1:", match1 !== undefined);
24+
console.log("✓ Match data2:", match2 !== undefined);
25+
console.log("✓ Match data3:", match3 !== undefined);
26+
console.log("✓ Non-existent match:", nonMatch === undefined);
27+
28+
const keys = await cache.keys();
29+
console.log(
30+
"✓ Cache keys count:",
31+
Array.isArray(keys) ? keys.length : "not an array",
32+
);
33+
console.log(" Keys type:", typeof keys);
34+
const deleted = await cache.delete("https://api.example.com/data1");
35+
console.log("✓ Delete result:", deleted);
36+
37+
const deletedMatch = await cache.match("https://api.example.com/data1");
38+
console.log("✓ Deleted URL no longer matches:", deletedMatch === undefined);
39+
await caches.open("another-cache");
40+
const cacheNames = await caches.keys();
41+
console.log("✓ Cache names:", cacheNames);
42+
const deletedCache = await caches.delete("test-cache-simple");
43+
const deletedAnother = await caches.delete("another-cache");
44+
console.log("✓ Deleted test-cache-simple:", deletedCache);
45+
console.log("✓ Deleted another-cache:", deletedAnother);

runtime/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ tokio.workspace = true
2121
oxc-miette.workspace = true
2222
oxc_diagnostics.workspace = true
2323
serde.workspace = true
24+
serde_json.workspace = true
2425
url.workspace = true
2526
base64-simd.workspace = true
2627
image = { workspace = true, optional = true }
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// deno-lint-ignore-file no-explicit-any
2+
// This Source Code Form is subject to the terms of the Mozilla Public
3+
// License, v. 2.0. If a copy of the MPL was not distributed with this
4+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
6+
/**
7+
* Implementation of the Cache and CacheStorage APIs for Andromeda
8+
* Based on: https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage
9+
* Spec: https://w3c.github.io/ServiceWorker/#cache-interface
10+
*
11+
* Note: This is a simplified implementation that wraps the native sync functions
12+
* in promises to maintain compatibility with the Web API specification.
13+
*/
14+
15+
type RequestInfo = Request | string | URL;
16+
17+
interface CacheQueryOptions {
18+
ignoreSearch?: boolean;
19+
ignoreMethod?: boolean;
20+
ignoreVary?: boolean;
21+
}
22+
23+
class Cache {
24+
#cacheName: string;
25+
26+
constructor(cacheName: string) {
27+
this.#cacheName = cacheName;
28+
}
29+
30+
/**
31+
* Returns a Promise that resolves to the response associated with the first matching request in the Cache object.
32+
*/
33+
match(
34+
request: RequestInfo,
35+
options?: CacheQueryOptions,
36+
): Promise<Response | undefined> {
37+
return Promise.resolve(
38+
cache_match(this.#cacheName, request as any, options),
39+
);
40+
}
41+
42+
/**
43+
* Returns a Promise that resolves to an array of all matching responses in the Cache object.
44+
*/
45+
matchAll(
46+
request?: RequestInfo,
47+
options?: CacheQueryOptions,
48+
): Promise<Response[]> {
49+
return Promise.resolve(
50+
cache_matchAll(this.#cacheName, request as any, options),
51+
);
52+
}
53+
54+
/**
55+
* Takes a URL, retrieves it and adds the resulting response object to the given cache.
56+
*/
57+
add(request: RequestInfo): Promise<void> {
58+
return Promise.resolve(cache_add(this.#cacheName, request as any));
59+
}
60+
61+
/**
62+
* Takes an array of URLs, retrieves them, and adds the resulting response objects to the given cache.
63+
*/
64+
addAll(requests: RequestInfo[]): Promise<void> {
65+
return Promise.resolve(cache_addAll(this.#cacheName, requests as any));
66+
}
67+
68+
/**
69+
* Takes both a request and its response and adds it to the given cache.
70+
*/
71+
put(request: RequestInfo, response: Response): Promise<void> {
72+
// Clone the response to ensure it can be consumed
73+
const responseClone = response.clone();
74+
return Promise.resolve(
75+
cache_put(this.#cacheName, request as any, responseClone),
76+
);
77+
}
78+
79+
/**
80+
* Finds the Cache entry whose key is the request, and if found, deletes the Cache entry and returns a Promise that resolves to true.
81+
*/
82+
delete(
83+
request: RequestInfo,
84+
options?: CacheQueryOptions,
85+
): Promise<boolean> {
86+
return Promise.resolve(
87+
cache_delete(this.#cacheName, request as any, options),
88+
);
89+
}
90+
91+
/**
92+
* Returns a Promise that resolves to an array of Cache keys.
93+
*/
94+
keys(
95+
request?: RequestInfo,
96+
options?: CacheQueryOptions,
97+
): Promise<Request[]> {
98+
return Promise.resolve(
99+
cache_keys(this.#cacheName, request as any, options),
100+
);
101+
}
102+
}
103+
104+
class CacheStorage {
105+
/**
106+
* Returns a Promise that resolves to the Cache object matching the cacheName.
107+
*/
108+
open(cacheName: string): Promise<Cache> {
109+
// Call the sync function
110+
cacheStorage_open(cacheName);
111+
return Promise.resolve(new Cache(cacheName));
112+
}
113+
114+
/**
115+
* Returns a Promise that resolves to true if a Cache object matching the cacheName exists.
116+
*/
117+
has(cacheName: string): Promise<boolean> {
118+
return Promise.resolve(cacheStorage_has(cacheName));
119+
}
120+
121+
/**
122+
* Finds the Cache object matching the cacheName, and if found, deletes the Cache object and returns a Promise that resolves to true.
123+
*/
124+
delete(cacheName: string): Promise<boolean> {
125+
return Promise.resolve(cacheStorage_delete(cacheName));
126+
}
127+
128+
/**
129+
* Returns a Promise that will resolve with an array containing strings corresponding to all of the named Cache objects.
130+
*/
131+
keys(): Promise<string[]> {
132+
return Promise.resolve(cacheStorage_keys());
133+
}
134+
135+
/**
136+
* Checks if a given Request is a key in any of the Cache objects that the CacheStorage object tracks.
137+
*/
138+
match(
139+
request: RequestInfo,
140+
options?: CacheQueryOptions,
141+
): Promise<Response | undefined> {
142+
return Promise.resolve(cacheStorage_match(request as any, options));
143+
}
144+
}
145+
146+
// Create global CacheStorage instance
147+
let cacheStorageInstance: CacheStorage | undefined;
148+
149+
function getCacheStorage(): CacheStorage {
150+
if (!cacheStorageInstance) {
151+
cacheStorageInstance = new CacheStorage();
152+
}
153+
return cacheStorageInstance;
154+
}
155+
156+
// Define the global 'caches' property
157+
Object.defineProperty(globalThis, "caches", {
158+
get: () => getCacheStorage(),
159+
configurable: true,
160+
enumerable: true,
161+
});

0 commit comments

Comments
 (0)