@liorandb/core 1.0.17 → 1.0.19
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.ts +118 -32
- package/dist/index.js +475 -207
- package/package.json +1 -1
- package/src/core/checkpoint.ts +111 -0
- package/src/core/collection.ts +150 -107
- package/src/core/compaction.ts +79 -32
- package/src/core/database.ts +98 -84
- package/src/core/wal.ts +213 -0
- package/src/types/index.ts +107 -34
package/dist/index.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
// src/LioranManager.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import path9 from "path";
|
|
3
|
+
import fs9 from "fs";
|
|
4
4
|
import process2 from "process";
|
|
5
5
|
|
|
6
6
|
// src/core/database.ts
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import { execFile } from "child_process";
|
|
10
|
-
import { promisify } from "util";
|
|
7
|
+
import path6 from "path";
|
|
8
|
+
import fs6 from "fs";
|
|
11
9
|
|
|
12
10
|
// src/core/collection.ts
|
|
13
11
|
import { ClassicLevel as ClassicLevel3 } from "classic-level";
|
|
14
12
|
|
|
15
13
|
// src/core/query.ts
|
|
16
|
-
function getByPath(obj,
|
|
17
|
-
return
|
|
14
|
+
function getByPath(obj, path10) {
|
|
15
|
+
return path10.split(".").reduce((o, p) => o ? o[p] : void 0, obj);
|
|
18
16
|
}
|
|
19
17
|
function matchDocument(doc, query) {
|
|
20
18
|
for (const key of Object.keys(query)) {
|
|
@@ -295,12 +293,13 @@ var Index = class {
|
|
|
295
293
|
|
|
296
294
|
// src/core/compaction.ts
|
|
297
295
|
var TMP_SUFFIX = "__compact_tmp";
|
|
298
|
-
var OLD_SUFFIX = "
|
|
296
|
+
var OLD_SUFFIX = "__compact_old";
|
|
297
|
+
var INDEX_DIR = "__indexes";
|
|
299
298
|
async function compactCollectionEngine(col) {
|
|
300
|
-
await crashRecovery(col.dir);
|
|
301
299
|
const baseDir = col.dir;
|
|
302
300
|
const tmpDir = baseDir + TMP_SUFFIX;
|
|
303
301
|
const oldDir = baseDir + OLD_SUFFIX;
|
|
302
|
+
await crashRecovery(baseDir);
|
|
304
303
|
safeRemove(tmpDir);
|
|
305
304
|
safeRemove(oldDir);
|
|
306
305
|
await snapshotRebuild(col, tmpDir);
|
|
@@ -309,9 +308,13 @@ async function compactCollectionEngine(col) {
|
|
|
309
308
|
}
|
|
310
309
|
async function snapshotRebuild(col, tmpDir) {
|
|
311
310
|
fs2.mkdirSync(tmpDir, { recursive: true });
|
|
312
|
-
const tmpDB = new ClassicLevel2(tmpDir, {
|
|
311
|
+
const tmpDB = new ClassicLevel2(tmpDir, {
|
|
312
|
+
valueEncoding: "utf8"
|
|
313
|
+
});
|
|
313
314
|
for await (const [key, val] of col.db.iterator()) {
|
|
314
|
-
|
|
315
|
+
if (val !== void 0) {
|
|
316
|
+
await tmpDB.put(key, val);
|
|
317
|
+
}
|
|
315
318
|
}
|
|
316
319
|
await tmpDB.close();
|
|
317
320
|
await col.db.close();
|
|
@@ -323,38 +326,44 @@ function atomicSwap(base, tmp, old) {
|
|
|
323
326
|
async function crashRecovery(baseDir) {
|
|
324
327
|
const tmp = baseDir + TMP_SUFFIX;
|
|
325
328
|
const old = baseDir + OLD_SUFFIX;
|
|
326
|
-
|
|
329
|
+
const baseExists = fs2.existsSync(baseDir);
|
|
330
|
+
const tmpExists = fs2.existsSync(tmp);
|
|
331
|
+
const oldExists = fs2.existsSync(old);
|
|
332
|
+
if (tmpExists && oldExists) {
|
|
327
333
|
safeRemove(baseDir);
|
|
328
334
|
fs2.renameSync(tmp, baseDir);
|
|
329
335
|
safeRemove(old);
|
|
336
|
+
return;
|
|
330
337
|
}
|
|
331
|
-
if (
|
|
338
|
+
if (!baseExists && oldExists) {
|
|
332
339
|
fs2.renameSync(old, baseDir);
|
|
340
|
+
return;
|
|
333
341
|
}
|
|
334
|
-
if (
|
|
342
|
+
if (tmpExists && !oldExists) {
|
|
335
343
|
safeRemove(tmp);
|
|
336
344
|
}
|
|
337
345
|
}
|
|
338
346
|
async function rebuildIndexes(col) {
|
|
339
|
-
const indexRoot = path2.join(col.dir,
|
|
340
|
-
safeRemove(indexRoot);
|
|
341
|
-
fs2.mkdirSync(indexRoot, { recursive: true });
|
|
347
|
+
const indexRoot = path2.join(col.dir, INDEX_DIR);
|
|
342
348
|
for (const idx of col["indexes"].values()) {
|
|
343
349
|
try {
|
|
344
350
|
await idx.close();
|
|
345
351
|
} catch {
|
|
346
352
|
}
|
|
347
353
|
}
|
|
354
|
+
safeRemove(indexRoot);
|
|
355
|
+
fs2.mkdirSync(indexRoot, { recursive: true });
|
|
348
356
|
const newIndexes = /* @__PURE__ */ new Map();
|
|
349
357
|
for (const idx of col["indexes"].values()) {
|
|
350
|
-
const
|
|
358
|
+
const rebuilt = new Index(col.dir, idx.field, {
|
|
351
359
|
unique: idx.unique
|
|
352
360
|
});
|
|
353
361
|
for await (const [, enc] of col.db.iterator()) {
|
|
362
|
+
if (!enc) continue;
|
|
354
363
|
const doc = decryptData(enc);
|
|
355
|
-
await
|
|
364
|
+
await rebuilt.insert(doc);
|
|
356
365
|
}
|
|
357
|
-
newIndexes.set(idx.field,
|
|
366
|
+
newIndexes.set(idx.field, rebuilt);
|
|
358
367
|
}
|
|
359
368
|
col["indexes"] = newIndexes;
|
|
360
369
|
}
|
|
@@ -370,26 +379,43 @@ var Collection = class {
|
|
|
370
379
|
db;
|
|
371
380
|
queue = Promise.resolve();
|
|
372
381
|
schema;
|
|
382
|
+
schemaVersion = 1;
|
|
383
|
+
migrations = [];
|
|
373
384
|
indexes = /* @__PURE__ */ new Map();
|
|
374
|
-
constructor(dir, schema) {
|
|
385
|
+
constructor(dir, schema, schemaVersion = 1) {
|
|
375
386
|
this.dir = dir;
|
|
376
387
|
this.db = new ClassicLevel3(dir, { valueEncoding: "utf8" });
|
|
377
388
|
this.schema = schema;
|
|
389
|
+
this.schemaVersion = schemaVersion;
|
|
378
390
|
}
|
|
379
|
-
/*
|
|
380
|
-
|
|
381
|
-
this.indexes.set(index.field, index);
|
|
382
|
-
}
|
|
383
|
-
getIndex(field) {
|
|
384
|
-
return this.indexes.get(field);
|
|
385
|
-
}
|
|
386
|
-
/* -------------------------- CORE -------------------------- */
|
|
387
|
-
setSchema(schema) {
|
|
391
|
+
/* ===================== SCHEMA ===================== */
|
|
392
|
+
setSchema(schema, version) {
|
|
388
393
|
this.schema = schema;
|
|
394
|
+
this.schemaVersion = version;
|
|
395
|
+
}
|
|
396
|
+
addMigration(migration) {
|
|
397
|
+
this.migrations.push(migration);
|
|
398
|
+
this.migrations.sort((a, b) => a.from - b.from);
|
|
389
399
|
}
|
|
390
400
|
validate(doc) {
|
|
391
401
|
return this.schema ? validateSchema(this.schema, doc) : doc;
|
|
392
402
|
}
|
|
403
|
+
migrateIfNeeded(doc) {
|
|
404
|
+
let currentVersion = doc.__v ?? 1;
|
|
405
|
+
if (currentVersion === this.schemaVersion) {
|
|
406
|
+
return doc;
|
|
407
|
+
}
|
|
408
|
+
let working = doc;
|
|
409
|
+
for (const migration of this.migrations) {
|
|
410
|
+
if (migration.from === currentVersion) {
|
|
411
|
+
working = migration.migrate(working);
|
|
412
|
+
currentVersion = migration.to;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
working.__v = this.schemaVersion;
|
|
416
|
+
return this.validate(working);
|
|
417
|
+
}
|
|
418
|
+
/* ===================== QUEUE ===================== */
|
|
393
419
|
_enqueue(task) {
|
|
394
420
|
this.queue = this.queue.then(task).catch(console.error);
|
|
395
421
|
return this.queue;
|
|
@@ -406,7 +432,19 @@ var Collection = class {
|
|
|
406
432
|
} catch {
|
|
407
433
|
}
|
|
408
434
|
}
|
|
409
|
-
/*
|
|
435
|
+
/* ===================== INDEX MANAGEMENT ===================== */
|
|
436
|
+
registerIndex(index) {
|
|
437
|
+
this.indexes.set(index.field, index);
|
|
438
|
+
}
|
|
439
|
+
getIndex(field) {
|
|
440
|
+
return this.indexes.get(field);
|
|
441
|
+
}
|
|
442
|
+
async _updateIndexes(oldDoc, newDoc) {
|
|
443
|
+
for (const index of this.indexes.values()) {
|
|
444
|
+
await index.update(oldDoc, newDoc);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/* ===================== COMPACTION ===================== */
|
|
410
448
|
async compact() {
|
|
411
449
|
return this._enqueue(async () => {
|
|
412
450
|
try {
|
|
@@ -418,6 +456,7 @@ var Collection = class {
|
|
|
418
456
|
await rebuildIndexes(this);
|
|
419
457
|
});
|
|
420
458
|
}
|
|
459
|
+
/* ===================== INTERNAL EXEC ===================== */
|
|
421
460
|
async _exec(op, args) {
|
|
422
461
|
switch (op) {
|
|
423
462
|
case "insertOne":
|
|
@@ -442,16 +481,14 @@ var Collection = class {
|
|
|
442
481
|
throw new Error(`Unknown operation: ${op}`);
|
|
443
482
|
}
|
|
444
483
|
}
|
|
445
|
-
/*
|
|
446
|
-
async _updateIndexes(oldDoc, newDoc) {
|
|
447
|
-
for (const index of this.indexes.values()) {
|
|
448
|
-
await index.update(oldDoc, newDoc);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
/* ---------------- Storage ---------------- */
|
|
484
|
+
/* ===================== STORAGE ===================== */
|
|
452
485
|
async _insertOne(doc) {
|
|
453
486
|
const _id = doc._id ?? uuid();
|
|
454
|
-
const final = this.validate({
|
|
487
|
+
const final = this.validate({
|
|
488
|
+
_id,
|
|
489
|
+
...doc,
|
|
490
|
+
__v: this.schemaVersion
|
|
491
|
+
});
|
|
455
492
|
await this.db.put(String(_id), encryptData(final));
|
|
456
493
|
await this._updateIndexes(null, final);
|
|
457
494
|
return final;
|
|
@@ -461,7 +498,11 @@ var Collection = class {
|
|
|
461
498
|
const out = [];
|
|
462
499
|
for (const d of docs) {
|
|
463
500
|
const _id = d._id ?? uuid();
|
|
464
|
-
const final = this.validate({
|
|
501
|
+
const final = this.validate({
|
|
502
|
+
_id,
|
|
503
|
+
...d,
|
|
504
|
+
__v: this.schemaVersion
|
|
505
|
+
});
|
|
465
506
|
batch.push({
|
|
466
507
|
type: "put",
|
|
467
508
|
key: String(_id),
|
|
@@ -475,7 +516,7 @@ var Collection = class {
|
|
|
475
516
|
}
|
|
476
517
|
return out;
|
|
477
518
|
}
|
|
478
|
-
/*
|
|
519
|
+
/* ===================== QUERY ===================== */
|
|
479
520
|
async _getCandidateIds(query) {
|
|
480
521
|
const indexedFields = new Set(this.indexes.keys());
|
|
481
522
|
return runIndexedQuery(
|
|
@@ -497,15 +538,26 @@ var Collection = class {
|
|
|
497
538
|
}
|
|
498
539
|
);
|
|
499
540
|
}
|
|
541
|
+
async _readAndMigrate(id) {
|
|
542
|
+
const enc = await this.db.get(id);
|
|
543
|
+
if (!enc) return null;
|
|
544
|
+
const raw = decryptData(enc);
|
|
545
|
+
const migrated = this.migrateIfNeeded(raw);
|
|
546
|
+
if (raw.__v !== this.schemaVersion) {
|
|
547
|
+
await this.db.put(id, encryptData(migrated));
|
|
548
|
+
await this._updateIndexes(raw, migrated);
|
|
549
|
+
}
|
|
550
|
+
return migrated;
|
|
551
|
+
}
|
|
500
552
|
async _find(query) {
|
|
501
553
|
const ids = await this._getCandidateIds(query);
|
|
502
554
|
const out = [];
|
|
503
555
|
for (const id of ids) {
|
|
504
556
|
try {
|
|
505
|
-
const
|
|
506
|
-
if (
|
|
507
|
-
|
|
508
|
-
|
|
557
|
+
const doc = await this._readAndMigrate(id);
|
|
558
|
+
if (doc && matchDocument(doc, query)) {
|
|
559
|
+
out.push(doc);
|
|
560
|
+
}
|
|
509
561
|
} catch {
|
|
510
562
|
}
|
|
511
563
|
}
|
|
@@ -514,8 +566,7 @@ var Collection = class {
|
|
|
514
566
|
async _findOne(query) {
|
|
515
567
|
if (query?._id) {
|
|
516
568
|
try {
|
|
517
|
-
|
|
518
|
-
return enc ? decryptData(enc) : null;
|
|
569
|
+
return await this._readAndMigrate(String(query._id));
|
|
519
570
|
} catch {
|
|
520
571
|
return null;
|
|
521
572
|
}
|
|
@@ -523,10 +574,10 @@ var Collection = class {
|
|
|
523
574
|
const ids = await this._getCandidateIds(query);
|
|
524
575
|
for (const id of ids) {
|
|
525
576
|
try {
|
|
526
|
-
const
|
|
527
|
-
if (
|
|
528
|
-
|
|
529
|
-
|
|
577
|
+
const doc = await this._readAndMigrate(id);
|
|
578
|
+
if (doc && matchDocument(doc, query)) {
|
|
579
|
+
return doc;
|
|
580
|
+
}
|
|
530
581
|
} catch {
|
|
531
582
|
}
|
|
532
583
|
}
|
|
@@ -537,40 +588,34 @@ var Collection = class {
|
|
|
537
588
|
let count = 0;
|
|
538
589
|
for (const id of ids) {
|
|
539
590
|
try {
|
|
540
|
-
const
|
|
541
|
-
if (
|
|
542
|
-
|
|
591
|
+
const doc = await this._readAndMigrate(id);
|
|
592
|
+
if (doc && matchDocument(doc, filter)) {
|
|
593
|
+
count++;
|
|
594
|
+
}
|
|
543
595
|
} catch {
|
|
544
596
|
}
|
|
545
597
|
}
|
|
546
598
|
return count;
|
|
547
599
|
}
|
|
548
|
-
/*
|
|
600
|
+
/* ===================== UPDATE ===================== */
|
|
549
601
|
async _updateOne(filter, update, options) {
|
|
550
602
|
const ids = await this._getCandidateIds(filter);
|
|
551
603
|
for (const id of ids) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
} catch {
|
|
604
|
+
const existing = await this._readAndMigrate(id);
|
|
605
|
+
if (!existing) continue;
|
|
606
|
+
if (matchDocument(existing, filter)) {
|
|
607
|
+
const updated = this.validate({
|
|
608
|
+
...applyUpdate(existing, update),
|
|
609
|
+
_id: existing._id,
|
|
610
|
+
__v: this.schemaVersion
|
|
611
|
+
});
|
|
612
|
+
await this.db.put(id, encryptData(updated));
|
|
613
|
+
await this._updateIndexes(existing, updated);
|
|
614
|
+
return updated;
|
|
564
615
|
}
|
|
565
616
|
}
|
|
566
617
|
if (options?.upsert) {
|
|
567
|
-
|
|
568
|
-
_id: uuid(),
|
|
569
|
-
...applyUpdate({}, update)
|
|
570
|
-
});
|
|
571
|
-
await this.db.put(String(doc._id), encryptData(doc));
|
|
572
|
-
await this._updateIndexes(null, doc);
|
|
573
|
-
return doc;
|
|
618
|
+
return this._insertOne(applyUpdate({}, update));
|
|
574
619
|
}
|
|
575
620
|
return null;
|
|
576
621
|
}
|
|
@@ -578,36 +623,31 @@ var Collection = class {
|
|
|
578
623
|
const ids = await this._getCandidateIds(filter);
|
|
579
624
|
const out = [];
|
|
580
625
|
for (const id of ids) {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
} catch {
|
|
626
|
+
const existing = await this._readAndMigrate(id);
|
|
627
|
+
if (!existing) continue;
|
|
628
|
+
if (matchDocument(existing, filter)) {
|
|
629
|
+
const updated = this.validate({
|
|
630
|
+
...applyUpdate(existing, update),
|
|
631
|
+
_id: existing._id,
|
|
632
|
+
__v: this.schemaVersion
|
|
633
|
+
});
|
|
634
|
+
await this.db.put(id, encryptData(updated));
|
|
635
|
+
await this._updateIndexes(existing, updated);
|
|
636
|
+
out.push(updated);
|
|
593
637
|
}
|
|
594
638
|
}
|
|
595
639
|
return out;
|
|
596
640
|
}
|
|
597
|
-
/*
|
|
641
|
+
/* ===================== DELETE ===================== */
|
|
598
642
|
async _deleteOne(filter) {
|
|
599
643
|
const ids = await this._getCandidateIds(filter);
|
|
600
644
|
for (const id of ids) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
await this._updateIndexes(value, null);
|
|
608
|
-
return true;
|
|
609
|
-
}
|
|
610
|
-
} catch {
|
|
645
|
+
const existing = await this._readAndMigrate(id);
|
|
646
|
+
if (!existing) continue;
|
|
647
|
+
if (matchDocument(existing, filter)) {
|
|
648
|
+
await this.db.del(id);
|
|
649
|
+
await this._updateIndexes(existing, null);
|
|
650
|
+
return true;
|
|
611
651
|
}
|
|
612
652
|
}
|
|
613
653
|
return false;
|
|
@@ -616,21 +656,17 @@ var Collection = class {
|
|
|
616
656
|
const ids = await this._getCandidateIds(filter);
|
|
617
657
|
let count = 0;
|
|
618
658
|
for (const id of ids) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
await this._updateIndexes(value, null);
|
|
626
|
-
count++;
|
|
627
|
-
}
|
|
628
|
-
} catch {
|
|
659
|
+
const existing = await this._readAndMigrate(id);
|
|
660
|
+
if (!existing) continue;
|
|
661
|
+
if (matchDocument(existing, filter)) {
|
|
662
|
+
await this.db.del(id);
|
|
663
|
+
await this._updateIndexes(existing, null);
|
|
664
|
+
count++;
|
|
629
665
|
}
|
|
630
666
|
}
|
|
631
667
|
return count;
|
|
632
668
|
}
|
|
633
|
-
/*
|
|
669
|
+
/* ===================== PUBLIC API ===================== */
|
|
634
670
|
insertOne(doc) {
|
|
635
671
|
return this._enqueue(() => this._exec("insertOne", [doc]));
|
|
636
672
|
}
|
|
@@ -793,10 +829,230 @@ var MigrationEngine = class {
|
|
|
793
829
|
}
|
|
794
830
|
};
|
|
795
831
|
|
|
832
|
+
// src/core/wal.ts
|
|
833
|
+
import fs4 from "fs";
|
|
834
|
+
import path4 from "path";
|
|
835
|
+
var MAX_WAL_SIZE = 16 * 1024 * 1024;
|
|
836
|
+
var WAL_DIR = "__wal";
|
|
837
|
+
var CRC32_TABLE = (() => {
|
|
838
|
+
const table = new Uint32Array(256);
|
|
839
|
+
for (let i = 0; i < 256; i++) {
|
|
840
|
+
let c = i;
|
|
841
|
+
for (let k = 0; k < 8; k++) {
|
|
842
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
843
|
+
}
|
|
844
|
+
table[i] = c >>> 0;
|
|
845
|
+
}
|
|
846
|
+
return table;
|
|
847
|
+
})();
|
|
848
|
+
function crc32(input) {
|
|
849
|
+
let crc = 4294967295;
|
|
850
|
+
for (let i = 0; i < input.length; i++) {
|
|
851
|
+
const byte = input.charCodeAt(i);
|
|
852
|
+
crc = CRC32_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
853
|
+
}
|
|
854
|
+
return (crc ^ 4294967295) >>> 0;
|
|
855
|
+
}
|
|
856
|
+
var WALManager = class {
|
|
857
|
+
walDir;
|
|
858
|
+
currentGen = 1;
|
|
859
|
+
lsn = 0;
|
|
860
|
+
fd = null;
|
|
861
|
+
constructor(baseDir) {
|
|
862
|
+
this.walDir = path4.join(baseDir, WAL_DIR);
|
|
863
|
+
fs4.mkdirSync(this.walDir, { recursive: true });
|
|
864
|
+
this.currentGen = this.detectLastGeneration();
|
|
865
|
+
}
|
|
866
|
+
/* -------------------------
|
|
867
|
+
INTERNAL HELPERS
|
|
868
|
+
------------------------- */
|
|
869
|
+
walPath(gen = this.currentGen) {
|
|
870
|
+
return path4.join(
|
|
871
|
+
this.walDir,
|
|
872
|
+
`wal-${String(gen).padStart(6, "0")}.log`
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
detectLastGeneration() {
|
|
876
|
+
if (!fs4.existsSync(this.walDir)) return 1;
|
|
877
|
+
const files = fs4.readdirSync(this.walDir);
|
|
878
|
+
let max = 0;
|
|
879
|
+
for (const f of files) {
|
|
880
|
+
const m = f.match(/^wal-(\d+)\.log$/);
|
|
881
|
+
if (m) max = Math.max(max, Number(m[1]));
|
|
882
|
+
}
|
|
883
|
+
return max || 1;
|
|
884
|
+
}
|
|
885
|
+
async open() {
|
|
886
|
+
if (!this.fd) {
|
|
887
|
+
this.fd = await fs4.promises.open(this.walPath(), "a");
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async rotate() {
|
|
891
|
+
if (this.fd) {
|
|
892
|
+
await this.fd.close();
|
|
893
|
+
this.fd = null;
|
|
894
|
+
}
|
|
895
|
+
this.currentGen++;
|
|
896
|
+
}
|
|
897
|
+
/* -------------------------
|
|
898
|
+
APPEND
|
|
899
|
+
------------------------- */
|
|
900
|
+
async append(record) {
|
|
901
|
+
await this.open();
|
|
902
|
+
const full = {
|
|
903
|
+
...record,
|
|
904
|
+
lsn: ++this.lsn
|
|
905
|
+
};
|
|
906
|
+
const body = JSON.stringify(full);
|
|
907
|
+
const stored = {
|
|
908
|
+
...full,
|
|
909
|
+
crc: crc32(body)
|
|
910
|
+
};
|
|
911
|
+
await this.fd.write(JSON.stringify(stored) + "\n");
|
|
912
|
+
await this.fd.sync();
|
|
913
|
+
const stat = await this.fd.stat();
|
|
914
|
+
if (stat.size >= MAX_WAL_SIZE) {
|
|
915
|
+
await this.rotate();
|
|
916
|
+
}
|
|
917
|
+
return full.lsn;
|
|
918
|
+
}
|
|
919
|
+
/* -------------------------
|
|
920
|
+
REPLAY
|
|
921
|
+
------------------------- */
|
|
922
|
+
async replay(fromLSN, apply) {
|
|
923
|
+
if (!fs4.existsSync(this.walDir)) return;
|
|
924
|
+
const files = fs4.readdirSync(this.walDir).filter((f) => f.startsWith("wal-")).sort();
|
|
925
|
+
for (const file of files) {
|
|
926
|
+
const filePath = path4.join(this.walDir, file);
|
|
927
|
+
const data = fs4.readFileSync(filePath, "utf8");
|
|
928
|
+
const lines = data.split("\n");
|
|
929
|
+
for (let i = 0; i < lines.length; i++) {
|
|
930
|
+
const line = lines[i];
|
|
931
|
+
if (!line.trim()) continue;
|
|
932
|
+
let parsed;
|
|
933
|
+
try {
|
|
934
|
+
parsed = JSON.parse(line);
|
|
935
|
+
} catch {
|
|
936
|
+
console.error("WAL parse error, stopping replay");
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
const { crc, ...record } = parsed;
|
|
940
|
+
const expected = crc32(JSON.stringify(record));
|
|
941
|
+
if (expected !== crc) {
|
|
942
|
+
console.error(
|
|
943
|
+
"WAL checksum mismatch, stopping replay",
|
|
944
|
+
{ file, line: i + 1 }
|
|
945
|
+
);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (record.lsn <= fromLSN) continue;
|
|
949
|
+
this.lsn = Math.max(this.lsn, record.lsn);
|
|
950
|
+
await apply(record);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
/* -------------------------
|
|
955
|
+
CLEANUP
|
|
956
|
+
------------------------- */
|
|
957
|
+
async cleanup(beforeGen) {
|
|
958
|
+
if (!fs4.existsSync(this.walDir)) return;
|
|
959
|
+
const files = fs4.readdirSync(this.walDir);
|
|
960
|
+
for (const f of files) {
|
|
961
|
+
const m = f.match(/^wal-(\d+)\.log$/);
|
|
962
|
+
if (!m) continue;
|
|
963
|
+
const gen = Number(m[1]);
|
|
964
|
+
if (gen < beforeGen) {
|
|
965
|
+
fs4.unlinkSync(path4.join(this.walDir, f));
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
/* -------------------------
|
|
970
|
+
GETTERS
|
|
971
|
+
------------------------- */
|
|
972
|
+
getCurrentLSN() {
|
|
973
|
+
return this.lsn;
|
|
974
|
+
}
|
|
975
|
+
getCurrentGen() {
|
|
976
|
+
return this.currentGen;
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
// src/core/checkpoint.ts
|
|
981
|
+
import fs5 from "fs";
|
|
982
|
+
import path5 from "path";
|
|
983
|
+
var CHECKPOINT_FILE = "__checkpoint.json";
|
|
984
|
+
var TMP_SUFFIX2 = ".tmp";
|
|
985
|
+
var FORMAT_VERSION = 1;
|
|
986
|
+
var CheckpointManager = class {
|
|
987
|
+
filePath;
|
|
988
|
+
data;
|
|
989
|
+
constructor(baseDir) {
|
|
990
|
+
this.filePath = path5.join(baseDir, CHECKPOINT_FILE);
|
|
991
|
+
this.data = {
|
|
992
|
+
lsn: 0,
|
|
993
|
+
walGen: 1,
|
|
994
|
+
time: 0,
|
|
995
|
+
version: FORMAT_VERSION
|
|
996
|
+
};
|
|
997
|
+
this.load();
|
|
998
|
+
}
|
|
999
|
+
/* -------------------------
|
|
1000
|
+
LOAD (Crash-safe)
|
|
1001
|
+
------------------------- */
|
|
1002
|
+
load() {
|
|
1003
|
+
if (!fs5.existsSync(this.filePath)) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
try {
|
|
1007
|
+
const raw = fs5.readFileSync(this.filePath, "utf8");
|
|
1008
|
+
const parsed = JSON.parse(raw);
|
|
1009
|
+
if (typeof parsed.lsn === "number" && typeof parsed.walGen === "number") {
|
|
1010
|
+
this.data = parsed;
|
|
1011
|
+
}
|
|
1012
|
+
} catch {
|
|
1013
|
+
console.error("Checkpoint corrupted, starting from zero");
|
|
1014
|
+
this.data = {
|
|
1015
|
+
lsn: 0,
|
|
1016
|
+
walGen: 1,
|
|
1017
|
+
time: 0,
|
|
1018
|
+
version: FORMAT_VERSION
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
/* -------------------------
|
|
1023
|
+
SAVE (Atomic Write)
|
|
1024
|
+
------------------------- */
|
|
1025
|
+
save(lsn, walGen) {
|
|
1026
|
+
const newData = {
|
|
1027
|
+
lsn,
|
|
1028
|
+
walGen,
|
|
1029
|
+
time: Date.now(),
|
|
1030
|
+
version: FORMAT_VERSION
|
|
1031
|
+
};
|
|
1032
|
+
const tmpPath = this.filePath + TMP_SUFFIX2;
|
|
1033
|
+
try {
|
|
1034
|
+
fs5.writeFileSync(
|
|
1035
|
+
tmpPath,
|
|
1036
|
+
JSON.stringify(newData, null, 2),
|
|
1037
|
+
{ encoding: "utf8" }
|
|
1038
|
+
);
|
|
1039
|
+
fs5.renameSync(tmpPath, this.filePath);
|
|
1040
|
+
this.data = newData;
|
|
1041
|
+
} catch (err) {
|
|
1042
|
+
console.error("Failed to write checkpoint:", err);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
/* -------------------------
|
|
1046
|
+
GET CURRENT
|
|
1047
|
+
------------------------- */
|
|
1048
|
+
get() {
|
|
1049
|
+
return this.data;
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
|
|
796
1053
|
// src/core/database.ts
|
|
797
|
-
var exec = promisify(execFile);
|
|
798
1054
|
var META_FILE = "__db_meta.json";
|
|
799
|
-
var META_VERSION =
|
|
1055
|
+
var META_VERSION = 2;
|
|
800
1056
|
var DEFAULT_SCHEMA_VERSION = "v1";
|
|
801
1057
|
var DBTransactionContext = class {
|
|
802
1058
|
constructor(db, txId) {
|
|
@@ -819,11 +1075,26 @@ var DBTransactionContext = class {
|
|
|
819
1075
|
});
|
|
820
1076
|
}
|
|
821
1077
|
async commit() {
|
|
822
|
-
|
|
823
|
-
|
|
1078
|
+
for (const op of this.ops) {
|
|
1079
|
+
const recordOp = {
|
|
1080
|
+
tx: this.txId,
|
|
1081
|
+
type: "op",
|
|
1082
|
+
payload: op
|
|
1083
|
+
};
|
|
1084
|
+
await this.db.wal.append(recordOp);
|
|
1085
|
+
}
|
|
1086
|
+
const commitRecord = {
|
|
1087
|
+
tx: this.txId,
|
|
1088
|
+
type: "commit"
|
|
1089
|
+
};
|
|
1090
|
+
await this.db.wal.append(commitRecord);
|
|
824
1091
|
await this.db.applyTransaction(this.ops);
|
|
825
|
-
|
|
826
|
-
|
|
1092
|
+
const appliedRecord = {
|
|
1093
|
+
tx: this.txId,
|
|
1094
|
+
type: "applied"
|
|
1095
|
+
};
|
|
1096
|
+
await this.db.wal.append(appliedRecord);
|
|
1097
|
+
await this.db.postCommitMaintenance();
|
|
827
1098
|
}
|
|
828
1099
|
};
|
|
829
1100
|
var LioranDB = class _LioranDB {
|
|
@@ -831,26 +1102,56 @@ var LioranDB = class _LioranDB {
|
|
|
831
1102
|
dbName;
|
|
832
1103
|
manager;
|
|
833
1104
|
collections;
|
|
834
|
-
walPath;
|
|
835
1105
|
metaPath;
|
|
836
1106
|
meta;
|
|
837
1107
|
migrator;
|
|
838
1108
|
static TX_SEQ = 0;
|
|
1109
|
+
wal;
|
|
1110
|
+
checkpoint;
|
|
839
1111
|
constructor(basePath, dbName, manager) {
|
|
840
1112
|
this.basePath = basePath;
|
|
841
1113
|
this.dbName = dbName;
|
|
842
1114
|
this.manager = manager;
|
|
843
1115
|
this.collections = /* @__PURE__ */ new Map();
|
|
844
|
-
this.
|
|
845
|
-
|
|
846
|
-
fs4.mkdirSync(basePath, { recursive: true });
|
|
1116
|
+
this.metaPath = path6.join(basePath, META_FILE);
|
|
1117
|
+
fs6.mkdirSync(basePath, { recursive: true });
|
|
847
1118
|
this.loadMeta();
|
|
1119
|
+
this.wal = new WALManager(basePath);
|
|
1120
|
+
this.checkpoint = new CheckpointManager(basePath);
|
|
848
1121
|
this.migrator = new MigrationEngine(this);
|
|
849
|
-
this.
|
|
1122
|
+
this.initialize().catch(console.error);
|
|
1123
|
+
}
|
|
1124
|
+
/* ------------------------- INIT & RECOVERY ------------------------- */
|
|
1125
|
+
async initialize() {
|
|
1126
|
+
await this.recoverFromWAL();
|
|
1127
|
+
}
|
|
1128
|
+
async recoverFromWAL() {
|
|
1129
|
+
const checkpointData = this.checkpoint.get();
|
|
1130
|
+
const fromLSN = checkpointData.lsn;
|
|
1131
|
+
const committed = /* @__PURE__ */ new Set();
|
|
1132
|
+
const applied = /* @__PURE__ */ new Set();
|
|
1133
|
+
const ops = /* @__PURE__ */ new Map();
|
|
1134
|
+
await this.wal.replay(fromLSN, async (record) => {
|
|
1135
|
+
if (record.type === "commit") {
|
|
1136
|
+
committed.add(record.tx);
|
|
1137
|
+
} else if (record.type === "applied") {
|
|
1138
|
+
applied.add(record.tx);
|
|
1139
|
+
} else if (record.type === "op") {
|
|
1140
|
+
if (!ops.has(record.tx)) ops.set(record.tx, []);
|
|
1141
|
+
ops.get(record.tx).push(record.payload);
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
for (const tx of committed) {
|
|
1145
|
+
if (applied.has(tx)) continue;
|
|
1146
|
+
const txOps = ops.get(tx);
|
|
1147
|
+
if (txOps) {
|
|
1148
|
+
await this.applyTransaction(txOps);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
850
1151
|
}
|
|
851
1152
|
/* ------------------------- META ------------------------- */
|
|
852
1153
|
loadMeta() {
|
|
853
|
-
if (!
|
|
1154
|
+
if (!fs6.existsSync(this.metaPath)) {
|
|
854
1155
|
this.meta = {
|
|
855
1156
|
version: META_VERSION,
|
|
856
1157
|
indexes: {},
|
|
@@ -859,14 +1160,14 @@ var LioranDB = class _LioranDB {
|
|
|
859
1160
|
this.saveMeta();
|
|
860
1161
|
return;
|
|
861
1162
|
}
|
|
862
|
-
this.meta = JSON.parse(
|
|
1163
|
+
this.meta = JSON.parse(fs6.readFileSync(this.metaPath, "utf8"));
|
|
863
1164
|
if (!this.meta.schemaVersion) {
|
|
864
1165
|
this.meta.schemaVersion = DEFAULT_SCHEMA_VERSION;
|
|
865
1166
|
this.saveMeta();
|
|
866
1167
|
}
|
|
867
1168
|
}
|
|
868
1169
|
saveMeta() {
|
|
869
|
-
|
|
1170
|
+
fs6.writeFileSync(this.metaPath, JSON.stringify(this.meta, null, 2));
|
|
870
1171
|
}
|
|
871
1172
|
getSchemaVersion() {
|
|
872
1173
|
return this.meta.schemaVersion;
|
|
@@ -875,7 +1176,7 @@ var LioranDB = class _LioranDB {
|
|
|
875
1176
|
this.meta.schemaVersion = v;
|
|
876
1177
|
this.saveMeta();
|
|
877
1178
|
}
|
|
878
|
-
/* -------------------------
|
|
1179
|
+
/* ------------------------- DB MIGRATIONS ------------------------- */
|
|
879
1180
|
migrate(from, to, fn) {
|
|
880
1181
|
this.migrator.register(from, to, async (db) => {
|
|
881
1182
|
await fn(db);
|
|
@@ -885,44 +1186,7 @@ var LioranDB = class _LioranDB {
|
|
|
885
1186
|
async applyMigrations(targetVersion) {
|
|
886
1187
|
await this.migrator.upgradeToLatest();
|
|
887
1188
|
}
|
|
888
|
-
/* -------------------------
|
|
889
|
-
async writeWAL(entries) {
|
|
890
|
-
const fd = await fs4.promises.open(this.walPath, "a");
|
|
891
|
-
for (const e of entries) {
|
|
892
|
-
await fd.write(JSON.stringify(e) + "\n");
|
|
893
|
-
}
|
|
894
|
-
await fd.sync();
|
|
895
|
-
await fd.close();
|
|
896
|
-
}
|
|
897
|
-
async clearWAL() {
|
|
898
|
-
try {
|
|
899
|
-
await fs4.promises.unlink(this.walPath);
|
|
900
|
-
} catch {
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
async recoverFromWAL() {
|
|
904
|
-
if (!fs4.existsSync(this.walPath)) return;
|
|
905
|
-
const raw = await fs4.promises.readFile(this.walPath, "utf8");
|
|
906
|
-
const committed = /* @__PURE__ */ new Set();
|
|
907
|
-
const applied = /* @__PURE__ */ new Set();
|
|
908
|
-
const ops = /* @__PURE__ */ new Map();
|
|
909
|
-
for (const line of raw.split("\n")) {
|
|
910
|
-
if (!line.trim()) continue;
|
|
911
|
-
const entry = JSON.parse(line);
|
|
912
|
-
if ("commit" in entry) committed.add(entry.tx);
|
|
913
|
-
else if ("applied" in entry) applied.add(entry.tx);
|
|
914
|
-
else {
|
|
915
|
-
if (!ops.has(entry.tx)) ops.set(entry.tx, []);
|
|
916
|
-
ops.get(entry.tx).push(entry);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
for (const tx of committed) {
|
|
920
|
-
if (applied.has(tx)) continue;
|
|
921
|
-
const txOps = ops.get(tx);
|
|
922
|
-
if (txOps) await this.applyTransaction(txOps);
|
|
923
|
-
}
|
|
924
|
-
await this.clearWAL();
|
|
925
|
-
}
|
|
1189
|
+
/* ------------------------- TX APPLY ------------------------- */
|
|
926
1190
|
async applyTransaction(ops) {
|
|
927
1191
|
for (const { col, op, args } of ops) {
|
|
928
1192
|
const collection = this.collection(col);
|
|
@@ -930,15 +1194,21 @@ var LioranDB = class _LioranDB {
|
|
|
930
1194
|
}
|
|
931
1195
|
}
|
|
932
1196
|
/* ------------------------- COLLECTION ------------------------- */
|
|
933
|
-
collection(name, schema) {
|
|
1197
|
+
collection(name, schema, schemaVersion) {
|
|
934
1198
|
if (this.collections.has(name)) {
|
|
935
1199
|
const col2 = this.collections.get(name);
|
|
936
|
-
if (schema)
|
|
1200
|
+
if (schema && schemaVersion !== void 0) {
|
|
1201
|
+
col2.setSchema(schema, schemaVersion);
|
|
1202
|
+
}
|
|
937
1203
|
return col2;
|
|
938
1204
|
}
|
|
939
|
-
const colPath =
|
|
940
|
-
|
|
941
|
-
const col = new Collection(
|
|
1205
|
+
const colPath = path6.join(this.basePath, name);
|
|
1206
|
+
fs6.mkdirSync(colPath, { recursive: true });
|
|
1207
|
+
const col = new Collection(
|
|
1208
|
+
colPath,
|
|
1209
|
+
schema,
|
|
1210
|
+
schemaVersion ?? 1
|
|
1211
|
+
);
|
|
942
1212
|
const metas = this.meta.indexes[name] ?? [];
|
|
943
1213
|
for (const m of metas) {
|
|
944
1214
|
col.registerIndex(new Index(colPath, m.field, m.options));
|
|
@@ -955,11 +1225,11 @@ var LioranDB = class _LioranDB {
|
|
|
955
1225
|
for await (const [key, enc] of col.db.iterator()) {
|
|
956
1226
|
if (!enc) continue;
|
|
957
1227
|
try {
|
|
958
|
-
const doc =
|
|
1228
|
+
const doc = decryptData(enc);
|
|
959
1229
|
await index.insert(doc);
|
|
960
1230
|
} catch (err) {
|
|
961
|
-
const
|
|
962
|
-
console.warn(`
|
|
1231
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1232
|
+
console.warn(`Index build skipped doc ${key}: ${msg}`);
|
|
963
1233
|
}
|
|
964
1234
|
}
|
|
965
1235
|
col.registerIndex(index);
|
|
@@ -971,12 +1241,10 @@ var LioranDB = class _LioranDB {
|
|
|
971
1241
|
}
|
|
972
1242
|
/* ------------------------- COMPACTION ------------------------- */
|
|
973
1243
|
async compactCollection(name) {
|
|
974
|
-
await this.clearWAL();
|
|
975
1244
|
const col = this.collection(name);
|
|
976
1245
|
await col.compact();
|
|
977
1246
|
}
|
|
978
1247
|
async compactAll() {
|
|
979
|
-
await this.clearWAL();
|
|
980
1248
|
for (const name of this.collections.keys()) {
|
|
981
1249
|
await this.compactCollection(name);
|
|
982
1250
|
}
|
|
@@ -989,6 +1257,9 @@ var LioranDB = class _LioranDB {
|
|
|
989
1257
|
await tx.commit();
|
|
990
1258
|
return result;
|
|
991
1259
|
}
|
|
1260
|
+
/* ------------------------- POST COMMIT ------------------------- */
|
|
1261
|
+
async postCommitMaintenance() {
|
|
1262
|
+
}
|
|
992
1263
|
/* ------------------------- SHUTDOWN ------------------------- */
|
|
993
1264
|
async close() {
|
|
994
1265
|
for (const col of this.collections.values()) {
|
|
@@ -1000,21 +1271,18 @@ var LioranDB = class _LioranDB {
|
|
|
1000
1271
|
this.collections.clear();
|
|
1001
1272
|
}
|
|
1002
1273
|
};
|
|
1003
|
-
function decryptData2(enc) {
|
|
1004
|
-
throw new Error("Function not implemented.");
|
|
1005
|
-
}
|
|
1006
1274
|
|
|
1007
1275
|
// src/utils/rootpath.ts
|
|
1008
1276
|
import os2 from "os";
|
|
1009
|
-
import
|
|
1010
|
-
import
|
|
1277
|
+
import path7 from "path";
|
|
1278
|
+
import fs7 from "fs";
|
|
1011
1279
|
function getDefaultRootPath() {
|
|
1012
1280
|
let dbPath = process.env.LIORANDB_PATH;
|
|
1013
1281
|
if (!dbPath) {
|
|
1014
1282
|
const homeDir = os2.homedir();
|
|
1015
|
-
dbPath =
|
|
1016
|
-
if (!
|
|
1017
|
-
|
|
1283
|
+
dbPath = path7.join(homeDir, "LioranDB", "db");
|
|
1284
|
+
if (!fs7.existsSync(dbPath)) {
|
|
1285
|
+
fs7.mkdirSync(dbPath, { recursive: true });
|
|
1018
1286
|
}
|
|
1019
1287
|
process.env.LIORANDB_PATH = dbPath;
|
|
1020
1288
|
}
|
|
@@ -1029,24 +1297,24 @@ import net from "net";
|
|
|
1029
1297
|
|
|
1030
1298
|
// src/ipc/socketPath.ts
|
|
1031
1299
|
import os3 from "os";
|
|
1032
|
-
import
|
|
1300
|
+
import path8 from "path";
|
|
1033
1301
|
function getIPCSocketPath(rootPath) {
|
|
1034
1302
|
if (os3.platform() === "win32") {
|
|
1035
1303
|
return `\\\\.\\pipe\\liorandb_${rootPath.replace(/[:\\\/]/g, "_")}`;
|
|
1036
1304
|
}
|
|
1037
|
-
return
|
|
1305
|
+
return path8.join(rootPath, ".lioran.sock");
|
|
1038
1306
|
}
|
|
1039
1307
|
|
|
1040
1308
|
// src/ipc/client.ts
|
|
1041
1309
|
function delay(ms) {
|
|
1042
1310
|
return new Promise((r) => setTimeout(r, ms));
|
|
1043
1311
|
}
|
|
1044
|
-
async function connectWithRetry(
|
|
1312
|
+
async function connectWithRetry(path10) {
|
|
1045
1313
|
let attempt = 0;
|
|
1046
1314
|
while (true) {
|
|
1047
1315
|
try {
|
|
1048
1316
|
return await new Promise((resolve, reject) => {
|
|
1049
|
-
const socket = net.connect(
|
|
1317
|
+
const socket = net.connect(path10, () => resolve(socket));
|
|
1050
1318
|
socket.once("error", reject);
|
|
1051
1319
|
});
|
|
1052
1320
|
} catch (err) {
|
|
@@ -1129,11 +1397,11 @@ var DBQueue = class {
|
|
|
1129
1397
|
return this.exec("compact:all", {});
|
|
1130
1398
|
}
|
|
1131
1399
|
/* ----------------------------- SNAPSHOT API ----------------------------- */
|
|
1132
|
-
snapshot(
|
|
1133
|
-
return this.exec("snapshot", { path:
|
|
1400
|
+
snapshot(path10) {
|
|
1401
|
+
return this.exec("snapshot", { path: path10 });
|
|
1134
1402
|
}
|
|
1135
|
-
restore(
|
|
1136
|
-
return this.exec("restore", { path:
|
|
1403
|
+
restore(path10) {
|
|
1404
|
+
return this.exec("restore", { path: path10 });
|
|
1137
1405
|
}
|
|
1138
1406
|
/* ------------------------------ SHUTDOWN ------------------------------ */
|
|
1139
1407
|
async shutdown() {
|
|
@@ -1148,7 +1416,7 @@ var dbQueue = new DBQueue();
|
|
|
1148
1416
|
|
|
1149
1417
|
// src/ipc/server.ts
|
|
1150
1418
|
import net2 from "net";
|
|
1151
|
-
import
|
|
1419
|
+
import fs8 from "fs";
|
|
1152
1420
|
var IPCServer = class {
|
|
1153
1421
|
server;
|
|
1154
1422
|
manager;
|
|
@@ -1159,7 +1427,7 @@ var IPCServer = class {
|
|
|
1159
1427
|
}
|
|
1160
1428
|
start() {
|
|
1161
1429
|
if (!this.socketPath.startsWith("\\\\.\\")) {
|
|
1162
|
-
if (
|
|
1430
|
+
if (fs8.existsSync(this.socketPath)) fs8.unlinkSync(this.socketPath);
|
|
1163
1431
|
}
|
|
1164
1432
|
this.server = net2.createServer((socket) => {
|
|
1165
1433
|
let buffer = "";
|
|
@@ -1261,7 +1529,7 @@ var IPCServer = class {
|
|
|
1261
1529
|
if (this.server) this.server.close();
|
|
1262
1530
|
if (!this.socketPath.startsWith("\\\\.\\")) {
|
|
1263
1531
|
try {
|
|
1264
|
-
|
|
1532
|
+
fs8.unlinkSync(this.socketPath);
|
|
1265
1533
|
} catch {
|
|
1266
1534
|
}
|
|
1267
1535
|
}
|
|
@@ -1279,8 +1547,8 @@ var LioranManager = class {
|
|
|
1279
1547
|
constructor(options = {}) {
|
|
1280
1548
|
const { rootPath, encryptionKey } = options;
|
|
1281
1549
|
this.rootPath = rootPath || getDefaultRootPath();
|
|
1282
|
-
if (!
|
|
1283
|
-
|
|
1550
|
+
if (!fs9.existsSync(this.rootPath)) {
|
|
1551
|
+
fs9.mkdirSync(this.rootPath, { recursive: true });
|
|
1284
1552
|
}
|
|
1285
1553
|
if (encryptionKey) {
|
|
1286
1554
|
setEncryptionKey(encryptionKey);
|
|
@@ -1303,18 +1571,18 @@ var LioranManager = class {
|
|
|
1303
1571
|
}
|
|
1304
1572
|
}
|
|
1305
1573
|
tryAcquireLock() {
|
|
1306
|
-
const lockPath =
|
|
1574
|
+
const lockPath = path9.join(this.rootPath, ".lioran.lock");
|
|
1307
1575
|
try {
|
|
1308
|
-
this.lockFd =
|
|
1309
|
-
|
|
1576
|
+
this.lockFd = fs9.openSync(lockPath, "wx");
|
|
1577
|
+
fs9.writeSync(this.lockFd, String(process2.pid));
|
|
1310
1578
|
return true;
|
|
1311
1579
|
} catch {
|
|
1312
1580
|
try {
|
|
1313
|
-
const pid = Number(
|
|
1581
|
+
const pid = Number(fs9.readFileSync(lockPath, "utf8"));
|
|
1314
1582
|
if (!this.isProcessAlive(pid)) {
|
|
1315
|
-
|
|
1316
|
-
this.lockFd =
|
|
1317
|
-
|
|
1583
|
+
fs9.unlinkSync(lockPath);
|
|
1584
|
+
this.lockFd = fs9.openSync(lockPath, "wx");
|
|
1585
|
+
fs9.writeSync(this.lockFd, String(process2.pid));
|
|
1318
1586
|
return true;
|
|
1319
1587
|
}
|
|
1320
1588
|
} catch {
|
|
@@ -1335,8 +1603,8 @@ var LioranManager = class {
|
|
|
1335
1603
|
if (this.openDBs.has(name)) {
|
|
1336
1604
|
return this.openDBs.get(name);
|
|
1337
1605
|
}
|
|
1338
|
-
const dbPath =
|
|
1339
|
-
await
|
|
1606
|
+
const dbPath = path9.join(this.rootPath, name);
|
|
1607
|
+
await fs9.promises.mkdir(dbPath, { recursive: true });
|
|
1340
1608
|
const db = new LioranDB(dbPath, name, this);
|
|
1341
1609
|
this.openDBs.set(name, db);
|
|
1342
1610
|
return db;
|
|
@@ -1357,7 +1625,7 @@ var LioranManager = class {
|
|
|
1357
1625
|
}
|
|
1358
1626
|
}
|
|
1359
1627
|
}
|
|
1360
|
-
|
|
1628
|
+
fs9.mkdirSync(path9.dirname(snapshotPath), { recursive: true });
|
|
1361
1629
|
const tar = await import("tar");
|
|
1362
1630
|
await tar.c({
|
|
1363
1631
|
gzip: true,
|
|
@@ -1375,8 +1643,8 @@ var LioranManager = class {
|
|
|
1375
1643
|
return dbQueue.exec("restore", { path: snapshotPath });
|
|
1376
1644
|
}
|
|
1377
1645
|
await this.closeAll();
|
|
1378
|
-
|
|
1379
|
-
|
|
1646
|
+
fs9.rmSync(this.rootPath, { recursive: true, force: true });
|
|
1647
|
+
fs9.mkdirSync(this.rootPath, { recursive: true });
|
|
1380
1648
|
const tar = await import("tar");
|
|
1381
1649
|
await tar.x({
|
|
1382
1650
|
file: snapshotPath,
|
|
@@ -1401,8 +1669,8 @@ var LioranManager = class {
|
|
|
1401
1669
|
}
|
|
1402
1670
|
this.openDBs.clear();
|
|
1403
1671
|
try {
|
|
1404
|
-
if (this.lockFd)
|
|
1405
|
-
|
|
1672
|
+
if (this.lockFd) fs9.closeSync(this.lockFd);
|
|
1673
|
+
fs9.unlinkSync(path9.join(this.rootPath, ".lioran.lock"));
|
|
1406
1674
|
} catch {
|
|
1407
1675
|
}
|
|
1408
1676
|
await this.ipcServer?.close();
|