lmcs-db 1.0.4 → 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.
Files changed (39) hide show
  1. package/README.md +241 -85
  2. package/dist/core/collection.d.ts +33 -0
  3. package/dist/core/collection.js +287 -0
  4. package/dist/core/database.d.ts +35 -0
  5. package/dist/core/database.js +165 -0
  6. package/dist/core/indexer.d.ts +20 -0
  7. package/dist/core/indexer.js +89 -0
  8. package/dist/core/transaction-context.d.ts +13 -0
  9. package/dist/core/transaction-context.js +48 -0
  10. package/dist/core/transaction.d.ts +25 -0
  11. package/dist/core/transaction.js +122 -0
  12. package/dist/crypto/key-derivation.d.ts +0 -0
  13. package/dist/crypto/key-derivation.js +1 -0
  14. package/dist/crypto/manager.d.ts +22 -0
  15. package/dist/crypto/manager.js +76 -0
  16. package/dist/crypto/vault.d.ts +18 -0
  17. package/dist/crypto/vault.js +44 -0
  18. package/dist/index.d.ts +5 -2
  19. package/dist/index.js +12 -9
  20. package/dist/persistence/AsyncWriteWorker.js +11 -7
  21. package/dist/storage/aol.d.ts +26 -0
  22. package/dist/storage/aol.js +166 -0
  23. package/dist/storage/base.d.ts +36 -0
  24. package/dist/storage/base.js +13 -0
  25. package/dist/storage/binary.d.ts +21 -0
  26. package/dist/storage/binary.js +124 -0
  27. package/dist/storage/index.d.ts +5 -0
  28. package/dist/storage/index.js +13 -0
  29. package/dist/storage/json.d.ts +18 -0
  30. package/dist/storage/json.js +153 -0
  31. package/dist/storage/memory.d.ts +14 -0
  32. package/dist/storage/memory.js +42 -0
  33. package/dist/utils/checksum.d.ts +0 -0
  34. package/dist/utils/checksum.js +1 -0
  35. package/dist/utils/errors.d.ts +16 -0
  36. package/dist/utils/errors.js +37 -0
  37. package/dist/utils/lock.d.ts +9 -0
  38. package/dist/utils/lock.js +75 -0
  39. package/package.json +11 -5
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.JSONStorage = void 0;
37
+ const base_1 = require("./base");
38
+ const manager_1 = require("../crypto/manager");
39
+ const promises_1 = require("fs/promises");
40
+ const path_1 = require("path");
41
+ const lock_1 = require("../utils/lock");
42
+ class JSONStorage extends base_1.BaseStorage {
43
+ autosaveInterval;
44
+ cache = [];
45
+ crypto;
46
+ locker = new lock_1.FileLocker();
47
+ dirty = false;
48
+ autosaveTimer;
49
+ isInitialized = false;
50
+ constructor(config, autosaveInterval = 5000) {
51
+ super(config);
52
+ this.autosaveInterval = autosaveInterval;
53
+ if (config.encryptionKey) {
54
+ this.crypto = new manager_1.CryptoManager(config.encryptionKey);
55
+ }
56
+ }
57
+ async initialize() {
58
+ const filePath = this.getFilePath("json");
59
+ await (0, promises_1.mkdir)((0, path_1.dirname)(filePath), { recursive: true });
60
+ try {
61
+ await (0, promises_1.access)(filePath);
62
+ const content = await (0, promises_1.readFile)(filePath, "utf8");
63
+ if (content.trim()) {
64
+ let jsonContent;
65
+ if (this.crypto) {
66
+ const encrypted = JSON.parse(content);
67
+ jsonContent = this.crypto.decrypt(encrypted);
68
+ }
69
+ else {
70
+ jsonContent = content;
71
+ }
72
+ this.cache = JSON.parse(jsonContent);
73
+ }
74
+ }
75
+ catch (err) {
76
+ if (err.code !== "ENOENT") {
77
+ // Se falhar na descriptografia, limpa (chave errada)
78
+ console.warn("Failed to load existing data, starting fresh");
79
+ this.cache = [];
80
+ }
81
+ }
82
+ // Autosave
83
+ if (this.autosaveInterval > 0) {
84
+ this.autosaveTimer = setInterval(() => {
85
+ if (this.dirty)
86
+ this.flush().catch(console.error);
87
+ }, this.autosaveInterval);
88
+ }
89
+ this.isInitialized = true;
90
+ }
91
+ async append(entry) {
92
+ if (!this.isInitialized)
93
+ throw new Error("Storage not initialized");
94
+ // Calcula checksum se necessário
95
+ if (this.config.enableChecksums) {
96
+ const { createHash } = await Promise.resolve().then(() => __importStar(require("crypto")));
97
+ entry.checksum = createHash("sha256")
98
+ .update(JSON.stringify({ ...entry, checksum: "" }))
99
+ .digest("hex");
100
+ }
101
+ this.cache.push(entry);
102
+ this.dirty = true;
103
+ }
104
+ async *readStream() {
105
+ for (const entry of this.cache) {
106
+ yield entry;
107
+ }
108
+ }
109
+ async flush() {
110
+ if (!this.dirty || !this.isInitialized)
111
+ return;
112
+ const filePath = this.getFilePath("json");
113
+ const lockPath = `${filePath}.lock`;
114
+ await this.locker.withLock(lockPath, async () => {
115
+ const content = JSON.stringify(this.cache, null, 2);
116
+ if (this.crypto) {
117
+ const encrypted = this.crypto.encrypt(content);
118
+ await (0, promises_1.writeFile)(filePath, JSON.stringify(encrypted), "utf8");
119
+ }
120
+ else {
121
+ await (0, promises_1.writeFile)(filePath, content, "utf8");
122
+ }
123
+ this.dirty = false;
124
+ });
125
+ }
126
+ async close() {
127
+ if (this.autosaveTimer)
128
+ clearInterval(this.autosaveTimer);
129
+ await this.flush();
130
+ this.cache = [];
131
+ this.isInitialized = false;
132
+ }
133
+ async compact() {
134
+ // Remove duplicados (mantém último estado)
135
+ const seen = new Map();
136
+ for (const entry of this.cache) {
137
+ const key = `${entry.collection}:${entry.id}`;
138
+ if (entry.op === "DELETE") {
139
+ seen.delete(key);
140
+ }
141
+ else {
142
+ seen.set(key, entry);
143
+ }
144
+ }
145
+ this.cache = Array.from(seen.values());
146
+ this.dirty = true;
147
+ await this.flush();
148
+ }
149
+ clear() {
150
+ throw new Error("Method not implemented.");
151
+ }
152
+ }
153
+ exports.JSONStorage = JSONStorage;
@@ -0,0 +1,14 @@
1
+ import { BaseStorage, LogEntry, StorageConfig } from "./base";
2
+ export declare class MemoryStorage extends BaseStorage {
3
+ private logs;
4
+ private isInitialized;
5
+ constructor(config: StorageConfig);
6
+ initialize(): Promise<void>;
7
+ append(entry: LogEntry): Promise<void>;
8
+ readStream(): AsyncGenerator<LogEntry>;
9
+ flush(): Promise<void>;
10
+ close(): Promise<void>;
11
+ clear(): Promise<void>;
12
+ getSize(): number;
13
+ compact?(): Promise<void>;
14
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryStorage = void 0;
4
+ const base_1 = require("./base");
5
+ class MemoryStorage extends base_1.BaseStorage {
6
+ logs = [];
7
+ isInitialized = false;
8
+ constructor(config) {
9
+ super(config);
10
+ }
11
+ async initialize() {
12
+ this.isInitialized = true;
13
+ this.logs = [];
14
+ }
15
+ async append(entry) {
16
+ if (!this.isInitialized)
17
+ throw new Error("Storage not initialized");
18
+ this.logs.push({ ...entry });
19
+ }
20
+ async *readStream() {
21
+ for (const entry of this.logs) {
22
+ yield entry;
23
+ }
24
+ }
25
+ async flush() {
26
+ // Nada a fazer em memória
27
+ }
28
+ async close() {
29
+ this.logs = [];
30
+ this.isInitialized = false;
31
+ }
32
+ async clear() {
33
+ this.logs = [];
34
+ }
35
+ getSize() {
36
+ return this.logs.length;
37
+ }
38
+ compact() {
39
+ throw new Error("Method not implemented.");
40
+ }
41
+ }
42
+ exports.MemoryStorage = MemoryStorage;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,16 @@
1
+ export declare class LMCSError extends Error {
2
+ code: string;
3
+ constructor(message: string, code: string);
4
+ }
5
+ export declare class ValidationError extends LMCSError {
6
+ constructor(message: string);
7
+ }
8
+ export declare class CorruptionError extends LMCSError {
9
+ constructor(message: string);
10
+ }
11
+ export declare class ConcurrencyError extends LMCSError {
12
+ constructor(message: string);
13
+ }
14
+ export declare class TransactionError extends LMCSError {
15
+ constructor(message: string);
16
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransactionError = exports.ConcurrencyError = exports.CorruptionError = exports.ValidationError = exports.LMCSError = void 0;
4
+ class LMCSError extends Error {
5
+ code;
6
+ constructor(message, code) {
7
+ super(message);
8
+ this.code = code;
9
+ this.name = this.constructor.name;
10
+ Error.captureStackTrace(this, this.constructor);
11
+ }
12
+ }
13
+ exports.LMCSError = LMCSError;
14
+ class ValidationError extends LMCSError {
15
+ constructor(message) {
16
+ super(message, 'VALIDATION_ERROR');
17
+ }
18
+ }
19
+ exports.ValidationError = ValidationError;
20
+ class CorruptionError extends LMCSError {
21
+ constructor(message) {
22
+ super(message, 'DATA_CORRUPTION');
23
+ }
24
+ }
25
+ exports.CorruptionError = CorruptionError;
26
+ class ConcurrencyError extends LMCSError {
27
+ constructor(message) {
28
+ super(message, 'CONCURRENCY_ERROR');
29
+ }
30
+ }
31
+ exports.ConcurrencyError = ConcurrencyError;
32
+ class TransactionError extends LMCSError {
33
+ constructor(message) {
34
+ super(message, 'TRANSACTION_ERROR');
35
+ }
36
+ }
37
+ exports.TransactionError = TransactionError;
@@ -0,0 +1,9 @@
1
+ export declare class FileLocker {
2
+ private locks;
3
+ private queues;
4
+ acquire(filePath: string, options?: {
5
+ retries?: number;
6
+ }): Promise<void>;
7
+ release(filePath: string): Promise<void>;
8
+ withLock<T>(filePath: string, fn: () => Promise<T>): Promise<T>;
9
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileLocker = void 0;
37
+ const lockfile = __importStar(require("proper-lockfile"));
38
+ class FileLocker {
39
+ locks = new Map();
40
+ queues = new Map();
41
+ async acquire(filePath, options) {
42
+ const release = await lockfile.lock(filePath, {
43
+ retries: options?.retries ?? 5,
44
+ stale: 5000,
45
+ realpath: false
46
+ });
47
+ this.locks.set(filePath, release);
48
+ }
49
+ async release(filePath) {
50
+ const release = this.locks.get(filePath);
51
+ if (release) {
52
+ await release();
53
+ this.locks.delete(filePath);
54
+ }
55
+ }
56
+ async withLock(filePath, fn) {
57
+ // Serialize access within the same process
58
+ const currentQueue = this.queues.get(filePath) || Promise.resolve();
59
+ let result;
60
+ const nextPromise = currentQueue.then(async () => {
61
+ await this.acquire(filePath);
62
+ try {
63
+ result = await fn();
64
+ }
65
+ finally {
66
+ await this.release(filePath);
67
+ }
68
+ });
69
+ // Update queue, catching errors to ensure the chain continues
70
+ this.queues.set(filePath, nextPromise.catch(() => { }));
71
+ await nextPromise;
72
+ return result;
73
+ }
74
+ }
75
+ exports.FileLocker = FileLocker;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "lmcs-db",
3
- "version": "1.0.4",
4
- "description": "LmcsDb is a database with multiple storage engines and criptography",
3
+ "version": "2.0.0",
4
+ "description": "Lightweight Modular Collection Storage - Secure & Transactional",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
9
  "prepublishOnly": "npm run build",
10
- "test": "ts-node test/demo.ts"
10
+ "test": "jest"
11
11
  },
12
12
  "keywords": [
13
13
  "database",
@@ -37,11 +37,17 @@
37
37
  },
38
38
  "homepage": "https://github.com/leandroasilva/lmcs-db#readme",
39
39
  "dependencies": {
40
+ "proper-lockfile": "^4.1.2",
40
41
  "uuid": "^11.1.0"
41
42
  },
42
43
  "devDependencies": {
43
- "@types/node": "^18.0.0",
44
- "ts-node": "^10.0.0",
44
+ "@types/jest": "^30.0.0",
45
+ "@types/node": "^20.0.0",
46
+ "@types/proper-lockfile": "^4.1.4",
47
+ "@types/uuid": "^9.0.8",
48
+ "jest": "^30.2.0",
49
+ "ts-jest": "^29.4.6",
50
+ "tsx": "^4.21.0",
45
51
  "typescript": "^5.0.0"
46
52
  }
47
53
  }