lmcs-db 1.0.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -68
- package/dist/LmcsDB.d.ts +13 -0
- package/dist/LmcsDB.js +20 -5
- package/dist/LmcsDB.js.map +1 -1
- package/dist/core/collection.d.ts +33 -0
- package/dist/core/collection.js +287 -0
- package/dist/core/database.d.ts +35 -0
- package/dist/core/database.js +165 -0
- package/dist/core/indexer.d.ts +20 -0
- package/dist/core/indexer.js +89 -0
- package/dist/core/transaction-context.d.ts +13 -0
- package/dist/core/transaction-context.js +48 -0
- package/dist/core/transaction.d.ts +25 -0
- package/dist/core/transaction.js +122 -0
- package/dist/crypto/key-derivation.d.ts +0 -0
- package/dist/crypto/key-derivation.js +1 -0
- package/dist/crypto/manager.d.ts +22 -0
- package/dist/crypto/manager.js +76 -0
- package/dist/crypto/vault.d.ts +18 -0
- package/dist/crypto/vault.js +44 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +12 -9
- package/dist/persistence/AsyncWriteWorker.d.ts +30 -0
- package/dist/persistence/AsyncWriteWorker.js +76 -0
- package/dist/persistence/AsyncWriteWorker.js.map +1 -0
- package/dist/storage/BinaryStorage.d.ts +3 -0
- package/dist/storage/BinaryStorage.js +43 -5
- package/dist/storage/BinaryStorage.js.map +1 -1
- package/dist/storage/aol.d.ts +26 -0
- package/dist/storage/aol.js +166 -0
- package/dist/storage/base.d.ts +36 -0
- package/dist/storage/base.js +13 -0
- package/dist/storage/binary.d.ts +21 -0
- package/dist/storage/binary.js +124 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +13 -0
- package/dist/storage/json.d.ts +18 -0
- package/dist/storage/json.js +153 -0
- package/dist/storage/memory.d.ts +14 -0
- package/dist/storage/memory.js +42 -0
- package/dist/utils/checksum.d.ts +0 -0
- package/dist/utils/checksum.js +1 -0
- package/dist/utils/errors.d.ts +16 -0
- package/dist/utils/errors.js +37 -0
- package/dist/utils/lock.d.ts +9 -0
- package/dist/utils/lock.js +75 -0
- package/package.json +11 -5
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransactionManager = void 0;
|
|
4
|
+
const errors_1 = require("../utils/errors");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
class TransactionManager {
|
|
7
|
+
activeTransactions = new Map();
|
|
8
|
+
storage;
|
|
9
|
+
constructor(storage) {
|
|
10
|
+
this.storage = storage;
|
|
11
|
+
}
|
|
12
|
+
async begin() {
|
|
13
|
+
const txId = (0, uuid_1.v4)();
|
|
14
|
+
const tx = {
|
|
15
|
+
id: txId,
|
|
16
|
+
operations: [],
|
|
17
|
+
status: 'pending',
|
|
18
|
+
timestamp: Date.now()
|
|
19
|
+
};
|
|
20
|
+
this.activeTransactions.set(txId, tx);
|
|
21
|
+
await this.storage.append({
|
|
22
|
+
op: 'BEGIN',
|
|
23
|
+
collection: '_transactions',
|
|
24
|
+
id: txId,
|
|
25
|
+
checksum: '',
|
|
26
|
+
timestamp: tx.timestamp,
|
|
27
|
+
txId
|
|
28
|
+
});
|
|
29
|
+
return txId;
|
|
30
|
+
}
|
|
31
|
+
async addOperation(txId, op) {
|
|
32
|
+
const tx = this.activeTransactions.get(txId);
|
|
33
|
+
if (!tx)
|
|
34
|
+
throw new errors_1.TransactionError('Transaction not found');
|
|
35
|
+
if (tx.status !== 'pending')
|
|
36
|
+
throw new errors_1.TransactionError('Transaction already finalized');
|
|
37
|
+
tx.operations.push(op);
|
|
38
|
+
}
|
|
39
|
+
async commit(txId) {
|
|
40
|
+
const tx = this.activeTransactions.get(txId);
|
|
41
|
+
if (!tx)
|
|
42
|
+
throw new errors_1.TransactionError('Transaction not found');
|
|
43
|
+
// Apply operations to storage
|
|
44
|
+
for (const op of tx.operations) {
|
|
45
|
+
let storageOp;
|
|
46
|
+
switch (op.type) {
|
|
47
|
+
case 'insert':
|
|
48
|
+
storageOp = 'INSERT';
|
|
49
|
+
break;
|
|
50
|
+
case 'update':
|
|
51
|
+
storageOp = 'UPDATE';
|
|
52
|
+
break;
|
|
53
|
+
case 'delete':
|
|
54
|
+
storageOp = 'DELETE';
|
|
55
|
+
break;
|
|
56
|
+
default: continue;
|
|
57
|
+
}
|
|
58
|
+
await this.storage.append({
|
|
59
|
+
op: storageOp,
|
|
60
|
+
collection: op.collection,
|
|
61
|
+
id: op.id,
|
|
62
|
+
data: op.newData || {},
|
|
63
|
+
checksum: '',
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
txId: txId
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
await this.storage.append({
|
|
69
|
+
op: 'COMMIT',
|
|
70
|
+
collection: '_transactions',
|
|
71
|
+
id: txId,
|
|
72
|
+
checksum: '',
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
txId
|
|
75
|
+
});
|
|
76
|
+
const operations = [...tx.operations];
|
|
77
|
+
tx.status = 'committed';
|
|
78
|
+
this.activeTransactions.delete(txId);
|
|
79
|
+
return operations;
|
|
80
|
+
}
|
|
81
|
+
async rollback(txId) {
|
|
82
|
+
const tx = this.activeTransactions.get(txId);
|
|
83
|
+
if (!tx)
|
|
84
|
+
return;
|
|
85
|
+
await this.storage.append({
|
|
86
|
+
op: 'ROLLBACK',
|
|
87
|
+
collection: '_transactions',
|
|
88
|
+
id: txId,
|
|
89
|
+
checksum: '',
|
|
90
|
+
timestamp: Date.now(),
|
|
91
|
+
txId
|
|
92
|
+
});
|
|
93
|
+
tx.status = 'aborted';
|
|
94
|
+
this.activeTransactions.delete(txId);
|
|
95
|
+
}
|
|
96
|
+
getTransaction(txId) {
|
|
97
|
+
return this.activeTransactions.get(txId);
|
|
98
|
+
}
|
|
99
|
+
async recover() {
|
|
100
|
+
const pendingTxs = new Map();
|
|
101
|
+
for await (const entry of this.storage.readStream()) {
|
|
102
|
+
if (entry.collection !== '_transactions')
|
|
103
|
+
continue;
|
|
104
|
+
if (entry.op === 'BEGIN') {
|
|
105
|
+
pendingTxs.set(entry.id, {
|
|
106
|
+
id: entry.id,
|
|
107
|
+
operations: [],
|
|
108
|
+
status: 'pending',
|
|
109
|
+
timestamp: entry.timestamp
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else if (entry.op === 'COMMIT' || entry.op === 'ROLLBACK') {
|
|
113
|
+
pendingTxs.delete(entry.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
for (const [txId, tx] of pendingTxs) {
|
|
117
|
+
console.log(`Recovering incomplete transaction ${txId}`);
|
|
118
|
+
await this.rollback(txId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.TransactionManager = TransactionManager;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface EncryptedData {
|
|
2
|
+
data: string;
|
|
3
|
+
iv: string;
|
|
4
|
+
tag: string;
|
|
5
|
+
salt: string;
|
|
6
|
+
version: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class CryptoManager {
|
|
9
|
+
private masterKey?;
|
|
10
|
+
private password?;
|
|
11
|
+
private _salt?;
|
|
12
|
+
private static readonly ALGORITHM;
|
|
13
|
+
private static readonly ITERATIONS;
|
|
14
|
+
private static readonly KEYLEN;
|
|
15
|
+
private static readonly VERSION;
|
|
16
|
+
constructor(password?: string);
|
|
17
|
+
private deriveKey;
|
|
18
|
+
encrypt(text: string): EncryptedData;
|
|
19
|
+
decrypt(encrypted: EncryptedData): string;
|
|
20
|
+
validateKey(testData?: EncryptedData): Promise<boolean>;
|
|
21
|
+
static hash(data: string): string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CryptoManager = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
class CryptoManager {
|
|
6
|
+
masterKey;
|
|
7
|
+
password;
|
|
8
|
+
_salt;
|
|
9
|
+
static ALGORITHM = 'aes-256-gcm';
|
|
10
|
+
static ITERATIONS = 100000;
|
|
11
|
+
static KEYLEN = 32;
|
|
12
|
+
static VERSION = 1;
|
|
13
|
+
constructor(password) {
|
|
14
|
+
if (password) {
|
|
15
|
+
this.password = password;
|
|
16
|
+
// Derivação inicial - salt aleatório gerado aqui
|
|
17
|
+
const salt = (0, crypto_1.randomBytes)(32);
|
|
18
|
+
this.masterKey = this.deriveKey(password, salt);
|
|
19
|
+
this._salt = salt.toString('hex'); // Guarda para referência
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
deriveKey(password, salt) {
|
|
23
|
+
return (0, crypto_1.pbkdf2Sync)(password, salt, CryptoManager.ITERATIONS, CryptoManager.KEYLEN, 'sha256');
|
|
24
|
+
}
|
|
25
|
+
encrypt(text) {
|
|
26
|
+
if (!this.masterKey)
|
|
27
|
+
throw new Error('CryptoManager not initialized with password');
|
|
28
|
+
const iv = (0, crypto_1.randomBytes)(16);
|
|
29
|
+
const cipher = (0, crypto_1.createCipheriv)(CryptoManager.ALGORITHM, this.masterKey, iv);
|
|
30
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
31
|
+
encrypted += cipher.final('hex');
|
|
32
|
+
return {
|
|
33
|
+
data: encrypted,
|
|
34
|
+
iv: iv.toString('hex'),
|
|
35
|
+
tag: cipher.getAuthTag().toString('hex'),
|
|
36
|
+
salt: this._salt,
|
|
37
|
+
version: CryptoManager.VERSION
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
decrypt(encrypted) {
|
|
41
|
+
if (!this.masterKey && !this.password)
|
|
42
|
+
throw new Error('CryptoManager not initialized with password');
|
|
43
|
+
let key = this.masterKey;
|
|
44
|
+
// Se o salt do dado for diferente do atual, e temos a senha, re-derivamos a chave
|
|
45
|
+
if (encrypted.salt && this._salt && encrypted.salt !== this._salt && this.password) {
|
|
46
|
+
key = this.deriveKey(this.password, Buffer.from(encrypted.salt, 'hex'));
|
|
47
|
+
}
|
|
48
|
+
else if (!key && this.password && encrypted.salt) {
|
|
49
|
+
// Caso onde só temos senha mas não inicializamos chave padrão (não deve ocorrer pelo construtor, mas por segurança)
|
|
50
|
+
key = this.deriveKey(this.password, Buffer.from(encrypted.salt, 'hex'));
|
|
51
|
+
}
|
|
52
|
+
if (!key)
|
|
53
|
+
throw new Error('Cannot decrypt: missing key or password');
|
|
54
|
+
const decipher = (0, crypto_1.createDecipheriv)(CryptoManager.ALGORITHM, key, Buffer.from(encrypted.iv, 'hex'));
|
|
55
|
+
decipher.setAuthTag(Buffer.from(encrypted.tag, 'hex'));
|
|
56
|
+
let decrypted = decipher.update(encrypted.data, 'hex', 'utf8');
|
|
57
|
+
decrypted += decipher.final('utf8');
|
|
58
|
+
return decrypted;
|
|
59
|
+
}
|
|
60
|
+
// Valida se a chave fornecida pode descriptografar os dados existentes
|
|
61
|
+
async validateKey(testData) {
|
|
62
|
+
if (!this.masterKey || !testData)
|
|
63
|
+
return false;
|
|
64
|
+
try {
|
|
65
|
+
this.decrypt(testData);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
static hash(data) {
|
|
73
|
+
return (0, crypto_1.createHash)('sha256').update(data).digest('hex');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.CryptoManager = CryptoManager;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface EncryptedPayload {
|
|
2
|
+
ciphertext: string;
|
|
3
|
+
iv: string;
|
|
4
|
+
authTag: string;
|
|
5
|
+
salt: string;
|
|
6
|
+
iterations: number;
|
|
7
|
+
version: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class CryptoVault {
|
|
10
|
+
private derivedKey;
|
|
11
|
+
private readonly ALGORITHM;
|
|
12
|
+
private readonly VERSION;
|
|
13
|
+
private readonly ITERATIONS;
|
|
14
|
+
private salt;
|
|
15
|
+
constructor(password: string, existingSalt?: string);
|
|
16
|
+
encrypt(data: string): EncryptedPayload;
|
|
17
|
+
decrypt(payload: EncryptedPayload): string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CryptoVault = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
class CryptoVault {
|
|
6
|
+
derivedKey;
|
|
7
|
+
ALGORITHM = 'aes-256-gcm';
|
|
8
|
+
VERSION = 1;
|
|
9
|
+
ITERATIONS = 100000;
|
|
10
|
+
salt;
|
|
11
|
+
constructor(password, existingSalt) {
|
|
12
|
+
if (!password || typeof password !== 'string') {
|
|
13
|
+
throw new Error('Password must be a non-empty string');
|
|
14
|
+
}
|
|
15
|
+
const saltBuffer = existingSalt
|
|
16
|
+
? Buffer.from(existingSalt, 'hex')
|
|
17
|
+
: (0, crypto_1.randomBytes)(32);
|
|
18
|
+
this.salt = saltBuffer.toString('hex');
|
|
19
|
+
this.derivedKey = (0, crypto_1.pbkdf2Sync)(password, saltBuffer, this.ITERATIONS, 32, 'sha256');
|
|
20
|
+
}
|
|
21
|
+
encrypt(data) {
|
|
22
|
+
const iv = (0, crypto_1.randomBytes)(16);
|
|
23
|
+
const cipher = (0, crypto_1.createCipheriv)(this.ALGORITHM, this.derivedKey, iv);
|
|
24
|
+
let ciphertext = cipher.update(data, 'utf8', 'hex');
|
|
25
|
+
ciphertext += cipher.final('hex');
|
|
26
|
+
return {
|
|
27
|
+
ciphertext,
|
|
28
|
+
iv: iv.toString('hex'),
|
|
29
|
+
authTag: cipher.getAuthTag().toString('hex'),
|
|
30
|
+
salt: this.salt,
|
|
31
|
+
iterations: this.ITERATIONS,
|
|
32
|
+
version: this.VERSION
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
decrypt(payload) {
|
|
36
|
+
const key = (0, crypto_1.pbkdf2Sync)(this.derivedKey.toString('hex'), Buffer.from(payload.salt, 'hex'), payload.iterations, 32, 'sha256');
|
|
37
|
+
const decipher = (0, crypto_1.createDecipheriv)(this.ALGORITHM, key, Buffer.from(payload.iv, 'hex'));
|
|
38
|
+
decipher.setAuthTag(Buffer.from(payload.authTag, 'hex'));
|
|
39
|
+
let decrypted = decipher.update(payload.ciphertext, 'hex', 'utf8');
|
|
40
|
+
decrypted += decipher.final('utf8');
|
|
41
|
+
return decrypted;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.CryptoVault = CryptoVault;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { Database, createDatabase, DatabaseOptions, StorageType } from './core/database';
|
|
2
|
+
export { Collection, QueryOptions } from './core/collection';
|
|
3
|
+
export { CryptoManager, EncryptedData } from './crypto/manager';
|
|
4
|
+
export { AOLStorage } from './storage/aol';
|
|
5
|
+
export { BinaryStorage } from './storage/binary';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
var
|
|
8
|
-
Object.defineProperty(exports, "
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
exports.BinaryStorage = exports.AOLStorage = exports.CryptoManager = exports.Collection = exports.createDatabase = exports.Database = void 0;
|
|
4
|
+
var database_1 = require("./core/database");
|
|
5
|
+
Object.defineProperty(exports, "Database", { enumerable: true, get: function () { return database_1.Database; } });
|
|
6
|
+
Object.defineProperty(exports, "createDatabase", { enumerable: true, get: function () { return database_1.createDatabase; } });
|
|
7
|
+
var collection_1 = require("./core/collection");
|
|
8
|
+
Object.defineProperty(exports, "Collection", { enumerable: true, get: function () { return collection_1.Collection; } });
|
|
9
|
+
var manager_1 = require("./crypto/manager");
|
|
10
|
+
Object.defineProperty(exports, "CryptoManager", { enumerable: true, get: function () { return manager_1.CryptoManager; } });
|
|
11
|
+
var aol_1 = require("./storage/aol");
|
|
12
|
+
Object.defineProperty(exports, "AOLStorage", { enumerable: true, get: function () { return aol_1.AOLStorage; } });
|
|
13
|
+
var binary_1 = require("./storage/binary");
|
|
14
|
+
Object.defineProperty(exports, "BinaryStorage", { enumerable: true, get: function () { return binary_1.BinaryStorage; } });
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type WriteFn = (data: string) => Promise<void>;
|
|
2
|
+
declare class AsyncWriteWorker {
|
|
3
|
+
private writing;
|
|
4
|
+
private pendingPayload?;
|
|
5
|
+
private resolveIdle?;
|
|
6
|
+
private writeFn;
|
|
7
|
+
private enqueuedCount;
|
|
8
|
+
private writesCount;
|
|
9
|
+
private lastDurationMs;
|
|
10
|
+
private lastBytes;
|
|
11
|
+
private totalBytes;
|
|
12
|
+
private lastStartedAt?;
|
|
13
|
+
private lastFinishedAt?;
|
|
14
|
+
constructor(writeFn: WriteFn);
|
|
15
|
+
enqueue(data: string): void;
|
|
16
|
+
private process;
|
|
17
|
+
flush(): Promise<void>;
|
|
18
|
+
getStats(): {
|
|
19
|
+
enqueuedCount: number;
|
|
20
|
+
writesCount: number;
|
|
21
|
+
inFlight: boolean;
|
|
22
|
+
hasPending: boolean;
|
|
23
|
+
lastDurationMs: number;
|
|
24
|
+
lastBytes: number;
|
|
25
|
+
totalBytes: number;
|
|
26
|
+
lastStartedAt: number | undefined;
|
|
27
|
+
lastFinishedAt: number | undefined;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export default AsyncWriteWorker;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class AsyncWriteWorker {
|
|
4
|
+
writing = false;
|
|
5
|
+
pendingPayload;
|
|
6
|
+
resolveIdle;
|
|
7
|
+
writeFn;
|
|
8
|
+
enqueuedCount = 0;
|
|
9
|
+
writesCount = 0;
|
|
10
|
+
lastDurationMs = 0;
|
|
11
|
+
lastBytes = 0;
|
|
12
|
+
totalBytes = 0;
|
|
13
|
+
lastStartedAt;
|
|
14
|
+
lastFinishedAt;
|
|
15
|
+
constructor(writeFn) {
|
|
16
|
+
this.writeFn = writeFn;
|
|
17
|
+
}
|
|
18
|
+
enqueue(data) {
|
|
19
|
+
this.enqueuedCount += 1;
|
|
20
|
+
this.pendingPayload = data;
|
|
21
|
+
if (!this.writing) {
|
|
22
|
+
this.process();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async process() {
|
|
26
|
+
if (!this.pendingPayload)
|
|
27
|
+
return;
|
|
28
|
+
this.writing = true;
|
|
29
|
+
const payload = this.pendingPayload;
|
|
30
|
+
this.pendingPayload = undefined;
|
|
31
|
+
try {
|
|
32
|
+
this.lastStartedAt = Date.now();
|
|
33
|
+
this.lastBytes = Buffer.byteLength(payload, 'utf-8');
|
|
34
|
+
await this.writeFn(payload);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
this.lastFinishedAt = Date.now();
|
|
38
|
+
this.lastDurationMs = (this.lastFinishedAt - (this.lastStartedAt || this.lastFinishedAt));
|
|
39
|
+
this.writesCount += 1;
|
|
40
|
+
this.totalBytes += this.lastBytes;
|
|
41
|
+
this.writing = false;
|
|
42
|
+
if (this.pendingPayload) {
|
|
43
|
+
setImmediate(() => this.process());
|
|
44
|
+
}
|
|
45
|
+
else if (this.resolveIdle) {
|
|
46
|
+
const fn = this.resolveIdle;
|
|
47
|
+
this.resolveIdle = undefined;
|
|
48
|
+
fn();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async flush() {
|
|
53
|
+
if (this.writing || this.pendingPayload) {
|
|
54
|
+
await new Promise((resolve) => {
|
|
55
|
+
this.resolveIdle = resolve;
|
|
56
|
+
if (!this.writing && this.pendingPayload) {
|
|
57
|
+
this.process();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
getStats() {
|
|
63
|
+
return {
|
|
64
|
+
enqueuedCount: this.enqueuedCount,
|
|
65
|
+
writesCount: this.writesCount,
|
|
66
|
+
inFlight: this.writing,
|
|
67
|
+
hasPending: !!this.pendingPayload,
|
|
68
|
+
lastDurationMs: this.lastDurationMs,
|
|
69
|
+
lastBytes: this.lastBytes,
|
|
70
|
+
totalBytes: this.totalBytes,
|
|
71
|
+
lastStartedAt: this.lastStartedAt,
|
|
72
|
+
lastFinishedAt: this.lastFinishedAt
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.default = AsyncWriteWorker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncWriteWorker.js","sourceRoot":"","sources":["../../src/persistence/AsyncWriteWorker.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,9 @@ declare class BinaryStorage implements IDatabaseStorage {
|
|
|
3
3
|
private filePath;
|
|
4
4
|
constructor(databaseName: string, customPath?: string);
|
|
5
5
|
private xorBuffer;
|
|
6
|
+
private crc32;
|
|
7
|
+
private buildFileBuffer;
|
|
8
|
+
private parseFileBuffer;
|
|
6
9
|
save(data: string): Promise<void>;
|
|
7
10
|
load(): Promise<string>;
|
|
8
11
|
}
|
|
@@ -17,16 +17,54 @@ class BinaryStorage {
|
|
|
17
17
|
}
|
|
18
18
|
return result;
|
|
19
19
|
}
|
|
20
|
+
crc32(buffer) {
|
|
21
|
+
let crc = 0xffffffff;
|
|
22
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
23
|
+
crc ^= buffer[i];
|
|
24
|
+
for (let j = 0; j < 8; j++) {
|
|
25
|
+
const mask = -(crc & 1);
|
|
26
|
+
crc = (crc >>> 1) ^ (0xEDB88320 & mask);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
30
|
+
}
|
|
31
|
+
buildFileBuffer(data) {
|
|
32
|
+
const payload = Buffer.from(data, 'utf-8');
|
|
33
|
+
const obf = this.xorBuffer(payload);
|
|
34
|
+
const magic = Buffer.from('LMCSDB1');
|
|
35
|
+
const flags = Buffer.from([0]);
|
|
36
|
+
const lenBuf = Buffer.alloc(4);
|
|
37
|
+
lenBuf.writeUInt32BE(obf.length, 0);
|
|
38
|
+
const crcBuf = Buffer.alloc(4);
|
|
39
|
+
crcBuf.writeUInt32BE(this.crc32(obf), 0);
|
|
40
|
+
return Buffer.concat([magic, flags, lenBuf, crcBuf, obf]);
|
|
41
|
+
}
|
|
42
|
+
parseFileBuffer(buffer) {
|
|
43
|
+
if (buffer.length < 16)
|
|
44
|
+
return '{}';
|
|
45
|
+
const magic = buffer.subarray(0, 7).toString('utf-8');
|
|
46
|
+
if (magic !== 'LMCSDB1')
|
|
47
|
+
return '{}';
|
|
48
|
+
const len = buffer.readUInt32BE(8);
|
|
49
|
+
const crc = buffer.readUInt32BE(12);
|
|
50
|
+
const start = 16;
|
|
51
|
+
const end = start + len;
|
|
52
|
+
if (end > buffer.length)
|
|
53
|
+
return '{}';
|
|
54
|
+
const obf = buffer.subarray(start, end);
|
|
55
|
+
if (this.crc32(obf) !== crc)
|
|
56
|
+
return '{}';
|
|
57
|
+
const payload = this.xorBuffer(obf);
|
|
58
|
+
return payload.toString('utf-8');
|
|
59
|
+
}
|
|
20
60
|
async save(data) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
await fs_1.default.promises.writeFile(this.filePath, obfuscated);
|
|
61
|
+
const buf = this.buildFileBuffer(data);
|
|
62
|
+
await fs_1.default.promises.writeFile(this.filePath, buf);
|
|
24
63
|
}
|
|
25
64
|
async load() {
|
|
26
65
|
try {
|
|
27
66
|
const buffer = await fs_1.default.promises.readFile(this.filePath);
|
|
28
|
-
|
|
29
|
-
return deobfuscated.toString('utf-8');
|
|
67
|
+
return this.parseFileBuffer(buffer);
|
|
30
68
|
}
|
|
31
69
|
catch (error) {
|
|
32
70
|
if (error.code === 'ENOENT') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BinaryStorage.js","sourceRoot":"","sources":["../../src/storage/BinaryStorage.ts"],"names":[],"mappings":";;;;;AAAA,4CAAoB;AACpB,gDAAwB;AAGxB,MAAM,aAAa;IAGjB,YAAY,YAAoB,EAAE,UAAmB;QACnD,MAAM,QAAQ,GAAG,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC7C,IAAI,CAAC,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC;IAC/D,CAAC;IAEO,SAAS,CAAC,MAAc,EAAE,MAAc,IAAI;QAClD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;QAC9B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;
|
|
1
|
+
{"version":3,"file":"BinaryStorage.js","sourceRoot":"","sources":["../../src/storage/BinaryStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BaseStorage, LogEntry, StorageConfig } from "./base";
|
|
2
|
+
export declare class AOLStorage extends BaseStorage {
|
|
3
|
+
private buffer;
|
|
4
|
+
private crypto?;
|
|
5
|
+
private locker;
|
|
6
|
+
private bufferSize;
|
|
7
|
+
private compactionTimer?;
|
|
8
|
+
private isInitialized;
|
|
9
|
+
private writeCount;
|
|
10
|
+
constructor(config: StorageConfig & {
|
|
11
|
+
compactionInterval?: number;
|
|
12
|
+
bufferSize?: number;
|
|
13
|
+
});
|
|
14
|
+
initialize(): Promise<void>;
|
|
15
|
+
append(entry: LogEntry): Promise<void>;
|
|
16
|
+
flush(): Promise<void>;
|
|
17
|
+
readStream(): AsyncGenerator<LogEntry>;
|
|
18
|
+
compact(): Promise<void>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
getStats(): {
|
|
21
|
+
buffered: number;
|
|
22
|
+
totalWrites: number;
|
|
23
|
+
};
|
|
24
|
+
clear?(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export { LogEntry };
|