@fangorn-network/westmarch 0.0.2-dev → 0.0.3-dev

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/site/store.js ADDED
@@ -0,0 +1,87 @@
1
+ // What a person does on the page, kept on their own device: what they saved, what they
2
+ // passed on, and what they searched this session. Nothing leaves the browser unless they
3
+ // export it. The saved items are also the likes a taste is built from (consume/taste.js),
4
+ // so "For you" is theirs, and so is the file they can hand an agent.
5
+ //
6
+ // Saved items are snapshots keyed by the publisher's own id (the identity role), not the
7
+ // row's CID: a record re-published with a new CID must still be the item they saved.
8
+ import { exportTaste, taste } from "../consume/taste.js";
9
+ import { packVec, unpackVec } from "../consume/embed.js";
10
+
11
+ const read = (s, k, dflt) => { try { return JSON.parse(s?.getItem(k) ?? "null") ?? dflt; } catch { return dflt; } };
12
+ const write = (s, k, v) => { try { s?.setItem(k, JSON.stringify(v)); } catch { /* private mode or full: the page still works */ } };
13
+
14
+ /** `local` keeps saved items across visits, `session` keeps this visit's searches. */
15
+ export function createStore({ local, session, app = "app" } = {}) {
16
+ const K = { saved: `${app}:saved`, passed: `${app}:passed`, history: `${app}:history` };
17
+ return {
18
+ saved: () => read(local, K.saved, []),
19
+ passed: () => read(local, K.passed, []),
20
+ history: () => read(session, K.history, []),
21
+ isSaved(key) { return this.saved().some((s) => s.key === key); },
22
+ toggleSave(item) {
23
+ const all = this.saved(), at = all.findIndex((s) => s.key === item.key);
24
+ if (at >= 0) all.splice(at, 1); else all.push({ ...item, savedAt: new Date().toISOString() });
25
+ write(local, K.saved, all);
26
+ write(local, K.passed, this.passed().filter((p) => p.key !== item.key));
27
+ return at < 0;
28
+ },
29
+ pass(item) {
30
+ write(local, K.passed, [...this.passed().filter((p) => p.key !== item.key), item].slice(-50));
31
+ write(local, K.saved, this.saved().filter((s) => s.key !== item.key));
32
+ },
33
+ remember(query) {
34
+ const q = query.trim();
35
+ if (!q) return;
36
+ write(session, K.history, [{ q, at: new Date().toISOString() }, ...this.history().filter((h) => h.q !== q)].slice(0, 50));
37
+ },
38
+ clear(which) { write(which === "history" ? session : local, K[which], []); },
39
+ /** The taste these choices make, or null before the first save. */
40
+ taste() {
41
+ const vec = (x) => ({ id: x.key, title: x.title, vector: x.v ? unpackVec(x.v) : null });
42
+ return taste(this.saved().map(vec), this.passed().map(vec));
43
+ },
44
+ /** Everything, for the person to keep or hand to an agent. */
45
+ bundle({ name = app, url = "" } = {}) {
46
+ const strip = ({ v, ...x }) => x;
47
+ return { app: name, url, exported: new Date().toISOString(), saved: this.saved().map(strip),
48
+ searches: this.history(), taste: exportTaste(this.taste()) };
49
+ },
50
+ };
51
+ }
52
+
53
+ /** A saved-item snapshot of a row: enough to list it, link it and rank by it later. */
54
+ export const snapshot = (r, { key, title, subtitle, place, date, link, detail }) => ({
55
+ key, title, subtitle, place, date, url: link ?? null, detail: detail ?? "", ...(r.vector ? { v: packVec(Array.from(r.vector)) } : {}),
56
+ });
57
+
58
+ const csvCell = (v) => `"${String(v ?? "").replace(/"/g, '""')}"`;
59
+ export const toCSV = (items) => ["title,place,subtitle,date,url,detail", ...items.map((i) =>
60
+ [i.title, i.place, i.subtitle, i.date, i.url, i.detail].map(csvCell).join(","))].join("\n");
61
+ export const toMarkdown = (items, name = "Saved") => [`# ${name}`, "", ...items.map((i) =>
62
+ `- **${i.url ? `[${i.title}](${i.url})` : i.title}** — ${[i.place, i.subtitle].filter(Boolean).join(" · ")}${i.detail ? `\n ${i.detail}` : ""}`)].join("\n");
63
+
64
+ // ── self-check: `node site/store.js` ──
65
+ if (typeof process !== "undefined" && process.argv[1]?.endsWith("/store.js")) {
66
+ const mem = () => { const m = new Map(); return { getItem: (k) => m.get(k) ?? null, setItem: (k, v) => m.set(k, v) }; };
67
+ const s = createStore({ local: mem(), session: mem(), app: "t" });
68
+ const row = (i) => ({ vector: Array.from({ length: 8 }, (_, j) => (j === i ? 1 : 0.1)) });
69
+ const a = snapshot(row(0), { key: "a", title: "Budget", place: "X", date: "2026-01-01" });
70
+ const b = snapshot(row(1), { key: "b", title: "Roads, \"east\"", place: "Y" });
71
+ const eq = (x, y, m) => { if (JSON.stringify(x) !== JSON.stringify(y)) throw new Error(`${m}: ${JSON.stringify(x)}`); };
72
+ eq(s.taste(), null, "no taste before a save");
73
+ s.toggleSave(a); s.toggleSave(b);
74
+ eq(s.saved().map((x) => x.key), ["a", "b"], "saved in order");
75
+ if (!s.taste()?.q) throw new Error("saves make a taste");
76
+ s.pass(b);
77
+ eq([s.saved().map((x) => x.key), s.passed().map((x) => x.key)], [["a"], ["b"]], "passing on an item unsaves it");
78
+ eq(s.taste().rejected, ["Roads, \"east\""], "and it steers the taste away");
79
+ s.remember("snow"); s.remember("budget"); s.remember("snow");
80
+ eq(s.history().map((h) => h.q), ["snow", "budget"], "history: newest first, no repeats");
81
+ const out = s.bundle({ name: "T" });
82
+ if (out.saved[0].v || !out.taste?.q || out.searches.length !== 2) throw new Error("the bundle carries choices and taste, not raw vectors");
83
+ if (!toCSV([b]).includes('"Roads, ""east"""')) throw new Error("CSV quoting");
84
+ if (createStore({ local: { getItem() { throw new Error("blocked"); }, setItem() { throw new Error("blocked"); } } }).saved().length !== 0)
85
+ throw new Error("blocked storage reads as empty");
86
+ console.log("store.js self-check ok — saves and passes steer a taste, history dedupes, the bundle carries no raw vectors, CSV quotes, blocked storage is empty not fatal");
87
+ }
package/site/worker.js ADDED
@@ -0,0 +1,103 @@
1
+ // The app's front door (Cloudflare Pages advanced mode): the static site, plus two things a
2
+ // static site cannot do. `westmarch-ship` writes this to site/_worker.js with CONFIG filled in.
3
+ //
4
+ // GET /doc?u=<url> a record's source document, served from this origin so the page can
5
+ // show it inline. Source sites forbid framing and send no CORS, so the
6
+ // page cannot fetch or frame them itself. Only hosts the app's own
7
+ // records link to (CONFIG.docHosts), only PDFs, cached for a day.
8
+ // GET /paid/<item id> with CONFIG.paid: 402 + the price; with a valid X-PAYMENT header, the
9
+ // facilitator verifies and settles it, then the record is returned
10
+ // GET /_paid/* 404 to everyone: the records sit here as static files, and the
11
+ // only way to one is through a settled payment
12
+ // anything else the static site, with the headers _headers would have set
13
+ //
14
+ // A record that does not exist is a 404 before any price is quoted: nobody pays for nothing.
15
+ // Each record's sha256 is in the app's public (committed) data, so a buyer can check it.
16
+
17
+ const CONFIG = /*CONFIG*/ { docHosts: [], paid: null } /*END*/;
18
+
19
+ const CORS = { "access-control-allow-origin": "*", "access-control-allow-headers": "X-PAYMENT, content-type", "access-control-expose-headers": "X-PAYMENT-RESPONSE" };
20
+ const json = (o, status = 200, headers = {}) =>
21
+ new Response(JSON.stringify(o), { status, headers: { "content-type": "application/json", ...CORS, ...headers } });
22
+ const note = (msg, status) => new Response(`<!doctype html><meta charset="utf-8"><body style="font:15px system-ui;padding:24px;color:#555">${msg}</body>`,
23
+ { status, headers: { "content-type": "text/html; charset=utf-8" } });
24
+
25
+ async function doc(request, url) {
26
+ let src;
27
+ try { src = new URL(url.searchParams.get("u") ?? ""); } catch { return note("No document named.", 400); }
28
+ if (src.protocol !== "https:" || !CONFIG.docHosts.includes(src.hostname)) return note("Not one of this app's sources.", 403);
29
+ const cache = caches.default, key = new Request(url.href);
30
+ const hit = await cache.match(key);
31
+ if (hit) return hit;
32
+ const up = await fetch(src.href, { headers: { "user-agent": "Mozilla/5.0 (compatible; fangorn-app/0.1; +https://fangorn.network)" }, redirect: "follow" });
33
+ // A redirect may not leave the allowed hosts, and only a PDF is shown: an HTML page from
34
+ // someone else's site, served from this origin, would run as this app.
35
+ if (!CONFIG.docHosts.includes(new URL(up.url).hostname)) return note("The source redirected elsewhere.", 403);
36
+ if (!up.ok || !(up.headers.get("content-type") ?? "").includes("pdf"))
37
+ return note(`This document is not a PDF. <a href="${src.href.replace(/"/g, "&quot;")}" target="_blank" rel="noopener">Open it on the source site</a>.`, 415);
38
+ const res = new Response(up.body, { headers: { "content-type": "application/pdf", "content-disposition": "inline",
39
+ "cache-control": "public, max-age=86400", "x-content-type-options": "nosniff" } });
40
+ await cache.put(key, res.clone());
41
+ return res;
42
+ }
43
+
44
+ async function fileFor(id) {
45
+ const d = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(id)));
46
+ return [...d].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 32);
47
+ }
48
+
49
+ async function facilitator(path, payment, requirements) {
50
+ const r = await fetch(`${CONFIG.paid.facilitator}/${path}`, {
51
+ method: "POST", headers: { "content-type": "application/json" },
52
+ body: JSON.stringify({ x402Version: 1, paymentPayload: payment, paymentRequirements: requirements }),
53
+ });
54
+ const text = await r.text();
55
+ try { return JSON.parse(text); } catch { return { error: `facilitator ${path}: HTTP ${r.status} ${text.slice(0, 200)}` }; }
56
+ }
57
+
58
+ async function sell(request, env, url) {
59
+ const P = CONFIG.paid;
60
+ const id = decodeURIComponent(url.pathname.slice("/paid/".length));
61
+ const record = await env.ASSETS.fetch(new URL(`/_paid/${await fileFor(id)}.json`, url));
62
+ // Pages answers an unknown path with index.html and a 200 (its single-page fallback), so
63
+ // "ok" is not "exists": only a JSON body is a record. Nobody is quoted a price for nothing.
64
+ if (!record.ok || !(record.headers.get("content-type") ?? "").includes("json")) return json({ error: `no paid record for ${id}` }, 404);
65
+ const requirements = {
66
+ scheme: "exact", network: P.network, maxAmountRequired: P.price, resource: url.href,
67
+ description: P.description, mimeType: "application/json", payTo: P.payTo,
68
+ maxTimeoutSeconds: 120, asset: P.asset, extra: { name: P.name, version: P.version },
69
+ };
70
+ const refuse = (error) => json({ x402Version: 1, error, accepts: [requirements] }, 402);
71
+ const header = request.headers.get("X-PAYMENT");
72
+ if (!header) return refuse("X-PAYMENT header is required");
73
+ let payment;
74
+ try { payment = JSON.parse(atob(header)); } catch { return refuse("X-PAYMENT is not base64 JSON"); }
75
+ const verified = await facilitator("verify", payment, requirements);
76
+ if (!verified.isValid) return refuse(verified.invalidReason ?? verified.error ?? "payment did not verify");
77
+ const settled = await facilitator("settle", payment, requirements);
78
+ if (!settled.success) return refuse(settled.errorReason ?? settled.error ?? "settlement failed");
79
+ return new Response(await record.text(), {
80
+ headers: { "content-type": "application/json", "x-payment-response": btoa(JSON.stringify(settled)), ...CORS },
81
+ });
82
+ }
83
+
84
+ export default {
85
+ async fetch(request, env) {
86
+ const url = new URL(request.url);
87
+ if (request.method === "OPTIONS") return new Response(null, { status: 204, headers: CORS });
88
+ if (url.pathname.startsWith("/_paid/") || url.pathname === "/_worker.js") return new Response("not found", { status: 404 });
89
+ if (url.pathname === "/doc") return doc(request, url);
90
+ if (url.pathname.startsWith("/paid/")) return CONFIG.paid ? sell(request, env, url) : json({ error: "nothing is sold here" }, 404);
91
+
92
+ const res = await env.ASSETS.fetch(request);
93
+ const out = new Response(res.body, res);
94
+ const p = url.pathname;
95
+ if (p === "/.well-known/agent-card.json" || p.startsWith("/view/")) out.headers.set("access-control-allow-origin", "*");
96
+ if (p.startsWith("/assets/")) out.headers.set("cache-control", "public, max-age=31536000, immutable");
97
+ // Shards are content-named, but not forever: a retracted record's shard is deleted, and
98
+ // pages.dev's edge cache cannot be purged. An hour bounds how long a deleted one lingers.
99
+ else if (/^\/view\/cdn\/domains\/[^/]+\/shards\//.test(p))
100
+ out.headers.set("cache-control", (res.headers.get("content-type") ?? "").includes("html") ? "no-store" : "public, max-age=3600");
101
+ return out;
102
+ },
103
+ };
@@ -1,6 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./transformers.web-u9uSoh5E.js","./rolldown-runtime-Dd_uD5pT.js","./preload-helper-uBIymjUX.js","./__vite-browser-external-CKnVkMRn.js","./roles-YDRjv_PY.js","./roles-C9V_uDr_.js"])))=>i.map(i=>d[i]);
2
- import{i as e}from"./rolldown-runtime-Dd_uD5pT.js";import{t}from"./preload-helper-uBIymjUX.js";import{c as n,d as r,f as i,g as a,h as o,m as s,n as c,p as l,r as u,s as d}from"./roles-C9V_uDr_.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var f=`nomic-ai/nomic-embed-text-v1.5`,p=256,m=null,ee=()=>m??=(async()=>{let{pipeline:e}=await t(async()=>{let{pipeline:e}=await import(`./transformers.web-u9uSoh5E.js`);return{pipeline:e}},__vite__mapDeps([0,1,2]),import.meta.url);return e(`feature-extraction`,f,{dtype:`q8`})})();function h(e,t=p){let n=e.length,r=0;for(let t=0;t<n;t++)r+=e[t];r/=n;let i=0;for(let t=0;t<n;t++)i+=(e[t]-r)**2;let a=Math.sqrt(i/n+1e-5),o=Array(Math.min(t,n));for(let t=0;t<o.length;t++)o[t]=(e[t]-r)/a;let s=0;for(let e of o)s+=e*e;if(s=Math.sqrt(s),s)for(let e=0;e<o.length;e++)o[e]/=s;return o}async function g(e){return h((await(await ee())(`search_query: ${e}`,{pooling:`mean`,normalize:!1})).data)}var _=()=>typeof window<`u`&&typeof Worker<`u`,v=null,te=0,y=new Map;function ne(){return v||(v=new Worker(new URL(new URL(`embed.worker-CQqmvS52.js`,import.meta.url).href,``+import.meta.url),{type:`module`}),v.onmessage=({data:e})=>{let t=y.get(e.id);t&&(y.delete(e.id),e.error?t.reject(Error(e.error)):t.resolve(e.vec))},v.onerror=e=>{for(let t of y.values())t.reject(Error(`embed worker: ${e?.message??`failed to start`}`));y.clear(),v=null},v)}var re=(e,t)=>new Promise((n,r)=>{let i=++te;y.set(i,{resolve:n,reject:r}),ne().postMessage({id:i,kind:e,text:t})}),ie=e=>_()?re(`query`,e):g(e);function ae(e){let t=new Uint8Array(e.length);for(let n=0;n<e.length;n++){let r=Math.round(Math.max(-1,Math.min(1,e[n]))*127);t[n]=r<0?r+256:r}let n=``;for(let e of t)n+=String.fromCharCode(e);return btoa(n)}function b(e){try{let t=atob(e),n=new Float32Array(t.length);for(let e=0;e<t.length;e++){let r=t.charCodeAt(e);n[e]=(r>127?r-256:r)/127}return n.length?n:null}catch{return null}}function oe(e){let t=new Uint8Array(Math.ceil(e.length/8));for(let n=0;n<e.length;n++)e[n]>=0&&(t[n>>3]|=128>>(n&7));let n=``;for(let e of t)n+=String.fromCharCode(e);return btoa(n)}function se(e){try{let t=atob(e.padEnd(Math.ceil(e.length/4)*4,`=`)),n=new Float32Array(t.length*8);for(let e=0;e<n.length;e++)n[e]=t.charCodeAt(e>>3)>>7-(e&7)&1?1:-1;return n.length?n:null}catch{return null}}if(typeof process<`u`&&import.meta.url===`file://${process.argv[1]}`&&process.argv[1].endsWith(`/embed.js`)){let e=h(Array.from({length:768},(e,t)=>Math.sin(t))),t=0;for(let n of e)t+=n*n;if(Math.abs(Math.sqrt(t)-1)>1e-6)throw Error(`matryoshka must L2-normalize`);if(e.length!==p)throw Error(`matryoshka must truncate to ${p}`);let n=b(ae(e));if(n.length!==e.length)throw Error(`pack/unpack changed the dimension`);let r=0,i=0;for(let t=0;t<e.length;t++)r+=e[t]*n[t],i+=n[t]*n[t];let a=r/Math.sqrt(i);if(a<.999)throw Error(`int8 round-trip cosine ${a} — too lossy to rank with`);let o=b(ae([-1,-.5,0,.5,1]));if(o[0]>-.99||o[1]>-.49||o[1]<-.51)throw Error(`negative components corrupted: ${[...o]}`);if(b(`!!!not base64!!!`)!==null)throw Error(`malformed vector must return null, not throw`);let s=oe(e);if(s.length>48)throw Error(`sign bits must stay link-sized, got ${s.length} chars`);let c=se(s);if(c.length!==e.length)throw Error(`sign round-trip changed the dimension`);for(let t=0;t<e.length;t++)if(Math.sign(c[t])!==(e[t]<0?-1:1))throw Error(`sign flipped at ${t}`);if(oe(c)!==s)throw Error(`sign packing must be idempotent`);if(se(`!!!not base64!!!`)!==null)throw Error(`malformed bits must return null, not throw`);console.log(`embed.js self-check ok — matryoshka norm, int8 round-trip, negatives, malformed input, sign bits`)}var x=(e,t,n)=>{let r=0;for(let n=0;n<t.length&&n<e.vector.length;n++)r+=e.vector[n]*t[n];return r/(e.norm*n)},S=e=>{let t=0;for(let n of e)t+=n*n;return Math.sqrt(t)||1};function C(e,t=1){if(e.length<3)return-1/0;let n=e.reduce((e,t)=>e+t,0)/e.length,r=Math.sqrt(e.reduce((e,t)=>e+(t-n)**2,0)/e.length);return r>1e-6?n+t*r:-1/0}function w(e,{limit:t=10,lambda:n=.7,key:r=`s`}={}){if(n>=1||e.length<=1)return e.slice(0,t);let i=e.slice(0,Math.max(t*6,30)),a=[];for(;a.length<t&&i.length;){let e=0,t=-1/0;for(let o=0;o<i.length;o++){let s=0;for(let e of a){if(!i[o].row?.vector||!e.row?.vector)continue;let t=x(i[o].row,e.row.vector,e.row.norm);t>s&&(s=t)}let c=n*i[o][r]-(1-n)*s;c>t&&(t=c,e=o)}a.push(i.splice(e,1)[0])}return a}var ce=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),le=e=>RegExp(`\\b${ce(e)}\\b`);function T(e,t){let n=String(e.text??``).toLowerCase();if(!n)return 0;let r=t.split(/\s+/).filter(Boolean);if(!r.length)return 0;let i=0;for(let e of r)le(e).test(n)&&i++;if(!i)return 0;let a=n.indexOf(t);return Math.min(1,.5*(i/r.length)+(le(t).test(n)?.35:0)+(a>=0?.15*(1-a/n.length):0))}function ue(e,t){let n=e[0],r=0;for(let i of e){let e=T({text:i.text},t);e>r&&(r=e,n=i)}return n}function de(e=[],t){let n=S(t);return e.map(e=>{let r=e.coverage;if(!r?.vectors?.length)return{...e,affinity:null};let i=-1;for(let e of r.vectors){let r=0,a=0;for(let n=0;n<e.length&&n<t.length;n++)r+=e[n]*t[n],a+=e[n]*e[n];let o=r/((Math.sqrt(a)||1)*n);o>i&&(i=o)}return{...e,affinity:i}}).sort((e,t)=>(t.affinity??-1/0)-(e.affinity??-1/0))}if(typeof process<`u`&&import.meta.url===`file://${process.argv[1]}`&&process.argv[1].endsWith(`/rank.js`)){if(T({text:`intoxicated education`},`cat`)!==0)throw Error(`substring matched — word boundaries lost`);if(T({text:`a cat sat`},`cat`)<=0)throw Error(`whole word must match`);let e=T({text:`cold war submarines`},`cold war`),t=T({text:`cold nights, the war ended`},`cold war`);if(!(e>t))throw Error(`phrase bonus lost: ${e} vs ${t}`);if(T({text:`what is c++`},`c++`)===void 0)throw Error(`must not throw on regex metachars`);if(Math.abs(x({vector:[1,0,0,0],norm:1},[1,0],1)-1)>1e-9)throw Error(`cosine must truncate to the query`);if(C([.5,.5,.5,.5])!==-1/0)throw Error(`no spread must admit everything, not nothing`);if(C([.4,.4])!==-1/0)throw Error(`too few rows to have a distribution`);let n=C([.4,.41,.42,.9]);if(!(.9>=n&&.42<n))throw Error(`floor did not separate the outlier: ${n}`);let r=[{text:`opening titles`},{text:`the submarine dives`}];if(ue(r,`submarine`).text!==`the submarine dives`)throw Error(`wrong passage quoted`);if(ue(r,`nothing here`).text!==`opening titles`)throw Error(`must fall back to the first passage`);{let e=(e,t)=>({vector:[e,t],norm:Math.sqrt(e*e+t*t)||1}),t=[{row:{id:`ep1`,...e(1,0)},s:.9},{row:{id:`ep2`,...e(1,.01)},s:.89},{row:{id:`ep3`,...e(1,.02)},s:.88},{row:{id:`other`,...e(0,1)},s:.7}],n=w(t,{limit:2,lambda:1}).map(e=>e.row.id);if(n.join()!==`ep1,ep2`)throw Error(`lambda 1 must be the old behaviour: ${n}`);let r=w(t,{limit:2,lambda:.7}).map(e=>e.row.id);if(r.join()!==`ep1,other`)throw Error(`MMR must not return the same episode twice: ${r}`);if(w(t,{limit:4,lambda:.5})[0].row.id!==`ep1`)throw Error(`the top hit must survive`);if(w([{row:{id:`a`},s:1}],{limit:3}).length!==1)throw Error(`vectorless rows must pass through`)}let i=de([{name:`old`,coverage:null},{name:`far`,coverage:{vectors:[[0,1]]}},{name:`near`,coverage:{vectors:[[1,0]]}}],[1,0]);if(i.map(e=>e.name).join(`,`)!==`near,far,old`)throw Error(`domains misranked: ${i.map(e=>e.name)}`);if(i[2].affinity!==null)throw Error(`an unmeasured domain must be null, not 0`);console.log(`rank.js self-check ok — word boundaries, matryoshka cosine, z-floor, MMR drops near-duplicates without moving the top hit, passage pick, domain affinity`)}var E=null,D=null,O=null,k=new Map,A=new Map,fe=new Map,pe=e=>[e.name,e.path,e.desc].filter(Boolean).join(` `),me=null,he=null;function j({resolveView:e,rowText:t,onManifests:n}={}){e&&(me=e),t&&(pe=t),n&&(he=n)}var M=()=>{k.clear(),A.clear(),fe.clear(),E=null,D=null,O=null};function ge(e){D=e?ve(e):null}var _e=()=>D,ve=e=>String(e).replace(/\/+$/,``).replace(/\/(stream|cdn)$/,``);function N(){return me?E??=Promise.resolve().then(()=>me()).then(e=>e?ve(e):null).catch(e=>{throw E=null,e}):Promise.resolve(null)}function ye(e){let t=(Array.isArray(e)?e:String(e??``).split(`,`)).map(e=>String(e).trim()).filter(Boolean);O=t.length?t:null}var be=e=>!O||O.some(t=>e===t||e.split(`/`).pop()===t||e.startsWith(t)),xe=`no quickbeam view configured — pass a view base, or wire one with configure({ resolveView })`;async function P(e,t){let n=e??D??await N();if(!n)throw Error(xe);let r=A.get(n);r||A.set(n,r=new Set),t&&r.add(t);let i=k.get(n);if(i)return i;let a=Oe(n,e=>{for(let t of r)try{t(e)}catch{}}).finally(()=>A.delete(n)).catch(e=>{throw k.delete(n),e});return k.set(n,a),a}async function F(e){let t=await fetch(e);if(!t.ok)throw Error(`shard: HTTP ${t.status} for ${e}`);return Se(new Uint8Array(await t.arrayBuffer()))}var Se=async e=>e[0]===31&&e[1]===139?await new Response(new Blob([e]).stream().pipeThrough(new DecompressionStream(`gzip`))).text():new TextDecoder().decode(e);function Ce(e,t){let n=e.fields??{},r=typeof e.v==`string`?b(e.v):Array.isArray(e.embedding)?e.embedding:typeof n.embed?.vec==`string`?b(n.embed.vec):null,i=0;if(r)for(let e of r)i+=e*e;return{id:e.track_id,owner:e.owner,...n,text:pe(n,t),vector:r,norm:Math.sqrt(i)||1}}async function we(e){let t=0;for(let n of e)t+=n.length;let n=new Uint8Array(t),r=0;for(let t of e)n.set(t,r),r+=t.length;return[...new Uint8Array(await crypto.subtle.digest(`SHA-256`,n))].map(e=>e.toString(16).padStart(2,`0`)).join(``)}async function Te(e,t,n,r){if(!t)return;if(!r){console.warn(`shard: cannot verify ${e} — the transport decoded it (Content-Encoding), and the manifest digest is over the gzipped bytes`);return}let i=await we(n);if(i!==t)throw Error(`shard: ${e} does not match the digest in its manifest (expected ${t.slice(0,12)}…, got ${i.slice(0,12)}…)`)}async function Ee(e,t,n){let r=await fetch(e);if(!r.ok)throw Error(`shard: HTTP ${r.status} for ${e}`);if(!r.body){let i=new Uint8Array(await r.arrayBuffer());await Te(e,n,[i],i[0]===31&&i[1]===139);for(let e of(await Se(i)).split(`
3
- `))t(e);return}let i=r.body.getReader(),a=await i.read(),o=a.value??new Uint8Array,s=n?[]:null,c=new ReadableStream({start(e){o.length&&(s?.push(o),e.enqueue(o)),a.done&&e.close()},async pull(e){let{done:t,value:n}=await i.read();t?e.close():(s?.push(n),e.enqueue(n))},cancel(e){return i.cancel(e)}}),l=o[0]===31&&o[1]===139,u=l?c.pipeThrough(new DecompressionStream(`gzip`)):c,d=new TextDecoder,f=``;for(let r=u.getReader();;){let{done:i,value:a}=await r.read();f+=i?d.decode():d.decode(a,{stream:!0});let o=f.split(`
4
- `);f=i?``:o.pop();for(let e of o)t(e);if(i){f&&t(f),await Te(e,n,s,l);return}}}var De=250;async function Oe(e,t){let n=`${e}/cdn`,{domains:r=[]}=JSON.parse(await F(`${n}/catalog`)),i=r.filter(e=>be(e.name));O&&i.length!==r.length&&console.info(`shard: pulling ${i.length} of ${r.length} domains (allowlist: ${O.join(`,`)})`),O&&!i.length&&console.warn(`shard: allowlist ${O.join(`,`)} matched none of ${r.map(e=>e.name).join(`, `)} — nothing will load`),i.length||console.warn(`shard: quickbeam view ${n} has no baked domains yet — nothing to read until the watcher bakes one.`);let a=new Map,o=new Set,s=await Promise.all(i.map(async e=>{let t=`${n}/domains/${encodeURIComponent(e.name)}`,r=JSON.parse(await F(`${t}/manifest`));for(let e of r.tombstones??[])o.add(e);return{at:t,manifest:r,shards:r.shards??[]}})),c=s.map(e=>e.manifest);fe.set(e,c);try{he?.(c,e)}catch(e){console.warn(`shard: onManifests threw, continuing with the default rowText —`,e)}let l=Date.now(),u=e=>{!t||!e&&Date.now()-l<De||(t([...a.values()]),l=Date.now())};return await Promise.all(s.map(async({at:t,shards:n})=>{for(let r of n)await Ee(`${t}/shards/${r.file}`,t=>{if(!t.trim())return;let n=Ce(JSON.parse(t),e);o.has(n.id)||a.set(n.id,n),u()},r.sha256),u(!0)})),[...a.values()]}function ke(e,t){let n=null,r=!1;return(async()=>{let i=t??D??await N();if(!i||r)return;n=new EventSource(`${i}/stream`);let a=()=>{M(),e()};n.addEventListener(`added`,a),n.addEventListener(`change`,a)})().catch(()=>{}),()=>{r=!0,n?.close()}}async function Ae(e,{url:t}={}){let n=t??D??await N();if(!n)throw Error(xe);let{domains:r=[]}=JSON.parse(await F(`${n}/cdn/catalog`));return de(r,e)}if(typeof process<`u`&&import.meta.url===`file://${process.argv[1]}`&&process.argv[1].endsWith(`/shard.js`)){let{gzipSync:n}=await t(async()=>{let{gzipSync:t}=await import(`./__vite-browser-external-CKnVkMRn.js`).then(t=>e(t.default,1));return{gzipSync:t}},__vite__mapDeps([3,1]),import.meta.url),r=console.warn,i=console.info;console.warn=console.info=()=>{};let a=(e,t,n={},r)=>({track_id:`${e}:${t}`,owner:e,...r?{embedding:r}:{},fields:{entityType:`file`,name:t.split(`/`).pop(),path:t,...n}}),o=e=>e.map(e=>JSON.stringify(e)).join(`
5
- `),s=`https://registry.test/q/qb_test`,c=e=>{let t=Buffer.from(e),n=Math.max(1,Math.floor(t.length/2));return{ok:!0,headers:new Headers,arrayBuffer:async()=>t,body:new ReadableStream({start(e){e.enqueue(new Uint8Array(t.subarray(0,n))),e.enqueue(new Uint8Array(t.subarray(n))),e.close()}})}},l=async e=>[...new Uint8Array(await crypto.subtle.digest(`SHA-256`,e))].map(e=>e.toString(16).padStart(2,`0`)).join(``),u=async(e,{tombstones:t={},gzipped:r=!0,keep:i=!1,coverage:a={},digest:u=!1,corrupt:d=!1}={})=>{let f=new Map;for(let[r,i]of Object.entries(e)){let e=i.map((e,t)=>`shard-000${t}-${r}.ndjson.gz`),a=new Map(e.map((e,t)=>[e,o(i[t])])),s=u?await Promise.all(e.map(async e=>l(n(Buffer.from(d?a.get(e)+`
6
- `:a.get(e)))))):e.map(()=>void 0);f.set(r,{manifest:JSON.stringify({name:r,shards:e.map((e,t)=>({file:e,...s[t]?{sha256:s[t]}:{}})),tombstones:t[r]??[]}),bodies:a})}globalThis.fetch=async e=>{let{pathname:t}=new URL(String(e),`https://app.test`);if(t.includes(`/stream/`))return{ok:!1,status:404};if(t.endsWith(`/cdn/catalog`))return c(JSON.stringify({domains:[...f.keys()].map(e=>({name:e,...a[e]?{coverage:a[e]}:{}}))}));let i=t.match(/\/cdn\/domains\/([^/]+)\/manifest$/);if(i)return f.has(i[1])?c(f.get(i[1]).manifest):{ok:!1,status:404};if(i=t.match(/\/cdn\/domains\/([^/]+)\/shards\/([^/]+)$/),i){let e=f.get(i[1])?.bodies.get(i[2]);return e===void 0?{ok:!1,status:404}:c(r?n(Buffer.from(e)):Buffer.from(e))}return{ok:!1,status:404}},i||M(),j({resolveView:()=>`${s}/stream`})},d=async(e,t={})=>u({d0:e},{...t,tombstones:{d0:t.tombstones??[]}}),f=e=>e.map(e=>e.path).sort().join(`,`);if(await d([[a(`0xa`,`a.md`)]]),f(await P())!==`a.md`)throw Error(`resolveView never reached the loader`);if(await d([[a(`0xa`,`plain.md`)]],{gzipped:!1}),f(await P())!==`plain.md`)throw Error(`an already-decoded body must still parse`);if(await d([[a(`0xa`,`signed.md`)]],{digest:!0}),f(await P())!==`signed.md`)throw Error(`a shard matching its digest must load`);await d([[a(`0xa`,`tampered.md`)]],{digest:!0,corrupt:!0});let p=null;try{await P()}catch(e){p=e}if(!p)throw Error(`a shard that does not match its digest must fail the load`);if(!/does not match the digest/.test(p.message))throw Error(`wrong error: ${p.message}`);p=null;try{await P()}catch(e){p=e}if(!p)throw Error(`a failed verification must not leave the rows in the cache`);let m=``;if(console.warn=e=>{m+=e},await d([[a(`0xa`,`unverifiable.md`)]],{digest:!0,gzipped:!1}),f(await P())!==`unverifiable.md`)throw Error(`an unverifiable body must still load`);if(!/cannot verify/.test(m))throw Error(`an unverifiable shard must say so, not pass silently`);if(console.warn=()=>{},await u({aaa:[[a(`0xa`,`a.md`,{desc:`alpha`}),a(`0xa`,`gone.md`,{desc:`retracted`})]],bbb:[[a(`0xb`,`b.md`,{desc:`beta`})]]},{tombstones:{aaa:[`0xa:gone.md`]}}),f(await P())!==`a.md,b.md`)throw Error(`domains must merge and tombstones retract`);for(let e of[`bbb`,[`bbb`],`bb`])if(await u({aaa:[[a(`0xa`,`a.md`)]],bbb:[[a(`0xb`,`b.md`)]]}),ye(e),f(await P())!==`b.md`)throw Error(`allowlist ${JSON.stringify(e)} did not narrow the load`);if(await u({aaa:[[a(`0xa`,`a.md`)]]}),ye(null),f(await P())!==`a.md`)throw Error(`a null allowlist must pull everything`);await d([[a(`0xa`,`a.md`),a(`0xa`,`gone.md`)],[a(`0xa`,`b.md`)]],{tombstones:[`0xa:gone.md`]});{let e=[],t=await P(void 0,t=>e.push(t.length));if(e.length!==2||e[0]!==1||e[1]!==2)throw Error(`must emit per shard, got ${e}`);if(t.length!==2)throw Error(`the final rows must still be whole`);if(e.some(e=>e>2))throw Error(`a tombstoned row must never surface in a partial emit`)}await d([[a(`0xa`,`a.md`)],[a(`0xa`,`b.md`)]]);{let e=[],t=[],n=P(void 0,t=>e.push(t.length)),r=P(void 0,e=>t.push(e.length));if(await Promise.all([n,r]),!e.length)throw Error(`the starting caller must still receive partials`);if(!t.length)throw Error(`a caller that joins an in-flight load must receive partials too`)}if(M(),j({resolveView:()=>`${s}/stream`}),globalThis.fetch=async()=>({ok:!1,status:500}),await P().then(()=>{throw Error(`a 500 must reject`)},()=>{}),await d([[a(`0xa`,`a.md`)]],{keep:!0}),f(await P())!==`a.md`)throw Error(`a failed load poisoned the cache — the next read must retry`);{let e=[];await u({d0:[[a(`0xa`,`a.md`,{blurb:`declared prose`})]]}),j({onManifests:t=>e.push(`manifests:${t[0]?.name}`),rowText:t=>(e.push(`rowText`),t.blurb??``)});let t=await P();if(e[0]!==`manifests:d0`)throw Error(`onManifests must fire first, got ${e}`);if(!e.includes(`rowText`))throw Error(`rowText never ran`);if(t[0].text!==`declared prose`)throw Error(`the manifest-derived rowText did not reach toRow`);if(M(),j({resolveView:()=>`${s}/stream`,onManifests:()=>{throw Error(`boom`)}}),(await P()).length!==1)throw Error(`a throwing onManifests must not fail the load`);j({onManifests:()=>{}})}if(await d([[a(`0xa`,`ep1.md`,{cues:[{text:`the submarine dives`}]})]]),(await P())[0].text.includes(`submarine`))throw Error(`the default rowText must not know about cues`);if(j({rowText:e=>e.cues?.length?e.cues.map(e=>e.text).join(` `):[e.name,e.path,e.desc].filter(Boolean).join(` `)}),M(),j({resolveView:()=>`${s}/stream`}),!(await P())[0].text.includes(`submarine`))throw Error(`a configured rowText must reach toRow`);{let e=new Set;if(j({rowText:(t,n)=>(e.add(n),t.name??``),onManifests:(t,n)=>{e.add(`m:${n}`)}}),M(),j({resolveView:()=>`${s}/stream`}),await P(),!e.has(s))throw Error(`rowText must receive its trimmed view base, saw ${[...e]}`);if(!e.has(`m:${s}`))throw Error(`onManifests must receive its view`);j({rowText:e=>[e.name,e.path,e.desc].filter(Boolean).join(` `),onManifests:()=>{}})}await d([[a(`0xa`,`v.md`,{},[3,4]),a(`0xa`,`packed.md`,{embed:{vec:`fwA=`}}),a(`0xa`,`none.md`)]]);{let e=new Map((await P()).map(e=>[e.path,e]));if(Math.abs(e.get(`v.md`).norm-5)>1e-9)throw Error(`norm must be precomputed from the plain float array`);if(!e.get(`packed.md`).vector)throw Error(`the base64 int8 form a publisher commits must unpack`);if(e.get(`none.md`).vector!==null)throw Error(`a row with no vector must be null, and rank lexically`);if(e.get(`none.md`).norm!==1)throw Error(`a zero norm would divide search by zero`)}{await u({d0:[[{track_id:`p1`,owner:`0xa`,v:`AH+B`,fields:{entityType:`file`,name:`packed.md`,path:`packed.md`}},{track_id:`p2`,owner:`0xa`,embedding:[0,1,-1],fields:{entityType:`file`,name:`old.md`,path:`old.md`}}]]});let e=new Map((await P()).map(e=>[e.path,e])),t=e.get(`packed.md`).vector,n=e.get(`old.md`).vector;if(!t)throw Error("the bake's `v` key must unpack");for(let e=0;e<3;e++)if(Math.abs(t[e]-n[e])>1/127)throw Error(`packed and float forms disagree at ${e}: ${t[e]} vs ${n[e]}`);if(Math.abs(e.get(`packed.md`).norm-e.get(`old.md`).norm)>.02)throw Error(`norm must match across wire forms`)}await u({near:[[a(`0xa`,`a.md`)]],far:[[a(`0xb`,`b.md`)]],old:[[a(`0xc`,`c.md`)]]},{coverage:{near:{vectors:[[1,0]]},far:{vectors:[[0,1]]}}});{let e=await Ae([1,0]);if(e.map(e=>e.name).join(`,`)!==`near,far,old`)throw Error(`suggestDomains misranked: ${e.map(e=>e.name)}`)}await d([[a(`0xa`,`before.md`)]]);{let e=new Map,t=``,n=!1;globalThis.EventSource=class{constructor(e){t=e}addEventListener(t,n){e.set(t,n)}close(){n=!0}};let r=0,i=ke(()=>{r++});if(await new Promise(e=>setTimeout(e,0)),t!==`${s}/stream`)throw Error(`watchShard must open the view's SSE stream, got ${t}`);if(e.has(`snapshot`))throw Error(`snapshot is the connect-time census, not a change`);if(f(await P())!==`before.md`)throw Error(`watchShard fixture never loaded`);if(await d([[a(`0xa`,`after.md`)]],{keep:!0}),e.get(`change`)(),r!==1)throw Error("a `change` event must reach the caller");if(f(await P())!==`after.md`)throw Error("a `change` must drop the cache so the next read re-pulls");if(i(),!n)throw Error(`the unsubscribe must close the stream`)}if(await u({d0:[[a(`0xa`,`home.md`)]],other:[[a(`0xb`,`guest.md`)]]}),await P(),ge(`${s}/cdn`),_e()!==s)throw Error(`setView must trim to the base every route hangs off`);if(ge(null),_e()!==null)throw Error(`setView(null) must go back to the app default`);console.warn=r,console.info=i,console.log(`shard.js self-check ok — resolveView, gzip sniff, shard digests, domain merge + tombstones + allowlist, streaming, late subscriber, cache poisoning, rowText, vectors, suggestDomains, watchShard`)}var je=new Set([`text`,`vector`,`norm`,`id`,`owner`,`embed`]),I=(e,t,n=[])=>{let r=c(e,t,n),i=d(e,t);return i?{...r,url:i}:r};function Me(e){let t=new Map,n=0,r=0;for(let i of e){i.vector&&(n++,r||=i.vector.length);for(let e of Object.keys(i))je.has(e)||i[e]!=null&&i[e]!==``&&t.set(e,(t.get(e)??0)+1)}return{rows:e.length,withVectors:n,vectorDim:r,owners:[...new Set(e.map(e=>e.owner).filter(Boolean))],fields:[...t].sort((e,t)=>t[1]-e[1]).map(([t,n])=>({name:t,on:n,pct:Math.round(100*n/(e.length||1))}))}}var Ne=.15,Pe=new WeakMap,Fe=(e,t)=>{let n=Pe.get(e);return n===void 0&&(n=[s(e,t),i(e,t)??``,...(t.tags??[]).flatMap(t=>a(e[t]??``)),e.text??``].join(` `),Pe.set(e,n)),n};function L(e,t,n,{qv:r=null,limit:i=10,fields:a,where:o,lexBoost:s=Ne}={}){let c=t.trim().toLowerCase();if(!c)return[];let l=r?S(r):1;o&&(e=e.filter(e=>R(e,o)));let u=e.map(e=>r&&e.vector?{r:e,score:x(e,r,l)+(s?s*T({text:Fe(e,n)},c):0),mode:`semantic`}:{r:e,score:T({text:Fe(e,n)},c),mode:`lexical`}),d=u.filter(e=>e.mode===`semantic`).map(e=>e.score),f=Math.min(C(d),d.length?Math.max(...d):1/0);return u.filter(e=>e.mode===`semantic`?e.score>=f:e.score>0).sort((e,t)=>t.score-e.score).slice(0,i).map(({r:e,score:t,mode:r})=>({...I(e,n,a),score:Number(t.toFixed(4)),mode:r}))}var R=(e,t)=>!t||Object.entries(t).every(([t,n])=>a(e[t]??``).some(e=>e.toLowerCase()===String(n).toLowerCase()));function z(e,t,{limit:n=20,where:r}={}){let i=new Map,o=0;for(let n of e){if(!R(n,r))continue;let e=n[t];if(e==null||e===``){o++;continue}for(let t of a(e))i.set(t,(i.get(t)??0)+1)}let s=[...i].sort((e,t)=>t[1]-e[1]);return{field:t,distinct:i.size,missing:o,top:s.slice(0,n).map(([e,t])=>({value:e,count:t}))}}function B(e,t,{type:n,where:r,sort:i,desc:a=!0,limit:s=20,offset:c=0,fields:l}={}){let d=e.filter(e=>(!n||o(e)?.toLowerCase()===String(n).toLowerCase())&&R(e,r)),f=d.length;if(i){let e=e=>e==null||e===``?null:Number(e);d=[...d].sort((t,n)=>{let[r,o]=[t[i],n[i]];if((r==null||r===``)!=(o==null||o===``))return r==null||r===``?1:-1;let[s,c]=[e(r),e(o)],l=Number.isFinite(s)&&Number.isFinite(c)?s-c:String(r??``).localeCompare(String(o??``));return a?-l:l})}return{total:f,offset:c,returned:Math.min(s,Math.max(0,f-c)),collections:n?void 0:u(t,e).map(e=>({type:e.type,plural:e.plural,count:e.count})),rows:d.slice(c,c+s).map(e=>I(e,t,l))}}function Ie(e,t,n,{limit:r=10,fields:i}={}){let a=e.find(e=>e.id===t);if(!a?.vector)return{seed:a?I(a,n,i):null,near:[]};let o=a.norm;return{seed:I(a,n,i),near:e.filter(e=>e.id!==t&&e.vector).map(e=>({r:e,score:x(e,a.vector,o)})).sort((e,t)=>t.score-e.score).slice(0,r).map(({r:e,score:t})=>({...I(e,n,i),score:Number(t.toFixed(4))}))}}function V(e,t,n){if(!t)return null;let r=e.find(e=>e.id===t)??(n?.identity?e.find(e=>String(e[n.identity])===String(t)):null)??e.find(e=>e.path===t||e.name===t||s(e,n)===t);if(!r)return null;let{vector:i,norm:a,...o}=r;return{...o,hasVector:!!i,vectorDim:i?.length??0}}if(typeof process<`u`&&import.meta.url===`file://${process.argv[1]}`&&process.argv[1].endsWith(`/tools.js`)){let{rolesFrom:e}=await t(async()=>{let{rolesFrom:e}=await import(`./roles-YDRjv_PY.js`);return{rolesFrom:e}},__vite__mapDeps([4,5]),import.meta.url),n=(e,t,n)=>({id:e,owner:`0xa`,...t,text:[t.name,t.desc].filter(Boolean).join(` `),vector:n??null,norm:n?S(n):1}),r=[n(`1`,{name:`Duet S01E02`,series:`Duet`,year:`1987`,subject:`['Sitcom', 'Comedy']`,desc:`a forgotten fox sitcom`},[1,0,0]),n(`2`,{name:`Duet S01E03`,series:`Duet`,year:`1987`,subject:`['Sitcom']`,desc:`more of the same sitcom`},[.99,.1,0]),n(`3`,{name:`Atomic Cafe`,series:``,year:`1982`,subject:`['Documentary', 'Cold War']`,desc:`nuclear test footage`},[0,1,0]),n(`4`,{name:`untitled reel`,year:`1954`})],i=e([],r);if(i.title[0]!==`name`)throw Error(`fixture roles wrong: ${i.title}`);let o=Me(r);if(o.rows!==4||o.withVectors!==3||o.vectorDim!==3)throw Error(`describe lost the corpus shape`);let s=o.fields.find(e=>e.name===`series`);if(s.on!==2||s.pct!==50)throw Error(`empty strings must not count as coverage: ${JSON.stringify(s)}`);if(o.fields.some(e=>je.has(e.name)))throw Error(`plumbing fields must not be offered as content`);if(o.owners.join()!==`0xa`)throw Error(`owners lost`);let c=L(r,`sitcom`,i,{qv:[1,0,0],limit:10});if(c[0].id!==`1`||c[0].title!==`Duet S01E02`)throw Error(`semantic: wrong top hit ${c[0]?.id}`);if(c[0].mode!==`semantic`)throw Error(`mode must be reported, not inferred`);if(c.some(e=>e.id===`3`))throw Error(`the z-floor must drop the unrelated row: ${JSON.stringify(c)}`);if(!L([r[0],r[1],r[2]],`sitcom`,i,{qv:[1,0,0]}).length)throw Error(`an exact match must never be filtered out by its own floor`);if(c=L(r,`nuclear`,i,{limit:10}),c[0]?.id!==`3`||c[0].mode!==`lexical`)throw Error(`must degrade to lexical, and say so`);if(L(r,` `,i).length)throw Error(`an empty query must return nothing, not everything`);if(L(r,`untitled reel`,i)[0]?.id!==`4`)throw Error(`a vectorless row must still be searchable lexically`);let l=z(r,`subject`);if(l.top[0].value!==`Sitcom`||l.top[0].count!==2)throw Error(`list-ish values must split: ${JSON.stringify(l.top)}`);if(l.missing!==1)throw Error(`a field absent from a row must be counted missing, not skipped silently`);if(z(r,`year`).distinct!==3)throw Error(`plain values must not be split`);if(a(`Sitcom,Comedy,Fox`).length!==3)throw Error(`a machine-joined list must split`);if(a(`Wilson, Michael G.`).length!==1)throw Error(`a comma followed by a space is prose, not a list`);if(a(`1987`).length!==1)throw Error(`a plain value must stay whole`);let u=z(r,`year`,{where:{subject:`Sitcom`}});if(u.distinct!==1||u.top[0].value!==`1987`)throw Error(`where must narrow the facet: ${JSON.stringify(u.top)}`);if(z(r,`year`,{where:{subject:`sitcom`}}).distinct!==1)throw Error(`where must match case-insensitively`);if(z(r,`year`,{where:{subject:`Sit`}}).distinct!==0)throw Error(`where must match whole values, not substrings`);let d=B(r,i,{limit:2});if(d.total!==4||d.rows.length!==2)throw Error(`browse must page: ${JSON.stringify(d)}`);if(d.collections.length!==0)throw Error(`a typeless corpus must report zero collections, not invent one`);if(B(r,i,{offset:3}).rows.length!==1)throw Error(`offset must page to the tail`);if(B(r,i,{offset:99}).rows.length!==0)throw Error(`an offset past the end must be empty, not wrapped`);let f=r.map(e=>({...e,entityType:e.id===`3`?`Film`:`Episode`})),p=B(f,i,{limit:1}).collections;if(p.map(e=>`${e.type}:${e.count}`).join()!==`Episode:3,Film:1`)throw Error(`collections must count and sort: ${JSON.stringify(p)}`);if(B(f,i,{type:`film`}).total!==1)throw Error(`type must match case-insensitively`);if(B(f,i,{type:`film`}).collections!==void 0)throw Error(`a typed browse is already inside a collection`);if(B(f,i,{type:`Episode`,where:{year:`1987`}}).total!==2)throw Error(`browse must honour where`);let m=[{id:`a`,rating:`9`},{id:`b`,rating:`10`},{id:`c`}],ee=B(m,i,{sort:`rating`}).rows.map(e=>e.id);if(ee.join()!==`b,a,c`)throw Error(`numeric sort + missing-last wrong: ${ee}`);if(B(m,i,{sort:`rating`,desc:!1}).rows[0].id!==`a`)throw Error(`ascending sort ignored`);let h=Ie(r,`1`,i);if(h.near[0].id!==`2`)throw Error(`nearest neighbour missed the near-identical row`);if(h.near.some(e=>e.id===`1`))throw Error(`a row must not be its own neighbour`);if(h.near.some(e=>e.id===`4`))throw Error(`a vectorless row cannot be a neighbour`);if(Ie(r,`4`,i).near.length)throw Error(`a vectorless seed must return nothing, not throw`);if(Ie(r,`nope`,i).seed!==null)throw Error(`an unknown seed must be null, not undefined`);let g=V(r,`3`,i);if(g.name!==`Atomic Cafe`||!g.hasVector||g.vectorDim!==3)throw Error(`getRow lost the record`);if(`vector`in g||`norm`in g)throw Error(`the raw vector must never reach a tool result`);if(V(r,`nope`,i)!==null)throw Error(`an unknown id must be null`);let _=I(n(`x`,{name:`n`,categories:`d`.repeat(500)}),e([{role_map:{title:`name`,tags:[`categories`]}}]));if(_.categories.length>200)throw Error(`brief must truncate a long field`);if(_.title!==`n`)throw Error(`brief must title from the declared field`);{let e=[{id:`a`,name:`cold war thriller`,year:1965,vector:[1,0],norm:1},{id:`b`,name:`cold war comedy`,year:1999,vector:[1,0],norm:1},{id:`c`,name:`beach party`,year:1964,vector:[0,1],norm:1},{id:`d`,name:`surf movie`,year:1966,vector:[0,1],norm:1}],t={title:[`name`],subtitle:[],tags:[],text:[`name`],measures:[],fields:[`year`]};if(!L(e,`cold war`,t,{qv:[1,0],limit:10}).some(e=>e.title===`cold war comedy`))throw Error(`unfiltered search must see every era`);let n=L(e,`cold war`,t,{qv:[1,0],limit:10,where:{year:1965}});if(n.length!==1||n[0].title!==`cold war thriller`)throw Error(`where must narrow a semantic search: ${JSON.stringify(n)}`);if(L(e,`cold war`,t,{qv:[0,1],limit:10,where:{year:1964}})[0]?.title!==`beach party`)throw Error(`the floor must be measured over the filtered rows`)}{let e=[{id:`a`,name:`cold war thriller`,vector:[1,0],norm:1}],t={title:[`name`],subtitle:[],tags:[],text:[`name`],measures:[],fields:[]},n=L(e,`cold war`,t,{qv:[1,0],limit:5});if(!Array.isArray(n))throw Error(`search must return a flat array, not {hits}`);if(n[0].score===void 0||n[0].mode===void 0)throw Error(`search rows carry score+mode`);for(let n of[void 0,null,``])if(V(e,n,t)!==null)throw Error(`getRow(${JSON.stringify(n)}) must be null`)}console.log(`tools.js self-check ok — coverage not schema, semantic/lexical modes + z-floor, list-ish facets + where, neighbours, whole-row fetch, previews truncate`)}var H=e=>({content:[{type:`text`,text:JSON.stringify(e)}]}),U=(e,t=[])=>({type:`object`,properties:e,required:t});function Le(e){let t=document.modelContext;t?.registerTool&&(t.registerTool({name:`describe`,description:`What this app holds: its fields and how often each is present. Call it first.`,inputSchema:U({}),execute:async()=>H(Me(e.rows))}),t.registerTool({name:`search`,description:"Search by meaning: a question in plain words. Optional equality filters in `where`.",inputSchema:U({query:{type:`string`},limit:{type:`number`},where:{type:`object`}},[`query`]),execute:async({query:t,limit:n=10,where:r})=>H(L(e.rows,t,e.roles,{qv:await e.queryVector(t),limit:n,where:r}))}),t.registerTool({name:`get`,description:`One record in full, by the id search returns.`,inputSchema:U({id:{type:`string`}},[`id`]),execute:async({id:t})=>H(V(e.rows,t,e.roles)??{error:`no record ${t}`})}),t.registerTool({name:`count`,description:`Count the distinct values of a field: the shape of the data before reading it.`,inputSchema:U({field:{type:`string`},where:{type:`object`},limit:{type:`number`}},[`field`]),execute:async({field:t,where:n,limit:r=20})=>H(z(e.rows,t,{where:n,limit:r}))}))}function Re(e,t){let n=t=>new Set(e.slice(0,5e3).flatMap(e=>a(e[t]??``))).size;return t.facets?.[0]??t.tags.find(e=>{let t=n(e);return t>=2&&t<=12})??null}function ze(e,t,{facet:r=null,only:a=null,today:o=new Date().toISOString().slice(0,10)}={}){let c=new Map;for(let o of e){let e=String(n(o,t.temporal)??``).slice(0,10);if(!e)continue;let l=r?String(o[r]??``):``;if(a&&l!==a)continue;let u=i(o,t)??``,d=`${l}|${u}|${e}`,f=c.get(d);f||c.set(d,f={key:d,date:e,place:l,label:u,byTitle:new Map});let p=s(o,t).toLowerCase(),m=f.byTitle.get(p);(!m||W(m,t).length<W(o,t).length)&&f.byTitle.set(p,o)}let l=[...c.values()].map(({byTitle:e,...t})=>({...t,items:[...e.values()]}));return{upcoming:l.filter(e=>e.date>o).sort((e,t)=>e.date.localeCompare(t.date)||e.place.localeCompare(t.place)),past:l.filter(e=>e.date<=o).sort((e,t)=>t.date.localeCompare(e.date)||e.place.localeCompare(t.place))}}function W(e,t){let n=s(e,t),r=String(e.text??``),i=r.startsWith(n.replace(/\.$/,``))?r.slice(n.replace(/\.$/,``).length).replace(/^[.\s]+/,``):r;return i===n?``:i.trim()}if(typeof process<`u`&&process.argv[1]?.endsWith(`/feed.js`)){let{rolesFrom:e}=await t(async()=>{let{rolesFrom:e}=await import(`./roles-YDRjv_PY.js`);return{rolesFrom:e}},__vite__mapDeps([4,5]),import.meta.url),n=e([{role_map:{title:`heading`,subtitle:`meeting`,temporal:`date`,tags:[`city`,`body`],text:[`text`]},presentation:{facets:[`city`]}}]),r=(e,t,n,r=n)=>({city:e,date:t,heading:n,meeting:`Board · ${t}`,body:`Board`,text:r}),i=[r(`A`,`2026-01-01`,`Budget`),r(`A`,`2026-01-01`,`Budget`,`Budget. Approved 5-0.`),r(`A`,`2026-01-01`,`Roads`),r(`B`,`2026-02-01`,`Parks`),r(`A`,`2026-03-01`,`Zoning`),{heading:`undated`}],a=(e,t,n)=>{if(JSON.stringify(e)!==JSON.stringify(t))throw Error(`${n}: ${JSON.stringify(e)}`)};a(Re(i,n),`city`,`the declared facet wins`);let{upcoming:o,past:s}=ze(i,n,{facet:`city`,today:`2026-02-15`});a(o.map(e=>e.key),[`A|Board · 2026-03-01|2026-03-01`],`a future date is coming up`),a(s.map(e=>`${e.place}${e.date}`),[`B2026-02-01`,`A2026-01-01`],`past, latest first`),a(s[1].items.map(e=>W(e,n)),[`Approved 5-0.`,``],`one item per title, the one that says what happened`),a(ze(i,n,{facet:`city`,only:`B`,today:`2026-02-15`}).past.length,1,`narrowed to one place`),console.log(`feed.js self-check ok — declared facet, upcoming vs past, one item per title keeping the decision, narrowed by place`)}var G=e=>document.querySelector(e),K={rows:[],roles:r([]),queryVector:e=>ie(e).catch(()=>null)};j({onManifests:e=>{K.roles=r(e)},rowText:e=>l(e,K.roles)});var Be=await fetch(`./.well-known/agent-card.json`).then(e=>e.json()).catch(()=>({}));document.title=Be.name??`Fangorn app`,G(`h1`).textContent=Be.name??`Fangorn app`,G(`#about`).textContent=Be.description??``,K.rows=await P(new URL(`view`,location.href).href),Le(K);var Ve=new Map(K.rows.map(e=>[e.id,e])),q=(e,t={},...n)=>{let r=Object.assign(document.createElement(e),t);return r.append(...n.filter(e=>e!=null&&e!==!1)),r},He=(e,t=220)=>e.length>t?`${e.slice(0,e.lastIndexOf(` `,t))} …`:e,Ue=e=>new Date(`${e}T12:00:00`).toLocaleDateString(void 0,{weekday:`short`,month:`short`,day:`numeric`,year:`numeric`}),J=Re(K.rows,K.roles),Y=null;try{Y=new URL(location.href).searchParams.get(J??``)||null}catch{}var X=J?[...K.rows.reduce((e,t)=>{for(let n of a(t[J]??``))e.set(n,(e.get(n)??0)+1);return e},new Map)].sort((e,t)=>e[0].localeCompare(t[0])):[],We=G(`#chips`),Ge=()=>We.replaceChildren(...[[null,K.rows.length],...X].map(([e,t])=>q(`button`,{type:`button`,className:e===Y?`chip on`:`chip`,title:`${t.toLocaleString()} records`,onclick:()=>{Y=e,Ge(),Ye(),Z.value.trim()&&Z.oninput()}},e??`All`)));X.length>1&&Ge(),G(`#status`).textContent=`${K.rows.length.toLocaleString()} records${X.length>1?` · ${X.length} ${J===`city`?`places`:J}`:``}`;var Z=G(`input`);Z.placeholder=`Search ${K.rows.length.toLocaleString()} records by meaning…`,Z.disabled=!1;var Ke=(e,{where:t=!1}={})=>{let n=d(e,K.roles),r=s(e,K.roles),a=W(e,K.roles);return q(`li`,{},n?q(`a`,{href:n,target:`_blank`,rel:`noopener`},r):q(`span`,{},r),a?q(`p`,{className:`detail`},He(a)):null,t?q(`small`,{},[J&&e[J],i(e,K.roles)].filter(Boolean).join(` · `)):null)},qe=e=>{let t=q(`ul`,{},...e.items.slice(0,5).map(e=>Ke(e))),n=e.items.length>5?q(`button`,{type:`button`,className:`more`,onclick:n=>{t.append(...e.items.slice(5).map(e=>Ke(e))),n.target.remove()}},`${e.items.length-5} more`):null,r=e.label.replace(/\s*·\s*\d{4}-\d\d-\d\d$/,``);return q(`article`,{},q(`header`,{},q(`time`,{dateTime:e.date},Ue(e.date)),q(`h3`,{},[e.place,r].filter(Boolean).join(` — `))),t,n)},Q=G(`#feed`),Je=12;function Ye(){let{upcoming:e,past:t}=ze(K.rows,K.roles,{facet:J,only:Y}),n=(e,t,n)=>t.length?q(`section`,{},q(`h2`,{},e),...t.map(qe),n):null;Q.replaceChildren(...[n(`Coming up`,e.slice(0,8)),n(`Recently`,t.slice(0,Je),t.length>Je?q(`button`,{type:`button`,className:`more`,onclick:()=>{Je+=12,Ye()}},`Show more`):null)].filter(Boolean)),Q.children.length||Q.append(q(`p`,{className:`empty`},`Nothing dated here yet. Search above.`))}var $=G(`#results`);Z.oninput=async()=>{let e=Z.value.trim();if(Q.hidden=!!e,$.hidden=!e,!e)return $.replaceChildren();let t=L(K.rows,e,K.roles,{qv:await K.queryVector(e),limit:30,...Y&&J?{where:{[J]:Y}}:{}});e===Z.value.trim()&&$.replaceChildren(...t.length?t.map(e=>Ke(Ve.get(e.id)??e,{where:!0})):[q(`li`,{className:`empty`},`No matches.`)])},Ye();