@emdzej/bimmerz-vfs 0.1.0 → 0.2.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/dist/cache.d.ts +138 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +368 -0
- package/dist/cache.js.map +1 -0
- package/dist/cached-http.d.ts +122 -0
- package/dist/cached-http.d.ts.map +1 -0
- package/dist/cached-http.js +271 -0
- package/dist/cached-http.js.map +1 -0
- package/dist/cached-http.test.d.ts +12 -0
- package/dist/cached-http.test.d.ts.map +1 -0
- package/dist/cached-http.test.js +348 -0
- package/dist/cached-http.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage backend for `CachedHttpDirectory`. Two implementations:
|
|
3
|
+
*
|
|
4
|
+
* • **OPFS** (Origin Private File System) — preferred. Real
|
|
5
|
+
* filesystem semantics inside the browser sandbox, no quota
|
|
6
|
+
* prompts on writes ≤ 1 GB, and survives across tabs / refreshes.
|
|
7
|
+
* Requires Chrome 86+ / Safari 16+ / Firefox 111+ (gated on
|
|
8
|
+
* `navigator.storage.getDirectory`).
|
|
9
|
+
* • **IndexedDB** — fallback. Same durability story but with the
|
|
10
|
+
* awkward request/cursor API. Works everywhere.
|
|
11
|
+
*
|
|
12
|
+
* Each cached entry has TWO pieces:
|
|
13
|
+
*
|
|
14
|
+
* 1. The raw response bytes (one blob per key).
|
|
15
|
+
* 2. A small metadata record (`etag`, `last-modified`, `storedAt`,
|
|
16
|
+
* `validatedAt`, `url`). Used by `CachedHttpDirectory` to make
|
|
17
|
+
* conditional-GET requests and to surface staleness in `stats()`.
|
|
18
|
+
*
|
|
19
|
+
* The OPFS layout stores them as sibling files: `<key>` and
|
|
20
|
+
* `<key>.meta`. The IDB layout merges them into one object-store
|
|
21
|
+
* row keyed by `<key>`.
|
|
22
|
+
*
|
|
23
|
+
* Keys are origin-scoped: every backend instance is namespaced by a
|
|
24
|
+
* caller-supplied string (typically a hash of the `baseUrl`). The
|
|
25
|
+
* namespace is the OPFS subdirectory or the IDB key prefix. Clearing
|
|
26
|
+
* a namespace wipes one consumer's cache without touching anything
|
|
27
|
+
* else.
|
|
28
|
+
*/
|
|
29
|
+
/** Per-entry metadata persisted alongside the bytes. */
|
|
30
|
+
export interface CacheMetadata {
|
|
31
|
+
/** Original request URL. Useful for telemetry / debugging. */
|
|
32
|
+
url: string;
|
|
33
|
+
/** Strong / weak ETag header value, if the server returned one. */
|
|
34
|
+
etag?: string;
|
|
35
|
+
/** `Last-Modified` header value (RFC 7231 IMF-fixdate). */
|
|
36
|
+
lastModified?: string;
|
|
37
|
+
/** Content-Type header, captured so callers can serve via Response. */
|
|
38
|
+
contentType?: string;
|
|
39
|
+
/** Bytes (length of the data blob; redundant with the blob but
|
|
40
|
+
* cheap to keep — lets `stats()` answer without reading every file). */
|
|
41
|
+
size: number;
|
|
42
|
+
/** Wall-clock ms when the body was first stored. */
|
|
43
|
+
storedAt: number;
|
|
44
|
+
/** Wall-clock ms when the body was last confirmed fresh (200 or
|
|
45
|
+
* 304). Drives the `maxAgeMs` staleness check. */
|
|
46
|
+
validatedAt: number;
|
|
47
|
+
}
|
|
48
|
+
/** Combined entry returned from `get()`. */
|
|
49
|
+
export interface CacheEntry {
|
|
50
|
+
bytes: ArrayBuffer;
|
|
51
|
+
meta: CacheMetadata;
|
|
52
|
+
}
|
|
53
|
+
/** What every backend must implement. Async by necessity — both
|
|
54
|
+
* OPFS and IDB are Promise-only. */
|
|
55
|
+
export interface CacheBackend {
|
|
56
|
+
/** Backend name, surfaced in `stats()` so consumers can show the
|
|
57
|
+
* user which path the cache landed on. */
|
|
58
|
+
readonly kind: 'opfs' | 'idb' | 'memory';
|
|
59
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
60
|
+
put(key: string, entry: CacheEntry): Promise<void>;
|
|
61
|
+
delete(key: string): Promise<void>;
|
|
62
|
+
/** List keys with optional prefix filter. Order is unspecified. */
|
|
63
|
+
keys(prefix?: string): Promise<string[]>;
|
|
64
|
+
/** Remove every entry whose key starts with `prefix` (or all if
|
|
65
|
+
* no prefix is given). */
|
|
66
|
+
clear(prefix?: string): Promise<void>;
|
|
67
|
+
/** Aggregate counts for `stats()`. */
|
|
68
|
+
size(prefix?: string): Promise<{
|
|
69
|
+
entries: number;
|
|
70
|
+
totalBytes: number;
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
/** Implementation of `CacheBackend` over OPFS. Construct via
|
|
74
|
+
* `OpfsCacheBackend.open()` which resolves the root directory; the
|
|
75
|
+
* promise rejects when OPFS isn't available so callers can fall
|
|
76
|
+
* back to IDB. */
|
|
77
|
+
export declare class OpfsCacheBackend implements CacheBackend {
|
|
78
|
+
#private;
|
|
79
|
+
readonly kind: "opfs";
|
|
80
|
+
private constructor();
|
|
81
|
+
static open(namespace: string): Promise<OpfsCacheBackend>;
|
|
82
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
83
|
+
put(key: string, entry: CacheEntry): Promise<void>;
|
|
84
|
+
delete(key: string): Promise<void>;
|
|
85
|
+
keys(prefix?: string): Promise<string[]>;
|
|
86
|
+
clear(prefix?: string): Promise<void>;
|
|
87
|
+
size(prefix?: string): Promise<{
|
|
88
|
+
entries: number;
|
|
89
|
+
totalBytes: number;
|
|
90
|
+
}>;
|
|
91
|
+
}
|
|
92
|
+
/** Implementation of `CacheBackend` over IndexedDB. Construct via
|
|
93
|
+
* `IdbCacheBackend.open()`; one shared connection per origin is
|
|
94
|
+
* cached at the module level. */
|
|
95
|
+
export declare class IdbCacheBackend implements CacheBackend {
|
|
96
|
+
#private;
|
|
97
|
+
readonly kind: "idb";
|
|
98
|
+
constructor(namespace: string);
|
|
99
|
+
static open(namespace: string): Promise<IdbCacheBackend>;
|
|
100
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
101
|
+
put(key: string, entry: CacheEntry): Promise<void>;
|
|
102
|
+
delete(key: string): Promise<void>;
|
|
103
|
+
keys(prefix?: string): Promise<string[]>;
|
|
104
|
+
clear(prefix?: string): Promise<void>;
|
|
105
|
+
size(prefix?: string): Promise<{
|
|
106
|
+
entries: number;
|
|
107
|
+
totalBytes: number;
|
|
108
|
+
}>;
|
|
109
|
+
}
|
|
110
|
+
/** In-memory backend. Doesn't persist anything — used by tests and
|
|
111
|
+
* as a graceful no-op when neither OPFS nor IDB is available. */
|
|
112
|
+
export declare class MemoryCacheBackend implements CacheBackend {
|
|
113
|
+
#private;
|
|
114
|
+
readonly kind: "memory";
|
|
115
|
+
constructor(namespace: string);
|
|
116
|
+
get(key: string): Promise<CacheEntry | null>;
|
|
117
|
+
put(key: string, entry: CacheEntry): Promise<void>;
|
|
118
|
+
delete(key: string): Promise<void>;
|
|
119
|
+
keys(prefix?: string): Promise<string[]>;
|
|
120
|
+
clear(prefix?: string): Promise<void>;
|
|
121
|
+
size(prefix?: string): Promise<{
|
|
122
|
+
entries: number;
|
|
123
|
+
totalBytes: number;
|
|
124
|
+
}>;
|
|
125
|
+
}
|
|
126
|
+
export interface OpenCacheOptions {
|
|
127
|
+
/** Namespace for this cache instance. Typically a stable hash of
|
|
128
|
+
* the consumer's baseUrl so different `CachedHttpDirectory`
|
|
129
|
+
* instances don't trample each other. */
|
|
130
|
+
namespace: string;
|
|
131
|
+
/** Force a specific backend. Default: try OPFS, fall back to IDB,
|
|
132
|
+
* fall back to memory. */
|
|
133
|
+
prefer?: 'opfs' | 'idb' | 'memory';
|
|
134
|
+
}
|
|
135
|
+
/** Pick the best available backend at runtime. Returns the
|
|
136
|
+
* preferred one if it works, otherwise walks the fallback chain. */
|
|
137
|
+
export declare function openCacheBackend(options: OpenCacheOptions): Promise<CacheBackend>;
|
|
138
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage backend for `CachedHttpDirectory`. Two implementations:
|
|
3
|
+
*
|
|
4
|
+
* • **OPFS** (Origin Private File System) — preferred. Real
|
|
5
|
+
* filesystem semantics inside the browser sandbox, no quota
|
|
6
|
+
* prompts on writes ≤ 1 GB, and survives across tabs / refreshes.
|
|
7
|
+
* Requires Chrome 86+ / Safari 16+ / Firefox 111+ (gated on
|
|
8
|
+
* `navigator.storage.getDirectory`).
|
|
9
|
+
* • **IndexedDB** — fallback. Same durability story but with the
|
|
10
|
+
* awkward request/cursor API. Works everywhere.
|
|
11
|
+
*
|
|
12
|
+
* Each cached entry has TWO pieces:
|
|
13
|
+
*
|
|
14
|
+
* 1. The raw response bytes (one blob per key).
|
|
15
|
+
* 2. A small metadata record (`etag`, `last-modified`, `storedAt`,
|
|
16
|
+
* `validatedAt`, `url`). Used by `CachedHttpDirectory` to make
|
|
17
|
+
* conditional-GET requests and to surface staleness in `stats()`.
|
|
18
|
+
*
|
|
19
|
+
* The OPFS layout stores them as sibling files: `<key>` and
|
|
20
|
+
* `<key>.meta`. The IDB layout merges them into one object-store
|
|
21
|
+
* row keyed by `<key>`.
|
|
22
|
+
*
|
|
23
|
+
* Keys are origin-scoped: every backend instance is namespaced by a
|
|
24
|
+
* caller-supplied string (typically a hash of the `baseUrl`). The
|
|
25
|
+
* namespace is the OPFS subdirectory or the IDB key prefix. Clearing
|
|
26
|
+
* a namespace wipes one consumer's cache without touching anything
|
|
27
|
+
* else.
|
|
28
|
+
*/
|
|
29
|
+
/* ── OPFS backend ──────────────────────────────────────────────── */
|
|
30
|
+
/** OPFS file naming:
|
|
31
|
+
*
|
|
32
|
+
* <root>/<namespace>/<encoded-key> ← raw bytes
|
|
33
|
+
* <root>/<namespace>/<encoded-key>.meta ← JSON metadata
|
|
34
|
+
*
|
|
35
|
+
* `root` defaults to `bimmerz-vfs-cache`. Keys are URL-encoded so
|
|
36
|
+
* any character is safe — OPFS rejects `/` in names which we'd
|
|
37
|
+
* otherwise hit constantly. */
|
|
38
|
+
const OPFS_ROOT = 'bimmerz-vfs-cache';
|
|
39
|
+
const META_SUFFIX = '.meta';
|
|
40
|
+
function encodeKey(key) {
|
|
41
|
+
/* Replace forbidden OPFS characters. encodeURIComponent handles
|
|
42
|
+
most, but `:` (in URLs) is rejected on some platforms — strip
|
|
43
|
+
it explicitly. */
|
|
44
|
+
return encodeURIComponent(key).replace(/:/g, '%3A');
|
|
45
|
+
}
|
|
46
|
+
function decodeKey(encoded) {
|
|
47
|
+
return decodeURIComponent(encoded);
|
|
48
|
+
}
|
|
49
|
+
/** Implementation of `CacheBackend` over OPFS. Construct via
|
|
50
|
+
* `OpfsCacheBackend.open()` which resolves the root directory; the
|
|
51
|
+
* promise rejects when OPFS isn't available so callers can fall
|
|
52
|
+
* back to IDB. */
|
|
53
|
+
export class OpfsCacheBackend {
|
|
54
|
+
kind = 'opfs';
|
|
55
|
+
#root;
|
|
56
|
+
constructor(root) {
|
|
57
|
+
this.#root = root;
|
|
58
|
+
}
|
|
59
|
+
static async open(namespace) {
|
|
60
|
+
if (typeof navigator === 'undefined' || !navigator.storage?.getDirectory) {
|
|
61
|
+
throw new Error('OPFS not available');
|
|
62
|
+
}
|
|
63
|
+
const opfs = await navigator.storage.getDirectory();
|
|
64
|
+
const cacheRoot = await opfs.getDirectoryHandle(OPFS_ROOT, { create: true });
|
|
65
|
+
const nsDir = await cacheRoot.getDirectoryHandle(encodeKey(namespace), { create: true });
|
|
66
|
+
return new OpfsCacheBackend(nsDir);
|
|
67
|
+
}
|
|
68
|
+
async get(key) {
|
|
69
|
+
const encoded = encodeKey(key);
|
|
70
|
+
try {
|
|
71
|
+
const [bodyFile, metaFile] = await Promise.all([
|
|
72
|
+
this.#root.getFileHandle(encoded),
|
|
73
|
+
this.#root.getFileHandle(encoded + META_SUFFIX),
|
|
74
|
+
]);
|
|
75
|
+
const [bodyBlob, metaBlob] = await Promise.all([
|
|
76
|
+
bodyFile.getFile(),
|
|
77
|
+
metaFile.getFile(),
|
|
78
|
+
]);
|
|
79
|
+
const [bytes, metaText] = await Promise.all([
|
|
80
|
+
bodyBlob.arrayBuffer(),
|
|
81
|
+
metaBlob.text(),
|
|
82
|
+
]);
|
|
83
|
+
const meta = JSON.parse(metaText);
|
|
84
|
+
return { bytes, meta };
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
/* `getFileHandle` throws when the entry doesn't exist; any
|
|
88
|
+
other failure (corrupted JSON, partial write from a prior
|
|
89
|
+
crash) is also treated as a cache miss so the caller falls
|
|
90
|
+
back to a real fetch rather than serving garbage. */
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async put(key, entry) {
|
|
95
|
+
const encoded = encodeKey(key);
|
|
96
|
+
/* Write metadata FIRST so a crash mid-put leaves stale meta +
|
|
97
|
+
missing body rather than fresh body + stale meta. The reader
|
|
98
|
+
handles missing files defensively, so either failure mode
|
|
99
|
+
reads as a cache miss. */
|
|
100
|
+
const [bodyHandle, metaHandle] = await Promise.all([
|
|
101
|
+
this.#root.getFileHandle(encoded, { create: true }),
|
|
102
|
+
this.#root.getFileHandle(encoded + META_SUFFIX, { create: true }),
|
|
103
|
+
]);
|
|
104
|
+
const metaWriter = await metaHandle.createWritable();
|
|
105
|
+
await metaWriter.write(JSON.stringify(entry.meta));
|
|
106
|
+
await metaWriter.close();
|
|
107
|
+
const bodyWriter = await bodyHandle.createWritable();
|
|
108
|
+
await bodyWriter.write(entry.bytes);
|
|
109
|
+
await bodyWriter.close();
|
|
110
|
+
}
|
|
111
|
+
async delete(key) {
|
|
112
|
+
const encoded = encodeKey(key);
|
|
113
|
+
await Promise.allSettled([
|
|
114
|
+
this.#root.removeEntry(encoded),
|
|
115
|
+
this.#root.removeEntry(encoded + META_SUFFIX),
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
async keys(prefix) {
|
|
119
|
+
const out = [];
|
|
120
|
+
/* The iterator API isn't typed yet on FileSystemDirectoryHandle
|
|
121
|
+
in some lib targets; cast through `unknown`. */
|
|
122
|
+
const dir = this.#root;
|
|
123
|
+
for await (const handle of dir.values()) {
|
|
124
|
+
if (handle.kind !== 'file')
|
|
125
|
+
continue;
|
|
126
|
+
if (handle.name.endsWith(META_SUFFIX))
|
|
127
|
+
continue;
|
|
128
|
+
const key = decodeKey(handle.name);
|
|
129
|
+
if (prefix && !key.startsWith(prefix))
|
|
130
|
+
continue;
|
|
131
|
+
out.push(key);
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
async clear(prefix) {
|
|
136
|
+
const keys = await this.keys(prefix);
|
|
137
|
+
await Promise.all(keys.map((k) => this.delete(k)));
|
|
138
|
+
}
|
|
139
|
+
async size(prefix) {
|
|
140
|
+
const keys = await this.keys(prefix);
|
|
141
|
+
let totalBytes = 0;
|
|
142
|
+
/* We pull byte sizes from metadata (no full body read needed). */
|
|
143
|
+
await Promise.all(keys.map(async (key) => {
|
|
144
|
+
const meta = await this.#readMeta(key);
|
|
145
|
+
if (meta)
|
|
146
|
+
totalBytes += meta.size;
|
|
147
|
+
}));
|
|
148
|
+
return { entries: keys.length, totalBytes };
|
|
149
|
+
}
|
|
150
|
+
async #readMeta(key) {
|
|
151
|
+
try {
|
|
152
|
+
const handle = await this.#root.getFileHandle(encodeKey(key) + META_SUFFIX);
|
|
153
|
+
const blob = await handle.getFile();
|
|
154
|
+
return JSON.parse(await blob.text());
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/* ── IDB backend ───────────────────────────────────────────────── */
|
|
162
|
+
/** Stable DB name. One DB per origin, one object store keyed by
|
|
163
|
+
* `${namespace}:${key}` so all consumers share a single connection. */
|
|
164
|
+
const IDB_NAME = 'bimmerz-vfs-cache';
|
|
165
|
+
const IDB_STORE = 'entries';
|
|
166
|
+
const IDB_VERSION = 1;
|
|
167
|
+
/** Implementation of `CacheBackend` over IndexedDB. Construct via
|
|
168
|
+
* `IdbCacheBackend.open()`; one shared connection per origin is
|
|
169
|
+
* cached at the module level. */
|
|
170
|
+
export class IdbCacheBackend {
|
|
171
|
+
kind = 'idb';
|
|
172
|
+
#namespace;
|
|
173
|
+
#dbPromise;
|
|
174
|
+
constructor(namespace) {
|
|
175
|
+
this.#namespace = namespace;
|
|
176
|
+
this.#dbPromise = openDb();
|
|
177
|
+
}
|
|
178
|
+
static async open(namespace) {
|
|
179
|
+
if (typeof indexedDB === 'undefined') {
|
|
180
|
+
throw new Error('IndexedDB not available');
|
|
181
|
+
}
|
|
182
|
+
const instance = new IdbCacheBackend(namespace);
|
|
183
|
+
/* Eagerly resolve the DB connection so a failure surfaces
|
|
184
|
+
at construction time rather than first-use. */
|
|
185
|
+
await instance.#dbPromise;
|
|
186
|
+
return instance;
|
|
187
|
+
}
|
|
188
|
+
#rowKey(key) {
|
|
189
|
+
return `${this.#namespace}:${key}`;
|
|
190
|
+
}
|
|
191
|
+
async get(key) {
|
|
192
|
+
const db = await this.#dbPromise;
|
|
193
|
+
return new Promise((resolve, reject) => {
|
|
194
|
+
const tx = db.transaction(IDB_STORE, 'readonly');
|
|
195
|
+
const req = tx.objectStore(IDB_STORE).get(this.#rowKey(key));
|
|
196
|
+
req.onsuccess = () => {
|
|
197
|
+
const row = req.result;
|
|
198
|
+
resolve(row ? { bytes: row.bytes, meta: row.meta } : null);
|
|
199
|
+
};
|
|
200
|
+
req.onerror = () => reject(req.error);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
async put(key, entry) {
|
|
204
|
+
const db = await this.#dbPromise;
|
|
205
|
+
const row = {
|
|
206
|
+
k: this.#rowKey(key),
|
|
207
|
+
ns: this.#namespace,
|
|
208
|
+
bytes: entry.bytes,
|
|
209
|
+
meta: entry.meta,
|
|
210
|
+
};
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
const tx = db.transaction(IDB_STORE, 'readwrite');
|
|
213
|
+
const req = tx.objectStore(IDB_STORE).put(row);
|
|
214
|
+
req.onsuccess = () => resolve();
|
|
215
|
+
req.onerror = () => reject(req.error);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async delete(key) {
|
|
219
|
+
const db = await this.#dbPromise;
|
|
220
|
+
return new Promise((resolve, reject) => {
|
|
221
|
+
const tx = db.transaction(IDB_STORE, 'readwrite');
|
|
222
|
+
const req = tx.objectStore(IDB_STORE).delete(this.#rowKey(key));
|
|
223
|
+
req.onsuccess = () => resolve();
|
|
224
|
+
req.onerror = () => reject(req.error);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async keys(prefix) {
|
|
228
|
+
const db = await this.#dbPromise;
|
|
229
|
+
return new Promise((resolve, reject) => {
|
|
230
|
+
const tx = db.transaction(IDB_STORE, 'readonly');
|
|
231
|
+
const req = tx.objectStore(IDB_STORE).index('ns').openKeyCursor(IDBKeyRange.only(this.#namespace));
|
|
232
|
+
const out = [];
|
|
233
|
+
req.onsuccess = () => {
|
|
234
|
+
const cursor = req.result;
|
|
235
|
+
if (!cursor)
|
|
236
|
+
return resolve(out);
|
|
237
|
+
const rowKey = String(cursor.primaryKey);
|
|
238
|
+
const key = rowKey.slice(this.#namespace.length + 1);
|
|
239
|
+
if (!prefix || key.startsWith(prefix))
|
|
240
|
+
out.push(key);
|
|
241
|
+
cursor.continue();
|
|
242
|
+
};
|
|
243
|
+
req.onerror = () => reject(req.error);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async clear(prefix) {
|
|
247
|
+
const keys = await this.keys(prefix);
|
|
248
|
+
await Promise.all(keys.map((k) => this.delete(k)));
|
|
249
|
+
}
|
|
250
|
+
async size(prefix) {
|
|
251
|
+
const db = await this.#dbPromise;
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
const tx = db.transaction(IDB_STORE, 'readonly');
|
|
254
|
+
const req = tx.objectStore(IDB_STORE).index('ns').openCursor(IDBKeyRange.only(this.#namespace));
|
|
255
|
+
let entries = 0;
|
|
256
|
+
let totalBytes = 0;
|
|
257
|
+
req.onsuccess = () => {
|
|
258
|
+
const cursor = req.result;
|
|
259
|
+
if (!cursor)
|
|
260
|
+
return resolve({ entries, totalBytes });
|
|
261
|
+
const row = cursor.value;
|
|
262
|
+
const key = row.k.slice(this.#namespace.length + 1);
|
|
263
|
+
if (!prefix || key.startsWith(prefix)) {
|
|
264
|
+
entries += 1;
|
|
265
|
+
totalBytes += row.meta.size;
|
|
266
|
+
}
|
|
267
|
+
cursor.continue();
|
|
268
|
+
};
|
|
269
|
+
req.onerror = () => reject(req.error);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function openDb() {
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
const req = indexedDB.open(IDB_NAME, IDB_VERSION);
|
|
276
|
+
req.onupgradeneeded = () => {
|
|
277
|
+
const db = req.result;
|
|
278
|
+
const store = db.createObjectStore(IDB_STORE, { keyPath: 'k' });
|
|
279
|
+
store.createIndex('ns', 'ns', { unique: false });
|
|
280
|
+
};
|
|
281
|
+
req.onsuccess = () => resolve(req.result);
|
|
282
|
+
req.onerror = () => reject(req.error);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/* ── Memory backend (for tests + SSR) ─────────────────────────── */
|
|
286
|
+
/** In-memory backend. Doesn't persist anything — used by tests and
|
|
287
|
+
* as a graceful no-op when neither OPFS nor IDB is available. */
|
|
288
|
+
export class MemoryCacheBackend {
|
|
289
|
+
kind = 'memory';
|
|
290
|
+
#store = new Map();
|
|
291
|
+
#namespace;
|
|
292
|
+
constructor(namespace) {
|
|
293
|
+
this.#namespace = namespace;
|
|
294
|
+
}
|
|
295
|
+
#ns(key) {
|
|
296
|
+
return `${this.#namespace}:${key}`;
|
|
297
|
+
}
|
|
298
|
+
async get(key) {
|
|
299
|
+
const e = this.#store.get(this.#ns(key));
|
|
300
|
+
return e ? { bytes: e.bytes, meta: { ...e.meta } } : null;
|
|
301
|
+
}
|
|
302
|
+
async put(key, entry) {
|
|
303
|
+
this.#store.set(this.#ns(key), entry);
|
|
304
|
+
}
|
|
305
|
+
async delete(key) {
|
|
306
|
+
this.#store.delete(this.#ns(key));
|
|
307
|
+
}
|
|
308
|
+
async keys(prefix) {
|
|
309
|
+
const out = [];
|
|
310
|
+
for (const k of this.#store.keys()) {
|
|
311
|
+
if (!k.startsWith(`${this.#namespace}:`))
|
|
312
|
+
continue;
|
|
313
|
+
const local = k.slice(this.#namespace.length + 1);
|
|
314
|
+
if (!prefix || local.startsWith(prefix))
|
|
315
|
+
out.push(local);
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
async clear(prefix) {
|
|
320
|
+
for (const k of [...this.#store.keys()]) {
|
|
321
|
+
if (!k.startsWith(`${this.#namespace}:`))
|
|
322
|
+
continue;
|
|
323
|
+
const local = k.slice(this.#namespace.length + 1);
|
|
324
|
+
if (!prefix || local.startsWith(prefix))
|
|
325
|
+
this.#store.delete(k);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async size(prefix) {
|
|
329
|
+
let entries = 0;
|
|
330
|
+
let totalBytes = 0;
|
|
331
|
+
for (const [k, v] of this.#store) {
|
|
332
|
+
if (!k.startsWith(`${this.#namespace}:`))
|
|
333
|
+
continue;
|
|
334
|
+
const local = k.slice(this.#namespace.length + 1);
|
|
335
|
+
if (prefix && !local.startsWith(prefix))
|
|
336
|
+
continue;
|
|
337
|
+
entries += 1;
|
|
338
|
+
totalBytes += v.meta.size;
|
|
339
|
+
}
|
|
340
|
+
return { entries, totalBytes };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/** Pick the best available backend at runtime. Returns the
|
|
344
|
+
* preferred one if it works, otherwise walks the fallback chain. */
|
|
345
|
+
export async function openCacheBackend(options) {
|
|
346
|
+
const order = options.prefer
|
|
347
|
+
? options.prefer === 'opfs'
|
|
348
|
+
? ['opfs', 'idb', 'memory']
|
|
349
|
+
: options.prefer === 'idb'
|
|
350
|
+
? ['idb', 'memory']
|
|
351
|
+
: ['memory']
|
|
352
|
+
: ['opfs', 'idb', 'memory'];
|
|
353
|
+
let lastError;
|
|
354
|
+
for (const kind of order) {
|
|
355
|
+
try {
|
|
356
|
+
if (kind === 'opfs')
|
|
357
|
+
return await OpfsCacheBackend.open(options.namespace);
|
|
358
|
+
if (kind === 'idb')
|
|
359
|
+
return await IdbCacheBackend.open(options.namespace);
|
|
360
|
+
return new MemoryCacheBackend(options.namespace);
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
lastError = err;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
throw new Error(`No usable cache backend (last error: ${lastError instanceof Error ? lastError.message : String(lastError)})`);
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `CachedHttpDirectory` — drop-in replacement for `HttpDirectory`
|
|
3
|
+
* that persists fetched bytes (index + files) in OPFS (preferred)
|
|
4
|
+
* or IndexedDB.
|
|
5
|
+
*
|
|
6
|
+
* Why a wrapper, not changes to `HttpDirectory`: the base class is
|
|
7
|
+
* the right minimum surface for read-only HTTP. Caching is an
|
|
8
|
+
* orthogonal concern that some consumers don't want (development,
|
|
9
|
+
* one-shot CLI tools). Keeping the two separate also means a bug in
|
|
10
|
+
* the cache layer can't poison the simple path.
|
|
11
|
+
*
|
|
12
|
+
* What gets cached:
|
|
13
|
+
*
|
|
14
|
+
* • The `index.json` per directory.
|
|
15
|
+
* • The body of every `file(name).arrayBuffer()` call.
|
|
16
|
+
*
|
|
17
|
+
* Staleness policy: **stale-while-revalidate.**
|
|
18
|
+
*
|
|
19
|
+
* • First read of any URL: do a normal fetch, store body + ETag +
|
|
20
|
+
* Last-Modified + the `Content-Type`, return.
|
|
21
|
+
* • Subsequent reads inside `maxAgeMs` (default 5 min): serve
|
|
22
|
+
* cached bytes immediately, no network at all.
|
|
23
|
+
* • Subsequent reads OUTSIDE `maxAgeMs`: serve cached bytes
|
|
24
|
+
* immediately AND kick off a background conditional GET (`If-
|
|
25
|
+
* None-Match` / `If-Modified-Since`). When the server replies:
|
|
26
|
+
* - `304 Not Modified` → bump `validatedAt`. Body untouched.
|
|
27
|
+
* - `200 OK` → write the new body to cache. The next call
|
|
28
|
+
* then sees the fresh bytes.
|
|
29
|
+
* • Explicit `revalidate(file)` forces an immediate conditional
|
|
30
|
+
* fetch and returns once the network response settles.
|
|
31
|
+
*
|
|
32
|
+
* The first-after-update read returns the OLD bytes. That's the
|
|
33
|
+
* standard SWR tradeoff — favours latency over freshness, lets
|
|
34
|
+
* the dashboard render instantly on every load.
|
|
35
|
+
*/
|
|
36
|
+
import type { VirtualFile, VirtualDirectory, VirtualEntry } from './types.js';
|
|
37
|
+
import type { HttpDirectoryOptions } from './http.js';
|
|
38
|
+
import { type CacheBackend } from './cache.js';
|
|
39
|
+
/** Construction options for a `CachedHttpDirectory`. */
|
|
40
|
+
export interface CachedHttpDirectoryOptions extends HttpDirectoryOptions {
|
|
41
|
+
/** Cache backend. If omitted, one is auto-selected from OPFS / IDB
|
|
42
|
+
* / memory at first use. Sharing one backend across multiple
|
|
43
|
+
* directories is fine — namespaces keep entries separate. */
|
|
44
|
+
backend?: CacheBackend;
|
|
45
|
+
/** Override the auto-selected backend kind. */
|
|
46
|
+
preferBackend?: 'opfs' | 'idb' | 'memory';
|
|
47
|
+
/**
|
|
48
|
+
* Namespace string for this directory's cached entries. Default:
|
|
49
|
+
* the baseUrl with its scheme stripped. Two `CachedHttpDirectory`
|
|
50
|
+
* instances sharing a namespace share their cache — usually fine
|
|
51
|
+
* (same baseUrl = same content) but expose the knob for testing
|
|
52
|
+
* and for the case where two CDN URLs serve the same content and
|
|
53
|
+
* you want one cache hit covering both.
|
|
54
|
+
*/
|
|
55
|
+
namespace?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Optional canonicaliser for cache keys. Useful when URLs carry
|
|
58
|
+
* signed-token query strings that change per request — strip the
|
|
59
|
+
* token so equivalent URLs hit the same cache entry.
|
|
60
|
+
*/
|
|
61
|
+
cacheKey?: (url: string) => string;
|
|
62
|
+
/**
|
|
63
|
+
* Cache entries are served without revalidation when their age is
|
|
64
|
+
* below this threshold. Default 5 min (300 000 ms). Set to 0 for
|
|
65
|
+
* always-revalidate, or `Infinity` for never-revalidate.
|
|
66
|
+
*/
|
|
67
|
+
maxAgeMs?: number;
|
|
68
|
+
}
|
|
69
|
+
/** Public API exposed by every `CachedHttpDirectory` for cache
|
|
70
|
+
* management — same surface across nested subdirs. */
|
|
71
|
+
export interface CacheControl {
|
|
72
|
+
/** Bytes + entry count for this directory's namespace. */
|
|
73
|
+
stats(): Promise<{
|
|
74
|
+
backend: 'opfs' | 'idb' | 'memory';
|
|
75
|
+
entries: number;
|
|
76
|
+
totalBytes: number;
|
|
77
|
+
}>;
|
|
78
|
+
/** Drop every cached entry in this namespace. */
|
|
79
|
+
clear(): Promise<void>;
|
|
80
|
+
/** Force a fresh conditional GET on the given path. Resolves once
|
|
81
|
+
* the cache is consistent with the server. */
|
|
82
|
+
revalidate(path: string): Promise<void>;
|
|
83
|
+
/** Force-refresh the index for this directory (subset of
|
|
84
|
+
* `revalidate(<indexFile>)` exposed for clarity). */
|
|
85
|
+
revalidateIndex(): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
/** `VirtualFile` backed by a cache lookup + conditional fetch. */
|
|
88
|
+
export declare class CachedHttpFile implements VirtualFile {
|
|
89
|
+
#private;
|
|
90
|
+
readonly name: string;
|
|
91
|
+
readonly size: number;
|
|
92
|
+
constructor(args: {
|
|
93
|
+
name: string;
|
|
94
|
+
size: number;
|
|
95
|
+
url: string;
|
|
96
|
+
fetch: typeof globalThis.fetch;
|
|
97
|
+
backend: Promise<CacheBackend>;
|
|
98
|
+
cacheKey: string;
|
|
99
|
+
maxAgeMs: number;
|
|
100
|
+
});
|
|
101
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
102
|
+
/** Force a fresh conditional fetch and resolve once the cache is
|
|
103
|
+
* consistent with the server. */
|
|
104
|
+
revalidate(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
export declare class CachedHttpDirectory implements VirtualDirectory, CacheControl {
|
|
107
|
+
#private;
|
|
108
|
+
readonly name: string;
|
|
109
|
+
constructor(baseUrl: string, options?: CachedHttpDirectoryOptions);
|
|
110
|
+
file(name: string): Promise<VirtualFile | null>;
|
|
111
|
+
dir(name: string): Promise<VirtualDirectory | null>;
|
|
112
|
+
entries(): Promise<VirtualEntry[]>;
|
|
113
|
+
stats(): Promise<{
|
|
114
|
+
backend: 'opfs' | 'idb' | 'memory';
|
|
115
|
+
entries: number;
|
|
116
|
+
totalBytes: number;
|
|
117
|
+
}>;
|
|
118
|
+
clear(): Promise<void>;
|
|
119
|
+
revalidate(path: string): Promise<void>;
|
|
120
|
+
revalidateIndex(): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=cached-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-http.d.ts","sourceRoot":"","sources":["../src/cached-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAEL,KAAK,YAAY,EAGlB,MAAM,YAAY,CAAC;AAapB,wDAAwD;AACxD,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACtE;;kEAE8D;IAC9D,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC1C;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;uDACuD;AACvD,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,KAAK,IAAI,OAAO,CAAC;QACf,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,iDAAiD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;mDAC+C;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC;0DACsD;IACtD,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAwBD,kEAAkE;AAClE,qBAAa,cAAe,YAAW,WAAW;;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAOV,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;QAC/B,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QAC/B,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB;IAUK,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAwEzC;sCACkC;IAC5B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAKlC;AAID,qBAAa,mBAAoB,YAAW,gBAAgB,EAAE,YAAY;;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAcV,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B;IA6B/D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAmB/C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAkBnD,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAelC,KAAK,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAM7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBvC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CAGvC"}
|