@dbcube/core 3.0.21 → 3.0.23

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/dist/index.d.mts CHANGED
@@ -53,8 +53,6 @@ declare class QueryEngine {
53
53
  private createPersistentConnection;
54
54
  private createProcess;
55
55
  disconnect(): Promise<ResponseEngine>;
56
- static getStats(): any;
57
- static clearAll(): void;
58
56
  }
59
57
 
60
58
  interface SystemInfo {
@@ -124,11 +122,19 @@ interface RunResult {
124
122
  declare class SqliteExecutor {
125
123
  private binaryPath;
126
124
  private dbPath;
125
+ private connectionId;
126
+ private tcpPort;
127
+ private useTcp;
127
128
  constructor(dbPath: string);
128
129
  private getBinaryPath;
129
130
  private executeBinary;
130
131
  connect(): Promise<boolean>;
131
132
  exists(): Promise<boolean>;
133
+ private findAvailablePort;
134
+ private isPortAvailable;
135
+ private startTcpServer;
136
+ private sendTcpQuery;
137
+ private sleep;
132
138
  query(sql: string, params?: any[]): Promise<SqliteResult>;
133
139
  queryMultiple(sql: string): Promise<SqliteResult>;
134
140
  prepare(sql: string): {
package/dist/index.d.ts CHANGED
@@ -53,8 +53,6 @@ declare class QueryEngine {
53
53
  private createPersistentConnection;
54
54
  private createProcess;
55
55
  disconnect(): Promise<ResponseEngine>;
56
- static getStats(): any;
57
- static clearAll(): void;
58
56
  }
59
57
 
60
58
  interface SystemInfo {
@@ -124,11 +122,19 @@ interface RunResult {
124
122
  declare class SqliteExecutor {
125
123
  private binaryPath;
126
124
  private dbPath;
125
+ private connectionId;
126
+ private tcpPort;
127
+ private useTcp;
127
128
  constructor(dbPath: string);
128
129
  private getBinaryPath;
129
130
  private executeBinary;
130
131
  connect(): Promise<boolean>;
131
132
  exists(): Promise<boolean>;
133
+ private findAvailablePort;
134
+ private isPortAvailable;
135
+ private startTcpServer;
136
+ private sendTcpQuery;
137
+ private sleep;
132
138
  query(sql: string, params?: any[]): Promise<SqliteResult>;
133
139
  queryMultiple(sql: string): Promise<SqliteResult>;
134
140
  prepare(sql: string): {
package/dist/index.js CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/lib/Engine.ts
9
2
  import path3 from "path";
10
3
 
@@ -728,166 +721,11 @@ import path4 from "path";
728
721
  import { createRequire as createRequire2 } from "module";
729
722
  import * as net from "net";
730
723
  import { spawn as spawn2 } from "child_process";
731
- var MAX_CONNECTIONS_PER_DB = 10;
732
- var MAX_CACHE_ENTRIES = 1e3;
733
- var CACHE_CLEANUP_THRESHOLD = 0.9;
734
- var CONNECTION_IDLE_TIMEOUT = 3e5;
735
- var CONNECTION_MAX_AGE = 36e5;
736
- var LRUCache = class {
737
- cache;
738
- maxSize;
739
- cleanupThreshold;
740
- constructor(maxSize, cleanupThreshold = 0.9) {
741
- this.cache = /* @__PURE__ */ new Map();
742
- this.maxSize = maxSize;
743
- this.cleanupThreshold = cleanupThreshold;
744
- }
745
- get(key) {
746
- const entry = this.cache.get(key);
747
- if (!entry) return void 0;
748
- entry.lastAccessed = Date.now();
749
- entry.accessCount++;
750
- return entry.value;
751
- }
752
- set(key, value) {
753
- if (this.cache.size >= this.maxSize * this.cleanupThreshold) {
754
- this.evictLRU();
755
- }
756
- this.cache.set(key, {
757
- value,
758
- lastAccessed: Date.now(),
759
- accessCount: 1
760
- });
761
- }
762
- evictLRU() {
763
- const entries = Array.from(this.cache.entries()).sort((a, b) => {
764
- const scoreDiff = this.calculateScore(a[1]) - this.calculateScore(b[1]);
765
- if (scoreDiff !== 0) return scoreDiff;
766
- return a[1].lastAccessed - b[1].lastAccessed;
767
- });
768
- const toRemove = Math.floor(this.maxSize * 0.2);
769
- for (let i = 0; i < toRemove && i < entries.length; i++) {
770
- this.cache.delete(entries[i][0]);
771
- }
772
- }
773
- calculateScore(entry) {
774
- const now = Date.now();
775
- const age = now - entry.lastAccessed;
776
- const recencyScore = 1 / (age + 1);
777
- const frequencyScore = entry.accessCount;
778
- return recencyScore * 0.3 + frequencyScore * 0.7;
779
- }
780
- clear() {
781
- this.cache.clear();
782
- }
783
- size() {
784
- return this.cache.size;
785
- }
786
- };
787
- var ConnectionPool = class {
788
- pools;
789
- maxConnectionsPerDb;
790
- constructor(maxConnectionsPerDb = MAX_CONNECTIONS_PER_DB) {
791
- this.pools = /* @__PURE__ */ new Map();
792
- this.maxConnectionsPerDb = maxConnectionsPerDb;
793
- setInterval(() => this.cleanupIdleConnections(), 6e4);
794
- }
795
- getConnection(connectionId) {
796
- const pool = this.pools.get(connectionId);
797
- if (!pool || pool.length === 0) return null;
798
- for (let i = 0; i < pool.length; i++) {
799
- const conn = pool[i];
800
- if (this.isConnectionValid(conn)) {
801
- conn.lastUsed = Date.now();
802
- conn.requestCount++;
803
- return conn.socket;
804
- } else {
805
- conn.socket.destroy();
806
- pool.splice(i, 1);
807
- i--;
808
- }
809
- }
810
- return null;
811
- }
812
- addConnection(connectionId, socket) {
813
- if (!this.pools.has(connectionId)) {
814
- this.pools.set(connectionId, []);
815
- }
816
- const pool = this.pools.get(connectionId);
817
- if (pool.length >= this.maxConnectionsPerDb) {
818
- const oldest = pool.shift();
819
- if (oldest) {
820
- oldest.socket.destroy();
821
- }
822
- }
823
- pool.push({
824
- socket,
825
- lastUsed: Date.now(),
826
- createdAt: Date.now(),
827
- requestCount: 0
828
- });
829
- }
830
- removeConnection(connectionId, socket) {
831
- const pool = this.pools.get(connectionId);
832
- if (!pool) return;
833
- const index = pool.findIndex((c) => c.socket === socket);
834
- if (index !== -1) {
835
- pool[index].socket.destroy();
836
- pool.splice(index, 1);
837
- }
838
- if (pool.length === 0) {
839
- this.pools.delete(connectionId);
840
- }
841
- }
842
- closeAll(connectionId) {
843
- if (connectionId) {
844
- const pool = this.pools.get(connectionId);
845
- if (pool) {
846
- pool.forEach((conn) => conn.socket.destroy());
847
- this.pools.delete(connectionId);
848
- }
849
- } else {
850
- this.pools.forEach((pool) => {
851
- pool.forEach((conn) => conn.socket.destroy());
852
- });
853
- this.pools.clear();
854
- }
855
- }
856
- isConnectionValid(conn) {
857
- const now = Date.now();
858
- const age = now - conn.createdAt;
859
- return conn.socket.readyState === "open" && age < CONNECTION_MAX_AGE;
860
- }
861
- cleanupIdleConnections() {
862
- const now = Date.now();
863
- this.pools.forEach((pool, connectionId) => {
864
- for (let i = pool.length - 1; i >= 0; i--) {
865
- const conn = pool[i];
866
- const idleTime = now - conn.lastUsed;
867
- if (idleTime > CONNECTION_IDLE_TIMEOUT || !this.isConnectionValid(conn)) {
868
- conn.socket.destroy();
869
- pool.splice(i, 1);
870
- }
871
- }
872
- if (pool.length === 0) {
873
- this.pools.delete(connectionId);
874
- }
875
- });
876
- }
877
- getStats() {
878
- const stats = {};
879
- this.pools.forEach((pool, id) => {
880
- stats[id] = {
881
- activeConnections: pool.length,
882
- totalRequests: pool.reduce((sum, c) => sum + c.requestCount, 0)
883
- };
884
- });
885
- return stats;
886
- }
887
- };
888
724
  var globalTcpServers = /* @__PURE__ */ new Map();
889
- var connectionPool = new ConnectionPool(MAX_CONNECTIONS_PER_DB);
890
- var queryCache = new LRUCache(MAX_CACHE_ENTRIES, CACHE_CLEANUP_THRESHOLD);
725
+ var globalTcpConnections = /* @__PURE__ */ new Map();
726
+ var queryCache = /* @__PURE__ */ new Map();
727
+ var cacheSize = 0;
728
+ var MAX_CACHE_SIZE = 500;
891
729
  var QueryEngine = class {
892
730
  name;
893
731
  config;
@@ -917,8 +755,7 @@ var QueryEngine = class {
917
755
  }
918
756
  isPortAvailable(port) {
919
757
  return new Promise((resolve5) => {
920
- const net2 = __require("net");
921
- const tester = net2.createServer();
758
+ const tester = net.createServer();
922
759
  tester.once("error", () => resolve5(false));
923
760
  tester.once("listening", () => {
924
761
  tester.close();
@@ -1042,12 +879,14 @@ var QueryEngine = class {
1042
879
  return new Promise((resolve5) => setTimeout(resolve5, ms));
1043
880
  }
1044
881
  getCachedDML(dmlJson) {
1045
- const cached = queryCache.get(dmlJson);
1046
- if (cached !== void 0) {
1047
- return cached;
882
+ if (queryCache.has(dmlJson)) {
883
+ return queryCache.get(dmlJson);
1048
884
  }
1049
885
  const parsed = JSON.parse(dmlJson);
1050
- queryCache.set(dmlJson, parsed);
886
+ if (cacheSize < MAX_CACHE_SIZE) {
887
+ queryCache.set(dmlJson, parsed);
888
+ cacheSize++;
889
+ }
1051
890
  return parsed;
1052
891
  }
1053
892
  async startTcpServer() {
@@ -1161,12 +1000,13 @@ var QueryEngine = class {
1161
1000
  });
1162
1001
  }
1163
1002
  async sendTcpRequestFast(args) {
1164
- const existingConnection = connectionPool.getConnection(this.connectionId);
1003
+ const existingConnection = globalTcpConnections.get(this.connectionId);
1165
1004
  if (existingConnection && existingConnection.readyState === "open") {
1166
1005
  try {
1167
1006
  return await this.sendOnExistingConnection(existingConnection, args);
1168
1007
  } catch (error) {
1169
- connectionPool.removeConnection(this.connectionId, existingConnection);
1008
+ globalTcpConnections.delete(this.connectionId);
1009
+ existingConnection.destroy();
1170
1010
  }
1171
1011
  }
1172
1012
  return await this.createPersistentConnection(args);
@@ -1251,7 +1091,7 @@ var QueryEngine = class {
1251
1091
  }
1252
1092
  return;
1253
1093
  }
1254
- connectionPool.addConnection(this.connectionId, client);
1094
+ globalTcpConnections.set(this.connectionId, client);
1255
1095
  const command = {
1256
1096
  action: "execute",
1257
1097
  dml: dmlJson
@@ -1278,12 +1118,12 @@ var QueryEngine = class {
1278
1118
  client.on("error", (error) => {
1279
1119
  if (!isResolved) {
1280
1120
  isResolved = true;
1281
- connectionPool.removeConnection(this.connectionId, client);
1121
+ globalTcpConnections.delete(this.connectionId);
1282
1122
  reject(error);
1283
1123
  }
1284
1124
  });
1285
1125
  client.on("close", () => {
1286
- connectionPool.removeConnection(this.connectionId, client);
1126
+ globalTcpConnections.delete(this.connectionId);
1287
1127
  if (!isResolved && !responseBuffer.includes("PROCESS_RESPONSE:")) {
1288
1128
  reject(new Error("Connection closed without valid response"));
1289
1129
  }
@@ -1378,7 +1218,12 @@ var QueryEngine = class {
1378
1218
  });
1379
1219
  }
1380
1220
  async disconnect() {
1381
- connectionPool.closeAll(this.connectionId);
1221
+ const connection = globalTcpConnections.get(this.connectionId);
1222
+ if (connection && connection.readyState === "open") {
1223
+ connection.write(JSON.stringify({ action: "disconnect" }));
1224
+ connection.destroy();
1225
+ globalTcpConnections.delete(this.connectionId);
1226
+ }
1382
1227
  const serverInfo = globalTcpServers.get(this.connectionId);
1383
1228
  if (serverInfo && serverInfo.process && !serverInfo.process.killed) {
1384
1229
  serverInfo.process.kill();
@@ -1395,36 +1240,28 @@ var QueryEngine = class {
1395
1240
  data: null
1396
1241
  };
1397
1242
  }
1398
- // Método para obtener estadísticas del pool y cache
1399
- static getStats() {
1400
- return {
1401
- connectionPool: connectionPool.getStats(),
1402
- cache: {
1403
- size: queryCache.size(),
1404
- maxSize: MAX_CACHE_ENTRIES
1405
- }
1406
- };
1407
- }
1408
- // Método para limpiar todo el cache y conexiones
1409
- static clearAll() {
1410
- queryCache.clear();
1411
- connectionPool.closeAll();
1412
- }
1413
1243
  };
1414
1244
 
1415
1245
  // src/lib/SqliteExecutor.ts
1416
- import { exec } from "child_process";
1246
+ import { exec, spawn as spawn3 } from "child_process";
1417
1247
  import * as path5 from "path";
1418
1248
  import * as fs3 from "fs";
1419
1249
  import { promisify } from "util";
1420
1250
  import { createRequire as createRequire3 } from "module";
1251
+ import * as net2 from "net";
1421
1252
  var execAsync = promisify(exec);
1253
+ var globalSqliteServers = /* @__PURE__ */ new Map();
1254
+ var globalSqliteConnections = /* @__PURE__ */ new Map();
1422
1255
  var SqliteExecutor = class {
1423
1256
  binaryPath;
1424
1257
  dbPath;
1258
+ connectionId;
1259
+ tcpPort = 9944;
1260
+ useTcp = true;
1425
1261
  constructor(dbPath) {
1426
1262
  this.dbPath = dbPath;
1427
1263
  this.binaryPath = this.getBinaryPath();
1264
+ this.connectionId = `sqlite_${dbPath.replace(/[^a-zA-Z0-9]/g, "_")}`;
1428
1265
  }
1429
1266
  getBinaryPath() {
1430
1267
  const possibleDirs = [
@@ -1507,7 +1344,173 @@ var SqliteExecutor = class {
1507
1344
  return false;
1508
1345
  }
1509
1346
  }
1347
+ async findAvailablePort(startPort) {
1348
+ for (let port = startPort; port >= 9900; port--) {
1349
+ if (await this.isPortAvailable(port)) {
1350
+ return port;
1351
+ }
1352
+ }
1353
+ throw new Error("No available ports found in range 9900-9944");
1354
+ }
1355
+ isPortAvailable(port) {
1356
+ return new Promise((resolve5) => {
1357
+ const tester = net2.createServer();
1358
+ tester.once("error", () => resolve5(false));
1359
+ tester.once("listening", () => {
1360
+ tester.close();
1361
+ resolve5(true);
1362
+ });
1363
+ tester.listen(port, "127.0.0.1");
1364
+ });
1365
+ }
1366
+ async startTcpServer() {
1367
+ this.tcpPort = await this.findAvailablePort(this.tcpPort);
1368
+ return new Promise((resolve5, reject) => {
1369
+ const serverArgs = [
1370
+ "--action",
1371
+ "serve",
1372
+ "--id",
1373
+ this.connectionId,
1374
+ "--database-ref",
1375
+ "config",
1376
+ "--database",
1377
+ this.dbPath
1378
+ ];
1379
+ const serverProcess = spawn3(this.binaryPath, serverArgs);
1380
+ let started = false;
1381
+ const timeout = setTimeout(() => {
1382
+ if (!started) {
1383
+ serverProcess.kill();
1384
+ reject(new Error("SQLite TCP server startup timeout"));
1385
+ }
1386
+ }, 1e4);
1387
+ serverProcess.stdout.on("data", (data) => {
1388
+ const output = data.toString();
1389
+ if (output.includes("SQLite TCP Server started") || output.includes("PROCESS_RESPONSE:")) {
1390
+ if (!started) {
1391
+ started = true;
1392
+ clearTimeout(timeout);
1393
+ const match = output.match(/PROCESS_RESPONSE:(\{.*\})/);
1394
+ if (match) {
1395
+ try {
1396
+ const response = JSON.parse(match[1]);
1397
+ if (response.data && response.data.port) {
1398
+ this.tcpPort = response.data.port;
1399
+ }
1400
+ } catch (e) {
1401
+ }
1402
+ }
1403
+ globalSqliteServers.set(this.connectionId, {
1404
+ port: this.tcpPort,
1405
+ process: serverProcess
1406
+ });
1407
+ resolve5();
1408
+ }
1409
+ }
1410
+ });
1411
+ serverProcess.stderr.on("data", (data) => {
1412
+ console.error(`[SQLite Server Error] ${data.toString().trim()}`);
1413
+ });
1414
+ serverProcess.on("close", () => {
1415
+ globalSqliteServers.delete(this.connectionId);
1416
+ globalSqliteConnections.delete(this.connectionId);
1417
+ });
1418
+ serverProcess.on("error", (error) => {
1419
+ if (!started) {
1420
+ clearTimeout(timeout);
1421
+ reject(error);
1422
+ }
1423
+ });
1424
+ });
1425
+ }
1426
+ async sendTcpQuery(sql, params) {
1427
+ const serverInfo = globalSqliteServers.get(this.connectionId);
1428
+ if (!serverInfo || !serverInfo.process || serverInfo.process.killed) {
1429
+ await this.startTcpServer();
1430
+ await this.sleep(500);
1431
+ }
1432
+ return new Promise((resolve5, reject) => {
1433
+ const client = new net2.Socket();
1434
+ let responseBuffer = "";
1435
+ let isResolved = false;
1436
+ const timeout = setTimeout(() => {
1437
+ if (!isResolved) {
1438
+ isResolved = true;
1439
+ client.destroy();
1440
+ reject(new Error("TCP query timeout"));
1441
+ }
1442
+ }, 1e4);
1443
+ client.connect(this.tcpPort, "127.0.0.1", () => {
1444
+ const command = {
1445
+ action: "query",
1446
+ query: sql,
1447
+ params: params || []
1448
+ };
1449
+ client.write(JSON.stringify(command) + "\n");
1450
+ });
1451
+ client.on("data", (data) => {
1452
+ responseBuffer += data.toString();
1453
+ const match = responseBuffer.match(/PROCESS_RESPONSE:(\{.*\})/);
1454
+ if (match && !isResolved) {
1455
+ isResolved = true;
1456
+ clearTimeout(timeout);
1457
+ client.destroy();
1458
+ try {
1459
+ const response = JSON.parse(match[1]);
1460
+ if (response.code === 200) {
1461
+ resolve5({
1462
+ status: "success",
1463
+ message: response.message,
1464
+ data: response.data
1465
+ });
1466
+ } else {
1467
+ resolve5({
1468
+ status: "error",
1469
+ message: response.message,
1470
+ data: null
1471
+ });
1472
+ }
1473
+ } catch (error) {
1474
+ reject(new Error("Failed to parse TCP response"));
1475
+ }
1476
+ }
1477
+ });
1478
+ client.on("error", (error) => {
1479
+ if (!isResolved) {
1480
+ isResolved = true;
1481
+ clearTimeout(timeout);
1482
+ this.useTcp = false;
1483
+ this.executeBinary([
1484
+ "--action",
1485
+ "query",
1486
+ "--database",
1487
+ this.dbPath,
1488
+ "--query",
1489
+ sql,
1490
+ ...params && params.length > 0 ? ["--params", JSON.stringify(params)] : []
1491
+ ]).then(resolve5).catch(reject);
1492
+ }
1493
+ });
1494
+ client.on("close", () => {
1495
+ if (!isResolved) {
1496
+ clearTimeout(timeout);
1497
+ reject(new Error("Connection closed without valid response"));
1498
+ }
1499
+ });
1500
+ });
1501
+ }
1502
+ sleep(ms) {
1503
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
1504
+ }
1510
1505
  async query(sql, params) {
1506
+ if (this.useTcp) {
1507
+ try {
1508
+ return await this.sendTcpQuery(sql, params);
1509
+ } catch (error) {
1510
+ console.warn("TCP query failed, falling back to direct execution");
1511
+ this.useTcp = false;
1512
+ }
1513
+ }
1511
1514
  const args = [
1512
1515
  "--action",
1513
1516
  "query",