@xcr1234/dbhub-fork 1.0.0 → 1.1.0

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.
@@ -0,0 +1,321 @@
1
+ import {
2
+ quoteIdentifier
3
+ } from "./chunk-JFWX35TB.js";
4
+ import {
5
+ SQLRowLimiter
6
+ } from "./chunk-BRXZ5ZQB.js";
7
+ import {
8
+ ConnectorRegistry,
9
+ SafeURL,
10
+ obfuscateDSNPassword,
11
+ splitSQLStatements
12
+ } from "./chunk-C7WEAPX4.js";
13
+
14
+ // src/connectors/sqlite/index.ts
15
+ import fs from "fs/promises";
16
+ import initSqlJs from "sql.js";
17
+ var SQLiteDSNParser = class {
18
+ async parse(dsn, config) {
19
+ if (!this.isValidDSN(dsn)) {
20
+ const obfuscatedDSN = obfuscateDSNPassword(dsn);
21
+ const expectedFormat = this.getSampleDSN();
22
+ throw new Error(
23
+ `Invalid SQLite DSN format.
24
+ Provided: ${obfuscatedDSN}
25
+ Expected: ${expectedFormat}`
26
+ );
27
+ }
28
+ try {
29
+ const url = new SafeURL(dsn);
30
+ let dbPath;
31
+ if (url.hostname === "" && url.pathname === "/:memory:") {
32
+ dbPath = ":memory:";
33
+ } else {
34
+ if (url.pathname.startsWith("//")) {
35
+ dbPath = url.pathname.substring(2);
36
+ } else if (url.pathname.match(/^\/[A-Za-z]:\//)) {
37
+ dbPath = url.pathname.substring(1);
38
+ } else {
39
+ dbPath = url.pathname;
40
+ }
41
+ }
42
+ return { dbPath };
43
+ } catch (error) {
44
+ throw new Error(
45
+ `Failed to parse SQLite DSN: ${error instanceof Error ? error.message : String(error)}`
46
+ );
47
+ }
48
+ }
49
+ getSampleDSN() {
50
+ return "sqlite:///path/to/database.db";
51
+ }
52
+ isValidDSN(dsn) {
53
+ try {
54
+ return dsn.startsWith("sqlite://");
55
+ } catch (error) {
56
+ return false;
57
+ }
58
+ }
59
+ };
60
+ var SQLiteConnector = class _SQLiteConnector {
61
+ constructor() {
62
+ this.id = "sqlite";
63
+ this.name = "SQLite";
64
+ this.dsnParser = new SQLiteDSNParser();
65
+ this.db = null;
66
+ this.dbPath = ":memory:";
67
+ this.SQL = null;
68
+ this.sourceId = "default";
69
+ }
70
+ getId() {
71
+ return this.sourceId;
72
+ }
73
+ clone() {
74
+ return new _SQLiteConnector();
75
+ }
76
+ // Helper method to save in-memory database to disk
77
+ async saveToDisk() {
78
+ if (this.dbPath !== ":memory:" && this.db) {
79
+ const data = this.db.export();
80
+ const buffer = Buffer.from(data);
81
+ await fs.writeFile(this.dbPath, buffer);
82
+ }
83
+ }
84
+ // Wrapper equivalent to sqlite3 db.all()
85
+ async dbAll(sql, params = []) {
86
+ if (!this.db) throw new Error("Not connected to SQLite database");
87
+ const stmt = this.db.prepare(sql);
88
+ try {
89
+ if (params && params.length > 0) {
90
+ stmt.bind(params);
91
+ }
92
+ const rows = [];
93
+ while (stmt.step()) {
94
+ rows.push(stmt.getAsObject());
95
+ }
96
+ return rows;
97
+ } finally {
98
+ stmt.free();
99
+ }
100
+ }
101
+ // Wrapper equivalent to sqlite3 db.get()
102
+ async dbGet(sql, params = []) {
103
+ const rows = await this.dbAll(sql, params);
104
+ return rows.length > 0 ? rows[0] : void 0;
105
+ }
106
+ // Wrapper equivalent to sqlite3 db.run()
107
+ async dbRun(sql, params = []) {
108
+ if (!this.db) throw new Error("Not connected to SQLite database");
109
+ this.db.run(sql, params);
110
+ return { changes: this.db.getRowsModified() };
111
+ }
112
+ async connect(dsn, initScript, config) {
113
+ const parsedConfig = await this.dsnParser.parse(dsn, config);
114
+ this.dbPath = parsedConfig.dbPath;
115
+ try {
116
+ if (!this.SQL) {
117
+ this.SQL = await initSqlJs();
118
+ }
119
+ if (this.dbPath === ":memory:") {
120
+ this.db = new this.SQL.Database();
121
+ } else {
122
+ try {
123
+ const fileBuffer = await fs.readFile(this.dbPath);
124
+ this.db = new this.SQL.Database(fileBuffer);
125
+ } catch (err) {
126
+ if (err.code === "ENOENT") {
127
+ if (config?.readonly) {
128
+ throw new Error(`Database file not found in readonly mode: ${this.dbPath}`);
129
+ }
130
+ this.db = new this.SQL.Database();
131
+ await this.saveToDisk();
132
+ } else {
133
+ throw err;
134
+ }
135
+ }
136
+ }
137
+ if (initScript) {
138
+ this.db.run(initScript);
139
+ await this.saveToDisk();
140
+ }
141
+ } catch (error) {
142
+ console.error("Failed to connect to SQLite database (sql.js):", error);
143
+ throw error;
144
+ }
145
+ }
146
+ async disconnect() {
147
+ if (this.db) {
148
+ try {
149
+ await this.saveToDisk();
150
+ this.db.close();
151
+ this.db = null;
152
+ } catch (error) {
153
+ console.error("Error during SQLite disconnect:", error);
154
+ this.db = null;
155
+ }
156
+ }
157
+ return Promise.resolve();
158
+ }
159
+ async getSchemas() {
160
+ if (!this.db) throw new Error("Not connected to SQLite database");
161
+ return ["main"];
162
+ }
163
+ async getTables(schema) {
164
+ try {
165
+ const rows = await this.dbAll(`
166
+ SELECT name FROM sqlite_master
167
+ WHERE type='table' AND name NOT LIKE 'sqlite_%'
168
+ ORDER BY name
169
+ `);
170
+ return rows.map((row) => row.name);
171
+ } catch (error) {
172
+ throw error;
173
+ }
174
+ }
175
+ async tableExists(tableName, schema) {
176
+ try {
177
+ const row = await this.dbGet(`
178
+ SELECT name FROM sqlite_master
179
+ WHERE type='table' AND name = ?
180
+ `, [tableName]);
181
+ return !!row;
182
+ } catch (error) {
183
+ throw error;
184
+ }
185
+ }
186
+ async getTableIndexes(tableName, schema) {
187
+ try {
188
+ const indexInfoRows = await this.dbAll(`
189
+ SELECT name as index_name, 0 as is_unique
190
+ FROM sqlite_master
191
+ WHERE type = 'index' AND tbl_name = ?
192
+ `, [tableName]);
193
+ const quotedTableName = quoteIdentifier(tableName, "sqlite");
194
+ const indexListRows = await this.dbAll(
195
+ `PRAGMA index_list(${quotedTableName})`
196
+ );
197
+ const indexUniqueMap = /* @__PURE__ */ new Map();
198
+ for (const indexListRow of indexListRows) {
199
+ indexUniqueMap.set(indexListRow.name, indexListRow.unique === 1);
200
+ }
201
+ const tableInfo = await this.dbAll(
202
+ `PRAGMA table_info(${quotedTableName})`
203
+ );
204
+ const pkColumns = tableInfo.filter((col) => col.pk > 0).map((col) => col.name);
205
+ const results = [];
206
+ for (const indexInfo of indexInfoRows) {
207
+ const quotedIndexName = quoteIdentifier(indexInfo.index_name, "sqlite");
208
+ const indexDetailRows = await this.dbAll(
209
+ `PRAGMA index_info(${quotedIndexName})`
210
+ );
211
+ const columnNames = indexDetailRows.map((row) => row.name);
212
+ results.push({
213
+ index_name: indexInfo.index_name,
214
+ column_names: columnNames,
215
+ is_unique: indexUniqueMap.get(indexInfo.index_name) || false,
216
+ is_primary: false
217
+ });
218
+ }
219
+ if (pkColumns.length > 0) {
220
+ results.push({
221
+ index_name: "PRIMARY",
222
+ column_names: pkColumns,
223
+ is_unique: true,
224
+ is_primary: true
225
+ });
226
+ }
227
+ return results;
228
+ } catch (error) {
229
+ throw error;
230
+ }
231
+ }
232
+ async getTableSchema(tableName, schema) {
233
+ try {
234
+ const quotedTableName = quoteIdentifier(tableName, "sqlite");
235
+ const rows = await this.dbAll(`PRAGMA table_info(${quotedTableName})`);
236
+ return rows.map((row) => ({
237
+ column_name: row.name,
238
+ data_type: row.type,
239
+ is_nullable: row.notnull === 1 || row.pk > 0 ? "NO" : "YES",
240
+ column_default: row.dflt_value,
241
+ description: null
242
+ }));
243
+ } catch (error) {
244
+ throw error;
245
+ }
246
+ }
247
+ async getStoredProcedures(schema, routineType) {
248
+ if (!this.db) throw new Error("Not connected to SQLite database");
249
+ return [];
250
+ }
251
+ async getStoredProcedureDetail(procedureName, schema) {
252
+ throw new Error(
253
+ "SQLite does not support stored procedures. Functions are defined programmatically through the SQLite API, not stored in the database."
254
+ );
255
+ }
256
+ async executeSQL(sql, options, parameters) {
257
+ if (!this.db) throw new Error("Not connected to SQLite database");
258
+ try {
259
+ const statements = splitSQLStatements(sql, "sqlite");
260
+ let dataModified = false;
261
+ if (statements.length === 1) {
262
+ let processedStatement = statements[0];
263
+ const trimmedStatement = statements[0].toLowerCase().trim();
264
+ const isReadStatement = trimmedStatement.startsWith("select") || trimmedStatement.startsWith("with") || trimmedStatement.startsWith("explain") || trimmedStatement.startsWith("analyze") || trimmedStatement.startsWith("pragma") && (trimmedStatement.includes("table_info") || trimmedStatement.includes("index_info") || trimmedStatement.includes("index_list") || trimmedStatement.includes("foreign_key_list"));
265
+ if (options.maxRows) {
266
+ processedStatement = SQLRowLimiter.applyMaxRows(processedStatement, options.maxRows);
267
+ }
268
+ if (isReadStatement) {
269
+ try {
270
+ const params = parameters || [];
271
+ const rows = await this.dbAll(processedStatement, params);
272
+ return { rows, rowCount: rows.length };
273
+ } catch (error) {
274
+ console.error(`[SQLite executeSQL] ERROR: ${error.message}`);
275
+ throw error;
276
+ }
277
+ } else {
278
+ try {
279
+ const params = parameters || [];
280
+ const result = await this.dbRun(processedStatement, params);
281
+ await this.saveToDisk();
282
+ return { rows: [], rowCount: result.changes ?? 0 };
283
+ } catch (error) {
284
+ console.error(`[SQLite executeSQL] ERROR: ${error.message}`);
285
+ throw error;
286
+ }
287
+ }
288
+ } else {
289
+ if (parameters && parameters.length > 0) {
290
+ throw new Error("Parameters are not supported for multi-statement queries in SQLite");
291
+ }
292
+ let totalChanges = 0;
293
+ let allRows = [];
294
+ for (let statement of statements) {
295
+ const trimmedStatement = statement.toLowerCase().trim();
296
+ const isReadStatement = trimmedStatement.startsWith("select") || trimmedStatement.startsWith("with") || trimmedStatement.startsWith("explain") || trimmedStatement.startsWith("analyze") || trimmedStatement.startsWith("pragma") && (trimmedStatement.includes("table_info") || trimmedStatement.includes("index_info") || trimmedStatement.includes("index_list") || trimmedStatement.includes("foreign_key_list"));
297
+ if (isReadStatement) {
298
+ statement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
299
+ const rows = await this.dbAll(statement);
300
+ allRows.push(...rows);
301
+ } else {
302
+ const result = await this.dbRun(statement);
303
+ totalChanges += result.changes ?? 0;
304
+ dataModified = true;
305
+ }
306
+ }
307
+ if (dataModified) {
308
+ await this.saveToDisk();
309
+ }
310
+ return { rows: allRows, rowCount: totalChanges + allRows.length };
311
+ }
312
+ } catch (error) {
313
+ throw error;
314
+ }
315
+ }
316
+ };
317
+ var sqliteConnector = new SQLiteConnector();
318
+ ConnectorRegistry.register(sqliteConnector);
319
+ export {
320
+ SQLiteConnector
321
+ };
@@ -9,7 +9,6 @@ import {
9
9
  SafeURL,
10
10
  obfuscateDSNPassword
11
11
  } from "./chunk-C7WEAPX4.js";
12
- import "./chunk-FAIJPBT5.js";
13
12
 
14
13
  // src/connectors/sqlserver/index.ts
15
14
  import sql from "mssql";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcr1234/dbhub-fork",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "mcpName": "io.github.xcr1234/dbhub",
5
5
  "description": "Minimal, token-efficient Database MCP Server for PostgreSQL, MySQL, SQL Server, SQLite, MariaDB",
6
6
  "repository": {
@@ -1,40 +0,0 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
- }) : x)(function(x) {
11
- if (typeof require !== "undefined") return require.apply(this, arguments);
12
- throw Error('Dynamic require of "' + x + '" is not supported');
13
- });
14
- var __commonJS = (cb, mod) => function __require2() {
15
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
- };
17
- var __copyProps = (to, from, except, desc) => {
18
- if (from && typeof from === "object" || typeof from === "function") {
19
- for (let key of __getOwnPropNames(from))
20
- if (!__hasOwnProp.call(to, key) && key !== except)
21
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
- }
23
- return to;
24
- };
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
- // If the importer is in node compatibility mode or this is not an ESM
27
- // file that has been converted to a CommonJS file using a Babel-
28
- // compatible transform (i.e. "__esModule" has not been set), then set
29
- // "default" to the CommonJS "module.exports" for node compatibility.
30
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
- mod
32
- ));
33
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
34
-
35
- export {
36
- __require,
37
- __commonJS,
38
- __toESM,
39
- __publicField
40
- };