@dbcube/core 5.2.2 → 5.2.4

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.js CHANGED
@@ -1,3 +1,10 @@
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
+
1
8
  // src/lib/Engine.ts
2
9
  import path4 from "path";
3
10
 
@@ -132,15 +139,14 @@ import * as unzipper from "unzipper";
132
139
  import ora from "ora";
133
140
  import chalk from "chalk";
134
141
  import { fileURLToPath } from "url";
135
- import { dirname } from "path";
142
+ import { dirname as dirname2 } from "path";
136
143
  var { https } = followRedirects;
137
144
  var Downloader = class {
138
145
  static mainSpinner = null;
139
146
  static currentSpinner = null;
140
147
  static VERSION_URLS = {
141
148
  query: "https://raw.githubusercontent.com/Dbcube/binaries/main/query-engines.json",
142
- schema: "https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json",
143
- sqlite: "https://raw.githubusercontent.com/Dbcube/binaries/main/sqlite-engines.json"
149
+ schema: "https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json"
144
150
  };
145
151
  /**
146
152
  * Fetch latest version from GitHub
@@ -310,7 +316,7 @@ var Downloader = class {
310
316
  spinner: "dots12"
311
317
  }).start();
312
318
  const binariesToProcess = [];
313
- for (const prefix of ["query", "schema", "sqlite"]) {
319
+ for (const prefix of ["query", "schema"]) {
314
320
  try {
315
321
  const localVersion = this.getLocalVersion(binDir, prefix);
316
322
  const remoteVersion = await this.fetchLatestVersion(prefix);
@@ -475,34 +481,46 @@ var Downloader = class {
475
481
  }
476
482
  static extractBinary(zipPath, outputPath, prefix) {
477
483
  return new Promise((resolve5, reject) => {
478
- let extracted = false;
484
+ const outDir = path.dirname(outputPath);
485
+ let extracted = 0;
486
+ const pending = [];
479
487
  fs.createReadStream(zipPath).pipe(unzipper.Parse()).on("entry", (entry) => {
480
- if (entry.type === "File" && !extracted) {
481
- extracted = true;
482
- const writeStream = fs.createWriteStream(outputPath);
488
+ if (entry.type !== "File") {
489
+ entry.autodrain();
490
+ return;
491
+ }
492
+ const isMain = extracted === 0;
493
+ extracted++;
494
+ const target = isMain ? outputPath : path.join(outDir, path.basename(entry.path));
495
+ pending.push(new Promise((res, rej) => {
496
+ const writeStream = fs.createWriteStream(target);
483
497
  entry.pipe(writeStream);
484
498
  writeStream.on("finish", () => {
485
499
  if (process.platform !== "win32") {
486
- fs.chmodSync(outputPath, 493);
500
+ try {
501
+ fs.chmodSync(target, 493);
502
+ } catch {
503
+ }
487
504
  }
488
- this.cleanupFile(zipPath);
489
- resolve5();
490
- });
491
- writeStream.on("error", (err) => {
492
- this.cleanupFile(zipPath);
493
- reject(err);
505
+ res();
494
506
  });
495
- } else {
496
- entry.autodrain();
497
- }
507
+ writeStream.on("error", rej);
508
+ }));
498
509
  }).on("error", (err) => {
499
510
  this.cleanupFile(zipPath);
500
511
  reject(err);
501
512
  }).on("close", () => {
502
- if (!extracted) {
513
+ Promise.all(pending).then(() => {
503
514
  this.cleanupFile(zipPath);
504
- reject(new Error(`No se encontr\xF3 archivo v\xE1lido en el ZIP para ${prefix}`));
505
- }
515
+ if (extracted === 0) {
516
+ reject(new Error(`No se encontr\xF3 archivo v\xE1lido en el ZIP para ${prefix}`));
517
+ } else {
518
+ resolve5();
519
+ }
520
+ }).catch((err) => {
521
+ this.cleanupFile(zipPath);
522
+ reject(err);
523
+ });
506
524
  });
507
525
  });
508
526
  }
@@ -516,7 +534,7 @@ var Downloader = class {
516
534
  }
517
535
  static getDefaultBinDir() {
518
536
  const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath(import.meta.url) : "";
519
- const __dirname = __filename2 ? dirname(__filename2) : process.cwd();
537
+ const __dirname = __filename2 ? dirname2(__filename2) : process.cwd();
520
538
  const possibleDirs = [
521
539
  path.resolve(process.cwd(), ".dbcube", "bin"),
522
540
  path.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
@@ -546,7 +564,7 @@ import * as fs2 from "fs";
546
564
  import * as path2 from "path";
547
565
  import * as os3 from "os";
548
566
  import { fileURLToPath as fileURLToPath2 } from "url";
549
- import { dirname as dirname2 } from "path";
567
+ import { dirname as dirname3 } from "path";
550
568
  var Binary = class {
551
569
  static isDownloading = false;
552
570
  static downloadPromise = null;
@@ -579,7 +597,7 @@ var Binary = class {
579
597
  }
580
598
  static getBinDir() {
581
599
  const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath2(import.meta.url) : "";
582
- const __dirname = __filename2 ? dirname2(__filename2) : process.cwd();
600
+ const __dirname = __filename2 ? dirname3(__filename2) : process.cwd();
583
601
  const possibleDirs = [
584
602
  path2.resolve(process.cwd(), ".dbcube", "bin"),
585
603
  path2.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
@@ -895,7 +913,7 @@ var DaemonClient = class _DaemonClient {
895
913
  });
896
914
  }
897
915
  async execute(dml, txId) {
898
- const payload = { action: "execute", dml: JSON.stringify(dml) };
916
+ const payload = { action: "execute", dml };
899
917
  if (txId) payload.tx_id = txId;
900
918
  return this.send(payload);
901
919
  }
@@ -962,8 +980,8 @@ var Engine = class {
962
980
  const binaryPath = this.binary["query_engine"];
963
981
  if (!binaryPath) return null;
964
982
  const client = DaemonClient.get(this.name, binaryPath, this.arguments);
965
- const ok = await client.ensure();
966
- if (!ok) {
983
+ const ok2 = await client.ensure();
984
+ if (!ok2) {
967
985
  this.daemonFailed = true;
968
986
  return null;
969
987
  }
@@ -1175,14 +1193,265 @@ var Engine = class {
1175
1193
  };
1176
1194
 
1177
1195
  // src/lib/QueryEngine.ts
1178
- import path5 from "path";
1196
+ import path6 from "path";
1197
+
1198
+ // src/lib/EmbeddedEngine.ts
1199
+ import * as fs4 from "fs";
1200
+ import * as path5 from "path";
1201
+ if (!process.env.UV_THREADPOOL_SIZE) {
1202
+ process.env.UV_THREADPOOL_SIZE = "16";
1203
+ }
1204
+ var backend = null;
1205
+ var loadFailed = false;
1206
+ var handles = /* @__PURE__ */ new Map();
1207
+ var connecting = /* @__PURE__ */ new Map();
1208
+ function platTag() {
1209
+ const arch2 = process.arch === "arm64" ? "arm64" : "x64";
1210
+ if (process.platform === "win32") return { plat: "windows", ext: "dll", arch: arch2 };
1211
+ if (process.platform === "darwin") return { plat: "macos", ext: "dylib", arch: arch2 };
1212
+ return { plat: "linux", ext: "so", arch: arch2 };
1213
+ }
1214
+ function findFile(name) {
1215
+ const candidates = [
1216
+ path5.resolve(process.cwd(), ".dbcube", "bin", name),
1217
+ path5.resolve(process.cwd(), "node_modules", ".dbcube", "bin", name)
1218
+ ];
1219
+ for (const c of candidates) {
1220
+ if (fs4.existsSync(c)) return c;
1221
+ }
1222
+ return null;
1223
+ }
1224
+ function ok(data) {
1225
+ return { status: 200, message: "OK", data };
1226
+ }
1227
+ function errResp(e) {
1228
+ const msg = e instanceof Error ? e.message : String(e);
1229
+ return { status: 500, message: msg, data: null };
1230
+ }
1231
+ function loadNapiBackend() {
1232
+ const { plat, arch: arch2 } = platTag();
1233
+ const addonPath = findFile(`query-engine-node-${plat}-${arch2}.node`);
1234
+ if (!addonPath) return null;
1235
+ try {
1236
+ const addon = __require(addonPath);
1237
+ if (typeof addon.connect !== "function") return null;
1238
+ return {
1239
+ async connect(cfgJson) {
1240
+ return Number(await addon.connect(cfgJson));
1241
+ },
1242
+ async execute(handle, dml, txId) {
1243
+ try {
1244
+ const rows = await addon.execute(handle, dml, txId ?? void 0);
1245
+ return ok(rows);
1246
+ } catch (e) {
1247
+ return errResp(e);
1248
+ }
1249
+ },
1250
+ async executeBatch(handle, ops) {
1251
+ try {
1252
+ const results = await addon.executeBatch(handle, ops);
1253
+ return ok(results);
1254
+ } catch (e) {
1255
+ return errResp(e);
1256
+ }
1257
+ },
1258
+ async raw(handle, query, params, txId) {
1259
+ try {
1260
+ const rows = await addon.raw(handle, query, params, txId ?? void 0);
1261
+ return ok(rows);
1262
+ } catch (e) {
1263
+ return errResp(e);
1264
+ }
1265
+ },
1266
+ async begin(handle) {
1267
+ try {
1268
+ const txId = await addon.begin(handle);
1269
+ return ok({ tx_id: txId });
1270
+ } catch (e) {
1271
+ return errResp(e);
1272
+ }
1273
+ },
1274
+ async commit(_handle, txId) {
1275
+ try {
1276
+ await addon.commit(txId);
1277
+ return ok(null);
1278
+ } catch (e) {
1279
+ return errResp(e);
1280
+ }
1281
+ },
1282
+ async rollback(_handle, txId) {
1283
+ try {
1284
+ await addon.rollback(txId);
1285
+ return ok(null);
1286
+ } catch (e) {
1287
+ return errResp(e);
1288
+ }
1289
+ },
1290
+ async disconnect(handle) {
1291
+ try {
1292
+ addon.disconnect(handle);
1293
+ } catch {
1294
+ }
1295
+ }
1296
+ };
1297
+ } catch {
1298
+ return null;
1299
+ }
1300
+ }
1301
+ function loadKoffiBackend() {
1302
+ const { plat, ext, arch: arch2 } = platTag();
1303
+ const libPath = findFile(`query-engine-embedded-${plat}-${arch2}.${ext}`);
1304
+ if (!libPath) return null;
1305
+ try {
1306
+ const koffi = __require("koffi");
1307
+ const lib = koffi.load(libPath);
1308
+ const cConnect = lib.func("dbc_connect", "void *", ["str"]);
1309
+ const cExecute = lib.func("dbc_execute", "void *", ["uint64", "str", "str"]);
1310
+ const cExecuteBatch = lib.func("dbc_execute_batch", "void *", ["uint64", "str"]);
1311
+ const cRaw = lib.func("dbc_raw", "void *", ["uint64", "str", "str", "str"]);
1312
+ const cBegin = lib.func("dbc_begin", "void *", ["uint64"]);
1313
+ const cCommit = lib.func("dbc_commit", "void *", ["uint64", "str"]);
1314
+ const cRollback = lib.func("dbc_rollback", "void *", ["uint64", "str"]);
1315
+ const cDisconnect = lib.func("dbc_disconnect", "void *", ["uint64"]);
1316
+ const cFree = lib.func("dbc_free", "void", ["void *"]);
1317
+ const take = (ptr) => {
1318
+ if (!ptr) return { status: 500, message: "embedded engine returned null", data: null };
1319
+ const s = koffi.decode(ptr, "char", -1);
1320
+ cFree(ptr);
1321
+ try {
1322
+ const r = JSON.parse(s);
1323
+ return { status: r.status ?? 500, message: r.message ?? "", data: r.data ?? null };
1324
+ } catch (e) {
1325
+ return { status: 500, message: `Invalid embedded response: ${e.message}`, data: null };
1326
+ }
1327
+ };
1328
+ const call = (fn, ...args) => new Promise((resolve5, reject) => {
1329
+ fn.async(...args, (err, ptr) => {
1330
+ if (err) return reject(err);
1331
+ resolve5(take(ptr));
1332
+ });
1333
+ });
1334
+ return {
1335
+ async connect(cfgJson) {
1336
+ const res = await call(cConnect, cfgJson);
1337
+ if (res.status !== 200 || !res.data?.handle) {
1338
+ throw new Error(String(res.message || "embedded connect failed"));
1339
+ }
1340
+ return Number(res.data.handle);
1341
+ },
1342
+ execute: (handle, dml, txId) => call(cExecute, handle, JSON.stringify(dml), txId),
1343
+ executeBatch: (handle, ops) => call(cExecuteBatch, handle, JSON.stringify(ops)),
1344
+ raw: (handle, query, params, txId) => call(cRaw, handle, query, JSON.stringify(params ?? []), txId),
1345
+ begin: (handle) => call(cBegin, handle),
1346
+ commit: (handle, txId) => call(cCommit, handle, txId),
1347
+ rollback: (handle, txId) => call(cRollback, handle, txId),
1348
+ async disconnect(handle) {
1349
+ try {
1350
+ await call(cDisconnect, handle);
1351
+ } catch {
1352
+ }
1353
+ }
1354
+ };
1355
+ } catch {
1356
+ return null;
1357
+ }
1358
+ }
1359
+ function loadBackend() {
1360
+ if (backend) return backend;
1361
+ if (loadFailed) return null;
1362
+ if (process.env.DBCUBE_EMBEDDED === "0" || process.env.DBCUBE_EMBEDDED === "false") {
1363
+ loadFailed = true;
1364
+ return null;
1365
+ }
1366
+ backend = loadNapiBackend() ?? loadKoffiBackend();
1367
+ if (!backend) loadFailed = true;
1368
+ return backend;
1369
+ }
1370
+ var EmbeddedEngine = class {
1371
+ /** true si algún backend nativo está disponible en esta instalación. */
1372
+ static available() {
1373
+ return loadBackend() !== null;
1374
+ }
1375
+ static async handleFor(connectionId, config) {
1376
+ const existing = handles.get(connectionId);
1377
+ if (existing) return existing;
1378
+ const inFlight = connecting.get(connectionId);
1379
+ if (inFlight) return inFlight;
1380
+ const promise = (async () => {
1381
+ const be = loadBackend();
1382
+ if (!be) throw new Error("embedded engine not available");
1383
+ const motor = config.type === "postgres" ? "postgresql" : config.type;
1384
+ const cfg = {
1385
+ databaseRef: connectionId,
1386
+ motor,
1387
+ database: config.type === "sqlite" ? `${config.config.DATABASE}.db` : config.config.DATABASE,
1388
+ host: config.config.HOST,
1389
+ port: config.config.PORT != null ? Number(config.config.PORT) : void 0,
1390
+ user: config.config.USER,
1391
+ password: config.config.PASSWORD,
1392
+ maxConnections: config.pool?.maxConnections,
1393
+ minConnections: config.pool?.minConnections,
1394
+ acquireTimeoutMs: config.pool?.acquireTimeoutMs,
1395
+ idleTimeoutMs: config.pool?.idleTimeoutMs
1396
+ };
1397
+ const handle = await be.connect(JSON.stringify(cfg));
1398
+ handles.set(connectionId, handle);
1399
+ return handle;
1400
+ })();
1401
+ connecting.set(connectionId, promise);
1402
+ try {
1403
+ return await promise;
1404
+ } finally {
1405
+ connecting.delete(connectionId);
1406
+ }
1407
+ }
1408
+ static async executeDml(connectionId, config, dml, txId) {
1409
+ const be = loadBackend();
1410
+ const handle = await this.handleFor(connectionId, config);
1411
+ return be.execute(handle, dml, txId ?? null);
1412
+ }
1413
+ static async executeBatch(connectionId, config, ops) {
1414
+ const be = loadBackend();
1415
+ const handle = await this.handleFor(connectionId, config);
1416
+ return be.executeBatch(handle, ops);
1417
+ }
1418
+ static async rawQuery(connectionId, config, query, params, txId) {
1419
+ const be = loadBackend();
1420
+ const handle = await this.handleFor(connectionId, config);
1421
+ return be.raw(handle, query, params ?? [], txId ?? null);
1422
+ }
1423
+ static async begin(connectionId, config) {
1424
+ const be = loadBackend();
1425
+ const handle = await this.handleFor(connectionId, config);
1426
+ return be.begin(handle);
1427
+ }
1428
+ static async commit(connectionId, config, txId) {
1429
+ const be = loadBackend();
1430
+ const handle = await this.handleFor(connectionId, config);
1431
+ return be.commit(handle, txId);
1432
+ }
1433
+ static async rollback(connectionId, config, txId) {
1434
+ const be = loadBackend();
1435
+ const handle = await this.handleFor(connectionId, config);
1436
+ return be.rollback(handle, txId);
1437
+ }
1438
+ static async disconnect(connectionId) {
1439
+ const be = loadBackend();
1440
+ const handle = handles.get(connectionId);
1441
+ if (be && handle) {
1442
+ handles.delete(connectionId);
1443
+ await be.disconnect(handle);
1444
+ }
1445
+ }
1446
+ };
1447
+
1448
+ // src/lib/QueryEngine.ts
1179
1449
  import { createRequire as createRequire2 } from "module";
1180
1450
  import * as net2 from "net";
1181
1451
  import { spawn as spawn3 } from "child_process";
1182
1452
  var globalTcpServers = /* @__PURE__ */ new Map();
1183
- var globalTcpConnections = /* @__PURE__ */ new Map();
1184
- var connectionQueues = /* @__PURE__ */ new Map();
1185
- var connectionProcessing = /* @__PURE__ */ new Map();
1453
+ var socketPools = /* @__PURE__ */ new Map();
1454
+ var CLIENT_POOL_SIZE = 10;
1186
1455
  var queryCache = /* @__PURE__ */ new Map();
1187
1456
  var MAX_CACHE_SIZE = 500;
1188
1457
  var QueryEngine = class {
@@ -1193,9 +1462,9 @@ var QueryEngine = class {
1193
1462
  timeout;
1194
1463
  connectionId;
1195
1464
  tcpPort;
1196
- constructor(name, timeout = 3e4) {
1465
+ constructor(name, timeout = 3e4, explicitConfig) {
1197
1466
  this.name = name;
1198
- this.config = this.setConfig(name);
1467
+ this.config = explicitConfig ?? this.setConfig(name);
1199
1468
  this.arguments = this.setArguments();
1200
1469
  this.timeout = this.config?.daemon?.requestTimeoutMs ?? timeout;
1201
1470
  this.connectionId = `${name}_query_engine_${this.config.type}_${this.config.config.DATABASE}_${this.config.config.HOST || "localhost"}`;
@@ -1279,7 +1548,7 @@ var QueryEngine = class {
1279
1548
  setConfig(name) {
1280
1549
  const configInstance = new Config();
1281
1550
  try {
1282
- const configFilePath = path5.resolve(process.cwd(), "dbcube.config.js");
1551
+ const configFilePath = path6.resolve(process.cwd(), "dbcube.config.js");
1283
1552
  const requireUrl = typeof __filename !== "undefined" ? __filename : process.cwd();
1284
1553
  const require2 = createRequire2(requireUrl);
1285
1554
  if (require2.cache && require2.resolve) {
@@ -1325,38 +1594,79 @@ var QueryEngine = class {
1325
1594
  }
1326
1595
  return command;
1327
1596
  }
1597
+ /** El motor embebido (FFI) es el camino por defecto cuando la librería
1598
+ * nativa está disponible; el daemon TCP queda como fallback y como modo
1599
+ * multi-proceso explícito. */
1600
+ useEmbedded() {
1601
+ return EmbeddedEngine.available();
1602
+ }
1328
1603
  /**
1329
- * Executes a DML plan over the persistent TCP server.
1604
+ * Executes a DML plan. Embedded engine when available (in-process FFI,
1605
+ * no network hop); persistent TCP daemon otherwise.
1330
1606
  * Pass txId to run it inside an active transaction.
1331
1607
  */
1332
1608
  async executeDml(dml, txId) {
1333
- const command = { action: "execute", dml: JSON.stringify(dml) };
1609
+ if (this.useEmbedded()) {
1610
+ return EmbeddedEngine.executeDml(this.connectionId, this.config, dml, txId);
1611
+ }
1612
+ const command = { action: "execute", dml };
1334
1613
  if (txId) command.tx_id = txId;
1335
1614
  return this.executeWithTcpServer(command);
1336
1615
  }
1337
1616
  /**
1338
- * Executes raw SQL (or a MongoDB command document) with bound parameters
1339
- * over the persistent TCP server.
1617
+ * Atomic batch: N write plans inside one transaction with a single
1618
+ * engine round-trip. Falls back to begin/ops/commit over the daemon
1619
+ * when the embedded engine is not available.
1620
+ */
1621
+ async executeBatch(ops) {
1622
+ if (this.useEmbedded()) {
1623
+ return EmbeddedEngine.executeBatch(this.connectionId, this.config, ops);
1624
+ }
1625
+ const txId = await this.beginTransaction();
1626
+ try {
1627
+ const results = [];
1628
+ for (const op of ops) {
1629
+ const res = await this.executeDml(op, txId);
1630
+ if (res.status !== 200) {
1631
+ throw new Error(String(res.message));
1632
+ }
1633
+ results.push(res.data);
1634
+ }
1635
+ await this.commitTransaction(txId);
1636
+ return { status: 200, message: "Batch committed", data: results };
1637
+ } catch (error) {
1638
+ try {
1639
+ await this.rollbackTransaction(txId);
1640
+ } catch {
1641
+ }
1642
+ return { status: 500, message: error?.message ?? String(error), data: null };
1643
+ }
1644
+ }
1645
+ /**
1646
+ * Executes raw SQL (or a MongoDB command document) with bound parameters.
1340
1647
  */
1341
1648
  async rawQuery(query, params = [], txId) {
1649
+ if (this.useEmbedded()) {
1650
+ return EmbeddedEngine.rawQuery(this.connectionId, this.config, query, params, txId);
1651
+ }
1342
1652
  const command = { action: "raw", query, params };
1343
1653
  if (txId) command.tx_id = txId;
1344
1654
  return this.executeWithTcpServer(command);
1345
1655
  }
1346
- /** Starts a server-side transaction and returns its id. */
1656
+ /** Starts a transaction and returns its id. */
1347
1657
  async beginTransaction() {
1348
- const res = await this.executeWithTcpServer({ action: "begin" });
1658
+ const res = this.useEmbedded() ? await EmbeddedEngine.begin(this.connectionId, this.config) : await this.executeWithTcpServer({ action: "begin" });
1349
1659
  if (res.status !== 200 || !res.data?.tx_id) {
1350
1660
  throw new Error(String(res.message || "Failed to begin transaction (update the query-engine binary: npx dbcube update)"));
1351
1661
  }
1352
1662
  return res.data.tx_id;
1353
1663
  }
1354
1664
  async commitTransaction(txId) {
1355
- const res = await this.executeWithTcpServer({ action: "commit", tx_id: txId });
1665
+ const res = this.useEmbedded() ? await EmbeddedEngine.commit(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "commit", tx_id: txId });
1356
1666
  if (res.status !== 200) throw new Error(String(res.message || "Failed to commit transaction"));
1357
1667
  }
1358
1668
  async rollbackTransaction(txId) {
1359
- const res = await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
1669
+ const res = this.useEmbedded() ? await EmbeddedEngine.rollback(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
1360
1670
  if (res.status !== 200) throw new Error(String(res.message || "Failed to rollback transaction"));
1361
1671
  }
1362
1672
  async executeWithTcpServer(command) {
@@ -1375,7 +1685,63 @@ var QueryEngine = class {
1375
1685
  await this.startTcpServer();
1376
1686
  await this.waitForServerReady();
1377
1687
  }
1378
- return this.sendTcpRequestFast(command);
1688
+ const port = globalTcpServers.get(this.connectionId).port;
1689
+ const socket = await this.acquireSocket(port);
1690
+ try {
1691
+ const result = await this.executeOnConnection(socket, command);
1692
+ this.releaseSocket(socket, false);
1693
+ return result;
1694
+ } catch (error) {
1695
+ this.releaseSocket(socket, true);
1696
+ throw error;
1697
+ }
1698
+ }
1699
+ poolFor() {
1700
+ let pool = socketPools.get(this.connectionId);
1701
+ if (!pool) {
1702
+ pool = { free: [], total: 0, waiters: [] };
1703
+ socketPools.set(this.connectionId, pool);
1704
+ }
1705
+ return pool;
1706
+ }
1707
+ async acquireSocket(port) {
1708
+ const pool = this.poolFor();
1709
+ while (pool.free.length > 0) {
1710
+ const s = pool.free.pop();
1711
+ if (!s.destroyed) return s;
1712
+ pool.total--;
1713
+ }
1714
+ if (pool.total < CLIENT_POOL_SIZE) {
1715
+ pool.total++;
1716
+ try {
1717
+ return await this.createNewConnection(port);
1718
+ } catch (e) {
1719
+ pool.total--;
1720
+ throw e;
1721
+ }
1722
+ }
1723
+ return new Promise((resolve5) => pool.waiters.push(resolve5));
1724
+ }
1725
+ releaseSocket(socket, broken) {
1726
+ const pool = socketPools.get(this.connectionId);
1727
+ if (!pool) {
1728
+ socket.destroy();
1729
+ return;
1730
+ }
1731
+ if (broken || socket.destroyed) {
1732
+ pool.total--;
1733
+ try {
1734
+ socket.destroy();
1735
+ } catch {
1736
+ }
1737
+ return;
1738
+ }
1739
+ const waiter = pool.waiters.shift();
1740
+ if (waiter) {
1741
+ waiter(socket);
1742
+ } else {
1743
+ pool.free.push(socket);
1744
+ }
1379
1745
  }
1380
1746
  async waitForServerReady() {
1381
1747
  const maxRetries = 10;
@@ -1469,58 +1835,6 @@ var QueryEngine = class {
1469
1835
  });
1470
1836
  });
1471
1837
  }
1472
- async sendTcpRequestFast(command) {
1473
- return new Promise((resolve5, reject) => {
1474
- let queue = connectionQueues.get(this.connectionId);
1475
- if (!queue) {
1476
- queue = [];
1477
- connectionQueues.set(this.connectionId, queue);
1478
- }
1479
- queue.push({ command, resolve: resolve5, reject });
1480
- this.processQueue();
1481
- });
1482
- }
1483
- async processQueue() {
1484
- if (connectionProcessing.get(this.connectionId)) {
1485
- return;
1486
- }
1487
- const queue = connectionQueues.get(this.connectionId);
1488
- if (!queue || queue.length === 0) {
1489
- return;
1490
- }
1491
- connectionProcessing.set(this.connectionId, true);
1492
- try {
1493
- const serverInfo = globalTcpServers.get(this.connectionId);
1494
- if (!serverInfo) {
1495
- throw new Error("Server not initialized");
1496
- }
1497
- while (queue.length > 0) {
1498
- const request = queue.shift();
1499
- if (!request) break;
1500
- try {
1501
- let connection = globalTcpConnections.get(this.connectionId);
1502
- if (!connection || connection.destroyed || connection.readyState !== "open") {
1503
- connection = await this.createNewConnection(serverInfo.port);
1504
- globalTcpConnections.set(this.connectionId, connection);
1505
- }
1506
- const result = await this.executeOnConnection(connection, request.command);
1507
- request.resolve(result);
1508
- } catch (error) {
1509
- const staleConnection = globalTcpConnections.get(this.connectionId);
1510
- if (staleConnection) {
1511
- try {
1512
- staleConnection.destroy();
1513
- } catch {
1514
- }
1515
- }
1516
- globalTcpConnections.delete(this.connectionId);
1517
- request.reject(error);
1518
- }
1519
- }
1520
- } finally {
1521
- connectionProcessing.set(this.connectionId, false);
1522
- }
1523
- }
1524
1838
  async createNewConnection(port) {
1525
1839
  return new Promise((resolve5, reject) => {
1526
1840
  const client = new net2.Socket();
@@ -1668,11 +1982,16 @@ var QueryEngine = class {
1668
1982
  });
1669
1983
  }
1670
1984
  async disconnect() {
1671
- const connection = globalTcpConnections.get(this.connectionId);
1672
- if (connection && connection.readyState === "open") {
1673
- connection.write(JSON.stringify({ action: "disconnect" }));
1674
- connection.destroy();
1675
- globalTcpConnections.delete(this.connectionId);
1985
+ await EmbeddedEngine.disconnect(this.connectionId);
1986
+ const pool = socketPools.get(this.connectionId);
1987
+ if (pool) {
1988
+ for (const s of pool.free) {
1989
+ try {
1990
+ s.destroy();
1991
+ } catch {
1992
+ }
1993
+ }
1994
+ socketPools.delete(this.connectionId);
1676
1995
  }
1677
1996
  const serverInfo = globalTcpServers.get(this.connectionId);
1678
1997
  if (serverInfo && serverInfo.process && !serverInfo.process.killed) {
@@ -1692,346 +2011,70 @@ var QueryEngine = class {
1692
2011
  }
1693
2012
  };
1694
2013
 
1695
- // src/lib/SqliteExecutor.ts
1696
- import { exec } from "child_process";
1697
- import * as path6 from "path";
1698
- import * as fs4 from "fs";
1699
- import { promisify } from "util";
1700
- import { createRequire as createRequire3 } from "module";
1701
- import { fileURLToPath as fileURLToPath3 } from "url";
1702
- import { dirname as dirname3 } from "path";
1703
- var execAsync = promisify(exec);
1704
- var SqliteExecutor = class {
1705
- binaryPath;
1706
- dbPath;
1707
- constructor(dbPath) {
1708
- this.dbPath = dbPath;
1709
- this.binaryPath = this.getBinaryPath();
1710
- }
1711
- findVersionedBinary(binDir, platform2) {
1712
- try {
1713
- const files = fs4.readdirSync(binDir);
1714
- const extension = platform2 === "win32" ? ".exe" : "";
1715
- const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
1716
- const pattern = new RegExp(`^sqlite-engine-v\\d+\\.\\d+\\.\\d+-${platformName}-x64${extension.replace(".", "\\.")}$`);
1717
- const matchingFile = files.find((f) => pattern.test(f));
1718
- if (matchingFile) {
1719
- return path6.join(binDir, matchingFile);
1720
- }
1721
- } catch (error) {
1722
- }
1723
- return null;
1724
- }
1725
- getBinaryPath() {
1726
- const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath3(import.meta.url) : "";
1727
- const __dirname = __filename2 ? dirname3(__filename2) : process.cwd();
1728
- const possibleDirs = [
1729
- path6.resolve(process.cwd(), ".dbcube", "bin"),
1730
- path6.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
1731
- path6.resolve(__dirname, "..", "bin")
1732
- ];
1733
- const platform2 = process.platform;
1734
- const extension = platform2 === "win32" ? ".exe" : "";
1735
- const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
1736
- for (const dir of possibleDirs) {
1737
- const versionedPath = this.findVersionedBinary(dir, platform2);
1738
- if (versionedPath && fs4.existsSync(versionedPath)) {
1739
- return versionedPath;
1740
- }
1741
- }
1742
- const binaryName = `sqlite-engine-${platformName}-x64${extension}`;
1743
- for (const dir of possibleDirs) {
1744
- const fullPath = path6.join(dir, binaryName);
1745
- if (fs4.existsSync(fullPath)) {
1746
- return fullPath;
1747
- }
1748
- }
1749
- const fallbackName = `sqlite-engine${extension}`;
1750
- for (const dir of possibleDirs) {
1751
- const fullPath = path6.join(dir, fallbackName);
1752
- if (fs4.existsSync(fullPath)) {
1753
- return fullPath;
1754
- }
1755
- }
1756
- return path6.join(possibleDirs[0], binaryName);
1757
- }
1758
- async executeBinary(args) {
1759
- const escapedArgs = args.map((arg) => {
1760
- if (arg.includes(" ") || arg.includes('"') || arg.includes("(") || arg.includes(")")) {
1761
- return `"${arg.replace(/"/g, '\\"')}"`;
1762
- }
1763
- return arg;
1764
- });
1765
- const command = `"${this.binaryPath}" ${escapedArgs.join(" ")}`;
1766
- try {
1767
- const { stdout, stderr } = await execAsync(command, {
1768
- maxBuffer: 10 * 1024 * 1024,
1769
- // 10MB buffer
1770
- timeout: 3e4
1771
- // 30s timeout
1772
- });
1773
- if (stderr && stderr.trim()) {
1774
- console.warn("SQLite Engine Warning:", stderr);
1775
- }
1776
- const result = JSON.parse(stdout.trim());
1777
- return result;
1778
- } catch (error) {
1779
- if (error.stdout) {
1780
- try {
1781
- const result = JSON.parse(error.stdout.trim());
1782
- return result;
1783
- } catch (parseError) {
1784
- }
1785
- }
1786
- throw new Error(`SQLite execution failed: ${error.message}`);
1787
- }
1788
- }
1789
- async connect() {
1790
- try {
1791
- const result = await this.executeBinary([
1792
- "--action",
1793
- "connect",
1794
- "--database",
1795
- this.dbPath
1796
- ]);
1797
- return result.status === "success";
1798
- } catch (error) {
1799
- return false;
1800
- }
1801
- }
1802
- async exists() {
1803
- try {
1804
- const result = await this.executeBinary([
1805
- "--action",
1806
- "exists",
1807
- "--database",
1808
- this.dbPath
1809
- ]);
1810
- return result.status === "success" && result.data === true;
1811
- } catch (error) {
1812
- return false;
1813
- }
1814
- }
1815
- async query(sql, params) {
1816
- const args = [
1817
- "--action",
1818
- "query",
1819
- "--database",
1820
- this.dbPath,
1821
- "--query",
1822
- sql
1823
- ];
1824
- if (params && params.length > 0) {
1825
- args.push("--params", JSON.stringify(params));
1826
- }
1827
- return this.executeBinary(args);
1828
- }
1829
- // Método para múltiples queries (como lo hace better-sqlite3)
1830
- async queryMultiple(sql) {
1831
- return this.query(sql);
1832
- }
1833
- // Método prepare que simula prepared statements
1834
- prepare(sql) {
1835
- return {
1836
- all: async (...params) => {
1837
- const result = await this.query(sql, params);
1838
- if (result.status === "error") {
1839
- throw new Error(result.message);
1840
- }
1841
- return Array.isArray(result.data) ? result.data : [];
1842
- },
1843
- run: async (...params) => {
1844
- const result = await this.query(sql, params);
1845
- if (result.status === "error") {
1846
- throw new Error(result.message);
1847
- }
1848
- if (typeof result.data === "object" && result.data.hasOwnProperty("changes") && result.data.hasOwnProperty("lastID")) {
1849
- return result.data;
1850
- }
1851
- return { changes: 0, lastID: 0 };
1852
- }
1853
- };
1854
- }
1855
- // Para compatibilidad con better-sqlite3 API sincrona usando deasync si es necesario
1856
- prepareSync(sql) {
1857
- const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath3(import.meta.url) : "";
1858
- const requireUrl = __filename2 || import.meta.url;
1859
- const require2 = createRequire3(requireUrl);
1860
- const deasync = require2("deasync");
1861
- return {
1862
- all: (...params) => {
1863
- let result;
1864
- let done = false;
1865
- let error;
1866
- this.query(sql, params).then((res) => {
1867
- if (res.status === "error") {
1868
- error = new Error(res.message);
1869
- } else {
1870
- result = Array.isArray(res.data) ? res.data : [];
1871
- }
1872
- done = true;
1873
- }).catch((err) => {
1874
- error = err;
1875
- done = true;
1876
- });
1877
- deasync.loopWhile(() => !done);
1878
- if (error) throw error;
1879
- return result;
1880
- },
1881
- run: (...params) => {
1882
- let result;
1883
- let done = false;
1884
- let error;
1885
- this.query(sql, params).then((res) => {
1886
- if (res.status === "error") {
1887
- error = new Error(res.message);
1888
- } else if (typeof res.data === "object" && res.data.hasOwnProperty("changes") && res.data.hasOwnProperty("lastID")) {
1889
- result = res.data;
1890
- } else {
1891
- result = { changes: 0, lastID: 0 };
1892
- }
1893
- done = true;
1894
- }).catch((err) => {
1895
- error = err;
1896
- done = true;
1897
- });
1898
- deasync.loopWhile(() => !done);
1899
- if (error) throw error;
1900
- return result;
1901
- }
1902
- };
1903
- }
1904
- };
1905
-
1906
2014
  // src/lib/DbConfig.ts
1907
2015
  import * as path7 from "path";
1908
2016
  import fs5 from "fs";
1909
2017
  var rootPath = path7.resolve(process.cwd(), ".dbcube");
1910
2018
  var SQLite = class {
1911
- executor = null;
1912
2019
  database;
2020
+ engine = null;
1913
2021
  constructor(config) {
1914
- this.database = config.DATABASE;
2022
+ this.database = config.DATABASE || "config";
2023
+ }
2024
+ /** Ruta del archivo SQLite interno (.dbcube/<database>.db). */
2025
+ dbFilePath() {
2026
+ return path7.join(rootPath, this.database + ".db");
2027
+ }
2028
+ /** QueryEngine sqlite apuntando al archivo interno, con config explícito
2029
+ * (no vive en dbcube.config.js). La ruta relativa `.dbcube/<db>` deja que
2030
+ * QueryEngine le añada `.db` → `.dbcube/<db>.db`, relativo al CWD. */
2031
+ getEngine() {
2032
+ if (!this.engine) {
2033
+ const relativeDb = path7.join(".dbcube", this.database).replace(/\\/g, "/");
2034
+ this.engine = new QueryEngine(`dbcube-internal-${this.database}`, 3e4, {
2035
+ type: "sqlite",
2036
+ config: { DATABASE: relativeDb }
2037
+ });
2038
+ }
2039
+ return this.engine;
1915
2040
  }
1916
2041
  async ifExist() {
1917
- if (this.database) {
1918
- const dbPath = this.database || ":memory:";
1919
- const configPath = path7.join(rootPath, dbPath + ".db");
1920
- if (!fs5.existsSync(rootPath)) {
1921
- fs5.mkdirSync(rootPath, { recursive: true });
1922
- }
1923
- if (fs5.existsSync(configPath)) {
1924
- return true;
1925
- }
1926
- if (!this.executor) {
1927
- this.executor = new SqliteExecutor(configPath);
1928
- }
1929
- return await this.executor.exists();
2042
+ if (!fs5.existsSync(rootPath)) {
2043
+ fs5.mkdirSync(rootPath, { recursive: true });
1930
2044
  }
1931
- return false;
2045
+ return fs5.existsSync(this.dbFilePath());
1932
2046
  }
1933
2047
  async connect() {
1934
- return new Promise(async (resolve5, reject) => {
1935
- try {
1936
- if (!this.executor) {
1937
- const dbPath = this.database || ":memory:";
1938
- const configPath = path7.join(rootPath, dbPath + ".db");
1939
- if (!fs5.existsSync(rootPath)) {
1940
- fs5.mkdirSync(rootPath, { recursive: true });
1941
- }
1942
- this.executor = new SqliteExecutor(configPath);
1943
- const connected = await this.executor.connect();
1944
- if (!connected) {
1945
- throw new Error("Failed to connect to SQLite database");
1946
- }
1947
- }
1948
- resolve5(this.executor);
1949
- } catch (error) {
1950
- reject(error);
1951
- }
1952
- });
2048
+ if (!fs5.existsSync(rootPath)) {
2049
+ fs5.mkdirSync(rootPath, { recursive: true });
2050
+ }
2051
+ return this.getEngine();
1953
2052
  }
1954
2053
  async disconnect() {
1955
- return new Promise((resolve5) => {
1956
- if (this.executor) {
1957
- this.executor = null;
1958
- }
1959
- resolve5();
1960
- });
1961
2054
  }
1962
2055
  async query(sqlQuery) {
1963
- return new Promise(async (resolve5) => {
1964
- try {
1965
- if (typeof sqlQuery !== "string") {
1966
- throw new Error("The SQL query must be a string.");
1967
- }
1968
- if (!this.executor) {
1969
- await this.connect();
1970
- }
1971
- if (!this.executor) {
1972
- throw new Error("Database connection is not available.");
1973
- }
1974
- const result = await this.executor.queryMultiple(sqlQuery);
1975
- if (result.status === "error") {
1976
- resolve5({
1977
- status: "error",
1978
- message: result.message,
1979
- data: null
1980
- });
1981
- } else {
1982
- resolve5({
1983
- status: "success",
1984
- message: "Query executed successfully",
1985
- data: result.data
1986
- });
1987
- }
1988
- } catch (error) {
1989
- resolve5({
1990
- status: "error",
1991
- message: error.message || "An error occurred while executing the query.",
1992
- data: null
1993
- });
1994
- }
1995
- });
2056
+ return this.queryWithParameters(sqlQuery, []);
1996
2057
  }
1997
2058
  async queryWithParameters(sqlQuery, params = []) {
1998
- return new Promise(async (resolve5) => {
1999
- try {
2000
- if (typeof sqlQuery !== "string") {
2001
- throw new Error("The SQL query must be a string.");
2002
- }
2003
- if (!Array.isArray(params)) {
2004
- throw new Error("Parameters must be an array.");
2005
- }
2006
- if (!this.executor) {
2007
- await this.connect();
2008
- }
2009
- if (!this.executor) {
2010
- throw new Error("Database connection is not available.");
2011
- }
2012
- const result = await this.executor.query(sqlQuery, params);
2013
- if (result.status === "error") {
2014
- resolve5({
2015
- status: "error",
2016
- message: result.message,
2017
- data: null
2018
- });
2019
- } else {
2020
- resolve5({
2021
- status: "success",
2022
- message: "Query executed successfully",
2023
- data: result.data
2024
- });
2025
- }
2026
- } catch (error) {
2027
- console.log(error);
2028
- resolve5({
2029
- status: "error",
2030
- message: error.message || "An error occurred while executing the query.",
2031
- data: null
2032
- });
2059
+ try {
2060
+ if (typeof sqlQuery !== "string") {
2061
+ throw new Error("The SQL query must be a string.");
2033
2062
  }
2034
- });
2063
+ if (!Array.isArray(params)) {
2064
+ throw new Error("Parameters must be an array.");
2065
+ }
2066
+ const response = await this.getEngine().rawQuery(sqlQuery, params);
2067
+ if (response.status !== 200) {
2068
+ return { status: "error", message: response.message || "Query failed", data: null };
2069
+ }
2070
+ return { status: "success", message: "Query executed successfully", data: response.data };
2071
+ } catch (error) {
2072
+ return {
2073
+ status: "error",
2074
+ message: error.message || "An error occurred while executing the query.",
2075
+ data: null
2076
+ };
2077
+ }
2035
2078
  }
2036
2079
  convertToParameterizedQuery(sql) {
2037
2080
  const normalizedSql = sql.replace(/\s+/g, " ").trim();