rafmacalaba commited on
Commit
5ce28bb
·
verified ·
1 Parent(s): 62ba62f

cache: Cache API primary + OPFS fallback, add storage diagnostics

Browse files
Files changed (1) hide show
  1. main.js +66 -14
main.js CHANGED
@@ -123,9 +123,10 @@ function setStatus(msg) {
123
  pill.className = "status-pill " + t;
124
  }
125
 
126
- // --- OPFS model cache -----------------------------------------------------------
127
- // OPFS (Origin Private File System) is not evicted under storage pressure and has
128
- // no per-entry size limit, unlike the Cache API. Large model weights live here.
 
129
  const CACHE_DIR = "datause-models";
130
 
131
  function hashKey(str) {
@@ -137,27 +138,57 @@ function hashKey(str) {
137
  return (h >>> 0).toString(16).padStart(8, "0");
138
  }
139
 
140
- async function getCacheDir() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  const root = await navigator.storage.getDirectory();
142
  return root.getDirectoryHandle(CACHE_DIR, { create: true });
143
  }
144
 
145
- async function cacheGet(key) {
 
146
  try {
147
- if (!navigator.storage?.getDirectory) return null;
148
- const dir = await getCacheDir();
149
  const name = hashKey(key);
150
  await dir.getFileHandle(name + ".ok"); // marker proves a complete prior write
151
  const handle = await dir.getFileHandle(name);
152
  const file = await handle.getFile();
153
  return await file.arrayBuffer();
154
- } catch { return null; }
 
 
 
155
  }
156
 
