sequelae-mcp
Version:
Let Claude, Cursor, and other AI agents run real SQL queries on live Postgres databases. No more copy-pasting SQL, stale schema docs, or hallucinated DB adapters — just raw, real-time access. Now with MCP support!
112 lines • 3.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.sampleJsonbColumn = sampleJsonbColumn;
exports.analyzeJsonStructure = analyzeJsonStructure;
exports.formatJsonStructure = formatJsonStructure;
async function sampleJsonbColumn(client, table, column, limit = 10) {
try {
const query = `
SELECT ${column}
FROM ${table}
WHERE ${column} IS NOT NULL
LIMIT $1
`;
const result = await client.query(query, [limit]);
return result.rows.map(row => row[column]);
}
catch (error) {
console.error(`Error sampling JSONB column ${table}.${column}:`, error);
return [];
}
}
function analyzeJsonStructure(samples) {
const structure = {};
const totalSamples = samples.length;
if (totalSamples === 0)
return structure;
// Count occurrences of each field
const fieldCounts = {};
// Analyze each sample
for (const sample of samples) {
if (sample && typeof sample === 'object' && !Array.isArray(sample)) {
analyzeObject(sample, structure, fieldCounts);
}
}
// Mark fields as optional if they don't appear in all samples
for (const [field, info] of Object.entries(structure)) {
info.optional = (fieldCounts[field] || 0) < totalSamples;
}
return structure;
}
function analyzeObject(obj, structure, fieldCounts) {
for (const [key, value] of Object.entries(obj)) {
// Track field occurrence
fieldCounts[key] = (fieldCounts[key] || 0) + 1;
// Initialize field info if not exists
if (!structure[key]) {
structure[key] = {
types: new Set(),
optional: false,
};
}
const fieldInfo = structure[key];
const valueType = getJsonType(value);
fieldInfo.types.add(valueType);
// Handle arrays
if (Array.isArray(value)) {
if (!fieldInfo.arrayElementTypes) {
fieldInfo.arrayElementTypes = new Set();
}
for (const element of value) {
fieldInfo.arrayElementTypes.add(getJsonType(element));
}
}
// Handle nested objects
if (value && typeof value === 'object' && !Array.isArray(value)) {
if (!fieldInfo.nestedStructure) {
fieldInfo.nestedStructure = {};
}
const nestedCounts = {};
analyzeObject(value, fieldInfo.nestedStructure, nestedCounts);
}
}
}
function getJsonType(value) {
if (value === null)
return 'null';
if (Array.isArray(value))
return 'array';
const type = typeof value;
if (type === 'object')
return 'object';
return type;
}
function formatJsonStructure(structure, indent = 0) {
if (Object.keys(structure).length === 0) {
return ' (no data to analyze)';
}
const lines = [];
const indentStr = ' ' + ' '.repeat(indent);
for (const [field, info] of Object.entries(structure)) {
const types = Array.from(info.types);
const optional = info.optional ? '?' : '';
let typeStr;
if (types.length === 1 && types[0] === 'array' && info.arrayElementTypes) {
const elementTypes = Array.from(info.arrayElementTypes).join(' | ');
typeStr = `array<${elementTypes}>`;
}
else {
typeStr = types.join(' | ');
}
lines.push(`${indentStr}- ${field}${optional}: ${typeStr}`);
// Format nested structure
if (info.nestedStructure && types.includes('object')) {
const nestedLines = formatJsonStructure(info.nestedStructure, indent + 1);
if (nestedLines.trim()) {
lines.push(nestedLines);
}
}
}
return lines.join('\n');
}
//# sourceMappingURL=jsonb-analyzer.js.map