const STATE_ORDER = ["running", "success", "empty", "error", "exported"];
const QUERY_ORDER = ["success", "empty", "error", "export"];
const workspaceFixtures = {
"pg-prod-readonly": {
defaultSelection: "public.orders",
catalog: [
{
name: "public",
items: [
{
name: "orders",
kind: "Table",
descriptor: "table · 12 columns",
columns: 12,
primaryKey: "id",
search: "orders public table created_at status customer_id",
},
{
name: "customers",
kind: "Table",
descriptor: "table · 9 columns",
columns: 9,
primaryKey: "id",
search: "customers public table email country",
},
{
name: "payments",
kind: "Table",
descriptor: "table · 7 columns",
columns: 7,
primaryKey: "id",
search: "payments public table status amount",
},
],
},
{
name: "analytics",
items: [
{
name: "daily_orders",
kind: "View",
descriptor: "view · materialized summary",
columns: 6,
primaryKey: "—",
search: "daily_orders analytics view",
},
{
name: "top_customers",
kind: "View",
descriptor: "view · ranking output",
columns: 5,
primaryKey: "—",
search: "top_customers analytics view",
},
],
},
{
name: "ops",
items: [
{
name: "job_runs",
kind: "Table",
descriptor: "table · execution log",
columns: 8,
primaryKey: "id",
search: "job_runs ops table status finished_at",
},
{
name: "incident_log",
kind: "Table",
descriptor: "table · support evidence",
columns: 10,
primaryKey: "id",
search: "incident_log ops table created_at severity",
},
],
},
],
queries: {
success: {
label: "orders audit.sql",
selection: "public.orders",
sql: `select
id,
customer_id,
status,
created_at
from public.orders
where created_at > now() - interval '7 days'
order by created_at desc
limit 50;`,
settledState: "success",
columns: ["id", "customer_id", "status", "created_at"],
successRows: [
["8ec1...", "7cb3...", "paid", "2026-03-25 13:55:01 UTC"],
["80f8...", "5d10...", "processing", "2026-03-25 13:44:15 UTC"],
["5aa2...", "9ab1...", "failed", "2026-03-25 13:12:48 UTC"],
["44fe...", "7cb3...", "paid", "2026-03-25 12:58:22 UTC"],
],
successRowCount: 50,
successDuration: "182 ms",
emptyDuration: "95 ms",
errorDuration: "21 ms",
errorMessage: "Syntax error near `form`",
exportFormat: "csv",
exportPath: "/tmp/orders-last-7-days.csv",
},
empty: {
label: "empty result.sql",
selection: "public.orders",
sql: `select
id,
status,
created_at
from public.orders
where created_at > now() - interval '5 minutes'
and status = 'refunded'
order by created_at desc;`,
settledState: "empty",
columns: ["id", "status", "created_at"],
successRows: [],
successRowCount: 0,
successDuration: "95 ms",
emptyDuration: "95 ms",
errorDuration: "18 ms",
errorMessage: "No result envelope available",
exportFormat: "csv",
exportPath: "/tmp/orders-empty.csv",
},
error: {
label: "bad syntax.sql",
selection: "public.orders",
sql: `select
id,
status
form public.orders
where status = 'paid';`,
settledState: "error",
columns: ["id", "status"],
successRows: [],
successRowCount: 0,
successDuration: "—",
emptyDuration: "—",
errorDuration: "21 ms",
errorMessage: "Syntax error near `form`",
exportFormat: "csv",
exportPath: "/tmp/orders-error.csv",
},
export: {
label: "export review.sql",
selection: "public.orders",
sql: `select
id,
customer_id,
status,
created_at
from public.orders
where created_at >= date_trunc('day', now())
order by created_at desc;`,
settledState: "exported",
columns: ["id", "customer_id", "status", "created_at"],
successRows: [
["8ec1...", "7cb3...", "paid", "2026-03-25 13:55:01 UTC"],
["80f8...", "5d10...", "processing", "2026-03-25 13:44:15 UTC"],
["5aa2...", "9ab1...", "failed", "2026-03-25 13:12:48 UTC"],
["44fe...", "7cb3...", "paid", "2026-03-25 12:58:22 UTC"],
],
successRowCount: 50,
successDuration: "182 ms",
emptyDuration: "95 ms",
errorDuration: "21 ms",
errorMessage: "Export source query failed",
exportFormat: "csv",
exportPath: "/tmp/orders-last-7-days.csv",
},
},
},
"mysql-staging": {
defaultSelection: "qa_demo.orders",
catalog: [
{
name: "qa_demo",
items: [
{
name: "orders",
kind: "Table",
descriptor: "table · 11 columns",
columns: 11,
primaryKey: "id",
search: "orders qa_demo table created_at status region",
},
{
name: "order_audit",
kind: "Table",
descriptor: "table · change history",
columns: 8,
primaryKey: "audit_id",
search: "order_audit qa_demo table before after actor",
},
{
name: "refunds",
kind: "Table",
descriptor: "table · finance review",
columns: 6,
primaryKey: "id",
search: "refunds qa_demo table amount processed_at",
},
],
},
{
name: "reporting",
items: [
{
name: "daily_orders",
kind: "View",
descriptor: "view · daily summary",
columns: 5,
primaryKey: "—",
search: "daily_orders reporting view",
},
{
name: "refund_watchlist",
kind: "View",
descriptor: "view · issue triage",
columns: 4,
primaryKey: "—",
search: "refund_watchlist reporting view",
},
],
},
{
name: "ops",
items: [
{
name: "job_runs",
kind: "Table",
descriptor: "table · pipeline status",
columns: 7,
primaryKey: "id",
search: "job_runs ops table status finished_at",
},
{
name: "dead_letter_queue",
kind: "Table",
descriptor: "table · retry backlog",
columns: 5,
primaryKey: "id",
search: "dead_letter_queue ops table retry backlog",
},
],
},
],
queries: {
success: {
label: "staging diff.sql",
selection: "qa_demo.orders",
sql: `select
id,
region,
status,
updated_at
from qa_demo.orders
where updated_at >= now() - interval 1 day
order by updated_at desc
limit 50;`,
settledState: "success",
columns: ["id", "region", "status", "updated_at"],
successRows: [
["1024", "us-east", "paid", "2026-03-25 13:55:01 UTC"],
["1021", "eu-west", "processing", "2026-03-25 13:44:15 UTC"],
["1015", "ap-south", "failed", "2026-03-25 13:12:48 UTC"],
["1007", "us-west", "paid", "2026-03-25 12:58:22 UTC"],
],
successRowCount: 37,
successDuration: "164 ms",
emptyDuration: "88 ms",
errorDuration: "17 ms",
errorMessage: "Unknown column `statsu` in `field list`",
exportFormat: "json",
exportPath: "/tmp/mysql-staging-diff.json",
},
empty: {
label: "no drift.sql",
selection: "reporting.daily_orders",
sql: `select
order_date,
failed_count
from reporting.daily_orders
where order_date = current_date()
and failed_count > 50;`,
settledState: "empty",
columns: ["order_date", "failed_count"],
successRows: [],
successRowCount: 0,
successDuration: "88 ms",
emptyDuration: "88 ms",
errorDuration: "16 ms",
errorMessage: "No result envelope available",
exportFormat: "json",
exportPath: "/tmp/mysql-no-drift.json",
},
error: {
label: "bad filter.sql",
selection: "qa_demo.orders",
sql: `select
id,
statsu
from qa_demo.orders
where status = 'paid';`,
settledState: "error",
columns: ["id", "status"],
successRows: [],
successRowCount: 0,
successDuration: "—",
emptyDuration: "—",
errorDuration: "17 ms",
errorMessage: "Unknown column `statsu` in `field list`",
exportFormat: "json",
exportPath: "/tmp/mysql-error.json",
},
export: {
label: "handoff export.sql",
selection: "qa_demo.order_audit",
sql: `select
audit_id,
order_id,
actor,
changed_at
from qa_demo.order_audit
order by changed_at desc
limit 100;`,
settledState: "exported",
columns: ["audit_id", "order_id", "actor", "changed_at"],
successRows: [
["5008", "1024", "api", "2026-03-25 13:55:01 UTC"],
["5007", "1021", "worker", "2026-03-25 13:44:15 UTC"],
["5006", "1015", "ops", "2026-03-25 13:12:48 UTC"],
["5005", "1007", "api", "2026-03-25 12:58:22 UTC"],
],
successRowCount: 100,
successDuration: "201 ms",
emptyDuration: "112 ms",
errorDuration: "22 ms",
errorMessage: "Export source query failed",
exportFormat: "json",
exportPath: "/tmp/mysql-audit-export.json",
},
},
},
"sqlite-local": {
defaultSelection: "main.audit_events",
catalog: [
{
name: "main",
items: [
{
name: "audit_events",
kind: "Table",
descriptor: "table · local evidence log",
columns: 7,
primaryKey: "id",
search: "audit_events main table action actor event_time",
},
{
name: "attachments",
kind: "Table",
descriptor: "table · exported evidence",
columns: 5,
primaryKey: "id",
search: "attachments main table file_path created_at",
},
{
name: "query_history",
kind: "Table",
descriptor: "table · recent drafts",
columns: 4,
primaryKey: "id",
search: "query_history main table sql_label saved_at",
},
],
},
{
name: "temp",
items: [
{
name: "staged_rows",
kind: "Table",
descriptor: "table · transient compare set",
columns: 4,
primaryKey: "—",
search: "staged_rows temp table transient compare",
},
],
},
],
queries: {
success: {
label: "evidence review.sql",
selection: "main.audit_events",
sql: `select
id,
actor,
action,
event_time
from main.audit_events
order by event_time desc
limit 50;`,
settledState: "success",
columns: ["id", "actor", "action", "event_time"],
successRows: [
["91", "qa-bot", "attach_export", "2026-03-25 13:55:01 UTC"],
["90", "frontend", "query_ok", "2026-03-25 13:44:15 UTC"],
["89", "backend", "inspect_table", "2026-03-25 13:12:48 UTC"],
["88", "qa-bot", "open_file", "2026-03-25 12:58:22 UTC"],
],
successRowCount: 24,
successDuration: "14 ms",
emptyDuration: "8 ms",
errorDuration: "4 ms",
errorMessage: "SQLite error: no such column: acton",
exportFormat: "csv",
exportPath: "/tmp/sqlite-audit-events.csv",
},
empty: {
label: "no incidents.sql",
selection: "main.audit_events",
sql: `select
id,
action,
event_time
from main.audit_events
where action = 'incident_opened'
and event_time >= datetime('now', '-5 minutes');`,
settledState: "empty",
columns: ["id", "action", "event_time"],
successRows: [],
successRowCount: 0,
successDuration: "8 ms",
emptyDuration: "8 ms",
errorDuration: "4 ms",
errorMessage: "No result envelope available",
exportFormat: "csv",
exportPath: "/tmp/sqlite-empty.csv",
},
error: {
label: "bad column.sql",
selection: "main.audit_events",
sql: `select
id,
acton
from main.audit_events
where actor = 'qa-bot';`,
settledState: "error",
columns: ["id", "action"],
successRows: [],
successRowCount: 0,
successDuration: "—",
emptyDuration: "—",
errorDuration: "4 ms",
errorMessage: "SQLite error: no such column: acton",
exportFormat: "csv",
exportPath: "/tmp/sqlite-error.csv",
},
export: {
label: "attachment export.sql",
selection: "main.attachments",
sql: `select
id,
file_path,
created_at
from main.attachments
order by created_at desc
limit 25;`,
settledState: "exported",
columns: ["id", "file_path", "created_at"],
successRows: [
["17", "/tmp/evidence-1.csv", "2026-03-25 13:55:01 UTC"],
["16", "/tmp/evidence-2.csv", "2026-03-25 13:44:15 UTC"],
["15", "/tmp/evidence-3.csv", "2026-03-25 13:12:48 UTC"],
["14", "/tmp/evidence-4.csv", "2026-03-25 12:58:22 UTC"],
],
successRowCount: 25,
successDuration: "11 ms",
emptyDuration: "6 ms",
errorDuration: "4 ms",
errorMessage: "Export source query failed",
exportFormat: "csv",
exportPath: "/tmp/sqlite-attachments.csv",
},
},
},
};
const connectionCards = document.querySelectorAll(".connection-card");
const bottomTabs = document.querySelectorAll(".bottom-tab");
const tabContents = document.querySelectorAll(".tab-content");
const activeConnection = document.getElementById("active-connection");
const driverPill = document.getElementById("driver-pill");
const detailTarget = document.getElementById("detail-target");
const detailMode = document.getElementById("detail-mode");
const detailExport = document.getElementById("detail-export");
const detailTheme = document.getElementById("detail-theme");
const contextTarget = document.getElementById("context-target");
const contextMode = document.getElementById("context-mode");
const contextSelection = document.getElementById("context-selection");
const contextValidated = document.getElementById("context-validated");
const inspectorObject = document.getElementById("inspector-object");
const selectionKind = document.getElementById("selection-kind");
const selectionPrimaryKey = document.getElementById("selection-primary-key");
const selectionColumns = document.getElementById("selection-columns");
const summaryWorkspace = document.getElementById("summary-workspace");
const summaryWorkspaceCopy = document.getElementById("summary-workspace-copy");
const summarySelection = document.getElementById("summary-selection");
const summarySelectionCopy = document.getElementById("summary-selection-copy");
const summaryRun = document.getElementById("summary-run");
const summaryRunCopy = document.getElementById("summary-run-copy");
const summaryExport = document.getElementById("summary-export");
const summaryExportCopy = document.getElementById("summary-export-copy");
const executionBanner = document.getElementById("execution-banner");
const executionTitle = document.getElementById("execution-title");
const executionCopy = document.getElementById("execution-copy");
const durationValue = document.getElementById("duration-value");
const rowCount = document.getElementById("row-count");
const stateLabel = document.getElementById("state-label");
const editorCode = document.getElementById("editor-code");
const editorGutter = document.getElementById("editor-gutter");
const toolbarTabsContainer = document.getElementById("toolbar-tabs");
const schemaTree = document.getElementById("schema-tree");
const resultsHead = document.getElementById("results-head");
const resultsBody = document.getElementById("results-body");
const historyList = document.getElementById("history-list");
const resultSummary = document.getElementById("result-summary");
const resultCopy = document.getElementById("result-copy");
const exportTitle = document.getElementById("export-title");
const exportCopy = document.getElementById("export-copy");
const problemTitle = document.getElementById("problem-title");
const problemCopy = document.getElementById("problem-copy");
const contractTitle = document.getElementById("contract-title");
const contractCopy = document.getElementById("contract-copy");
const contractJson = document.getElementById("contract-json");
const statusMessage = document.getElementById("status-message");
const themeToggle = document.getElementById("theme-toggle");
const schemaSearch = document.getElementById("schema-search");
const clearSearch = document.getElementById("clear-search");
const runQueryButton = document.getElementById("run-query");
const cycleStateButton = document.getElementById("cycle-state");
const exportCurrentButton = document.getElementById("export-current");
const exportCsvButton = document.getElementById("export-csv");
const exportJsonButton = document.getElementById("export-json");
const commandButton = document.getElementById("command-button");
const commandPalette = document.getElementById("command-palette");
const commandBackdrop = document.getElementById("command-backdrop");
const commandCloseButton = document.getElementById("command-close");
const commandStateButtons = document.querySelectorAll("[data-command-state]");
const commandConnectionButtons = document.querySelectorAll("[data-command-connection]");
const commandActionButtons = document.querySelectorAll("[data-command-action]");
const appState = {
connection: "pg-prod-readonly",
query: "success",
selection: "public.orders",
activeState: "success",
activeTab: "results",
runTimer: null,
};
function getActiveConnectionCard() {
return document.querySelector(`.connection-card[data-connection="${appState.connection}"]`);
}
function getActiveConnectionData() {
return getActiveConnectionCard().dataset;
}
function getWorkspaceFixture() {
return workspaceFixtures[appState.connection];
}
function getQueryPreset(queryKey = appState.query) {
return getWorkspaceFixture().queries[queryKey];
}
function getSelectionMeta(selection = appState.selection) {
const fixture = getWorkspaceFixture();
for (const group of fixture.catalog) {
for (const item of group.items) {
if (`${group.name}.${item.name}` === selection) {
return {
schema: group.name,
selection,
...item,
};
}
}
}
return null;
}
function getTreeItems() {
return schemaTree.querySelectorAll(".tree-item");
}
function getSchemaGroups() {
return schemaTree.querySelectorAll(".tree-group");
}
function setActiveTab(tabName) {
appState.activeTab = tabName;
bottomTabs.forEach((tab) => {
tab.classList.toggle("is-active", tab.dataset.tab === tabName);
});
tabContents.forEach((content) => {
content.classList.toggle("is-active", content.dataset.content === tabName);
});
}
function renderToolbarTabs() {
const fixture = getWorkspaceFixture();
toolbarTabsContainer.innerHTML = QUERY_ORDER.map((queryKey) => {
const preset = fixture.queries[queryKey];
const isActive = queryKey === appState.query ? " is-active" : "";
return ``;
}).join("");
}
function renderSchemaTree() {
const fixture = getWorkspaceFixture();
schemaTree.innerHTML = fixture.catalog
.map((group) => {
const groupSearch = [group.name, ...group.items.map((item) => item.search)].join(" ").toLowerCase();
const items = group.items
.map((item) => {
const selection = `${group.name}.${item.name}`;
const isSelected = selection === appState.selection ? " is-selected" : "";
return `
`;
})
.join("");
return `
${group.name}
${items}