@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
377 lines • 18.3 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
export function quoteIdent(ident) {
return `"${ident.replace(/"/g, '""')}"`;
}
export function qualified(schema, name) {
return `${quoteIdent(schema)}.${quoteIdent(name)}`;
}
export async function listSchemas(accessor, allowlist) {
const result = await accessor.store.query('SELECT schema_name FROM information_schema.schemata ' +
"WHERE schema_name NOT IN ('pg_catalog', 'information_schema') " +
"AND schema_name NOT LIKE 'pg_%' " +
'ORDER BY schema_name');
let names = result.rows.map((r) => r.schema_name);
if (allowlist !== null) {
const allow = new Set(allowlist);
names = names.filter((n) => allow.has(n));
}
return names;
}
export async function listTables(accessor, schema) {
const result = await accessor.store.query('SELECT table_name FROM information_schema.tables ' +
"WHERE table_schema = $1 AND table_type = 'BASE TABLE' " +
'ORDER BY table_name', [schema]);
return result.rows.map((r) => r.table_name);
}
export async function listViews(accessor, schema) {
const result = await accessor.store.query('SELECT table_name FROM information_schema.views ' +
'WHERE table_schema = $1 ' +
'ORDER BY table_name', [schema]);
return result.rows.map((r) => r.table_name);
}
export async function listMatviews(accessor, schema) {
const result = await accessor.store.query('SELECT matviewname AS name FROM pg_matviews ' +
'WHERE schemaname = $1 ' +
'ORDER BY matviewname', [schema]);
return result.rows.map((r) => r.name);
}
export async function countRows(accessor, schema, name) {
const result = await accessor.store.query(`SELECT COUNT(*) AS count FROM ${qualified(schema, name)}`);
const value = result.rows[0]?.count ?? 0;
return Number(value);
}
export async function estimateSize(accessor, schema, name) {
const result = await accessor.store.query(`EXPLAIN (FORMAT JSON) SELECT * FROM ${qualified(schema, name)}`);
let plan = Object.values(result.rows[0] ?? {})[0];
if (typeof plan === 'string')
plan = JSON.parse(plan);
const top = plan[0]?.Plan ?? {};
const rows = Number(top['Plan Rows'] ?? 0) | 0;
const width = Number(top['Plan Width'] ?? 0) | 0;
return [rows, width];
}
export async function estimatedRowCount(accessor, schema, name) {
const result = await accessor.store.query('SELECT reltuples::bigint AS reltuples FROM pg_class c ' +
'JOIN pg_namespace n ON c.relnamespace = n.oid ' +
'WHERE n.nspname = $1 AND c.relname = $2', [schema, name]);
const value = result.rows[0]?.reltuples;
return value === undefined ? 0 : Number(value);
}
export async function tableSizeBytes(accessor, schema, name) {
const result = await accessor.store.query('SELECT pg_total_relation_size(c.oid) AS size FROM pg_class c ' +
'JOIN pg_namespace n ON c.relnamespace = n.oid ' +
'WHERE n.nspname = $1 AND c.relname = $2', [schema, name]);
const value = result.rows[0]?.size;
return value === undefined ? 0 : Number(value);
}
/**
* A relation's rows in key order, as an `ORDER BY` clause. A relation with no
* primary key (a view, a keyless table) gets none and keeps the order its scan
* returns. Mirrors Python's `key_order`.
*/
export function keyOrder(pk, descending = false) {
if (pk.length === 0)
return '';
const way = descending ? ' DESC' : '';
return ` ORDER BY ${pk.map((column) => `${quoteIdent(column)}${way}`).join(', ')}`;
}
export async function fetchRows(accessor, schema, name, options) {
const pk = await fetchRowKey(accessor, schema, name);
const result = await accessor.store.query(`SELECT * FROM ${qualified(schema, name)}${keyOrder(pk)} LIMIT $1 OFFSET $2`, [options.limit, options.offset]);
return result.rows;
}
/**
* Each key column's text, selected under a name no column of the relation
* has, for a page to start after. A driver's value need not be exact (pg-types
* parses a jsonb number as a double, and a browser mount takes any driver), so
* a key finer than its value sorts before the row it came from, and the page
* after it would answer that row again; the text is the exact key. asyncpg
* keeps exact values, so Python pages from the row.
*/
function keyTexts(pk, columns, source = '') {
let marker = '__mirage_key';
while (pk.some((_, i) => columns.has(`${marker}${String(i)}`)))
marker += '_';
const names = pk.map((_, i) => `${marker}${String(i)}`);
const select = pk.map((column, i) => `, ${source}${quoteIdent(column)}::text AS ${quoteIdent(names[i] ?? '')}`);
return [select.join(''), names];
}
/**
* A keyed relation's last `limit` rows, in key order, read off the end of its
* key with no count, and the exact key of the last one (`keyTexts`). Mirrors
* Python's `fetch_last_rows`.
*/
export async function fetchLastRows(accessor, schema, name, pk, limit, columns) {
const [select, names] = keyTexts(pk, columns);
const result = await accessor.store.query(`SELECT *${select} FROM (SELECT * FROM ${qualified(schema, name)}${keyOrder(pk, true)} ` +
`LIMIT $1) last${keyOrder(pk)}`, [limit]);
const last = result.rows[result.rows.length - 1];
return [
result.rows.map((row) => Object.fromEntries(Object.entries(row).filter(([k]) => !names.includes(k)))),
last === undefined ? null : names.map((column) => last[column]),
];
}
/**
* A keyed relation's rows past a key, in key order, and the exact key of the
* last one (`keyTexts`); null for the rows when their database JSON passes
* `maxBytes`, which the database answers before sending any of them
* (`fetchBoundedQuery`). Mirrors Python's `fetch_rows_after`.
*/
export async function fetchRowsAfter(accessor, schema, name, pk, after, limit, columns, maxBytes) {
let where = '';
const params = [limit];
if (after !== null) {
const key = pk.map((column) => quoteIdent(column)).join(', ');
const marks = pk.map((_, i) => `$${String(i + 2)}`).join(', ');
where = ` WHERE (${key}) > (${marks})`;
for (const item of after)
params.push(item);
}
const [select, names] = keyTexts(pk, columns, 'data.');
const rows = await fetchBoundedQuery(accessor, `SELECT * FROM ${qualified(schema, name)}${where}${keyOrder(pk)} LIMIT $1`, params, new Set([...columns, ...names]), maxBytes, select);
if (rows === null)
return [null, after];
const last = rows[rows.length - 1];
return [
rows.map((row) => Object.fromEntries(Object.entries(row).filter(([k]) => !names.includes(k)))),
last === undefined ? after : names.map((column) => last[column]),
];
}
/**
* Fetch native rows only when their database JSON fits the byte budget, in key
* order where the relation has a primary key.
*/
export async function fetchBoundedRows(accessor, schema, name, options) {
const columns = new Set((await fetchColumns(accessor, schema, name)).map((column) => column.name));
const pk = await fetchRowKey(accessor, schema, name);
return fetchBoundedQuery(accessor, `SELECT * FROM ${qualified(schema, name)}${keyOrder(pk)} LIMIT $1`, [options.limit], columns, options.maxBytes);
}
/**
* Budget an internally generated, row-limited SELECT before transferring rows.
* `extra` adds columns to each answered row past the budget, computed from the
* row (`data`), so they never count against `maxBytes` (`keyTexts`).
*/
export async function fetchBoundedQuery(accessor, query, params, columns, maxBytes, extra = '') {
let marker = '__mirage_bytes';
while (columns.has(marker))
marker += '_';
// Keep the gate and fetch in one statement/snapshot. The LEFT JOIN emits
// only a null row plus the size on overflow, never the oversized values.
const result = await accessor.store.query(`WITH data AS MATERIALIZED (${query}), ` +
'budget AS (SELECT COALESCE(SUM(' +
'octet_length(row_to_json(data)::text) + 1), 0) AS bytes FROM data) ' +
`SELECT data.*${extra}, budget.bytes AS ${quoteIdent(marker)} ` +
`FROM budget LEFT JOIN data ON budget.bytes <= $${String(params.length + 1)}`, [...params, maxBytes]);
const size = Number(result.rows[0]?.[marker]);
if (size > maxBytes)
return null;
if (size === 0)
return [];
return result.rows.map((row) => Object.fromEntries(Object.entries(row).filter(([key]) => key !== marker)));
}
export async function fetchColumns(accessor, schema, name) {
const result = await accessor.store.query('SELECT column_name, data_type, is_nullable ' +
'FROM information_schema.columns ' +
'WHERE table_schema = $1 AND table_name = $2 ' +
'ORDER BY ordinal_position', [schema, name]);
return result.rows.map((r) => ({
name: r.column_name,
type: r.data_type,
nullable: r.is_nullable === 'YES',
}));
}
export async function fetchPrimaryKey(accessor, schema, name) {
const result = await accessor.store.query('SELECT kcu.column_name ' +
'FROM information_schema.table_constraints tc ' +
'JOIN information_schema.key_column_usage kcu ' +
' ON tc.constraint_name = kcu.constraint_name ' +
' AND tc.table_schema = kcu.table_schema ' +
' AND tc.table_name = kcu.table_name ' +
"WHERE tc.constraint_type = 'PRIMARY KEY' " +
' AND tc.table_schema = $1 AND tc.table_name = $2 ' +
'ORDER BY kcu.ordinal_position', [schema, name]);
return result.rows.map((r) => r.column_name);
}
/**
* The columns a relation's rows are listed by: its primary key, when that key
* is unique over every row a plain read returns. A table with inheritance
* children reads their rows too, which its key does not cover, so it has none
* and keeps its scan order; a partitioned table's key covers every partition.
* Mirrors Python's `fetch_row_key`.
*/
export async function fetchRowKey(accessor, schema, name) {
const result = await accessor.store.query('SELECT a.attname AS column_name FROM pg_constraint con ' +
'JOIN pg_class c ON c.oid = con.conrelid ' +
'JOIN pg_namespace n ON n.oid = c.relnamespace ' +
'CROSS JOIN LATERAL unnest(con.conkey) WITH ORDINALITY ' +
'AS k(attnum, ord) ' +
'JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = k.attnum ' +
"WHERE con.contype = 'p' AND n.nspname = $1 AND c.relname = $2 " +
"AND (c.relkind = 'p' OR NOT c.relhassubclass) " +
'ORDER BY k.ord', [schema, name]);
return result.rows.map((r) => r.column_name);
}
export async function fetchForeignKeys(accessor, schema, name) {
const result = await accessor.store.query('SELECT con.conname AS constraint_name, ' +
' a.attname AS from_column, ' +
' af.attname AS to_column, ' +
' k.ord, ' +
' nf.nspname AS to_schema, ' +
' cf.relname AS to_table ' +
'FROM pg_constraint con ' +
'JOIN pg_class c ON c.oid = con.conrelid ' +
'JOIN pg_namespace n ON n.oid = c.relnamespace ' +
'JOIN pg_class cf ON cf.oid = con.confrelid ' +
'JOIN pg_namespace nf ON nf.oid = cf.relnamespace ' +
'JOIN unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON TRUE ' +
'JOIN unnest(con.confkey) WITH ORDINALITY AS kf(attnum, ord) ' +
' ON kf.ord = k.ord ' +
'JOIN pg_attribute a ' +
' ON a.attrelid = con.conrelid AND a.attnum = k.attnum ' +
'JOIN pg_attribute af ' +
' ON af.attrelid = con.confrelid AND af.attnum = kf.attnum ' +
"WHERE con.contype = 'f' AND n.nspname = $1 AND c.relname = $2 " +
'ORDER BY con.conname, k.ord', [schema, name]);
const grouped = new Map();
for (const row of result.rows) {
let fk = grouped.get(row.constraint_name);
if (fk === undefined) {
fk = {
columns: [],
references: { schema: row.to_schema, table: row.to_table, columns: [] },
};
grouped.set(row.constraint_name, fk);
}
fk.columns.push(row.from_column);
fk.references.columns.push(row.to_column);
}
return [...grouped.values()];
}
export async function fetchIndexes(accessor, schema, name) {
const result = await accessor.store.query('SELECT i.relname AS name, ' +
' ix.indisunique AS unique, ' +
' array_agg(a.attname::text ORDER BY x.ord) AS columns ' +
'FROM pg_class t ' +
'JOIN pg_namespace n ON t.relnamespace = n.oid ' +
'JOIN pg_index ix ON ix.indrelid = t.oid ' +
'JOIN pg_class i ON i.oid = ix.indexrelid ' +
'JOIN unnest(ix.indkey) WITH ORDINALITY AS x(attnum, ord) ON TRUE ' +
'JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = x.attnum ' +
'WHERE n.nspname = $1 AND t.relname = $2 ' +
'GROUP BY i.relname, ix.indisunique ' +
'ORDER BY i.relname', [schema, name]);
return result.rows.map((r) => ({
name: r.name,
columns: [...r.columns],
unique: r.unique,
}));
}
export async function fetchAllRelationships(accessor, schemas) {
if (schemas.length === 0)
return [];
const result = await accessor.store.query('SELECT con.conname AS constraint_name, ' +
' n.nspname AS from_schema, ' +
' c.relname AS from_table, ' +
' a.attname AS from_column, ' +
' af.attname AS to_column, ' +
' k.ord, ' +
' nf.nspname AS to_schema, ' +
' cf.relname AS to_table ' +
'FROM pg_constraint con ' +
'JOIN pg_class c ON c.oid = con.conrelid ' +
'JOIN pg_namespace n ON n.oid = c.relnamespace ' +
'JOIN pg_class cf ON cf.oid = con.confrelid ' +
'JOIN pg_namespace nf ON nf.oid = cf.relnamespace ' +
'JOIN unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON TRUE ' +
'JOIN unnest(con.confkey) WITH ORDINALITY AS kf(attnum, ord) ' +
' ON kf.ord = k.ord ' +
'JOIN pg_attribute a ' +
' ON a.attrelid = con.conrelid AND a.attnum = k.attnum ' +
'JOIN pg_attribute af ' +
' ON af.attrelid = con.confrelid AND af.attnum = kf.attnum ' +
"WHERE con.contype = 'f' AND n.nspname = ANY($1::text[]) " +
'ORDER BY n.nspname, c.relname, con.conname, k.ord', [schemas]);
const grouped = new Map();
for (const row of result.rows) {
const key = `${row.from_schema}\0${row.from_table}\0${row.constraint_name}`;
let rel = grouped.get(key);
if (rel === undefined) {
rel = {
from: { schema: row.from_schema, table: row.from_table, columns: [] },
to: { schema: row.to_schema, table: row.to_table, columns: [] },
kind: 'many_to_one',
};
grouped.set(key, rel);
}
rel.from.columns.push(row.from_column);
rel.to.columns.push(row.to_column);
}
return [...grouped.values()];
}
export async function fetchTableComment(accessor, schema, name) {
const result = await accessor.store.query("SELECT obj_description(c.oid, 'pg_class') " +
'FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ' +
'WHERE n.nspname = $1 AND c.relname = $2', [schema, name]);
return result.rows[0]?.obj_description ?? null;
}
export async function fetchColumnComments(accessor, schema, name) {
const result = await accessor.store.query('SELECT a.attname, col_description(c.oid, a.attnum) AS comment ' +
'FROM pg_class c ' +
'JOIN pg_namespace n ON n.oid = c.relnamespace ' +
'JOIN pg_attribute a ' +
' ON a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped ' +
'WHERE n.nspname = $1 AND c.relname = $2 ' +
'ORDER BY a.attnum', [schema, name]);
const out = new Map();
for (const r of result.rows) {
if (r.comment)
out.set(r.attname, r.comment);
}
return out;
}
export async function fetchEnumColumns(accessor, schema, name) {
const result = await accessor.store.query('SELECT a.attname, t.typname, ' +
' array_agg(e.enumlabel ORDER BY e.enumsortorder)::text[] AS labels ' +
'FROM pg_class c ' +
'JOIN pg_namespace n ON n.oid = c.relnamespace ' +
'JOIN pg_attribute a ' +
' ON a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped ' +
'JOIN pg_type t ON t.oid = a.atttypid ' +
'JOIN pg_enum e ON e.enumtypid = t.oid ' +
'WHERE n.nspname = $1 AND c.relname = $2 ' +
'GROUP BY a.attname, t.typname', [schema, name]);
const out = new Map();
for (const r of result.rows) {
out.set(r.attname, { type: r.typname, labels: [...r.labels] });
}
return out;
}
export async function fetchColumnStats(accessor, schema, name) {
// pg_stats is populated by ANALYZE, so it is empty for a freshly written
// relation until autovacuum gets to it. Callers treat it as best-effort;
// mirage never runs ANALYZE itself (a write and a cost on the user's DB).
const result = await accessor.store.query('SELECT attname, n_distinct, ' +
' most_common_vals::text::text[] AS mcv ' +
'FROM pg_stats WHERE schemaname = $1 AND tablename = $2', [schema, name]);
const out = new Map();
for (const r of result.rows) {
out.set(r.attname, {
n_distinct: Number(r.n_distinct),
most_common_vals: r.mcv ? [...r.mcv] : [],
});
}
return out;
}
//# sourceMappingURL=client.js.map