@dbcube/core 5.2.1 → 5.2.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/dist/bin.cjs +29 -17
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +31 -19
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +521 -224
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +542 -238
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -194,7 +194,7 @@ var Downloader = class {
|
|
|
194
194
|
*/
|
|
195
195
|
static async fetchLatestVersion(prefix) {
|
|
196
196
|
const url = this.VERSION_URLS[prefix];
|
|
197
|
-
return new Promise((
|
|
197
|
+
return new Promise((resolve6, reject) => {
|
|
198
198
|
https.get(url, (response) => {
|
|
199
199
|
let data = "";
|
|
200
200
|
response.on("data", (chunk) => {
|
|
@@ -204,7 +204,7 @@ var Downloader = class {
|
|
|
204
204
|
try {
|
|
205
205
|
const versions = JSON.parse(data);
|
|
206
206
|
if (versions && versions.length > 0) {
|
|
207
|
-
|
|
207
|
+
resolve6(versions[0].version);
|
|
208
208
|
} else {
|
|
209
209
|
reject(new Error("No versions found"));
|
|
210
210
|
}
|
|
@@ -413,7 +413,7 @@ var Downloader = class {
|
|
|
413
413
|
if (attempt < maxRetries && (errorMessage.includes("ECONNRESET") || errorMessage.includes("timeout") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("ENOTFOUND"))) {
|
|
414
414
|
attempt++;
|
|
415
415
|
console.log(`\u{1F504} Retrying ${binary.prefix}-engine (${attempt}/${maxRetries})...`);
|
|
416
|
-
await new Promise((
|
|
416
|
+
await new Promise((resolve6) => setTimeout(resolve6, 1e3 + Math.random() * 1e3));
|
|
417
417
|
} else {
|
|
418
418
|
throw new Error(`Error downloading ${binary.prefix}: ${errorMessage}`);
|
|
419
419
|
}
|
|
@@ -461,12 +461,12 @@ var Downloader = class {
|
|
|
461
461
|
return `[${filledBar}${emptyBar}] ${percentage}`;
|
|
462
462
|
}
|
|
463
463
|
static downloadFileWithProgress(url, outputPath, prefix) {
|
|
464
|
-
return new Promise((
|
|
464
|
+
return new Promise((resolve6, reject) => {
|
|
465
465
|
const request = https.get(url, { timeout: 0 }, (response) => {
|
|
466
466
|
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
467
467
|
const redirectUrl = response.headers.location;
|
|
468
468
|
if (redirectUrl) {
|
|
469
|
-
return this.downloadFileWithProgress(redirectUrl, outputPath, prefix).then(
|
|
469
|
+
return this.downloadFileWithProgress(redirectUrl, outputPath, prefix).then(resolve6).catch(reject);
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
if (response.statusCode !== 200) {
|
|
@@ -489,7 +489,7 @@ var Downloader = class {
|
|
|
489
489
|
}
|
|
490
490
|
});
|
|
491
491
|
response.on("end", () => {
|
|
492
|
-
file.end(() =>
|
|
492
|
+
file.end(() => resolve6());
|
|
493
493
|
});
|
|
494
494
|
response.on("error", (err) => {
|
|
495
495
|
file.close();
|
|
@@ -521,35 +521,47 @@ var Downloader = class {
|
|
|
521
521
|
this.mainSpinner.text = `\u{1F4E5} ${import_chalk.default.bold(binary)} - ${progressText}`;
|
|
522
522
|
}
|
|
523
523
|
static extractBinary(zipPath, outputPath, prefix) {
|
|
524
|
-
return new Promise((
|
|
525
|
-
|
|
524
|
+
return new Promise((resolve6, reject) => {
|
|
525
|
+
const outDir = path.dirname(outputPath);
|
|
526
|
+
let extracted = 0;
|
|
527
|
+
const pending = [];
|
|
526
528
|
fs.createReadStream(zipPath).pipe(unzipper.Parse()).on("entry", (entry) => {
|
|
527
|
-
if (entry.type
|
|
528
|
-
|
|
529
|
-
|
|
529
|
+
if (entry.type !== "File") {
|
|
530
|
+
entry.autodrain();
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const isMain = extracted === 0;
|
|
534
|
+
extracted++;
|
|
535
|
+
const target = isMain ? outputPath : path.join(outDir, path.basename(entry.path));
|
|
536
|
+
pending.push(new Promise((res, rej) => {
|
|
537
|
+
const writeStream = fs.createWriteStream(target);
|
|
530
538
|
entry.pipe(writeStream);
|
|
531
539
|
writeStream.on("finish", () => {
|
|
532
540
|
if (process.platform !== "win32") {
|
|
533
|
-
|
|
541
|
+
try {
|
|
542
|
+
fs.chmodSync(target, 493);
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
534
545
|
}
|
|
535
|
-
|
|
536
|
-
resolve5();
|
|
546
|
+
res();
|
|
537
547
|
});
|
|
538
|
-
writeStream.on("error",
|
|
539
|
-
|
|
540
|
-
reject(err);
|
|
541
|
-
});
|
|
542
|
-
} else {
|
|
543
|
-
entry.autodrain();
|
|
544
|
-
}
|
|
548
|
+
writeStream.on("error", rej);
|
|
549
|
+
}));
|
|
545
550
|
}).on("error", (err) => {
|
|
546
551
|
this.cleanupFile(zipPath);
|
|
547
552
|
reject(err);
|
|
548
553
|
}).on("close", () => {
|
|
549
|
-
|
|
554
|
+
Promise.all(pending).then(() => {
|
|
550
555
|
this.cleanupFile(zipPath);
|
|
551
|
-
|
|
552
|
-
|
|
556
|
+
if (extracted === 0) {
|
|
557
|
+
reject(new Error(`No se encontr\xF3 archivo v\xE1lido en el ZIP para ${prefix}`));
|
|
558
|
+
} else {
|
|
559
|
+
resolve6();
|
|
560
|
+
}
|
|
561
|
+
}).catch((err) => {
|
|
562
|
+
this.cleanupFile(zipPath);
|
|
563
|
+
reject(err);
|
|
564
|
+
});
|
|
553
565
|
});
|
|
554
566
|
});
|
|
555
567
|
}
|
|
@@ -864,22 +876,22 @@ var DaemonClient = class _DaemonClient {
|
|
|
864
876
|
child.unref();
|
|
865
877
|
}
|
|
866
878
|
tryConnect(port) {
|
|
867
|
-
return new Promise((
|
|
879
|
+
return new Promise((resolve6) => {
|
|
868
880
|
const socket = import_net.default.createConnection({ host: "127.0.0.1", port }, async () => {
|
|
869
881
|
socket.setNoDelay(true);
|
|
870
882
|
this.attach(socket);
|
|
871
883
|
try {
|
|
872
884
|
const pong = await this.send({ action: "ping" }, 2e3);
|
|
873
|
-
|
|
885
|
+
resolve6(pong.status === 200);
|
|
874
886
|
} catch {
|
|
875
887
|
this.detach();
|
|
876
|
-
|
|
888
|
+
resolve6(false);
|
|
877
889
|
}
|
|
878
890
|
});
|
|
879
|
-
socket.once("error", () =>
|
|
891
|
+
socket.once("error", () => resolve6(false));
|
|
880
892
|
socket.setTimeout(3e3, () => {
|
|
881
893
|
socket.destroy();
|
|
882
|
-
|
|
894
|
+
resolve6(false);
|
|
883
895
|
});
|
|
884
896
|
});
|
|
885
897
|
}
|
|
@@ -927,7 +939,7 @@ var DaemonClient = class _DaemonClient {
|
|
|
927
939
|
* single socket, so pending requests resolve in send order.
|
|
928
940
|
*/
|
|
929
941
|
send(payload, timeoutMs) {
|
|
930
|
-
return new Promise((
|
|
942
|
+
return new Promise((resolve6, reject) => {
|
|
931
943
|
if (!this.socket || this.socket.destroyed) {
|
|
932
944
|
reject(new Error("Daemon not connected"));
|
|
933
945
|
return;
|
|
@@ -938,12 +950,12 @@ var DaemonClient = class _DaemonClient {
|
|
|
938
950
|
reject(new Error("Daemon request timeout"));
|
|
939
951
|
this.detach();
|
|
940
952
|
}, timeoutMs ?? this.requestTimeout);
|
|
941
|
-
this.pending.push({ resolve:
|
|
953
|
+
this.pending.push({ resolve: resolve6, reject, timer });
|
|
942
954
|
this.socket.write(JSON.stringify(payload) + "\n");
|
|
943
955
|
});
|
|
944
956
|
}
|
|
945
957
|
async execute(dml, txId) {
|
|
946
|
-
const payload = { action: "execute", dml
|
|
958
|
+
const payload = { action: "execute", dml };
|
|
947
959
|
if (txId) payload.tx_id = txId;
|
|
948
960
|
return this.send(payload);
|
|
949
961
|
}
|
|
@@ -1010,8 +1022,8 @@ var Engine = class {
|
|
|
1010
1022
|
const binaryPath = this.binary["query_engine"];
|
|
1011
1023
|
if (!binaryPath) return null;
|
|
1012
1024
|
const client = DaemonClient.get(this.name, binaryPath, this.arguments);
|
|
1013
|
-
const
|
|
1014
|
-
if (!
|
|
1025
|
+
const ok2 = await client.ensure();
|
|
1026
|
+
if (!ok2) {
|
|
1015
1027
|
this.daemonFailed = true;
|
|
1016
1028
|
return null;
|
|
1017
1029
|
}
|
|
@@ -1142,7 +1154,7 @@ var Engine = class {
|
|
|
1142
1154
|
if (!this.binary) {
|
|
1143
1155
|
throw new Error("Binary not initialized");
|
|
1144
1156
|
}
|
|
1145
|
-
return new Promise((
|
|
1157
|
+
return new Promise((resolve6, reject) => {
|
|
1146
1158
|
const child = (0, import_child_process2.spawn)(this.binary[binary], [...this.arguments, ...args]);
|
|
1147
1159
|
let stdoutBuffer = "";
|
|
1148
1160
|
let stderrBuffer = "";
|
|
@@ -1158,18 +1170,18 @@ var Engine = class {
|
|
|
1158
1170
|
if (!isResolved) {
|
|
1159
1171
|
isResolved = true;
|
|
1160
1172
|
clearTimeout(timeoutId);
|
|
1161
|
-
|
|
1173
|
+
resolve6(response);
|
|
1162
1174
|
}
|
|
1163
1175
|
};
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1176
|
+
const tryParseLines = (buffer) => {
|
|
1177
|
+
let idx;
|
|
1178
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
1179
|
+
const line = buffer.slice(0, idx);
|
|
1180
|
+
buffer = buffer.slice(idx + 1);
|
|
1181
|
+
const marker = line.indexOf("PROCESS_RESPONSE:");
|
|
1182
|
+
if (marker === -1) continue;
|
|
1171
1183
|
try {
|
|
1172
|
-
const response = JSON.parse(
|
|
1184
|
+
const response = JSON.parse(line.slice(marker + "PROCESS_RESPONSE:".length));
|
|
1173
1185
|
resolveOnce({
|
|
1174
1186
|
status: response.status,
|
|
1175
1187
|
message: response.message,
|
|
@@ -1183,29 +1195,19 @@ var Engine = class {
|
|
|
1183
1195
|
});
|
|
1184
1196
|
}
|
|
1185
1197
|
}
|
|
1198
|
+
return buffer;
|
|
1199
|
+
};
|
|
1200
|
+
child.stdout.on("data", (data) => {
|
|
1201
|
+
const text = data.toString();
|
|
1202
|
+
const visible = text.split("\n").filter((l) => l.trim() && !l.includes("PROCESS_RESPONSE:")).join("\n");
|
|
1203
|
+
if (visible) console.log(visible);
|
|
1204
|
+
stdoutBuffer = tryParseLines(stdoutBuffer + text);
|
|
1186
1205
|
});
|
|
1187
1206
|
child.stderr.on("data", (data) => {
|
|
1188
1207
|
const text = data.toString();
|
|
1189
|
-
stderrBuffer += text;
|
|
1190
1208
|
const visible = text.split("\n").filter((l) => l.trim() && !l.includes("PROCESS_RESPONSE:")).join("\n");
|
|
1191
1209
|
if (visible) console.log(visible);
|
|
1192
|
-
|
|
1193
|
-
if (match) {
|
|
1194
|
-
try {
|
|
1195
|
-
const response = JSON.parse(match[1]);
|
|
1196
|
-
resolveOnce({
|
|
1197
|
-
status: response.status,
|
|
1198
|
-
message: response.message,
|
|
1199
|
-
data: response.data
|
|
1200
|
-
});
|
|
1201
|
-
} catch (error) {
|
|
1202
|
-
resolveOnce({
|
|
1203
|
-
status: 500,
|
|
1204
|
-
message: "Failed to parse response JSON",
|
|
1205
|
-
data: null
|
|
1206
|
-
});
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1210
|
+
stderrBuffer = tryParseLines(stderrBuffer + text);
|
|
1209
1211
|
});
|
|
1210
1212
|
child.on("close", (code) => {
|
|
1211
1213
|
clearTimeout(timeoutId);
|
|
@@ -1234,13 +1236,264 @@ var Engine = class {
|
|
|
1234
1236
|
|
|
1235
1237
|
// src/lib/QueryEngine.ts
|
|
1236
1238
|
var import_path5 = __toESM(require("path"));
|
|
1239
|
+
|
|
1240
|
+
// src/lib/EmbeddedEngine.ts
|
|
1241
|
+
var fs4 = __toESM(require("fs"));
|
|
1242
|
+
var path5 = __toESM(require("path"));
|
|
1243
|
+
if (!process.env.UV_THREADPOOL_SIZE) {
|
|
1244
|
+
process.env.UV_THREADPOOL_SIZE = "16";
|
|
1245
|
+
}
|
|
1246
|
+
var backend = null;
|
|
1247
|
+
var loadFailed = false;
|
|
1248
|
+
var handles = /* @__PURE__ */ new Map();
|
|
1249
|
+
var connecting = /* @__PURE__ */ new Map();
|
|
1250
|
+
function platTag() {
|
|
1251
|
+
const arch2 = process.arch === "arm64" ? "arm64" : "x64";
|
|
1252
|
+
if (process.platform === "win32") return { plat: "windows", ext: "dll", arch: arch2 };
|
|
1253
|
+
if (process.platform === "darwin") return { plat: "macos", ext: "dylib", arch: arch2 };
|
|
1254
|
+
return { plat: "linux", ext: "so", arch: arch2 };
|
|
1255
|
+
}
|
|
1256
|
+
function findFile(name) {
|
|
1257
|
+
const candidates = [
|
|
1258
|
+
path5.resolve(process.cwd(), ".dbcube", "bin", name),
|
|
1259
|
+
path5.resolve(process.cwd(), "node_modules", ".dbcube", "bin", name)
|
|
1260
|
+
];
|
|
1261
|
+
for (const c of candidates) {
|
|
1262
|
+
if (fs4.existsSync(c)) return c;
|
|
1263
|
+
}
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
function ok(data) {
|
|
1267
|
+
return { status: 200, message: "OK", data };
|
|
1268
|
+
}
|
|
1269
|
+
function errResp(e) {
|
|
1270
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1271
|
+
return { status: 500, message: msg, data: null };
|
|
1272
|
+
}
|
|
1273
|
+
function loadNapiBackend() {
|
|
1274
|
+
const { plat, arch: arch2 } = platTag();
|
|
1275
|
+
const addonPath = findFile(`query-engine-node-${plat}-${arch2}.node`);
|
|
1276
|
+
if (!addonPath) return null;
|
|
1277
|
+
try {
|
|
1278
|
+
const addon = require(addonPath);
|
|
1279
|
+
if (typeof addon.connect !== "function") return null;
|
|
1280
|
+
return {
|
|
1281
|
+
async connect(cfgJson) {
|
|
1282
|
+
return Number(await addon.connect(cfgJson));
|
|
1283
|
+
},
|
|
1284
|
+
async execute(handle, dml, txId) {
|
|
1285
|
+
try {
|
|
1286
|
+
const rows = await addon.execute(handle, dml, txId ?? void 0);
|
|
1287
|
+
return ok(rows);
|
|
1288
|
+
} catch (e) {
|
|
1289
|
+
return errResp(e);
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1292
|
+
async executeBatch(handle, ops) {
|
|
1293
|
+
try {
|
|
1294
|
+
const results = await addon.executeBatch(handle, ops);
|
|
1295
|
+
return ok(results);
|
|
1296
|
+
} catch (e) {
|
|
1297
|
+
return errResp(e);
|
|
1298
|
+
}
|
|
1299
|
+
},
|
|
1300
|
+
async raw(handle, query, params, txId) {
|
|
1301
|
+
try {
|
|
1302
|
+
const rows = await addon.raw(handle, query, params, txId ?? void 0);
|
|
1303
|
+
return ok(rows);
|
|
1304
|
+
} catch (e) {
|
|
1305
|
+
return errResp(e);
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
async begin(handle) {
|
|
1309
|
+
try {
|
|
1310
|
+
const txId = await addon.begin(handle);
|
|
1311
|
+
return ok({ tx_id: txId });
|
|
1312
|
+
} catch (e) {
|
|
1313
|
+
return errResp(e);
|
|
1314
|
+
}
|
|
1315
|
+
},
|
|
1316
|
+
async commit(_handle, txId) {
|
|
1317
|
+
try {
|
|
1318
|
+
await addon.commit(txId);
|
|
1319
|
+
return ok(null);
|
|
1320
|
+
} catch (e) {
|
|
1321
|
+
return errResp(e);
|
|
1322
|
+
}
|
|
1323
|
+
},
|
|
1324
|
+
async rollback(_handle, txId) {
|
|
1325
|
+
try {
|
|
1326
|
+
await addon.rollback(txId);
|
|
1327
|
+
return ok(null);
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
return errResp(e);
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
async disconnect(handle) {
|
|
1333
|
+
try {
|
|
1334
|
+
addon.disconnect(handle);
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
} catch {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
function loadKoffiBackend() {
|
|
1344
|
+
const { plat, ext, arch: arch2 } = platTag();
|
|
1345
|
+
const libPath = findFile(`query-engine-embedded-${plat}-${arch2}.${ext}`);
|
|
1346
|
+
if (!libPath) return null;
|
|
1347
|
+
try {
|
|
1348
|
+
const koffi = require("koffi");
|
|
1349
|
+
const lib = koffi.load(libPath);
|
|
1350
|
+
const cConnect = lib.func("dbc_connect", "void *", ["str"]);
|
|
1351
|
+
const cExecute = lib.func("dbc_execute", "void *", ["uint64", "str", "str"]);
|
|
1352
|
+
const cExecuteBatch = lib.func("dbc_execute_batch", "void *", ["uint64", "str"]);
|
|
1353
|
+
const cRaw = lib.func("dbc_raw", "void *", ["uint64", "str", "str", "str"]);
|
|
1354
|
+
const cBegin = lib.func("dbc_begin", "void *", ["uint64"]);
|
|
1355
|
+
const cCommit = lib.func("dbc_commit", "void *", ["uint64", "str"]);
|
|
1356
|
+
const cRollback = lib.func("dbc_rollback", "void *", ["uint64", "str"]);
|
|
1357
|
+
const cDisconnect = lib.func("dbc_disconnect", "void *", ["uint64"]);
|
|
1358
|
+
const cFree = lib.func("dbc_free", "void", ["void *"]);
|
|
1359
|
+
const take = (ptr) => {
|
|
1360
|
+
if (!ptr) return { status: 500, message: "embedded engine returned null", data: null };
|
|
1361
|
+
const s = koffi.decode(ptr, "char", -1);
|
|
1362
|
+
cFree(ptr);
|
|
1363
|
+
try {
|
|
1364
|
+
const r = JSON.parse(s);
|
|
1365
|
+
return { status: r.status ?? 500, message: r.message ?? "", data: r.data ?? null };
|
|
1366
|
+
} catch (e) {
|
|
1367
|
+
return { status: 500, message: `Invalid embedded response: ${e.message}`, data: null };
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
const call = (fn, ...args) => new Promise((resolve6, reject) => {
|
|
1371
|
+
fn.async(...args, (err, ptr) => {
|
|
1372
|
+
if (err) return reject(err);
|
|
1373
|
+
resolve6(take(ptr));
|
|
1374
|
+
});
|
|
1375
|
+
});
|
|
1376
|
+
return {
|
|
1377
|
+
async connect(cfgJson) {
|
|
1378
|
+
const res = await call(cConnect, cfgJson);
|
|
1379
|
+
if (res.status !== 200 || !res.data?.handle) {
|
|
1380
|
+
throw new Error(String(res.message || "embedded connect failed"));
|
|
1381
|
+
}
|
|
1382
|
+
return Number(res.data.handle);
|
|
1383
|
+
},
|
|
1384
|
+
execute: (handle, dml, txId) => call(cExecute, handle, JSON.stringify(dml), txId),
|
|
1385
|
+
executeBatch: (handle, ops) => call(cExecuteBatch, handle, JSON.stringify(ops)),
|
|
1386
|
+
raw: (handle, query, params, txId) => call(cRaw, handle, query, JSON.stringify(params ?? []), txId),
|
|
1387
|
+
begin: (handle) => call(cBegin, handle),
|
|
1388
|
+
commit: (handle, txId) => call(cCommit, handle, txId),
|
|
1389
|
+
rollback: (handle, txId) => call(cRollback, handle, txId),
|
|
1390
|
+
async disconnect(handle) {
|
|
1391
|
+
try {
|
|
1392
|
+
await call(cDisconnect, handle);
|
|
1393
|
+
} catch {
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
} catch {
|
|
1398
|
+
return null;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function loadBackend() {
|
|
1402
|
+
if (backend) return backend;
|
|
1403
|
+
if (loadFailed) return null;
|
|
1404
|
+
if (process.env.DBCUBE_EMBEDDED === "0" || process.env.DBCUBE_EMBEDDED === "false") {
|
|
1405
|
+
loadFailed = true;
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
backend = loadNapiBackend() ?? loadKoffiBackend();
|
|
1409
|
+
if (!backend) loadFailed = true;
|
|
1410
|
+
return backend;
|
|
1411
|
+
}
|
|
1412
|
+
var EmbeddedEngine = class {
|
|
1413
|
+
/** true si algún backend nativo está disponible en esta instalación. */
|
|
1414
|
+
static available() {
|
|
1415
|
+
return loadBackend() !== null;
|
|
1416
|
+
}
|
|
1417
|
+
static async handleFor(connectionId, config) {
|
|
1418
|
+
const existing = handles.get(connectionId);
|
|
1419
|
+
if (existing) return existing;
|
|
1420
|
+
const inFlight = connecting.get(connectionId);
|
|
1421
|
+
if (inFlight) return inFlight;
|
|
1422
|
+
const promise = (async () => {
|
|
1423
|
+
const be = loadBackend();
|
|
1424
|
+
if (!be) throw new Error("embedded engine not available");
|
|
1425
|
+
const motor = config.type === "postgres" ? "postgresql" : config.type;
|
|
1426
|
+
const cfg = {
|
|
1427
|
+
databaseRef: connectionId,
|
|
1428
|
+
motor,
|
|
1429
|
+
database: config.type === "sqlite" ? `${config.config.DATABASE}.db` : config.config.DATABASE,
|
|
1430
|
+
host: config.config.HOST,
|
|
1431
|
+
port: config.config.PORT != null ? Number(config.config.PORT) : void 0,
|
|
1432
|
+
user: config.config.USER,
|
|
1433
|
+
password: config.config.PASSWORD,
|
|
1434
|
+
maxConnections: config.pool?.maxConnections,
|
|
1435
|
+
minConnections: config.pool?.minConnections,
|
|
1436
|
+
acquireTimeoutMs: config.pool?.acquireTimeoutMs,
|
|
1437
|
+
idleTimeoutMs: config.pool?.idleTimeoutMs
|
|
1438
|
+
};
|
|
1439
|
+
const handle = await be.connect(JSON.stringify(cfg));
|
|
1440
|
+
handles.set(connectionId, handle);
|
|
1441
|
+
return handle;
|
|
1442
|
+
})();
|
|
1443
|
+
connecting.set(connectionId, promise);
|
|
1444
|
+
try {
|
|
1445
|
+
return await promise;
|
|
1446
|
+
} finally {
|
|
1447
|
+
connecting.delete(connectionId);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
static async executeDml(connectionId, config, dml, txId) {
|
|
1451
|
+
const be = loadBackend();
|
|
1452
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1453
|
+
return be.execute(handle, dml, txId ?? null);
|
|
1454
|
+
}
|
|
1455
|
+
static async executeBatch(connectionId, config, ops) {
|
|
1456
|
+
const be = loadBackend();
|
|
1457
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1458
|
+
return be.executeBatch(handle, ops);
|
|
1459
|
+
}
|
|
1460
|
+
static async rawQuery(connectionId, config, query, params, txId) {
|
|
1461
|
+
const be = loadBackend();
|
|
1462
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1463
|
+
return be.raw(handle, query, params ?? [], txId ?? null);
|
|
1464
|
+
}
|
|
1465
|
+
static async begin(connectionId, config) {
|
|
1466
|
+
const be = loadBackend();
|
|
1467
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1468
|
+
return be.begin(handle);
|
|
1469
|
+
}
|
|
1470
|
+
static async commit(connectionId, config, txId) {
|
|
1471
|
+
const be = loadBackend();
|
|
1472
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1473
|
+
return be.commit(handle, txId);
|
|
1474
|
+
}
|
|
1475
|
+
static async rollback(connectionId, config, txId) {
|
|
1476
|
+
const be = loadBackend();
|
|
1477
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1478
|
+
return be.rollback(handle, txId);
|
|
1479
|
+
}
|
|
1480
|
+
static async disconnect(connectionId) {
|
|
1481
|
+
const be = loadBackend();
|
|
1482
|
+
const handle = handles.get(connectionId);
|
|
1483
|
+
if (be && handle) {
|
|
1484
|
+
handles.delete(connectionId);
|
|
1485
|
+
await be.disconnect(handle);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
// src/lib/QueryEngine.ts
|
|
1237
1491
|
var import_module2 = require("module");
|
|
1238
1492
|
var net2 = __toESM(require("net"));
|
|
1239
1493
|
var import_child_process3 = require("child_process");
|
|
1240
1494
|
var globalTcpServers = /* @__PURE__ */ new Map();
|
|
1241
|
-
var
|
|
1242
|
-
var
|
|
1243
|
-
var connectionProcessing = /* @__PURE__ */ new Map();
|
|
1495
|
+
var socketPools = /* @__PURE__ */ new Map();
|
|
1496
|
+
var CLIENT_POOL_SIZE = 10;
|
|
1244
1497
|
var queryCache = /* @__PURE__ */ new Map();
|
|
1245
1498
|
var MAX_CACHE_SIZE = 500;
|
|
1246
1499
|
var QueryEngine = class {
|
|
@@ -1271,12 +1524,12 @@ var QueryEngine = class {
|
|
|
1271
1524
|
throw new Error("No available ports found in range 9900-9944");
|
|
1272
1525
|
}
|
|
1273
1526
|
isPortAvailable(port) {
|
|
1274
|
-
return new Promise((
|
|
1527
|
+
return new Promise((resolve6) => {
|
|
1275
1528
|
const tester = net2.createServer();
|
|
1276
|
-
tester.once("error", () =>
|
|
1529
|
+
tester.once("error", () => resolve6(false));
|
|
1277
1530
|
tester.once("listening", () => {
|
|
1278
1531
|
tester.close();
|
|
1279
|
-
|
|
1532
|
+
resolve6(true);
|
|
1280
1533
|
});
|
|
1281
1534
|
tester.listen(port, "127.0.0.1");
|
|
1282
1535
|
});
|
|
@@ -1383,38 +1636,79 @@ var QueryEngine = class {
|
|
|
1383
1636
|
}
|
|
1384
1637
|
return command;
|
|
1385
1638
|
}
|
|
1639
|
+
/** El motor embebido (FFI) es el camino por defecto cuando la librería
|
|
1640
|
+
* nativa está disponible; el daemon TCP queda como fallback y como modo
|
|
1641
|
+
* multi-proceso explícito. */
|
|
1642
|
+
useEmbedded() {
|
|
1643
|
+
return EmbeddedEngine.available();
|
|
1644
|
+
}
|
|
1386
1645
|
/**
|
|
1387
|
-
* Executes a DML plan
|
|
1646
|
+
* Executes a DML plan. Embedded engine when available (in-process FFI,
|
|
1647
|
+
* no network hop); persistent TCP daemon otherwise.
|
|
1388
1648
|
* Pass txId to run it inside an active transaction.
|
|
1389
1649
|
*/
|
|
1390
1650
|
async executeDml(dml, txId) {
|
|
1391
|
-
|
|
1651
|
+
if (this.useEmbedded()) {
|
|
1652
|
+
return EmbeddedEngine.executeDml(this.connectionId, this.config, dml, txId);
|
|
1653
|
+
}
|
|
1654
|
+
const command = { action: "execute", dml };
|
|
1392
1655
|
if (txId) command.tx_id = txId;
|
|
1393
1656
|
return this.executeWithTcpServer(command);
|
|
1394
1657
|
}
|
|
1395
1658
|
/**
|
|
1396
|
-
*
|
|
1397
|
-
* over the
|
|
1659
|
+
* Atomic batch: N write plans inside one transaction with a single
|
|
1660
|
+
* engine round-trip. Falls back to begin/ops/commit over the daemon
|
|
1661
|
+
* when the embedded engine is not available.
|
|
1662
|
+
*/
|
|
1663
|
+
async executeBatch(ops) {
|
|
1664
|
+
if (this.useEmbedded()) {
|
|
1665
|
+
return EmbeddedEngine.executeBatch(this.connectionId, this.config, ops);
|
|
1666
|
+
}
|
|
1667
|
+
const txId = await this.beginTransaction();
|
|
1668
|
+
try {
|
|
1669
|
+
const results = [];
|
|
1670
|
+
for (const op of ops) {
|
|
1671
|
+
const res = await this.executeDml(op, txId);
|
|
1672
|
+
if (res.status !== 200) {
|
|
1673
|
+
throw new Error(String(res.message));
|
|
1674
|
+
}
|
|
1675
|
+
results.push(res.data);
|
|
1676
|
+
}
|
|
1677
|
+
await this.commitTransaction(txId);
|
|
1678
|
+
return { status: 200, message: "Batch committed", data: results };
|
|
1679
|
+
} catch (error) {
|
|
1680
|
+
try {
|
|
1681
|
+
await this.rollbackTransaction(txId);
|
|
1682
|
+
} catch {
|
|
1683
|
+
}
|
|
1684
|
+
return { status: 500, message: error?.message ?? String(error), data: null };
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Executes raw SQL (or a MongoDB command document) with bound parameters.
|
|
1398
1689
|
*/
|
|
1399
1690
|
async rawQuery(query, params = [], txId) {
|
|
1691
|
+
if (this.useEmbedded()) {
|
|
1692
|
+
return EmbeddedEngine.rawQuery(this.connectionId, this.config, query, params, txId);
|
|
1693
|
+
}
|
|
1400
1694
|
const command = { action: "raw", query, params };
|
|
1401
1695
|
if (txId) command.tx_id = txId;
|
|
1402
1696
|
return this.executeWithTcpServer(command);
|
|
1403
1697
|
}
|
|
1404
|
-
/** Starts a
|
|
1698
|
+
/** Starts a transaction and returns its id. */
|
|
1405
1699
|
async beginTransaction() {
|
|
1406
|
-
const res = await this.executeWithTcpServer({ action: "begin" });
|
|
1700
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.begin(this.connectionId, this.config) : await this.executeWithTcpServer({ action: "begin" });
|
|
1407
1701
|
if (res.status !== 200 || !res.data?.tx_id) {
|
|
1408
1702
|
throw new Error(String(res.message || "Failed to begin transaction (update the query-engine binary: npx dbcube update)"));
|
|
1409
1703
|
}
|
|
1410
1704
|
return res.data.tx_id;
|
|
1411
1705
|
}
|
|
1412
1706
|
async commitTransaction(txId) {
|
|
1413
|
-
const res = await this.executeWithTcpServer({ action: "commit", tx_id: txId });
|
|
1707
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.commit(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "commit", tx_id: txId });
|
|
1414
1708
|
if (res.status !== 200) throw new Error(String(res.message || "Failed to commit transaction"));
|
|
1415
1709
|
}
|
|
1416
1710
|
async rollbackTransaction(txId) {
|
|
1417
|
-
const res = await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
|
|
1711
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.rollback(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
|
|
1418
1712
|
if (res.status !== 200) throw new Error(String(res.message || "Failed to rollback transaction"));
|
|
1419
1713
|
}
|
|
1420
1714
|
async executeWithTcpServer(command) {
|
|
@@ -1433,7 +1727,63 @@ var QueryEngine = class {
|
|
|
1433
1727
|
await this.startTcpServer();
|
|
1434
1728
|
await this.waitForServerReady();
|
|
1435
1729
|
}
|
|
1436
|
-
|
|
1730
|
+
const port = globalTcpServers.get(this.connectionId).port;
|
|
1731
|
+
const socket = await this.acquireSocket(port);
|
|
1732
|
+
try {
|
|
1733
|
+
const result = await this.executeOnConnection(socket, command);
|
|
1734
|
+
this.releaseSocket(socket, false);
|
|
1735
|
+
return result;
|
|
1736
|
+
} catch (error) {
|
|
1737
|
+
this.releaseSocket(socket, true);
|
|
1738
|
+
throw error;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
poolFor() {
|
|
1742
|
+
let pool = socketPools.get(this.connectionId);
|
|
1743
|
+
if (!pool) {
|
|
1744
|
+
pool = { free: [], total: 0, waiters: [] };
|
|
1745
|
+
socketPools.set(this.connectionId, pool);
|
|
1746
|
+
}
|
|
1747
|
+
return pool;
|
|
1748
|
+
}
|
|
1749
|
+
async acquireSocket(port) {
|
|
1750
|
+
const pool = this.poolFor();
|
|
1751
|
+
while (pool.free.length > 0) {
|
|
1752
|
+
const s = pool.free.pop();
|
|
1753
|
+
if (!s.destroyed) return s;
|
|
1754
|
+
pool.total--;
|
|
1755
|
+
}
|
|
1756
|
+
if (pool.total < CLIENT_POOL_SIZE) {
|
|
1757
|
+
pool.total++;
|
|
1758
|
+
try {
|
|
1759
|
+
return await this.createNewConnection(port);
|
|
1760
|
+
} catch (e) {
|
|
1761
|
+
pool.total--;
|
|
1762
|
+
throw e;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return new Promise((resolve6) => pool.waiters.push(resolve6));
|
|
1766
|
+
}
|
|
1767
|
+
releaseSocket(socket, broken) {
|
|
1768
|
+
const pool = socketPools.get(this.connectionId);
|
|
1769
|
+
if (!pool) {
|
|
1770
|
+
socket.destroy();
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
if (broken || socket.destroyed) {
|
|
1774
|
+
pool.total--;
|
|
1775
|
+
try {
|
|
1776
|
+
socket.destroy();
|
|
1777
|
+
} catch {
|
|
1778
|
+
}
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
const waiter = pool.waiters.shift();
|
|
1782
|
+
if (waiter) {
|
|
1783
|
+
waiter(socket);
|
|
1784
|
+
} else {
|
|
1785
|
+
pool.free.push(socket);
|
|
1786
|
+
}
|
|
1437
1787
|
}
|
|
1438
1788
|
async waitForServerReady() {
|
|
1439
1789
|
const maxRetries = 10;
|
|
@@ -1451,25 +1801,25 @@ var QueryEngine = class {
|
|
|
1451
1801
|
throw new Error("TCP server failed to become ready within timeout");
|
|
1452
1802
|
}
|
|
1453
1803
|
async isServerResponding(port) {
|
|
1454
|
-
return new Promise((
|
|
1804
|
+
return new Promise((resolve6) => {
|
|
1455
1805
|
const client = new net2.Socket();
|
|
1456
1806
|
const timeout = setTimeout(() => {
|
|
1457
1807
|
client.destroy();
|
|
1458
|
-
|
|
1808
|
+
resolve6(false);
|
|
1459
1809
|
}, 1e3);
|
|
1460
1810
|
client.connect(port, "127.0.0.1", () => {
|
|
1461
1811
|
clearTimeout(timeout);
|
|
1462
1812
|
client.destroy();
|
|
1463
|
-
|
|
1813
|
+
resolve6(true);
|
|
1464
1814
|
});
|
|
1465
1815
|
client.on("error", () => {
|
|
1466
1816
|
clearTimeout(timeout);
|
|
1467
|
-
|
|
1817
|
+
resolve6(false);
|
|
1468
1818
|
});
|
|
1469
1819
|
});
|
|
1470
1820
|
}
|
|
1471
1821
|
sleep(ms) {
|
|
1472
|
-
return new Promise((
|
|
1822
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
1473
1823
|
}
|
|
1474
1824
|
getCachedDML(dmlJson) {
|
|
1475
1825
|
if (queryCache.has(dmlJson)) {
|
|
@@ -1489,7 +1839,7 @@ var QueryEngine = class {
|
|
|
1489
1839
|
throw new Error("Binary not initialized");
|
|
1490
1840
|
}
|
|
1491
1841
|
this.tcpPort = await this.findAvailablePort(this.tcpPort);
|
|
1492
|
-
return new Promise((
|
|
1842
|
+
return new Promise((resolve6, reject) => {
|
|
1493
1843
|
const serverArgs = [...this.arguments, "--action", "server", "--tcp-port", this.tcpPort.toString()];
|
|
1494
1844
|
const serverProcess = (0, import_child_process3.spawn)(this.binary["query_engine"], serverArgs);
|
|
1495
1845
|
let started = false;
|
|
@@ -1509,7 +1859,7 @@ var QueryEngine = class {
|
|
|
1509
1859
|
port: this.tcpPort,
|
|
1510
1860
|
process: serverProcess
|
|
1511
1861
|
});
|
|
1512
|
-
|
|
1862
|
+
resolve6();
|
|
1513
1863
|
}
|
|
1514
1864
|
}
|
|
1515
1865
|
});
|
|
@@ -1527,60 +1877,8 @@ var QueryEngine = class {
|
|
|
1527
1877
|
});
|
|
1528
1878
|
});
|
|
1529
1879
|
}
|
|
1530
|
-
async sendTcpRequestFast(command) {
|
|
1531
|
-
return new Promise((resolve5, reject) => {
|
|
1532
|
-
let queue = connectionQueues.get(this.connectionId);
|
|
1533
|
-
if (!queue) {
|
|
1534
|
-
queue = [];
|
|
1535
|
-
connectionQueues.set(this.connectionId, queue);
|
|
1536
|
-
}
|
|
1537
|
-
queue.push({ command, resolve: resolve5, reject });
|
|
1538
|
-
this.processQueue();
|
|
1539
|
-
});
|
|
1540
|
-
}
|
|
1541
|
-
async processQueue() {
|
|
1542
|
-
if (connectionProcessing.get(this.connectionId)) {
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
const queue = connectionQueues.get(this.connectionId);
|
|
1546
|
-
if (!queue || queue.length === 0) {
|
|
1547
|
-
return;
|
|
1548
|
-
}
|
|
1549
|
-
connectionProcessing.set(this.connectionId, true);
|
|
1550
|
-
try {
|
|
1551
|
-
const serverInfo = globalTcpServers.get(this.connectionId);
|
|
1552
|
-
if (!serverInfo) {
|
|
1553
|
-
throw new Error("Server not initialized");
|
|
1554
|
-
}
|
|
1555
|
-
while (queue.length > 0) {
|
|
1556
|
-
const request = queue.shift();
|
|
1557
|
-
if (!request) break;
|
|
1558
|
-
try {
|
|
1559
|
-
let connection = globalTcpConnections.get(this.connectionId);
|
|
1560
|
-
if (!connection || connection.destroyed || connection.readyState !== "open") {
|
|
1561
|
-
connection = await this.createNewConnection(serverInfo.port);
|
|
1562
|
-
globalTcpConnections.set(this.connectionId, connection);
|
|
1563
|
-
}
|
|
1564
|
-
const result = await this.executeOnConnection(connection, request.command);
|
|
1565
|
-
request.resolve(result);
|
|
1566
|
-
} catch (error) {
|
|
1567
|
-
const staleConnection = globalTcpConnections.get(this.connectionId);
|
|
1568
|
-
if (staleConnection) {
|
|
1569
|
-
try {
|
|
1570
|
-
staleConnection.destroy();
|
|
1571
|
-
} catch {
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
globalTcpConnections.delete(this.connectionId);
|
|
1575
|
-
request.reject(error);
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
} finally {
|
|
1579
|
-
connectionProcessing.set(this.connectionId, false);
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
1880
|
async createNewConnection(port) {
|
|
1583
|
-
return new Promise((
|
|
1881
|
+
return new Promise((resolve6, reject) => {
|
|
1584
1882
|
const client = new net2.Socket();
|
|
1585
1883
|
client.setNoDelay(true);
|
|
1586
1884
|
client.setKeepAlive(true, 6e4);
|
|
@@ -1590,7 +1888,7 @@ var QueryEngine = class {
|
|
|
1590
1888
|
}, 5e3);
|
|
1591
1889
|
client.connect(port, "127.0.0.1", () => {
|
|
1592
1890
|
clearTimeout(timeout);
|
|
1593
|
-
|
|
1891
|
+
resolve6(client);
|
|
1594
1892
|
});
|
|
1595
1893
|
client.on("error", (error) => {
|
|
1596
1894
|
clearTimeout(timeout);
|
|
@@ -1599,7 +1897,7 @@ var QueryEngine = class {
|
|
|
1599
1897
|
});
|
|
1600
1898
|
}
|
|
1601
1899
|
async executeOnConnection(connection, command) {
|
|
1602
|
-
return new Promise((
|
|
1900
|
+
return new Promise((resolve6, reject) => {
|
|
1603
1901
|
let responseBuffer = "";
|
|
1604
1902
|
let isResolved = false;
|
|
1605
1903
|
const timeout = setTimeout(() => {
|
|
@@ -1612,15 +1910,19 @@ var QueryEngine = class {
|
|
|
1612
1910
|
}, this.timeout);
|
|
1613
1911
|
const onData = (data) => {
|
|
1614
1912
|
responseBuffer += data.toString();
|
|
1615
|
-
|
|
1616
|
-
|
|
1913
|
+
let idx;
|
|
1914
|
+
while ((idx = responseBuffer.indexOf("\n")) !== -1 && !isResolved) {
|
|
1915
|
+
const line = responseBuffer.slice(0, idx);
|
|
1916
|
+
responseBuffer = responseBuffer.slice(idx + 1);
|
|
1917
|
+
const marker = line.indexOf("PROCESS_RESPONSE:");
|
|
1918
|
+
if (marker === -1) continue;
|
|
1617
1919
|
isResolved = true;
|
|
1618
1920
|
clearTimeout(timeout);
|
|
1619
1921
|
connection.removeListener("data", onData);
|
|
1620
1922
|
connection.removeListener("error", onError);
|
|
1621
1923
|
try {
|
|
1622
|
-
const response = JSON.parse(
|
|
1623
|
-
|
|
1924
|
+
const response = JSON.parse(line.slice(marker + "PROCESS_RESPONSE:".length));
|
|
1925
|
+
resolve6({
|
|
1624
1926
|
status: response.status,
|
|
1625
1927
|
message: response.message,
|
|
1626
1928
|
data: response.data
|
|
@@ -1649,7 +1951,7 @@ var QueryEngine = class {
|
|
|
1649
1951
|
if (!this.binary) {
|
|
1650
1952
|
throw new Error("Binary not initialized");
|
|
1651
1953
|
}
|
|
1652
|
-
return new Promise((
|
|
1954
|
+
return new Promise((resolve6, reject) => {
|
|
1653
1955
|
const child = (0, import_child_process3.spawn)(this.binary[binary], [...this.arguments, ...args]);
|
|
1654
1956
|
let stdoutBuffer = "";
|
|
1655
1957
|
let stderrBuffer = "";
|
|
@@ -1665,15 +1967,18 @@ var QueryEngine = class {
|
|
|
1665
1967
|
if (!isResolved) {
|
|
1666
1968
|
isResolved = true;
|
|
1667
1969
|
clearTimeout(timeoutId);
|
|
1668
|
-
|
|
1970
|
+
resolve6(response);
|
|
1669
1971
|
}
|
|
1670
1972
|
};
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1973
|
+
const tryParseLines = (buffer) => {
|
|
1974
|
+
let idx;
|
|
1975
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
1976
|
+
const line = buffer.slice(0, idx);
|
|
1977
|
+
buffer = buffer.slice(idx + 1);
|
|
1978
|
+
const marker = line.indexOf("PROCESS_RESPONSE:");
|
|
1979
|
+
if (marker === -1) continue;
|
|
1675
1980
|
try {
|
|
1676
|
-
const response = JSON.parse(
|
|
1981
|
+
const response = JSON.parse(line.slice(marker + "PROCESS_RESPONSE:".length));
|
|
1677
1982
|
resolveOnce({
|
|
1678
1983
|
status: response.status,
|
|
1679
1984
|
message: response.message,
|
|
@@ -1687,26 +1992,13 @@ var QueryEngine = class {
|
|
|
1687
1992
|
});
|
|
1688
1993
|
}
|
|
1689
1994
|
}
|
|
1995
|
+
return buffer;
|
|
1996
|
+
};
|
|
1997
|
+
child.stdout.on("data", (data) => {
|
|
1998
|
+
stdoutBuffer = tryParseLines(stdoutBuffer + data.toString());
|
|
1690
1999
|
});
|
|
1691
2000
|
child.stderr.on("data", (data) => {
|
|
1692
|
-
stderrBuffer
|
|
1693
|
-
const match = stderrBuffer.match(/PROCESS_RESPONSE:(\{.*\})/);
|
|
1694
|
-
if (match) {
|
|
1695
|
-
try {
|
|
1696
|
-
const response = JSON.parse(match[1]);
|
|
1697
|
-
resolveOnce({
|
|
1698
|
-
status: response.status,
|
|
1699
|
-
message: response.message,
|
|
1700
|
-
data: response.data
|
|
1701
|
-
});
|
|
1702
|
-
} catch (error) {
|
|
1703
|
-
resolveOnce({
|
|
1704
|
-
status: 500,
|
|
1705
|
-
message: "Failed to parse response JSON",
|
|
1706
|
-
data: null
|
|
1707
|
-
});
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
2001
|
+
stderrBuffer = tryParseLines(stderrBuffer + data.toString());
|
|
1710
2002
|
});
|
|
1711
2003
|
child.on("close", (code) => {
|
|
1712
2004
|
clearTimeout(timeoutId);
|
|
@@ -1732,11 +2024,16 @@ var QueryEngine = class {
|
|
|
1732
2024
|
});
|
|
1733
2025
|
}
|
|
1734
2026
|
async disconnect() {
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
2027
|
+
await EmbeddedEngine.disconnect(this.connectionId);
|
|
2028
|
+
const pool = socketPools.get(this.connectionId);
|
|
2029
|
+
if (pool) {
|
|
2030
|
+
for (const s of pool.free) {
|
|
2031
|
+
try {
|
|
2032
|
+
s.destroy();
|
|
2033
|
+
} catch {
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
socketPools.delete(this.connectionId);
|
|
1740
2037
|
}
|
|
1741
2038
|
const serverInfo = globalTcpServers.get(this.connectionId);
|
|
1742
2039
|
if (serverInfo && serverInfo.process && !serverInfo.process.killed) {
|
|
@@ -1758,8 +2055,8 @@ var QueryEngine = class {
|
|
|
1758
2055
|
|
|
1759
2056
|
// src/lib/SqliteExecutor.ts
|
|
1760
2057
|
var import_child_process4 = require("child_process");
|
|
1761
|
-
var
|
|
1762
|
-
var
|
|
2058
|
+
var path7 = __toESM(require("path"));
|
|
2059
|
+
var fs5 = __toESM(require("fs"));
|
|
1763
2060
|
var import_util = require("util");
|
|
1764
2061
|
var import_module3 = require("module");
|
|
1765
2062
|
var import_url3 = require("url");
|
|
@@ -1775,13 +2072,13 @@ var SqliteExecutor = class {
|
|
|
1775
2072
|
}
|
|
1776
2073
|
findVersionedBinary(binDir, platform2) {
|
|
1777
2074
|
try {
|
|
1778
|
-
const files =
|
|
2075
|
+
const files = fs5.readdirSync(binDir);
|
|
1779
2076
|
const extension = platform2 === "win32" ? ".exe" : "";
|
|
1780
2077
|
const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
|
|
1781
2078
|
const pattern = new RegExp(`^sqlite-engine-v\\d+\\.\\d+\\.\\d+-${platformName}-x64${extension.replace(".", "\\.")}$`);
|
|
1782
2079
|
const matchingFile = files.find((f) => pattern.test(f));
|
|
1783
2080
|
if (matchingFile) {
|
|
1784
|
-
return
|
|
2081
|
+
return path7.join(binDir, matchingFile);
|
|
1785
2082
|
}
|
|
1786
2083
|
} catch (error) {
|
|
1787
2084
|
}
|
|
@@ -1791,34 +2088,34 @@ var SqliteExecutor = class {
|
|
|
1791
2088
|
const __filename2 = typeof import_meta3 !== "undefined" && import_meta3.url ? (0, import_url3.fileURLToPath)(import_meta3.url) : "";
|
|
1792
2089
|
const __dirname = __filename2 ? (0, import_path6.dirname)(__filename2) : process.cwd();
|
|
1793
2090
|
const possibleDirs = [
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
2091
|
+
path7.resolve(process.cwd(), ".dbcube", "bin"),
|
|
2092
|
+
path7.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
|
|
2093
|
+
path7.resolve(__dirname, "..", "bin")
|
|
1797
2094
|
];
|
|
1798
2095
|
const platform2 = process.platform;
|
|
1799
2096
|
const extension = platform2 === "win32" ? ".exe" : "";
|
|
1800
2097
|
const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
|
|
1801
2098
|
for (const dir of possibleDirs) {
|
|
1802
2099
|
const versionedPath = this.findVersionedBinary(dir, platform2);
|
|
1803
|
-
if (versionedPath &&
|
|
2100
|
+
if (versionedPath && fs5.existsSync(versionedPath)) {
|
|
1804
2101
|
return versionedPath;
|
|
1805
2102
|
}
|
|
1806
2103
|
}
|
|
1807
2104
|
const binaryName = `sqlite-engine-${platformName}-x64${extension}`;
|
|
1808
2105
|
for (const dir of possibleDirs) {
|
|
1809
|
-
const fullPath =
|
|
1810
|
-
if (
|
|
2106
|
+
const fullPath = path7.join(dir, binaryName);
|
|
2107
|
+
if (fs5.existsSync(fullPath)) {
|
|
1811
2108
|
return fullPath;
|
|
1812
2109
|
}
|
|
1813
2110
|
}
|
|
1814
2111
|
const fallbackName = `sqlite-engine${extension}`;
|
|
1815
2112
|
for (const dir of possibleDirs) {
|
|
1816
|
-
const fullPath =
|
|
1817
|
-
if (
|
|
2113
|
+
const fullPath = path7.join(dir, fallbackName);
|
|
2114
|
+
if (fs5.existsSync(fullPath)) {
|
|
1818
2115
|
return fullPath;
|
|
1819
2116
|
}
|
|
1820
2117
|
}
|
|
1821
|
-
return
|
|
2118
|
+
return path7.join(possibleDirs[0], binaryName);
|
|
1822
2119
|
}
|
|
1823
2120
|
async executeBinary(args) {
|
|
1824
2121
|
const escapedArgs = args.map((arg) => {
|
|
@@ -1969,9 +2266,9 @@ var SqliteExecutor = class {
|
|
|
1969
2266
|
};
|
|
1970
2267
|
|
|
1971
2268
|
// src/lib/DbConfig.ts
|
|
1972
|
-
var
|
|
2269
|
+
var path8 = __toESM(require("path"));
|
|
1973
2270
|
var import_fs2 = __toESM(require("fs"));
|
|
1974
|
-
var rootPath =
|
|
2271
|
+
var rootPath = path8.resolve(process.cwd(), ".dbcube");
|
|
1975
2272
|
var SQLite = class {
|
|
1976
2273
|
executor = null;
|
|
1977
2274
|
database;
|
|
@@ -1981,7 +2278,7 @@ var SQLite = class {
|
|
|
1981
2278
|
async ifExist() {
|
|
1982
2279
|
if (this.database) {
|
|
1983
2280
|
const dbPath = this.database || ":memory:";
|
|
1984
|
-
const configPath =
|
|
2281
|
+
const configPath = path8.join(rootPath, dbPath + ".db");
|
|
1985
2282
|
if (!import_fs2.default.existsSync(rootPath)) {
|
|
1986
2283
|
import_fs2.default.mkdirSync(rootPath, { recursive: true });
|
|
1987
2284
|
}
|
|
@@ -1996,11 +2293,11 @@ var SQLite = class {
|
|
|
1996
2293
|
return false;
|
|
1997
2294
|
}
|
|
1998
2295
|
async connect() {
|
|
1999
|
-
return new Promise(async (
|
|
2296
|
+
return new Promise(async (resolve6, reject) => {
|
|
2000
2297
|
try {
|
|
2001
2298
|
if (!this.executor) {
|
|
2002
2299
|
const dbPath = this.database || ":memory:";
|
|
2003
|
-
const configPath =
|
|
2300
|
+
const configPath = path8.join(rootPath, dbPath + ".db");
|
|
2004
2301
|
if (!import_fs2.default.existsSync(rootPath)) {
|
|
2005
2302
|
import_fs2.default.mkdirSync(rootPath, { recursive: true });
|
|
2006
2303
|
}
|
|
@@ -2010,22 +2307,22 @@ var SQLite = class {
|
|
|
2010
2307
|
throw new Error("Failed to connect to SQLite database");
|
|
2011
2308
|
}
|
|
2012
2309
|
}
|
|
2013
|
-
|
|
2310
|
+
resolve6(this.executor);
|
|
2014
2311
|
} catch (error) {
|
|
2015
2312
|
reject(error);
|
|
2016
2313
|
}
|
|
2017
2314
|
});
|
|
2018
2315
|
}
|
|
2019
2316
|
async disconnect() {
|
|
2020
|
-
return new Promise((
|
|
2317
|
+
return new Promise((resolve6) => {
|
|
2021
2318
|
if (this.executor) {
|
|
2022
2319
|
this.executor = null;
|
|
2023
2320
|
}
|
|
2024
|
-
|
|
2321
|
+
resolve6();
|
|
2025
2322
|
});
|
|
2026
2323
|
}
|
|
2027
2324
|
async query(sqlQuery) {
|
|
2028
|
-
return new Promise(async (
|
|
2325
|
+
return new Promise(async (resolve6) => {
|
|
2029
2326
|
try {
|
|
2030
2327
|
if (typeof sqlQuery !== "string") {
|
|
2031
2328
|
throw new Error("The SQL query must be a string.");
|
|
@@ -2038,20 +2335,20 @@ var SQLite = class {
|
|
|
2038
2335
|
}
|
|
2039
2336
|
const result = await this.executor.queryMultiple(sqlQuery);
|
|
2040
2337
|
if (result.status === "error") {
|
|
2041
|
-
|
|
2338
|
+
resolve6({
|
|
2042
2339
|
status: "error",
|
|
2043
2340
|
message: result.message,
|
|
2044
2341
|
data: null
|
|
2045
2342
|
});
|
|
2046
2343
|
} else {
|
|
2047
|
-
|
|
2344
|
+
resolve6({
|
|
2048
2345
|
status: "success",
|
|
2049
2346
|
message: "Query executed successfully",
|
|
2050
2347
|
data: result.data
|
|
2051
2348
|
});
|
|
2052
2349
|
}
|
|
2053
2350
|
} catch (error) {
|
|
2054
|
-
|
|
2351
|
+
resolve6({
|
|
2055
2352
|
status: "error",
|
|
2056
2353
|
message: error.message || "An error occurred while executing the query.",
|
|
2057
2354
|
data: null
|
|
@@ -2060,7 +2357,7 @@ var SQLite = class {
|
|
|
2060
2357
|
});
|
|
2061
2358
|
}
|
|
2062
2359
|
async queryWithParameters(sqlQuery, params = []) {
|
|
2063
|
-
return new Promise(async (
|
|
2360
|
+
return new Promise(async (resolve6) => {
|
|
2064
2361
|
try {
|
|
2065
2362
|
if (typeof sqlQuery !== "string") {
|
|
2066
2363
|
throw new Error("The SQL query must be a string.");
|
|
@@ -2076,13 +2373,13 @@ var SQLite = class {
|
|
|
2076
2373
|
}
|
|
2077
2374
|
const result = await this.executor.query(sqlQuery, params);
|
|
2078
2375
|
if (result.status === "error") {
|
|
2079
|
-
|
|
2376
|
+
resolve6({
|
|
2080
2377
|
status: "error",
|
|
2081
2378
|
message: result.message,
|
|
2082
2379
|
data: null
|
|
2083
2380
|
});
|
|
2084
2381
|
} else {
|
|
2085
|
-
|
|
2382
|
+
resolve6({
|
|
2086
2383
|
status: "success",
|
|
2087
2384
|
message: "Query executed successfully",
|
|
2088
2385
|
data: result.data
|
|
@@ -2090,7 +2387,7 @@ var SQLite = class {
|
|
|
2090
2387
|
}
|
|
2091
2388
|
} catch (error) {
|
|
2092
2389
|
console.log(error);
|
|
2093
|
-
|
|
2390
|
+
resolve6({
|
|
2094
2391
|
status: "error",
|
|
2095
2392
|
message: error.message || "An error occurred while executing the query.",
|
|
2096
2393
|
data: null
|
|
@@ -2184,8 +2481,8 @@ var DbConfig = new SQLite({ DATABASE: "config" });
|
|
|
2184
2481
|
var DbConfig_default = DbConfig;
|
|
2185
2482
|
|
|
2186
2483
|
// src/lib/FileLogger.ts
|
|
2187
|
-
var
|
|
2188
|
-
var
|
|
2484
|
+
var fs7 = __toESM(require("fs"));
|
|
2485
|
+
var path9 = __toESM(require("path"));
|
|
2189
2486
|
var import_events = require("events");
|
|
2190
2487
|
var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
2191
2488
|
static watchers = /* @__PURE__ */ new Map();
|
|
@@ -2200,9 +2497,9 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2200
2497
|
*/
|
|
2201
2498
|
static async write(filePath, message, level = "INFO", append = true) {
|
|
2202
2499
|
try {
|
|
2203
|
-
const dir =
|
|
2204
|
-
if (!
|
|
2205
|
-
|
|
2500
|
+
const dir = path9.dirname(filePath);
|
|
2501
|
+
if (!fs7.existsSync(dir)) {
|
|
2502
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
2206
2503
|
}
|
|
2207
2504
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2208
2505
|
const formattedMessage = `[${timestamp}] [${level}] ${message}
|
|
@@ -2212,9 +2509,9 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2212
2509
|
return true;
|
|
2213
2510
|
}
|
|
2214
2511
|
if (append) {
|
|
2215
|
-
await
|
|
2512
|
+
await fs7.promises.appendFile(filePath, formattedMessage, "utf8");
|
|
2216
2513
|
} else {
|
|
2217
|
-
await
|
|
2514
|
+
await fs7.promises.writeFile(filePath, formattedMessage, "utf8");
|
|
2218
2515
|
}
|
|
2219
2516
|
return true;
|
|
2220
2517
|
} catch (error) {
|
|
@@ -2239,12 +2536,12 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2239
2536
|
const buffer = _FileLogger.buffers.get(filePath);
|
|
2240
2537
|
if (buffer && buffer.length > 0) {
|
|
2241
2538
|
try {
|
|
2242
|
-
const dir =
|
|
2243
|
-
if (!
|
|
2244
|
-
|
|
2539
|
+
const dir = path9.dirname(filePath);
|
|
2540
|
+
if (!fs7.existsSync(dir)) {
|
|
2541
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
2245
2542
|
}
|
|
2246
2543
|
const content = buffer.join("");
|
|
2247
|
-
await
|
|
2544
|
+
await fs7.promises.appendFile(filePath, content, "utf8");
|
|
2248
2545
|
_FileLogger.buffers.delete(filePath);
|
|
2249
2546
|
return true;
|
|
2250
2547
|
} catch (error) {
|
|
@@ -2380,10 +2677,10 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2380
2677
|
// Si debe retornar como array de líneas
|
|
2381
2678
|
} = options;
|
|
2382
2679
|
try {
|
|
2383
|
-
if (!
|
|
2680
|
+
if (!fs7.existsSync(filePath)) {
|
|
2384
2681
|
return asArray ? [] : "";
|
|
2385
2682
|
}
|
|
2386
|
-
let content = await
|
|
2683
|
+
let content = await fs7.promises.readFile(filePath, "utf8");
|
|
2387
2684
|
if (asArray) {
|
|
2388
2685
|
let linesArray = content.split("\n").filter((line) => line.trim() !== "");
|
|
2389
2686
|
if (lines !== null) {
|
|
@@ -2426,15 +2723,15 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2426
2723
|
} = options;
|
|
2427
2724
|
let lastSize = 0;
|
|
2428
2725
|
let lastPosition = 0;
|
|
2429
|
-
if (
|
|
2430
|
-
const stats =
|
|
2726
|
+
if (fs7.existsSync(filePath)) {
|
|
2727
|
+
const stats = fs7.statSync(filePath);
|
|
2431
2728
|
lastSize = stats.size;
|
|
2432
2729
|
lastPosition = fromEnd ? stats.size : 0;
|
|
2433
2730
|
}
|
|
2434
2731
|
const listener = async (curr, prev) => {
|
|
2435
2732
|
try {
|
|
2436
2733
|
if (curr.size > lastSize) {
|
|
2437
|
-
const stream =
|
|
2734
|
+
const stream = fs7.createReadStream(filePath, {
|
|
2438
2735
|
start: lastPosition,
|
|
2439
2736
|
end: curr.size - 1,
|
|
2440
2737
|
encoding: "utf8"
|
|
@@ -2462,7 +2759,7 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2462
2759
|
console.error("Error en watcher:", error);
|
|
2463
2760
|
}
|
|
2464
2761
|
};
|
|
2465
|
-
|
|
2762
|
+
fs7.watchFile(filePath, { persistent, interval }, listener);
|
|
2466
2763
|
const watcherId = `${filePath}_${Date.now()}`;
|
|
2467
2764
|
_FileLogger.watchers.set(watcherId, listener);
|
|
2468
2765
|
return {
|
|
@@ -2470,7 +2767,7 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2470
2767
|
stop: () => {
|
|
2471
2768
|
const storedListener = _FileLogger.watchers.get(watcherId);
|
|
2472
2769
|
if (storedListener) {
|
|
2473
|
-
|
|
2770
|
+
fs7.unwatchFile(filePath, storedListener);
|
|
2474
2771
|
_FileLogger.watchers.delete(watcherId);
|
|
2475
2772
|
}
|
|
2476
2773
|
},
|
|
@@ -2483,7 +2780,7 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2483
2780
|
static stopAllWatchers() {
|
|
2484
2781
|
for (const [watcherId] of _FileLogger.watchers) {
|
|
2485
2782
|
const filePath = watcherId.split("_")[0];
|
|
2486
|
-
|
|
2783
|
+
fs7.unwatchFile(filePath);
|
|
2487
2784
|
}
|
|
2488
2785
|
_FileLogger.watchers.clear();
|
|
2489
2786
|
}
|
|
@@ -2513,7 +2810,7 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2513
2810
|
if (lines.length > maxLines) {
|
|
2514
2811
|
const keepLines = lines.slice(-maxLines);
|
|
2515
2812
|
const content = keepLines.join("\n") + "\n";
|
|
2516
|
-
await
|
|
2813
|
+
await fs7.promises.writeFile(filePath, content, "utf8");
|
|
2517
2814
|
return lines.length - maxLines;
|
|
2518
2815
|
}
|
|
2519
2816
|
return 0;
|
|
@@ -2528,8 +2825,8 @@ var FileLogger = class _FileLogger extends import_events.EventEmitter {
|
|
|
2528
2825
|
*/
|
|
2529
2826
|
static async deleteLogFile(filePath) {
|
|
2530
2827
|
try {
|
|
2531
|
-
if (
|
|
2532
|
-
await
|
|
2828
|
+
if (fs7.existsSync(filePath)) {
|
|
2829
|
+
await fs7.promises.unlink(filePath);
|
|
2533
2830
|
return true;
|
|
2534
2831
|
}
|
|
2535
2832
|
return false;
|