157
- async function cacheSet(key, buffer) {
 
158
  try {
159
- if (!navigator.storage?.getDirectory) return;
160
- const dir = await getCacheDir();
161
  const name = hashKey(key);
162
  try { await dir.removeEntry(name + ".ok"); } catch { /* not present yet */ }
163
  const handle = await dir.getFileHandle(name, { create: true });
@@ -165,7 +196,23 @@ async function cacheSet(key, buffer) {
165
  await writable.write(buffer);
166
  await writable.close();
167
  await dir.getFileHandle(name + ".ok", { create: true });
168
- } catch { /* storage full or interrupted — fall back to re-download next time */ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  }
170
 
171
  // --- download with progress + cache -------------------------------------------
@@ -201,8 +248,7 @@ async function loadFile(url, label) {
201
  log(`downloaded ${label} (${(received / 1e6).toFixed(1)} MB)`, "ok");
202
  showProgress(1, label);
203
  cacheSet(url, buffer.buffer)
204
- .then(() => log(`cached ${label}`, "ok"))
205
- .catch(() => log(`cache skipped for ${label} (quota?)`, "warn")); // never block on OPFS write
206
  return buffer.buffer;
207
  }
208
 
@@ -568,6 +614,12 @@ async function init() {
568
  .then((ok) => log(`persistent storage ${ok ? "granted" : "not granted"}`, ok ? "ok" : "warn"))
569
  .catch(() => {});
570
  }
 
 
 
 
 
 
571
  setStatus("Requesting WebGPU adapter…");
572
  const adapter = await navigator.gpu.requestAdapter();
573
  if (!adapter) { setStatus("WebGPU adapter not found"); log("WebGPU adapter not found", "warn"); return null; }
 
123
  pill.className = "status-pill " + t;
124
  }
125
 
126
+ // --- Browser model cache --------------------------------------------------------
127
+ // Primary: Cache API (same backend transformers.js uses, proven in HF Spaces).
128
+ // Fallback: OPFS, used when the Cache API is unavailable or rejects a write.
129
+ const CACHE_NAME = "datause-models";
130
  const CACHE_DIR = "datause-models";
131
 
132
  function hashKey(str) {
 
138
  return (h >>> 0).toString(16).padStart(8, "0");
139
  }
140
 
141
+ // Cache API backend ----------------------------------------------------------
142
+ async function readCacheApi(key) {
143
+ if (typeof caches === "undefined") return null;
144
+ try {
145
+ const cache = await caches.open(CACHE_NAME);
146
+ const resp = await cache.match(key);
147
+ if (!resp) return null;
148
+ return await resp.arrayBuffer();
149
+ } catch (e) {
150
+ log(`cache read failed (Cache API): ${e?.name || e}`, "warn");
151
+ return null;
152
+ }
153
+ }
154
+
155
+ async function writeCacheApi(key, buffer) {
156
+ if (typeof caches === "undefined") return false;
157
+ try {
158
+ const cache = await caches.open(CACHE_NAME);
159
+ await cache.put(key, new Response(buffer));
160
+ return true;
161
+ } catch (e) {
162
+ log(`cache write failed (Cache API): ${e?.name || e}`, "warn");
163
+ return false;
164
+ }
165
+ }
166
+
167
+ // OPFS backend ---------------------------------------------------------------
168
+ async function opfsCacheDir() {
169
  const root = await navigator.storage.getDirectory();
170
  return root.getDirectoryHandle(CACHE_DIR, { create: true });
171
  }
172
 
173
+ async function readOpfs(key) {
174
+ if (!navigator.storage?.getDirectory) return null;
175
  try {
176
+ const dir = await opfsCacheDir();
 
177
  const name = hashKey(key);
178
  await dir.getFileHandle(name + ".ok"); // marker proves a complete prior write
179
  const handle = await dir.getFileHandle(name);
180
  const file = await handle.getFile();
181
  return await file.arrayBuffer();
182
+ } catch (e) {
183
+ log(`cache read failed (OPFS): ${e?.name || e}`, "warn");
184
+ return null;
185
+ }
186
  }
187
 
188
+ async function writeOpfs(key, buffer) {
189
+ if (!navigator.storage?.getDirectory) return false;
190
  try {
191
+ const dir = await opfsCacheDir();
 
192
  const name = hashKey(key);
193
  try { await dir.removeEntry(name + ".ok"); } catch { /* not present yet */ }
194
  const handle = await dir.getFileHandle(name, { create: true });
 
196
  await writable.write(buffer);
197
  await writable.close();
198
  await dir.getFileHandle(name + ".ok", { create: true });
199
+ return true;
200
+ } catch (e) {
201
+ log(`cache write failed (OPFS): ${e?.name || e}`, "warn");
202
+ return false;
203
+ }
204
+ }
205
+
206
+ // Unified entry points -------------------------------------------------------
207
+ async function cacheGet(key) {
208
+ const viaCacheApi = await readCacheApi(key);
209
+ if (viaCacheApi) return viaCacheApi;
210
+ return readOpfs(key);
211
+ }
212
+
213
+ async function cacheSet(key, buffer) {
214
+ const ok = await writeCacheApi(key, buffer);
215
+ if (!ok) await writeOpfs(key, buffer);
216
  }
217
 
218
  // --- download with progress + cache -------------------------------------------
 
248
  log(`downloaded ${label} (${(received / 1e6).toFixed(1)} MB)`, "ok");
249
  showProgress(1, label);
250
  cacheSet(url, buffer.buffer)
251
+ .then(() => log(`cached ${label}`, "ok"));
 
252
  return buffer.buffer;
253
  }
254
 
 
614
  .then((ok) => log(`persistent storage ${ok ? "granted" : "not granted"}`, ok ? "ok" : "warn"))
615
  .catch(() => {});
616
  }
617
+ log(`env: caches=${typeof caches !== "undefined"} opfs=${!!navigator.storage?.getDirectory} embedded=${window.self !== window.top}`, "ok");
618
+ if (navigator.storage?.estimate) {
619
+ navigator.storage.estimate()
620
+ .then(({ usage, quota }) => log(`storage: ${(usage / 1e6).toFixed(1)} MB used / ${(quota / 1e6).toFixed(1)} MB quota`, "ok"))
621
+ .catch(() => {});
622
+ }
623
  setStatus("Requesting WebGPU adapter…");
624
  const adapter = await navigator.gpu.requestAdapter();
625
  if (!adapter) { setStatus("WebGPU adapter not found"); log("WebGPU adapter not found", "warn"); return null; }