UNPKG

@cheetah.js/orm

Version:
288 lines (287 loc) 9.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withDatabase = withDatabase; const globby_1 = __importDefault(require("globby")); const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const core_1 = require("@cheetah.js/core"); const entities_1 = require("../domain/entities"); const orm_1 = require("../orm"); const orm_service_1 = require("../orm.service"); const bun_pg_driver_1 = require("../driver/bun-pg.driver"); const DEFAULT_SCHEMA = 'public'; const DEFAULT_CONNECTION = { host: 'localhost', port: 5432, database: 'postgres', username: 'postgres', password: 'postgres', driver: bun_pg_driver_1.BunPgDriver, }; const sessionCache = new Map(); function getCacheKey(options) { const connection = resolveConnection(options.connection); return JSON.stringify({ host: connection.host, port: connection.port, database: connection.database, schema: options.schema ?? DEFAULT_SCHEMA, entityFile: options.entityFile, migrationPath: connection.migrationPath, }); } async function withDatabase(arg1, arg2, arg3) { const { routine: targetRoutine, options: targetOptions, statements } = await normalizeArgs(arg1, arg2, arg3); const cacheKey = getCacheKey(targetOptions); let cachedSession = sessionCache.get(cacheKey); const schemaStatements = await resolveSchemaStatements(statements, targetOptions); if (!cachedSession) { const session = await createSession(targetOptions); cachedSession = { orm: session.orm, tables: [], schema: session.schema, }; sessionCache.set(cacheKey, cachedSession); } const context = buildContext(cachedSession.orm); await dropAndRecreateSchema(context, cachedSession.schema); await prepareSchema(context, cachedSession.schema); await createTables(context, schemaStatements); await targetRoutine(context); } async function createSession(options) { const logger = selectLogger(options); const orm = new orm_1.Orm(logger); await initializeOrm(orm, options); return { orm, schema: options.schema ?? DEFAULT_SCHEMA }; } function selectLogger(options) { if (options.logger) { return options.logger; } const config = { applicationConfig: { logger: { level: 'info' } } }; return new core_1.LoggerService(config); } async function initializeOrm(orm, options) { const storage = new entities_1.EntityStorage(); if (options.entityFile) { const entityFiles = await (0, globby_1.default)(options.entityFile, { absolute: true }); for (const file of entityFiles) { await Promise.resolve(`${file}`).then(s => __importStar(require(s))); } } const service = new orm_service_1.OrmService(orm, storage, options.entityFile); const connection = resolveConnection(options.connection); await service.onInit(connection); } function resolveConnection(overrides) { if (!overrides) { return DEFAULT_CONNECTION; } return { ...DEFAULT_CONNECTION, ...overrides, driver: overrides.driver ?? bun_pg_driver_1.BunPgDriver, }; } function buildContext(orm) { return { orm, executeSql: (sql) => executeSql(orm, sql), }; } async function executeSql(orm, sql) { if (!orm.driverInstance) { throw new Error('Database driver not initialized. Call withDatabase() before executing SQL.'); } const result = await orm.driverInstance.executeSql(sql); return { rows: Array.isArray(result) ? result : [] }; } async function createTables(context, statements) { const payload = statements.filter(Boolean); if (payload.length < 1) { return; } await executeStatements(context, payload); } async function executeStatements(context, statements) { for (const statement of statements) { await context.executeSql(statement); } } async function dropAndRecreateSchema(context, schema) { await context.executeSql(`DROP SCHEMA IF EXISTS ${schema} CASCADE; CREATE SCHEMA ${schema};`); } async function normalizeArgs(tablesOrRoutine, routineOrOptions, optionsOrStatements) { if (Array.isArray(tablesOrRoutine)) { return { routine: routineOrOptions, options: optionsOrStatements ?? {}, statements: tablesOrRoutine, }; } return { routine: tablesOrRoutine, options: routineOrOptions ?? {}, statements: Array.isArray(optionsOrStatements) ? optionsOrStatements : [], }; } async function resolveSchemaStatements(statements, options) { const explicit = statements.filter(Boolean); if (explicit.length > 0) { return explicit; } const fromMigrations = await loadStatementsFromMigrations(options); return fromMigrations.filter(Boolean); } function normalizeGlobPatterns(patterns) { return patterns.map(normalizeGlobPattern); } function normalizeGlobPattern(pattern) { return pattern.replace(/\\/g, '/'); } async function loadStatementsFromMigrations(options) { const connection = resolveConnection(options.connection); const patterns = await resolveMigrationPatterns(connection); if (patterns.length < 1) { return []; } const normalizedPatterns = normalizeGlobPatterns(patterns); const files = await (0, globby_1.default)(normalizedPatterns, { absolute: true, expandDirectories: false }); if (files.length < 1) { return []; } return extractStatementsFromFiles(files); } async function resolveMigrationPatterns(connection) { if (connection.migrationPath) { return [connection.migrationPath]; } const inferred = await inferMigrationPathFromConfig(); if (inferred) { return [inferred]; } return []; } async function inferMigrationPathFromConfig() { const configFile = await findConfigFile(); if (!configFile) { return undefined; } const contents = await safeReadFile(configFile); if (!contents) { return undefined; } return extractMigrationPath(contents, configFile); } async function findConfigFile() { const candidates = [ 'cheetah.config.ts', 'cheetah.config.js', 'cheetah.config.mjs', 'cheetah.config.cjs', ]; for (const file of candidates) { const fullPath = path_1.default.resolve(process.cwd(), file); const exists = await fileExists(fullPath); if (exists) { return fullPath; } } return undefined; } async function fileExists(target) { try { await fs_1.promises.access(target); return true; } catch { return false; } } async function safeReadFile(file) { try { return await fs_1.promises.readFile(file, 'utf8'); } catch { return undefined; } } function extractMigrationPath(source, file) { const match = source.match(/migrationPath\s*:\s*['"`]([^'"`]+)['"`]/); if (!match) { return undefined; } const candidate = match[1].trim(); if (path_1.default.isAbsolute(candidate)) { return candidate; } const baseDir = path_1.default.dirname(file); return path_1.default.resolve(baseDir, candidate); } async function extractStatementsFromFiles(files) { const statements = []; for (const file of files) { const payload = await safeReadFile(file); if (!payload) { continue; } const extracted = extractStatements(payload); statements.push(...extracted); } return Array.from(new Set(statements)); } function extractStatements(payload) { const matches = payload.match(/(CREATE\s+(?:TABLE|TYPE|INDEX|SCHEMA)[\s\S]*?;|ALTER\s+TABLE[\s\S]*?\bADD\b[\s\S]*?;)/gi) ?? []; return matches.map((statement) => statement.replace(/\s+/g, ' ').trim()); } async function prepareSchema(context, schema) { await context.executeSql(buildCreateSchemaStatement(schema)); await ensureSearchPath(context, schema); } function buildCreateSchemaStatement(schema) { return `CREATE SCHEMA IF NOT EXISTS ${schema};`; } async function ensureSearchPath(context, schema) { await context.executeSql(buildSearchPathStatement(schema)); } function buildSearchPathStatement(schema) { return `SET search_path TO ${schema};`; }