UNPKG

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
"use strict"; 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