UNPKG

@algochad/prisma-core

Version:

A comprehensive NestJS library that provides EF-Core-like operations using Prisma and GraphQL. Features LINQ-style query builders, advanced data manipulation, GraphQL integration with genql, and a unified API for both Prisma and GraphQL operations. Includ

352 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaInspector = void 0; class SchemaInspector { static schemaCache = new Map(); static lastInspectionTime = new Map(); static CACHE_DURATION = 300000; static async inspectSchema(prismaClient) { const clientId = this.getClientId(prismaClient); const now = Date.now(); const lastInspection = this.lastInspectionTime.get(clientId) || 0; if (now - lastInspection < this.CACHE_DURATION && this.schemaCache.has(clientId)) { return this.schemaCache.get(clientId); } const schema = await this.extractSchemaFromClient(prismaClient); this.schemaCache.set(clientId, schema); this.lastInspectionTime.set(clientId, now); return schema; } static async getTableSchema(prismaClient, modelName) { const schema = await this.inspectSchema(prismaClient); if (schema.tables.has(modelName)) { return schema.tables.get(modelName); } for (const [tableName, tableSchema] of schema.tables) { if (tableSchema.modelName.toLowerCase() === modelName.toLowerCase()) { return tableSchema; } } return null; } static async extractSchemaFromClient(prismaClient) { const dmmf = this.getDMMF(prismaClient); if (!dmmf) { console.warn('Unable to access Prisma DMMF, using fallback schema inspection...'); return this.extractSchemaFromClientFallback(prismaClient); } const dialect = this.detectDialect(prismaClient); const tables = new Map(); const enums = new Map(); if (dmmf.datamodel?.enums) { for (const enumDef of dmmf.datamodel.enums) { enums.set(enumDef.name, enumDef.values.map((v) => v.name)); } } if (dmmf.datamodel?.models) { for (const model of dmmf.datamodel.models) { const tableSchema = this.extractTableSchema(model, dialect); tables.set(tableSchema.tableName, tableSchema); } } return { tables, enums, dialect, version: this.getVersion(prismaClient), }; } static extractTableSchema(model, dialect) { const columns = new Map(); const relations = new Map(); const primaryKey = []; const uniqueConstraints = []; const indexes = []; for (const field of model.fields) { if (field.kind === 'scalar') { const columnInfo = { name: field.name, type: this.mapPrismaTypeToColumnType(field.type), isOptional: field.isOptional, isArray: field.isList, isId: field.isId, isUnique: field.isUnique, isList: field.isList, default: field.default, }; columns.set(field.name, columnInfo); if (field.isId) { primaryKey.push(field.name); } } else if (field.kind === 'object') { const relationInfo = { name: field.name, type: this.determineRelationType(field), model: field.type, fields: field.relationFromFields || [], references: field.relationToFields || [], isOptional: field.isOptional, }; relations.set(field.name, relationInfo); if (field.relationFromFields) { for (let i = 0; i < field.relationFromFields.length; i++) { const fkField = field.relationFromFields[i]; if (!columns.has(fkField)) { columns.set(fkField, { name: fkField, type: 'string', isOptional: field.isOptional, isArray: false, isId: false, isUnique: false, isList: false, relationName: field.relationName, }); } } } } } if (model.uniqueFields) { for (const uniqueField of model.uniqueFields) { uniqueConstraints.push(uniqueField); } } if (model.indexes) { for (const index of model.indexes) { indexes.push({ name: index.name || `idx_${model.name}_${index.fields.join('_')}`, fields: index.fields, isUnique: index.isUnique || false, type: index.type, }); } } return { tableName: this.getTableName(model), modelName: model.name, columns, primaryKey, uniqueConstraints, relations, indexes, }; } static determineRelationType(field) { if (field.isList) { return 'one-to-many'; } if (field.relationFromFields && field.relationFromFields.length > 0) { return 'many-to-one'; } return 'one-to-one'; } static mapPrismaTypeToColumnType(prismaType) { switch (prismaType.toLowerCase()) { case 'string': return 'string'; case 'int': case 'float': return 'number'; case 'boolean': return 'boolean'; case 'datetime': return 'date'; case 'json': return 'json'; case 'bytes': return 'bytes'; case 'decimal': return 'decimal'; case 'bigint': return 'bigint'; default: return 'string'; } } static getTableName(model) { if (model.dbName) { return model.dbName; } return model.name; } static getDMMF(prismaClient) { const candidates = [ () => prismaClient._dmmf, () => prismaClient._engine?.dmmf, () => prismaClient._engineConfig?.dmmf, () => prismaClient.dmmf, () => prismaClient._fetcher?.dmmf, () => prismaClient._clientVersion?.dmmf, () => prismaClient.constructor?.dmmf, () => prismaClient.constructor?._dmmf, () => prismaClient._internals?.dmmf, () => prismaClient._client?.dmmf, () => typeof prismaClient.getDMMF === 'function' ? prismaClient.getDMMF() : null, () => typeof prismaClient._getDMMF === 'function' ? prismaClient._getDMMF() : null, ]; for (const getCandidateFunc of candidates) { try { const candidate = getCandidateFunc(); if (candidate && (candidate.datamodel || candidate.schema)) { console.log('✅ Successfully accessed Prisma DMMF'); return candidate; } } catch (error) { continue; } } console.warn('❌ Unable to access Prisma DMMF. Available client properties:', Object.keys(prismaClient)); for (const key of Object.keys(prismaClient)) { const value = prismaClient[key]; if (value && typeof value === 'object' && (value.datamodel || value.schema)) { console.log(`✅ Found DMMF-like object in property: ${key}`); return value; } } return null; } static detectDialect(prismaClient) { const dmmf = this.getDMMF(prismaClient); if (dmmf && dmmf.datamodel && dmmf.datamodel.datasource) { const provider = dmmf.datamodel.datasource.provider?.toLowerCase(); switch (provider) { case 'postgresql': case 'postgres': return 'postgresql'; case 'mysql': return 'mysql'; case 'sqlite': return 'sqlite'; default: console.warn(`Unknown database provider: ${provider}, defaulting to postgresql`); return 'postgresql'; } } const clientString = prismaClient.toString(); if (clientString.includes('postgresql') || clientString.includes('postgres')) { return 'postgresql'; } else if (clientString.includes('mysql')) { return 'mysql'; } else if (clientString.includes('sqlite')) { return 'sqlite'; } return 'postgresql'; } static getVersion(prismaClient) { return prismaClient._clientVersion || 'unknown'; } static getClientId(prismaClient) { const props = [ prismaClient._clientVersion, prismaClient._engine?.config?.generator?.previewFeatures?.join(','), typeof prismaClient._dmmf, ].filter(Boolean); return props.join('|') || 'default'; } static clearCache() { this.schemaCache.clear(); this.lastInspectionTime.clear(); } static getCachedSchema(prismaClient) { const clientId = this.getClientId(prismaClient); return this.schemaCache.get(clientId) || null; } static async extractSchemaFromClientFallback(prismaClient) { console.log('🔄 Using fallback schema extraction...'); const tables = new Map(); const enums = new Map(); const dialect = this.detectDialectFallback(prismaClient); const modelNames = this.extractModelNamesFromClient(prismaClient); for (const modelName of modelNames) { const tableSchema = { tableName: modelName.toLowerCase(), modelName: modelName, columns: new Map([ [ 'id', { name: 'id', type: 'string', isOptional: false, isArray: false, isId: true, isUnique: true, isList: false, }, ], ]), primaryKey: ['id'], uniqueConstraints: [], relations: new Map(), indexes: [], }; tables.set(tableSchema.tableName, tableSchema); } return { tables, enums, dialect, version: this.getVersion(prismaClient), }; } static extractModelNamesFromClient(prismaClient) { const modelNames = []; for (const key of Object.keys(prismaClient)) { const value = prismaClient[key]; if (key.startsWith('_') || key.startsWith('$') || typeof value === 'function' || ['constructor', 'prototype', 'toString', 'valueOf'].includes(key)) { continue; } if (value && typeof value === 'object' && (value.findMany || value.findFirst || value.create)) { modelNames.push(key); } } console.log(`📋 Detected ${modelNames.length} models:`, modelNames); return modelNames; } static detectDialectFallback(prismaClient) { const clientString = String(prismaClient); if (clientString.includes('postgresql') || clientString.includes('postgres')) { return 'postgresql'; } else if (clientString.includes('mysql')) { return 'mysql'; } else if (clientString.includes('sqlite')) { return 'sqlite'; } const dbUrl = process.env.DATABASE_URL || ''; if (dbUrl.includes('postgres')) { return 'postgresql'; } else if (dbUrl.includes('mysql')) { return 'mysql'; } else if (dbUrl.includes('sqlite')) { return 'sqlite'; } console.warn('⚠️ Could not detect database dialect, defaulting to postgresql'); return 'postgresql'; } } exports.SchemaInspector = SchemaInspector; //# sourceMappingURL=schema-inspector.js.map