363 lines · 14.7 KB
/**
 * 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;