Skip to main content
Glama
base-component.repository.ts5.3 kB
import { KuzuDBClient } from '../../db/kuzu'; import { Component } from '../../types'; import { formatGraphUniqueId } from '../../utils/id.utils'; import { loggers } from '../../utils/logger'; import { RepositoryRepository } from '../repository.repository'; /** * Base repository class for Component operations * Provides common functionality and utilities for all component repositories */ export abstract class BaseComponentRepository { protected kuzuClient: KuzuDBClient; protected repositoryRepo: RepositoryRepository; protected logger = loggers.repository(); constructor(kuzuClient: KuzuDBClient, repositoryRepo: RepositoryRepository) { if (!kuzuClient) { throw new Error('BaseComponentRepository requires an initialized KuzuDBClient instance.'); } if (!repositoryRepo) { throw new Error( 'BaseComponentRepository requires an initialized RepositoryRepository instance.', ); } this.kuzuClient = kuzuClient; this.repositoryRepo = repositoryRepo; } /** * Helper to escape strings for Cypher queries to prevent injection */ protected escapeStr(value: string): string { if (typeof value !== 'string') { return ''; } // Escape single quotes, backslashes, and other special characters return value .replace(/\\/g, '\\\\') .replace(/'/g, "\\'") .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\0/g, '\\0'); } /** * Helper to format Kuzu component data to internal Component type */ protected formatKuzuRowToComponent( kuzuRowData: any, repositoryName: string, branch: string, ): Component { const rawComponent = kuzuRowData.properties || kuzuRowData; const logicalId = rawComponent.id?.toString(); return { id: logicalId, name: rawComponent.name, kind: rawComponent.kind, status: rawComponent.status, branch: rawComponent.branch, repository: `${repositoryName}:${branch}`, depends_on: Array.isArray(rawComponent.depends_on) ? rawComponent.depends_on.map(String) : rawComponent.depends_on ? [String(rawComponent.depends_on)] : [], created_at: rawComponent.created_at ? new Date(rawComponent.created_at) : new Date(), updated_at: rawComponent.updated_at ? new Date(rawComponent.updated_at) : new Date(), } as Component; } /** * Helper to create graph unique ID for components */ protected createGraphUniqueId( repositoryName: string, branch: string, componentId: string, ): string { return formatGraphUniqueId(repositoryName, branch, componentId); } /** * Helper to ensure graph projection exists for algorithms */ protected async ensureGraphProjection(projectionName: string): Promise<void> { try { // First check if the graph projection exists const checkQuery = `CALL show_graphs() RETURN name;`; const checkResult = await this.kuzuClient.executeQuery(checkQuery); const existingGraphs = checkResult.map((row: any) => row.name); if (!existingGraphs.includes(projectionName)) { // Create the graph projection if it doesn't exist await this.kuzuClient.executeQuery( `CALL create_graph('${projectionName}', ['Component'], ['DEPENDS_ON']);`, ); this.logger.debug(`Created graph projection '${projectionName}'.`); } else { this.logger.debug(`Graph projection '${projectionName}' already exists.`); } } catch (projectionError: any) { this.logger.error( `Could not ensure graph projection '${projectionName}'. Error:`, projectionError, ); throw projectionError; } } /** * Helper to handle query results that may be arrays or objects with getAll method */ protected async normalizeQueryResult(result: any): Promise<any[]> { if (!result) { return []; } if (Array.isArray(result)) { return result; } if (typeof result.getAll === 'function') { return await result.getAll(); } // Handle cases where a single object might be returned return [result]; } /** * Helper to execute query with error handling and logging */ protected async executeQueryWithLogging( query: string, params: any = {}, operation: string, ): Promise<any[]> { try { this.logger.debug(`[${operation}] Executing query: ${query}`, { params }); const result = await this.kuzuClient.executeQuery(query, params); return await this.normalizeQueryResult(result); } catch (error: any) { this.logger.error(`[${operation}] Query execution failed:`, { error: error.message, query, params, stack: error.stack, }); throw error; } } /** * Helper to validate repository node ID format */ protected validateRepositoryNodeId(repositoryNodeId: string, operation: string): string { const repoIdParts = repositoryNodeId.split(':'); if (repoIdParts.length < 2) { const error = `Invalid repositoryNodeId format: ${repositoryNodeId} in ${operation}`; this.logger.error(error); throw new Error(error); } return repoIdParts[0]; // Return logical repository name } }

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/Jakedismo/KuzuMem-MCP'

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