Skip to main content
Glama
CloudflareD1Repository.jsβ€’8.78 kB
"use strict"; /** * CloudflareD1Repository.ts * * @semantic-intent Infrastructure adapter implementing ICloudflareD1Repository port * Translates between Cloudflare D1 REST API and domain entities * * @observable-anchoring * - Uses CloudflareAPIClient for HTTP communication * - Parses sqlite_master schema metadata * - Executes PRAGMA statements for table structure * - Transforms API responses into domain entities * * @intent-preservation * - Environment semantics maintained through DatabaseConfig * - Schema structure captured in domain entities * - Database relationships expressed as domain Relationship entities * * @semantic-over-structural * - Focuses on schema meaning, not just structure * - Interprets foreign key constraints semantically * - Understands index purpose (PK, FK, unique) * * @immutability-protection * - Returns frozen domain entities * - No mutable state in repository */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CloudflareD1Repository = void 0; const DatabaseSchema_1 = require("../../domain/entities/DatabaseSchema"); const TableInfo_1 = require("../../domain/entities/TableInfo"); const Column_1 = require("../../domain/entities/Column"); const Index_1 = require("../../domain/entities/Index"); const ForeignKey_1 = require("../../domain/entities/ForeignKey"); const Environment_1 = require("../../domain/value-objects/Environment"); /** * Repository adapter for Cloudflare D1 database access * Implements hexagonal architecture port using D1 REST API */ class CloudflareD1Repository { apiClient; databaseConfig; environment; constructor(apiClient, databaseConfig, environment = Environment_1.Environment.DEVELOPMENT) { this.apiClient = apiClient; this.databaseConfig = databaseConfig; this.environment = environment; Object.freeze(this); } /** * Fetch complete database schema for current environment */ async fetchDatabaseSchema(databaseId) { const dbId = databaseId ?? this.databaseConfig.getDatabaseId(this.environment); const dbName = this.databaseConfig.getDatabaseName(this.environment); const tables = await this.fetchTableDetails(dbId); return new DatabaseSchema_1.DatabaseSchema(dbName, this.environment, tables, new Date()); } /** * Fetch detailed table information including columns, indexes, foreign keys */ async fetchTableDetails(databaseId, tableName) { const dbId = databaseId ?? this.databaseConfig.getDatabaseId(this.environment); // Query sqlite_master for table definitions const sql = tableName ? `SELECT type, name, sql FROM sqlite_master WHERE type IN ('table', 'view') AND name = '${tableName}' ORDER BY name` : "SELECT type, name, sql FROM sqlite_master WHERE type IN ('table', 'view') ORDER BY name"; const response = await this.apiClient.query(dbId, sql); if (!response.success || !response.results || response.results.length === 0) { return []; } const tables = []; for (const row of response.results) { const tableRow = row; const tableType = tableRow.type; // Fetch column details using PRAGMA const columns = await this.fetchTableColumns(dbId, tableRow.name); // Fetch indexes for this table const indexes = await this.fetchTableIndexes(dbId, tableRow.name); // Fetch foreign keys for this table const foreignKeys = await this.fetchTableForeignKeys(dbId, tableRow.name); const table = new TableInfo_1.TableInfo(tableRow.name, tableType, columns, indexes, foreignKeys); tables.push(table); } return tables; } /** * Fetch index information for all tables or specific table */ async fetchIndexInformation(databaseId) { const dbId = databaseId ?? this.databaseConfig.getDatabaseId(this.environment); // Query sqlite_master for all indexes const sql = "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index' AND sql IS NOT NULL ORDER BY tbl_name, name"; const response = await this.apiClient.query(dbId, sql); if (!response.success || !response.results || response.results.length === 0) { return []; } const indexes = []; for (const row of response.results) { const indexRow = row; // Parse index columns from SQL const columns = this.parseIndexColumns(indexRow.sql); // Determine if unique from SQL const isUnique = indexRow.sql.toLowerCase().includes('unique'); const index = new Index_1.Index(indexRow.name, indexRow.tbl_name, columns, isUnique, false); indexes.push(index); } return indexes; } /** * Execute arbitrary SQL query */ async executeSQLQuery(databaseId, sql) { const response = await this.apiClient.query(databaseId, sql); return { success: response.success, results: response.results ?? [], meta: response.meta, }; } /** * Fetch column details for a specific table using PRAGMA */ async fetchTableColumns(databaseId, tableName) { const sql = `PRAGMA table_info('${tableName}')`; const response = await this.apiClient.query(databaseId, sql); if (!response.success || !response.results || response.results.length === 0) { return []; } const columns = []; for (const row of response.results) { const colRow = row; const column = new Column_1.Column(colRow.name, colRow.type, colRow.pk === 1, // isPrimaryKey colRow.notnull === 0, // isNullable (notnull=0 means nullable) colRow.dflt_value); columns.push(column); } return columns; } /** * Fetch index information for a specific table using PRAGMA */ async fetchTableIndexes(databaseId, tableName) { const sql = `PRAGMA index_list('${tableName}')`; const response = await this.apiClient.query(databaseId, sql); if (!response.success || !response.results || response.results.length === 0) { return []; } const indexes = []; for (const row of response.results) { const indexRow = row; // Fetch columns in this index const columns = await this.fetchIndexColumns(databaseId, indexRow.name); const index = new Index_1.Index(indexRow.name, tableName, columns, indexRow.unique === 1, // isUnique indexRow.origin === 'pk'); indexes.push(index); } return indexes; } /** * Fetch column names for a specific index using PRAGMA */ async fetchIndexColumns(databaseId, indexName) { const sql = `PRAGMA index_info('${indexName}')`; const response = await this.apiClient.query(databaseId, sql); if (!response.success || !response.results || response.results.length === 0) { return []; } const columns = []; for (const row of response.results) { const colRow = row; columns.push(colRow.name); } return columns; } /** * Fetch foreign key constraints for a specific table using PRAGMA */ async fetchTableForeignKeys(databaseId, tableName) { const sql = `PRAGMA foreign_key_list('${tableName}')`; const response = await this.apiClient.query(databaseId, sql); if (!response.success || !response.results || response.results.length === 0) { return []; } const foreignKeys = []; for (const row of response.results) { const fkRow = row; const foreignKey = new ForeignKey_1.ForeignKey(tableName, fkRow.from, fkRow.table, fkRow.to, fkRow.on_delete ?? 'NO ACTION', fkRow.on_update ?? 'NO ACTION'); foreignKeys.push(foreignKey); } return foreignKeys; } /** * Parse column names from index CREATE INDEX SQL statement */ parseIndexColumns(sql) { // Extract column names from CREATE INDEX statement // Example: CREATE INDEX idx_users_email ON users(email) // Example: CREATE UNIQUE INDEX idx_users_username ON users(username, status) const match = sql.match(/\(([^)]+)\)/); if (!match) { return []; } const columnsStr = match[1]; return columnsStr.split(',').map((col) => col.trim()); } } exports.CloudflareD1Repository = CloudflareD1Repository; //# sourceMappingURL=CloudflareD1Repository.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/semanticintent/semantic-d1-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server