agenticaichat 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -26
- package/bin/cli.js +301 -84
- package/dist/core/ChatbotEngine.d.ts +9 -1
- package/dist/core/ChatbotEngine.js +52 -19
- package/dist/core/EngineFactory.d.ts +45 -0
- package/dist/core/EngineFactory.js +126 -0
- package/dist/core/NLToSQLConverter.d.ts +6 -15
- package/dist/core/NLToSQLConverter.js +9 -114
- package/dist/engines/base/DatabaseEngine.d.ts +46 -0
- package/dist/engines/base/DatabaseEngine.js +42 -0
- package/dist/engines/nosql/MongoEngine.d.ts +13 -0
- package/dist/engines/nosql/MongoEngine.js +194 -0
- package/dist/engines/nosql/NoSqlEngine.d.ts +20 -0
- package/dist/engines/nosql/NoSqlEngine.js +29 -0
- package/dist/engines/sql/SqlEngine.d.ts +19 -0
- package/dist/engines/sql/SqlEngine.js +52 -0
- package/dist/engines/sql/dialects/MySQLDialect.d.ts +12 -0
- package/dist/engines/sql/dialects/MySQLDialect.js +109 -0
- package/dist/engines/sql/dialects/PostgresDialect.d.ts +11 -0
- package/dist/engines/sql/dialects/PostgresDialect.js +109 -0
- package/dist/engines/sql/dialects/SQLiteDialect.d.ts +10 -0
- package/dist/engines/sql/dialects/SQLiteDialect.js +101 -0
- package/dist/index.d.ts +17 -4
- package/dist/index.js +44 -8
- package/dist/llm/LLMFactory.d.ts +25 -0
- package/dist/llm/LLMFactory.js +137 -0
- package/dist/llm/providers/ClaudeProvider.d.ts +13 -0
- package/dist/llm/providers/ClaudeProvider.js +132 -0
- package/dist/llm/providers/DeepInfraProvider.d.ts +14 -0
- package/dist/llm/providers/DeepInfraProvider.js +144 -0
- package/dist/llm/providers/GeminiProvider.d.ts +13 -0
- package/dist/llm/providers/GeminiProvider.js +105 -0
- package/dist/llm/providers/GrokProvider.d.ts +14 -0
- package/dist/llm/providers/GrokProvider.js +144 -0
- package/dist/llm/providers/GroqProvider.d.ts +0 -0
- package/dist/llm/providers/GroqProvider.js +148 -0
- package/dist/llm/providers/OpenAIProvider.d.ts +13 -0
- package/dist/llm/providers/OpenAIProvider.js +136 -0
- package/dist/llm/providers/TogetherAIProvider.d.ts +14 -0
- package/dist/llm/providers/TogetherAIProvider.js +144 -0
- package/dist/llm/types.d.ts +34 -0
- package/dist/llm/types.js +2 -0
- package/dist/types/index.d.ts +23 -3
- package/dist/widget/ChatbotWidget.d.ts +1 -1
- package/dist/widget/ChatbotWidget.js +406 -125
- package/package.json +24 -5
- package/scripts/postinstall.js +126 -0
- package/templates/api-route.template.ts +24 -14
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MongoEngine = void 0;
|
|
4
|
+
const mongodb_1 = require("mongodb");
|
|
5
|
+
const NoSqlEngine_1 = require("./NoSqlEngine");
|
|
6
|
+
const logger_1 = require("../../utils/logger");
|
|
7
|
+
class MongoEngine extends NoSqlEngine_1.NoSqlEngine {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
super(config);
|
|
10
|
+
this.client = null;
|
|
11
|
+
this.db = null;
|
|
12
|
+
logger_1.logger.info('MongoDB engine initialized');
|
|
13
|
+
}
|
|
14
|
+
async connect() {
|
|
15
|
+
try {
|
|
16
|
+
logger_1.logger.info('Connecting to MongoDB...');
|
|
17
|
+
this.client = new mongodb_1.MongoClient(this.config.url);
|
|
18
|
+
await this.client.connect();
|
|
19
|
+
// Extract database name from config or URL
|
|
20
|
+
const dbName = this.config.database ||
|
|
21
|
+
new URL(this.config.url).pathname.slice(1);
|
|
22
|
+
this.db = this.client.db(dbName);
|
|
23
|
+
this.connected = true;
|
|
24
|
+
logger_1.logger.info('✅ MongoDB connected successfully');
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger_1.logger.error('MongoDB connection failed', error);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async disconnect() {
|
|
32
|
+
if (!this.client)
|
|
33
|
+
return;
|
|
34
|
+
try {
|
|
35
|
+
await this.client.close();
|
|
36
|
+
this.connected = false;
|
|
37
|
+
this.client = null;
|
|
38
|
+
this.db = null;
|
|
39
|
+
logger_1.logger.info('MongoDB disconnected');
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
logger_1.logger.error('MongoDB disconnect failed', error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async getSchema() {
|
|
46
|
+
if (!this.connected || !this.db) {
|
|
47
|
+
throw new Error('Not connected to MongoDB');
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
logger_1.logger.info('Extracting MongoDB schema...');
|
|
51
|
+
// Get all collections
|
|
52
|
+
const collections = await this.db.listCollections().toArray();
|
|
53
|
+
const schema = { tables: [] };
|
|
54
|
+
// Sample documents to infer schema
|
|
55
|
+
for (const collection of collections) {
|
|
56
|
+
const collectionName = collection.name;
|
|
57
|
+
// Get sample document to infer fields
|
|
58
|
+
const sampleDoc = await this.db.collection(collectionName).findOne({});
|
|
59
|
+
const columns = [];
|
|
60
|
+
if (sampleDoc) {
|
|
61
|
+
for (const [key, value] of Object.entries(sampleDoc)) {
|
|
62
|
+
columns.push({
|
|
63
|
+
name: key,
|
|
64
|
+
type: this.inferMongoType(value),
|
|
65
|
+
nullable: true // MongoDB fields are always nullable
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
schema.tables.push({
|
|
70
|
+
name: collectionName,
|
|
71
|
+
columns
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
logger_1.logger.info(`Schema extracted: ${schema.tables.length} collections found`);
|
|
75
|
+
return schema;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
logger_1.logger.error('Schema extraction failed', error);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async executeQuery(query) {
|
|
83
|
+
if (!this.connected || !this.db) {
|
|
84
|
+
throw new Error('Not connected to MongoDB');
|
|
85
|
+
}
|
|
86
|
+
// Parse query if it's a string
|
|
87
|
+
let parsedQuery;
|
|
88
|
+
if (typeof query === 'string') {
|
|
89
|
+
try {
|
|
90
|
+
parsedQuery = JSON.parse(query);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new Error('Invalid MongoDB query format. Expected JSON.');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
parsedQuery = query;
|
|
98
|
+
}
|
|
99
|
+
if (!this.validateQuery(parsedQuery)) {
|
|
100
|
+
throw new Error('Invalid MongoDB query');
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
logger_1.logger.debug_log('Executing MongoDB query:', parsedQuery);
|
|
104
|
+
const { collection, operation, filter = {}, options = {} } = parsedQuery;
|
|
105
|
+
if (!collection || !operation) {
|
|
106
|
+
throw new Error('MongoDB query must specify collection and operation');
|
|
107
|
+
}
|
|
108
|
+
const coll = this.db.collection(collection);
|
|
109
|
+
let result;
|
|
110
|
+
switch (operation.toLowerCase()) {
|
|
111
|
+
case 'find':
|
|
112
|
+
result = await coll.find(filter, options).toArray();
|
|
113
|
+
break;
|
|
114
|
+
case 'findone':
|
|
115
|
+
result = await coll.findOne(filter, options);
|
|
116
|
+
result = result ? [result] : [];
|
|
117
|
+
break;
|
|
118
|
+
case 'count':
|
|
119
|
+
case 'countdocuments':
|
|
120
|
+
result = await coll.countDocuments(filter);
|
|
121
|
+
result = [{ count: result }];
|
|
122
|
+
break;
|
|
123
|
+
case 'aggregate':
|
|
124
|
+
const pipeline = parsedQuery.pipeline || [];
|
|
125
|
+
result = await coll.aggregate(pipeline).toArray();
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
throw new Error(`Unsupported MongoDB operation: ${operation}`);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
rows: Array.isArray(result) ? result : [result],
|
|
132
|
+
rowCount: Array.isArray(result) ? result.length : 1
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
logger_1.logger.error('MongoDB query execution failed', error);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
validateQuery(query) {
|
|
141
|
+
if (!super.validateQuery(query)) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
// Parse if string
|
|
145
|
+
let parsedQuery;
|
|
146
|
+
if (typeof query === 'string') {
|
|
147
|
+
try {
|
|
148
|
+
parsedQuery = JSON.parse(query);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
logger_1.logger.error('Invalid JSON format for MongoDB query');
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
parsedQuery = query;
|
|
157
|
+
}
|
|
158
|
+
// Check required fields
|
|
159
|
+
if (!parsedQuery.collection) {
|
|
160
|
+
logger_1.logger.error('MongoDB query must specify a collection');
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (!parsedQuery.operation) {
|
|
164
|
+
logger_1.logger.error('MongoDB query must specify an operation');
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
// Validate operation is read-only
|
|
168
|
+
const allowedOperations = ['find', 'findone', 'count', 'countdocuments', 'aggregate'];
|
|
169
|
+
if (!allowedOperations.includes(parsedQuery.operation.toLowerCase())) {
|
|
170
|
+
logger_1.logger.warn(`Unsafe MongoDB operation: ${parsedQuery.operation}`);
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
inferMongoType(value) {
|
|
176
|
+
if (value === null || value === undefined)
|
|
177
|
+
return 'null';
|
|
178
|
+
if (typeof value === 'string')
|
|
179
|
+
return 'string';
|
|
180
|
+
if (typeof value === 'number') {
|
|
181
|
+
return Number.isInteger(value) ? 'int' : 'double';
|
|
182
|
+
}
|
|
183
|
+
if (typeof value === 'boolean')
|
|
184
|
+
return 'boolean';
|
|
185
|
+
if (value instanceof Date)
|
|
186
|
+
return 'date';
|
|
187
|
+
if (Array.isArray(value))
|
|
188
|
+
return 'array';
|
|
189
|
+
if (typeof value === 'object')
|
|
190
|
+
return 'object';
|
|
191
|
+
return 'unknown';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.MongoEngine = MongoEngine;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DatabaseEngine } from '../base/DatabaseEngine';
|
|
2
|
+
import { DatabaseConfig, DatabaseSchema, QueryResult } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for all NoSQL database engines
|
|
5
|
+
*/
|
|
6
|
+
export declare abstract class NoSqlEngine extends DatabaseEngine {
|
|
7
|
+
constructor(config: DatabaseConfig);
|
|
8
|
+
/**
|
|
9
|
+
* Validate NoSQL query
|
|
10
|
+
* Each NoSQL DB has different query format
|
|
11
|
+
*/
|
|
12
|
+
validateQuery(query: string | object): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Abstract methods that implementations must provide
|
|
15
|
+
*/
|
|
16
|
+
abstract connect(): Promise<void>;
|
|
17
|
+
abstract disconnect(): Promise<void>;
|
|
18
|
+
abstract getSchema(): Promise<DatabaseSchema>;
|
|
19
|
+
abstract executeQuery(query: object): Promise<QueryResult>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoSqlEngine = void 0;
|
|
4
|
+
const DatabaseEngine_1 = require("../base/DatabaseEngine");
|
|
5
|
+
const logger_1 = require("../../utils/logger");
|
|
6
|
+
/**
|
|
7
|
+
* Base class for all NoSQL database engines
|
|
8
|
+
*/
|
|
9
|
+
class NoSqlEngine extends DatabaseEngine_1.DatabaseEngine {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super(config);
|
|
12
|
+
if (config.category !== 'nosql') {
|
|
13
|
+
throw new Error('NoSqlEngine requires category to be "nosql"');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate NoSQL query
|
|
18
|
+
* Each NoSQL DB has different query format
|
|
19
|
+
*/
|
|
20
|
+
validateQuery(query) {
|
|
21
|
+
// Basic validation - can be overridden by specific engines
|
|
22
|
+
if (!query) {
|
|
23
|
+
logger_1.logger.error('Query cannot be empty');
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.NoSqlEngine = NoSqlEngine;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DatabaseEngine } from '../base/DatabaseEngine';
|
|
2
|
+
import { DatabaseConfig, DatabaseSchema, QueryResult } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for all SQL database engines
|
|
5
|
+
*/
|
|
6
|
+
export declare abstract class SqlEngine extends DatabaseEngine {
|
|
7
|
+
constructor(config: DatabaseConfig);
|
|
8
|
+
/**
|
|
9
|
+
* Validate SQL query for security
|
|
10
|
+
*/
|
|
11
|
+
validateQuery(query: string | object): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Abstract methods that dialects must implement
|
|
14
|
+
*/
|
|
15
|
+
abstract connect(): Promise<void>;
|
|
16
|
+
abstract disconnect(): Promise<void>;
|
|
17
|
+
abstract getSchema(): Promise<DatabaseSchema>;
|
|
18
|
+
abstract executeQuery(query: string): Promise<QueryResult>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SqlEngine = void 0;
|
|
4
|
+
const DatabaseEngine_1 = require("../base/DatabaseEngine");
|
|
5
|
+
const logger_1 = require("../../utils/logger");
|
|
6
|
+
/**
|
|
7
|
+
* Base class for all SQL database engines
|
|
8
|
+
*/
|
|
9
|
+
class SqlEngine extends DatabaseEngine_1.DatabaseEngine {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super(config);
|
|
12
|
+
if (config.category !== 'sql') {
|
|
13
|
+
throw new Error('SqlEngine requires category to be "sql"');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate SQL query for security
|
|
18
|
+
*/
|
|
19
|
+
validateQuery(query) {
|
|
20
|
+
if (typeof query !== 'string') {
|
|
21
|
+
logger_1.logger.error('SQL query must be a string');
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const sql = query.toUpperCase().trim();
|
|
25
|
+
// Check for dangerous keywords
|
|
26
|
+
const dangerousKeywords = [
|
|
27
|
+
'DROP',
|
|
28
|
+
'DELETE',
|
|
29
|
+
'TRUNCATE',
|
|
30
|
+
'ALTER',
|
|
31
|
+
'CREATE TABLE',
|
|
32
|
+
'CREATE DATABASE',
|
|
33
|
+
'GRANT',
|
|
34
|
+
'REVOKE',
|
|
35
|
+
'INSERT',
|
|
36
|
+
'UPDATE'
|
|
37
|
+
];
|
|
38
|
+
for (const keyword of dangerousKeywords) {
|
|
39
|
+
if (sql.includes(keyword)) {
|
|
40
|
+
logger_1.logger.warn(`Dangerous SQL keyword detected: ${keyword}`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Must start with SELECT
|
|
45
|
+
if (!sql.startsWith('SELECT') && !sql.startsWith('WITH')) {
|
|
46
|
+
logger_1.logger.warn('Only SELECT queries are allowed');
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.SqlEngine = SqlEngine;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import mysql from 'mysql2/promise';
|
|
2
|
+
import { SqlEngine } from '../SqlEngine';
|
|
3
|
+
import { DatabaseConfig, DatabaseSchema, QueryResult } from '../../../types';
|
|
4
|
+
export declare class MySQLDialect extends SqlEngine {
|
|
5
|
+
protected connection: mysql.Connection | null;
|
|
6
|
+
constructor(config: DatabaseConfig);
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
disconnect(): Promise<void>;
|
|
9
|
+
getSchema(): Promise<DatabaseSchema>;
|
|
10
|
+
executeQuery(query: string): Promise<QueryResult>;
|
|
11
|
+
private formatSchema;
|
|
12
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MySQLDialect = void 0;
|
|
7
|
+
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
8
|
+
const SqlEngine_1 = require("../SqlEngine");
|
|
9
|
+
const logger_1 = require("../../../utils/logger");
|
|
10
|
+
class MySQLDialect extends SqlEngine_1.SqlEngine {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config);
|
|
13
|
+
this.connection = null;
|
|
14
|
+
logger_1.logger.info('MySQL dialect initialized');
|
|
15
|
+
}
|
|
16
|
+
async connect() {
|
|
17
|
+
try {
|
|
18
|
+
logger_1.logger.info('Connecting to MySQL...');
|
|
19
|
+
this.connection = await promise_1.default.createConnection(this.config.url);
|
|
20
|
+
this.connected = true;
|
|
21
|
+
logger_1.logger.info('✅ MySQL connected successfully');
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
logger_1.logger.error('MySQL connection failed', error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async disconnect() {
|
|
29
|
+
if (!this.connection)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
await this.connection.end();
|
|
33
|
+
this.connected = false;
|
|
34
|
+
this.connection = null;
|
|
35
|
+
logger_1.logger.info('MySQL disconnected');
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
logger_1.logger.error('MySQL disconnect failed', error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async getSchema() {
|
|
42
|
+
if (!this.connected || !this.connection) {
|
|
43
|
+
throw new Error('Not connected to MySQL');
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
logger_1.logger.info('Extracting MySQL schema...');
|
|
47
|
+
const query = `
|
|
48
|
+
SELECT
|
|
49
|
+
TABLE_NAME as table_name,
|
|
50
|
+
COLUMN_NAME as column_name,
|
|
51
|
+
DATA_TYPE as data_type,
|
|
52
|
+
IS_NULLABLE as is_nullable
|
|
53
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
54
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
55
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION;
|
|
56
|
+
`;
|
|
57
|
+
const [rows] = await this.connection.query(query);
|
|
58
|
+
const schema = this.formatSchema(rows);
|
|
59
|
+
logger_1.logger.info(`Schema extracted: ${schema.tables.length} tables found`);
|
|
60
|
+
return schema;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger_1.logger.error('Schema extraction failed', error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async executeQuery(query) {
|
|
68
|
+
if (!this.connected || !this.connection) {
|
|
69
|
+
throw new Error('Not connected to MySQL');
|
|
70
|
+
}
|
|
71
|
+
if (!this.validateQuery(query)) {
|
|
72
|
+
throw new Error('Invalid or unsafe SQL query');
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
logger_1.logger.debug_log('Executing MySQL query:', query);
|
|
76
|
+
const [rows, fields] = await this.connection.query(query);
|
|
77
|
+
return {
|
|
78
|
+
rows: rows,
|
|
79
|
+
rowCount: Array.isArray(rows) ? rows.length : 0,
|
|
80
|
+
fields: fields
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger_1.logger.error('Query execution failed', error);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
formatSchema(rows) {
|
|
89
|
+
const tablesMap = new Map();
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
const tableName = row.table_name;
|
|
92
|
+
const column = {
|
|
93
|
+
name: row.column_name,
|
|
94
|
+
type: row.data_type,
|
|
95
|
+
nullable: row.is_nullable === 'YES'
|
|
96
|
+
};
|
|
97
|
+
if (!tablesMap.has(tableName)) {
|
|
98
|
+
tablesMap.set(tableName, []);
|
|
99
|
+
}
|
|
100
|
+
tablesMap.get(tableName).push(column);
|
|
101
|
+
}
|
|
102
|
+
const tables = [];
|
|
103
|
+
for (const [name, columns] of tablesMap.entries()) {
|
|
104
|
+
tables.push({ name, columns });
|
|
105
|
+
}
|
|
106
|
+
return { tables };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.MySQLDialect = MySQLDialect;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SqlEngine } from '../SqlEngine';
|
|
2
|
+
import { DatabaseConfig, DatabaseSchema, QueryResult } from '../../../types';
|
|
3
|
+
export declare class PostgresDialect extends SqlEngine {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(config: DatabaseConfig);
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
disconnect(): Promise<void>;
|
|
8
|
+
getSchema(): Promise<DatabaseSchema>;
|
|
9
|
+
executeQuery(query: string): Promise<QueryResult>;
|
|
10
|
+
private formatSchema;
|
|
11
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostgresDialect = void 0;
|
|
4
|
+
const pg_1 = require("pg");
|
|
5
|
+
const SqlEngine_1 = require("../SqlEngine");
|
|
6
|
+
const logger_1 = require("../../../utils/logger");
|
|
7
|
+
class PostgresDialect extends SqlEngine_1.SqlEngine {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
super(config);
|
|
10
|
+
this.client = null;
|
|
11
|
+
logger_1.logger.info('PostgreSQL dialect initialized');
|
|
12
|
+
}
|
|
13
|
+
async connect() {
|
|
14
|
+
try {
|
|
15
|
+
logger_1.logger.info('Connecting to PostgreSQL...');
|
|
16
|
+
this.client = new pg_1.Client({
|
|
17
|
+
connectionString: this.config.url
|
|
18
|
+
});
|
|
19
|
+
await this.client.connect();
|
|
20
|
+
this.connected = true;
|
|
21
|
+
logger_1.logger.info('✅ PostgreSQL connected successfully');
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
logger_1.logger.error('PostgreSQL connection failed', error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async disconnect() {
|
|
29
|
+
if (!this.client)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
await this.client.end();
|
|
33
|
+
this.connected = false;
|
|
34
|
+
this.client = null;
|
|
35
|
+
logger_1.logger.info('PostgreSQL disconnected');
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
logger_1.logger.error('PostgreSQL disconnect failed', error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async getSchema() {
|
|
42
|
+
if (!this.connected || !this.client) {
|
|
43
|
+
throw new Error('Not connected to PostgreSQL');
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
logger_1.logger.info('Extracting PostgreSQL schema...');
|
|
47
|
+
const query = `
|
|
48
|
+
SELECT
|
|
49
|
+
table_name,
|
|
50
|
+
column_name,
|
|
51
|
+
data_type,
|
|
52
|
+
is_nullable
|
|
53
|
+
FROM information_schema.columns
|
|
54
|
+
WHERE table_schema = 'public'
|
|
55
|
+
ORDER BY table_name, ordinal_position;
|
|
56
|
+
`;
|
|
57
|
+
const result = await this.client.query(query);
|
|
58
|
+
const schema = this.formatSchema(result.rows);
|
|
59
|
+
logger_1.logger.info(`Schema extracted: ${schema.tables.length} tables found`);
|
|
60
|
+
return schema;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger_1.logger.error('Schema extraction failed', error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async executeQuery(query) {
|
|
68
|
+
if (!this.connected || !this.client) {
|
|
69
|
+
throw new Error('Not connected to PostgreSQL');
|
|
70
|
+
}
|
|
71
|
+
if (!this.validateQuery(query)) {
|
|
72
|
+
throw new Error('Invalid or unsafe SQL query');
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
logger_1.logger.debug_log('Executing PostgreSQL query:', query);
|
|
76
|
+
const result = await this.client.query(query);
|
|
77
|
+
return {
|
|
78
|
+
rows: result.rows,
|
|
79
|
+
rowCount: result.rowCount || 0,
|
|
80
|
+
fields: result.fields
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger_1.logger.error('Query execution failed', error);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
formatSchema(rows) {
|
|
89
|
+
const tablesMap = new Map();
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
const tableName = row.table_name;
|
|
92
|
+
const column = {
|
|
93
|
+
name: row.column_name,
|
|
94
|
+
type: row.data_type,
|
|
95
|
+
nullable: row.is_nullable === 'YES'
|
|
96
|
+
};
|
|
97
|
+
if (!tablesMap.has(tableName)) {
|
|
98
|
+
tablesMap.set(tableName, []);
|
|
99
|
+
}
|
|
100
|
+
tablesMap.get(tableName).push(column);
|
|
101
|
+
}
|
|
102
|
+
const tables = [];
|
|
103
|
+
for (const [name, columns] of tablesMap.entries()) {
|
|
104
|
+
tables.push({ name, columns });
|
|
105
|
+
}
|
|
106
|
+
return { tables };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.PostgresDialect = PostgresDialect;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SqlEngine } from '../SqlEngine';
|
|
2
|
+
import { DatabaseConfig, DatabaseSchema, QueryResult } from '../../../types';
|
|
3
|
+
export declare class SQLiteDialect extends SqlEngine {
|
|
4
|
+
private db;
|
|
5
|
+
constructor(config: DatabaseConfig);
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
disconnect(): Promise<void>;
|
|
8
|
+
getSchema(): Promise<DatabaseSchema>;
|
|
9
|
+
executeQuery(query: string): Promise<QueryResult>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SQLiteDialect = void 0;
|
|
7
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
8
|
+
const SqlEngine_1 = require("../SqlEngine");
|
|
9
|
+
const logger_1 = require("../../../utils/logger");
|
|
10
|
+
class SQLiteDialect extends SqlEngine_1.SqlEngine {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config);
|
|
13
|
+
this.db = null;
|
|
14
|
+
logger_1.logger.info('SQLite dialect initialized');
|
|
15
|
+
}
|
|
16
|
+
async connect() {
|
|
17
|
+
try {
|
|
18
|
+
logger_1.logger.info('Connecting to SQLite...');
|
|
19
|
+
// Extract file path from URL (sqlite:///path/to/db.sqlite)
|
|
20
|
+
const dbPath = this.config.url.replace('sqlite:///', '').replace('sqlite://', '');
|
|
21
|
+
this.db = new better_sqlite3_1.default(dbPath);
|
|
22
|
+
this.connected = true;
|
|
23
|
+
logger_1.logger.info('✅ SQLite connected successfully');
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
logger_1.logger.error('SQLite connection failed', error);
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async disconnect() {
|
|
31
|
+
if (!this.db)
|
|
32
|
+
return;
|
|
33
|
+
try {
|
|
34
|
+
this.db.close();
|
|
35
|
+
this.connected = false;
|
|
36
|
+
this.db = null;
|
|
37
|
+
logger_1.logger.info('SQLite disconnected');
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
logger_1.logger.error('SQLite disconnect failed', error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async getSchema() {
|
|
44
|
+
if (!this.connected || !this.db) {
|
|
45
|
+
throw new Error('Not connected to SQLite');
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
logger_1.logger.info('Extracting SQLite schema...');
|
|
49
|
+
// Get all tables
|
|
50
|
+
const tablesQuery = `
|
|
51
|
+
SELECT name FROM sqlite_master
|
|
52
|
+
WHERE type='table'
|
|
53
|
+
AND name NOT LIKE 'sqlite_%';
|
|
54
|
+
`;
|
|
55
|
+
const tables = this.db.prepare(tablesQuery).all();
|
|
56
|
+
const schema = { tables: [] };
|
|
57
|
+
// Get columns for each table
|
|
58
|
+
for (const table of tables) {
|
|
59
|
+
const columnsQuery = `PRAGMA table_info(${table.name});`;
|
|
60
|
+
const columns = this.db.prepare(columnsQuery).all();
|
|
61
|
+
const tableSchema = {
|
|
62
|
+
name: table.name,
|
|
63
|
+
columns: columns.map(col => ({
|
|
64
|
+
name: col.name,
|
|
65
|
+
type: col.type,
|
|
66
|
+
nullable: col.notnull === 0
|
|
67
|
+
}))
|
|
68
|
+
};
|
|
69
|
+
schema.tables.push(tableSchema);
|
|
70
|
+
}
|
|
71
|
+
logger_1.logger.info(`Schema extracted: ${schema.tables.length} tables found`);
|
|
72
|
+
return schema;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logger_1.logger.error('Schema extraction failed', error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async executeQuery(query) {
|
|
80
|
+
if (!this.connected || !this.db) {
|
|
81
|
+
throw new Error('Not connected to SQLite');
|
|
82
|
+
}
|
|
83
|
+
if (!this.validateQuery(query)) {
|
|
84
|
+
throw new Error('Invalid or unsafe SQL query');
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
logger_1.logger.debug_log('Executing SQLite query:', query);
|
|
88
|
+
const stmt = this.db.prepare(query);
|
|
89
|
+
const rows = stmt.all();
|
|
90
|
+
return {
|
|
91
|
+
rows: rows,
|
|
92
|
+
rowCount: rows.length
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logger_1.logger.error('Query execution failed', error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.SQLiteDialect = SQLiteDialect;
|