Skip to main content
Glama
index.ts5.34 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ /** * src/db/index.ts * * Minimal, env-driven DB provider selector. * - Resolves from DB_PROVIDER or DB_DIALECT (or DATABASE_URL scheme), defaulting to sqlite. * - Normalizes synonyms (postgres/postgresql -> pg, mariadb -> mysql, sqlserver -> mssql, sqlite3 -> sqlite). * - Prefers factory exports (newDb/createDb/default()) else falls back to singletons (pgDb/mysqlDb/...). */ import type { DB } from "./provider.js"; type CanonicalDialect = "pg" | "mysql" | "mssql" | "oracle"; const DIALECT_SYNONYMS: Record<string, CanonicalDialect> = { // Postgres pg: "pg", postgres: "pg", postgresql: "pg", psql: "pg", // MySQL mysql: "mysql", mariadb: "mysql", maria: "mysql", // SQL Server mssql: "mssql", "ms-sql": "mssql", sqlserver: "mssql", "sql-server": "mssql", // Oracle oracle: "oracle", oracledb: "oracle", oci: "oracle", }; function canonicalizeDialect(input?: string | null): CanonicalDialect | undefined { if (!input) return undefined; const key = String(input).trim().toLowerCase(); return DIALECT_SYNONYMS[key]; } function dialectFromDatabaseUrl(url?: string): CanonicalDialect | undefined { if (!url) return undefined; try { const u = new URL(url); const proto = u.protocol.replace(":", "").toLowerCase(); // Map only to supported dialects via synonyms (pg/mysql/mssql/oracle) return DIALECT_SYNONYMS[proto]; } catch { // Non-URL strings (JDBC-ish, etc.) - we no longer guess "sqlite"; return undefined return undefined; } } function resolveDialectFromEnv(env = process.env): CanonicalDialect { const fromProvider = canonicalizeDialect(env.DB_PROVIDER); if (fromProvider) return fromProvider; const fromDialect = canonicalizeDialect(env.DB_DIALECT); if (fromDialect) return fromDialect; const fromUrl = dialectFromDatabaseUrl(env.DATABASE_URL); if (fromUrl) return fromUrl; throw new Error( "Unable to resolve DB dialect from env. " + "Please set DB_PROVIDER/DB_DIALECT/DATABASE_URL for a supported dialect (pg/mysql/mssql/oracle)." ) } /** Attach canonical dialect hint on the db object. */ function annotateDialect<T extends object>(db: T, dialect: CanonicalDialect): T & { dialect: CanonicalDialect } { if (!db) return { dialect } as any; if ((db as any).dialect !== dialect) { try { Object.defineProperty(db as any, "dialect", { value: dialect, enumerable: true }); } catch { (db as any).dialect = dialect; } } return db as any; } /** Prefer factory if available, else fall back to well-known singleton names. */ function materializeDb(mod: any, dialect: CanonicalDialect): DB { // 1) Factories (preferred) if (typeof mod?.newDb === "function") { const db = mod.newDb(); return annotateDialect(db, dialect); } if (typeof mod?.createDb === "function") { const db = mod.createDb(); return annotateDialect(db, dialect); } if (typeof mod?.default === "function") { const db = mod.default(); return annotateDialect(db, dialect); } // 2) default export already a db object? if (mod?.default && typeof mod.default === "object" && typeof mod.default.query === "function") { return annotateDialect(mod.default, dialect); } // 3) Well-known singleton names (your current exports) const knownSingletons: Record<CanonicalDialect, string[]> = { pg: ["pgDb", "db"], mysql: ["mysqlDb", "db"], mssql: ["mssqlDb", "db"], oracle: ["oracleDb", "db"], }; for (const key of knownSingletons[dialect]) { const val = mod?.[key]; if (val && typeof val.query === "function") { return annotateDialect(val, dialect); } } // 4) Heuristic: any object with query() for (const key of Object.keys(mod ?? {})) { const val = mod[key]; if (val && typeof val === "object" && typeof val.query === "function") { return annotateDialect(val, dialect); } } throw new Error( `Provider module for '${dialect}' does not expose a usable DB export. ` + `Expected a factory (newDb/createDb/default()) or a singleton (e.g., ${dialect}Db). ` + `Exports: [${Object.keys(mod ?? {}).join(", ")}]` ); } /** Load the provider module for a given canonical dialect. */ async function loadModule(dialect: CanonicalDialect): Promise<any> { switch (dialect) { case "pg": return import("./providers/postgres.js"); case "mysql": return import("./providers/mysql.js"); case "mssql": return import("./providers/mssql.js"); case "oracle": return import("./providers/oracle.js"); default: // This should be unreachable due to the CanonicalDialect union, // but we keep a defensive guard for future edits. throw new Error(`Unsupported dialect: ${dialect}`); } } /** * Public API: get a DB instance based on current env. * - Imports provider AFTER env resolution. * - Uses factory if present; otherwise singleton. */ export async function getDb(): Promise<DB> { const dialect = resolveDialectFromEnv(process.env); const mod = await loadModule(dialect); const db = materializeDb(mod, dialect); return db as DB; } /** Optional helper (e.g., for X-DB-Dialect header). */ export function getResolvedDialect(): CanonicalDialect { return resolveDialectFromEnv(process.env); }

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/Muhammad-Idzhans/mcp-server'

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