diigo-cache: /public/<username>/all, searched client-side
Every stored shared bookmark on one page, in the public page's microformats. Without JS that's the whole list, and the search form asks the server's filtered page. With JS, the newest 5 show and search hides and shows entries in place. The browser runs the server's own matcher: query.ts becomes query.js (JSDoc types, checkJs), which the worker imports as code and serves as text at /_query.js via a `?raw` import and a wrangler Text rule. `shown()` picks what the page shows; the page's script reads items back from each h-entry. Tests: shown(); /all lists all 270 stored shared items, none hidden; /_query.js is the module the worker runs; and client-side search over /all's parsed microformats finds what the server's ?q= finds. The script itself was checked once in headless Chromium (5 of 54 shown by default; ?q=fragments shows 8), not in CI. Generated-by: Claude Opus 5.5
This commit is contained in:
1 parent
1d8a220bbc
commit
c3caaaeed1
12 files changed
+357
-86
No files matched your search
@@ -5,6 +5,8 @@ import worker from "../src/index.js";
|
||||
import { idFromWebkey, toWebkey } from "../src/webkeys.js";
|
||||
import { PAGE_SIZE } from "../src/cache.js";
|
||||
import { mf2 } from "microformats-parser";
|
||||
import { shown } from "../src/query.js";
|
||||
import QUERY_JS from "../src/query.js?raw";
|
||||
|
||||
// matches tests/fake-webapp.js: 30 items, every third private
|
||||
const WEBAPP_ID = "fake-webapp-id";
|
||||
@@ -411,6 +413,54 @@ describe("public page filters (goal 1d)", () => {
|
||||
expect(rels.next).toBeUndefined();
|
||||
});
|
||||
|
||||
it("/all lists every stored shared bookmark, none hidden, as h-entries", async () => {
|
||||
const { page } = await wireStored("fake-deep-webapp", 3);
|
||||
const html = await (await call(`${page}/all`)).text();
|
||||
// 300, one a day, minus every 10th (private)
|
||||
const want = Array.from({ length: 300 }, (_, k) => k + 1).filter((n) => n % 10 !== 0);
|
||||
expect(titlesIn(html)).toEqual(dated(...want));
|
||||
expect(html).not.toMatch(/<li[^>]* hidden/);
|
||||
const { items } = mf2(html, { baseUrl: `https://diigo-cache.test${page}/all` });
|
||||
expect(items.find((i) => i.type?.includes("h-feed"))?.children).toHaveLength(270);
|
||||
// without JS, the search form asks the server
|
||||
expect(html).toContain(`<form id="search" method="get" action="${page}">`);
|
||||
expect(html).toContain('import { shown } from "/_query.js"');
|
||||
});
|
||||
|
||||
it("/_query.js serves the worker's own matcher, as a browser module", async () => {
|
||||
const res = await call("/_query.js");
|
||||
expect(res.headers.get("content-type")).toMatch(/^text\/javascript/);
|
||||
const js = await res.text();
|
||||
expect(js).toBe(QUERY_JS);
|
||||
expect(js).toMatch(/^export function shown\(/m);
|
||||
expect(js).not.toMatch(/^import /m);
|
||||
});
|
||||
|
||||
it("searching /all's microformats client-side finds what the server finds", async () => {
|
||||
const page = await wireDated();
|
||||
const all = await (await call(`${page}/all`)).text();
|
||||
const entries = mf2(all, { baseUrl: `https://diigo-cache.test${page}/all` }).items.find((i) => i.type?.includes("h-feed"))?.children ?? [];
|
||||
// as the page's script reads each h-entry
|
||||
const secs = (v: unknown) => (v === undefined ? undefined : Date.parse(String(v)) / 1000);
|
||||
const items = entries.map(({ properties: p }) => ({
|
||||
title: p.name?.[0],
|
||||
url: p["bookmark-of"]?.[0],
|
||||
description: p.summary?.[0],
|
||||
tags: (p.category ?? []).join(","),
|
||||
created_at: secs(p.published?.[0]),
|
||||
updated_at: secs(p.updated?.[0]),
|
||||
}));
|
||||
const names = items.map((i) => i.title);
|
||||
for (const q of ["fragments", "#ocap -#weekly", "newer_than:7d", "created:2026 dated", '"dated 1"']) {
|
||||
const server = titlesIn(await (await call(`${page}?q=${encodeURIComponent(q)}`)).text());
|
||||
const result = shown(items, q, new Date());
|
||||
if ("error" in result) throw new Error(result.error);
|
||||
// the same matches; /all keeps its updated order, where created: sorts by created
|
||||
expect(names.filter((_, i) => result.show[i]).sort(), q).toEqual(server.sort());
|
||||
expect(server.length, q).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("filtered pages link the next page with rel=next, keeping the filter", async () => {
|
||||
const { page } = await wireStored("fake-deep-webapp", 3);
|
||||
const html = await (await call(`${page}?days=300`)).text();
|
||||
|
||||
Reference in new issue
Block a user