/**
* API e2e for the editor: token protocol, guards, live co-editing room (two Yjs clients + an HTTP agent),
* commits from the room, PAT scope narrowing, socket revocation, and the opt-in container.
*
* EDITOR_E2E_URL=http://localhost:3000 GITFOR_TOKEN=gitfor_pat_... GITFOR_WRITER_TOKEN=gitfor_pat_... \
* GITFOR_E2E_D1=.wrangler/state/v3/d1/miniflare-D1DatabaseObject/<id>.sqlite bun run e2e/editor-api.e2e.ts
*/
import { CLOSE_DOWNGRADED } from "../src/lib/editor-protocol";
import {
BASE,
call,
check,
log,
finish,
HTTP,
join,
LOCAL_D1,
mint,
PAT,
quote,
seedRepo,
sleep,
sql,
until,
WRITER_PAT,
} from "./editor-helpers";
/**
* SQL in a RepoState / AccountState database via the dev server's `/__dev/scoped-sql` route (EMAIL_DEV_SINK=kv);
* returns the first column of the first row as text, like `sql` does for D1.
*/
async function scopedSql(scope: "repo" | "account", id: string, statement: string): Promise<string> {
const response = await fetch(`${BASE}/__dev/scoped-sql`, {
method: "POST",
headers: { "content-type": "application/json", origin: new URL(BASE).origin, "sec-fetch-site": "same-origin" },
body: JSON.stringify({ scope, id, sql: statement }),
});
if (!response.ok) throw new Error(`scoped sql failed: ${response.status} ${await response.text()}`);
const rows: Array<Record<string, unknown>> = await response.json();
const first = rows[0] ? Object.values(rows[0])[0] : undefined;