diigo-cache: bookmark cache in Durable Objects (goal 1c)
The dump worker becomes a page cache: a hit is served from DO SQLite storage with no diigo call; a miss asks the webapp instance in the cache's wiring and etches the page. - two independent caches, one behavior (PagedCache): DiigoCache keeps every item; PublicCache keeps shared items only, dropping private rows before they are stored; served at /public/<username> (HTML page form, or JSON) - create flow on one port: /_connect relays webapp's mint form, /_mint proxies the mint, /_wire wires a private or public cache; /_publish adds a public cache later - ids are webkeys: kept out of URLs (POST bodies), shown and logged only as a hash fingerprint; saved to the password manager under the holder's petname (never sent to the server) - items keyed on diigo's link_id; username from u_name; unkeyable items and every failure path are logged; each page read logs hit/miss - tests run the real worker + DOs in the vitest pool against a fake webapp (tests/fake-webapp.js) Generated-by: opencode (GLM) Generated-by: Claude Opus 5.5
This commit is contained in:
1 parent
c5e433cc9c
commit
6f409fc83a
15 files changed
+1664
-211
No files matched your search
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @file a stand-in for webapp's `WebAppFactory` entrypoint, for tests: an
|
||||
* auxiliary worker (plain JS — the vitest pool doesn't transform aux
|
||||
* workers) serving diigo-shaped `load_user_items` pages. 30 items, every
|
||||
* third private; one webapp id is known. Counts its diigo calls so tests
|
||||
* can tell a cache hit (no call) from a miss.
|
||||
*/
|
||||
import { RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
|
||||
|
||||
const KNOWN_WEBAPP = "fake-webapp-id";
|
||||
const TOTAL = 30;
|
||||
|
||||
/** item n (1-based, updated-desc order): diigo's field names, made-up values */
|
||||
function item(n) {
|
||||
return {
|
||||
link_id: 1000 + n,
|
||||
url: `https://example.com/${n}`,
|
||||
title: `bookmark ${n}`,
|
||||
description: "",
|
||||
private: n % 3 === 0,
|
||||
u_name: "alice_99",
|
||||
first_by: "someone-else",
|
||||
user_id: 4242,
|
||||
tags: "",
|
||||
created_at: 1700000000,
|
||||
updated_at: 1700001000 - n,
|
||||
};
|
||||
}
|
||||
|
||||
let calls = 0;
|
||||
|
||||
class FakeGet extends RpcTarget {
|
||||
#path;
|
||||
#params;
|
||||
constructor(path, params) {
|
||||
super();
|
||||
this.#path = path;
|
||||
this.#params = params;
|
||||
}
|
||||
pathjoin(ref) {
|
||||
return new FakeGet(ref, this.#params);
|
||||
}
|
||||
query(params) {
|
||||
return new FakeGet(this.#path, { ...this.#params, ...params });
|
||||
}
|
||||
async get() {
|
||||
calls += 1;
|
||||
if (this.#path !== "load_user_items") throw new Error(`fake webapp: no route ${this.#path}`);
|
||||
const count = Number(this.#params.count);
|
||||
const page = Number(this.#params.page_num);
|
||||
const items = [];
|
||||
for (let n = (page - 1) * count + 1; n <= Math.min(page * count, TOTAL); n += 1) items.push(item(n));
|
||||
return JSON.stringify({ total: TOTAL, items });
|
||||
}
|
||||
}
|
||||
|
||||
export class WebAppFactory extends WorkerEntrypoint {
|
||||
async at(id) {
|
||||
if (id !== KNOWN_WEBAPP) throw new Error("fake webapp: unknown id");
|
||||
return new FakeGet("", {});
|
||||
}
|
||||
/** how many diigo calls so far (test hook) */
|
||||
async calls() {
|
||||
return calls;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
fetch() {
|
||||
return new Response("fake webapp", { status: 404 });
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user