@liorandb/core 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/LioranManager.js +21 -34
- package/src/index.d.ts +7 -104
- package/src/utils/encryption.js +33 -6
- package/src/utils/secureKey.js +6 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liorandb/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "LioranDB is a lightweight, local-first, peer-to-peer, file-based database with a MongoDB-style Node.js API and a simple CLI for seamless distributed development.",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
package/src/LioranManager.js
CHANGED
|
@@ -1,34 +1,35 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import { LioranDB } from "./core/database.js";
|
|
4
|
+
import { setEncryptionKey } from "./utils/encryption.js";
|
|
5
|
+
import { getDefaultRootPath } from "./utils/rootpath.js";
|
|
4
6
|
|
|
5
7
|
export class LioranManager {
|
|
6
|
-
constructor() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
const {
|
|
10
|
+
rootPath,
|
|
11
|
+
encryptionKey
|
|
12
|
+
} = options;
|
|
13
|
+
|
|
14
|
+
// Root DB path (custom OR default)
|
|
15
|
+
this.rootPath = rootPath || getDefaultRootPath();
|
|
13
16
|
|
|
14
17
|
if (!fs.existsSync(this.rootPath)) {
|
|
15
18
|
fs.mkdirSync(this.rootPath, { recursive: true });
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
// Optional custom encryption key
|
|
22
|
+
if (encryptionKey) {
|
|
23
|
+
setEncryptionKey(encryptionKey);
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
this.openDBs = new Map();
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
// -----------------------------------------
|
|
22
|
-
// MongoDB-style: client.db("name")
|
|
23
|
-
// Auto-create database if not exists
|
|
24
|
-
// -----------------------------------------
|
|
25
29
|
async db(name) {
|
|
26
30
|
return this.openDatabase(name);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
// -----------------------------------------
|
|
30
|
-
// Create a new database (returns db instance)
|
|
31
|
-
// -----------------------------------------
|
|
32
33
|
async createDatabase(name) {
|
|
33
34
|
const dbPath = path.join(this.rootPath, name);
|
|
34
35
|
|
|
@@ -40,13 +41,9 @@ export class LioranManager {
|
|
|
40
41
|
return this.openDatabase(name);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
// -----------------------------------------
|
|
44
|
-
// Open DB - AUTO CREATE if missing
|
|
45
|
-
// -----------------------------------------
|
|
46
44
|
async openDatabase(name) {
|
|
47
45
|
const dbPath = path.join(this.rootPath, name);
|
|
48
46
|
|
|
49
|
-
// Auto-create if not exists
|
|
50
47
|
if (!fs.existsSync(dbPath)) {
|
|
51
48
|
await fs.promises.mkdir(dbPath, { recursive: true });
|
|
52
49
|
}
|
|
@@ -61,9 +58,6 @@ export class LioranManager {
|
|
|
61
58
|
return db;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
|
-
// -----------------------------------------
|
|
65
|
-
// Close database
|
|
66
|
-
// -----------------------------------------
|
|
67
61
|
async closeDatabase(name) {
|
|
68
62
|
if (!this.openDBs.has(name)) return;
|
|
69
63
|
|
|
@@ -76,25 +70,22 @@ export class LioranManager {
|
|
|
76
70
|
this.openDBs.delete(name);
|
|
77
71
|
}
|
|
78
72
|
|
|
79
|
-
// -----------------------------------------
|
|
80
|
-
// Rename database
|
|
81
|
-
// -----------------------------------------
|
|
82
73
|
async renameDatabase(oldName, newName) {
|
|
83
74
|
const oldPath = path.join(this.rootPath, oldName);
|
|
84
75
|
const newPath = path.join(this.rootPath, newName);
|
|
85
76
|
|
|
86
|
-
if (!fs.existsSync(oldPath))
|
|
87
|
-
|
|
77
|
+
if (!fs.existsSync(oldPath)) {
|
|
78
|
+
throw new Error(`Database "${oldName}" not found`);
|
|
79
|
+
}
|
|
80
|
+
if (fs.existsSync(newPath)) {
|
|
81
|
+
throw new Error(`Database "${newName}" already exists`);
|
|
82
|
+
}
|
|
88
83
|
|
|
89
84
|
await this.closeDatabase(oldName);
|
|
90
85
|
await fs.promises.rename(oldPath, newPath);
|
|
91
|
-
|
|
92
86
|
return true;
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
// -----------------------------------------
|
|
96
|
-
// Delete / Drop database
|
|
97
|
-
// -----------------------------------------
|
|
98
89
|
async deleteDatabase(name) {
|
|
99
90
|
return this.dropDatabase(name);
|
|
100
91
|
}
|
|
@@ -106,13 +97,9 @@ export class LioranManager {
|
|
|
106
97
|
|
|
107
98
|
await this.closeDatabase(name);
|
|
108
99
|
await fs.promises.rm(dbPath, { recursive: true, force: true });
|
|
109
|
-
|
|
110
100
|
return true;
|
|
111
101
|
}
|
|
112
102
|
|
|
113
|
-
// -----------------------------------------
|
|
114
|
-
// List all databases
|
|
115
|
-
// -----------------------------------------
|
|
116
103
|
async listDatabases() {
|
|
117
104
|
const items = await fs.promises.readdir(this.rootPath, { withFileTypes: true });
|
|
118
105
|
return items.filter(i => i.isDirectory()).map(i => i.name);
|
package/src/index.d.ts
CHANGED
|
@@ -1,107 +1,19 @@
|
|
|
1
|
-
// ================================================
|
|
2
|
-
// @liorandb/core - Type Definitions
|
|
3
|
-
// ================================================
|
|
4
|
-
|
|
5
1
|
declare module "@liorandb/core" {
|
|
6
|
-
// --------------------------
|
|
7
|
-
// Generic Query Operators
|
|
8
|
-
// --------------------------
|
|
9
|
-
|
|
10
|
-
export interface QueryOperators<T> {
|
|
11
|
-
$gt?: T;
|
|
12
|
-
$gte?: T;
|
|
13
|
-
$lt?: T;
|
|
14
|
-
$lte?: T;
|
|
15
|
-
$ne?: T;
|
|
16
|
-
$in?: T[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type FilterQuery<T> = {
|
|
20
|
-
[K in keyof T]?: T[K] | QueryOperators<T[K]>;
|
|
21
|
-
};
|
|
22
2
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export interface UpdateSet<T> {
|
|
28
|
-
$set?: Partial<T>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface UpdateInc<T> {
|
|
32
|
-
$inc?: {
|
|
33
|
-
[K in keyof T]?: number;
|
|
34
|
-
};
|
|
3
|
+
export interface LioranManagerOptions {
|
|
4
|
+
rootPath?: string;
|
|
5
|
+
encryptionKey?: string | Buffer;
|
|
35
6
|
}
|
|
36
7
|
|
|
37
|
-
export type UpdateQuery<T> = Partial<T> | (UpdateSet<T> & UpdateInc<T>);
|
|
38
|
-
|
|
39
|
-
// --------------------------
|
|
40
|
-
// Collection Class
|
|
41
|
-
// --------------------------
|
|
42
|
-
|
|
43
|
-
export class Collection<T extends { _id?: string }> {
|
|
44
|
-
constructor(dir: string);
|
|
45
|
-
|
|
46
|
-
close(): Promise<void>;
|
|
47
|
-
|
|
48
|
-
insertOne(doc: T): Promise<T>;
|
|
49
|
-
insertMany(docs: T[]): Promise<T[]>;
|
|
50
|
-
|
|
51
|
-
find(query?: FilterQuery<T>): Promise<T[]>;
|
|
52
|
-
findOne(query?: FilterQuery<T>): Promise<T | null>;
|
|
53
|
-
|
|
54
|
-
updateOne(
|
|
55
|
-
filter: FilterQuery<T>,
|
|
56
|
-
update: UpdateQuery<T>,
|
|
57
|
-
options?: { upsert?: boolean }
|
|
58
|
-
): Promise<T | null>;
|
|
59
|
-
|
|
60
|
-
updateMany(
|
|
61
|
-
filter: FilterQuery<T>,
|
|
62
|
-
update: UpdateQuery<T>
|
|
63
|
-
): Promise<T[]>;
|
|
64
|
-
|
|
65
|
-
deleteOne(filter: FilterQuery<T>): Promise<boolean>;
|
|
66
|
-
deleteMany(filter: FilterQuery<T>): Promise<number>;
|
|
67
|
-
|
|
68
|
-
countDocuments(filter?: FilterQuery<T>): Promise<number>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// --------------------------
|
|
72
|
-
// LioranDB: Database Instance
|
|
73
|
-
// --------------------------
|
|
74
|
-
|
|
75
|
-
export class LioranDB {
|
|
76
|
-
basePath: string;
|
|
77
|
-
dbName: string;
|
|
78
|
-
|
|
79
|
-
constructor(basePath: string, dbName: string, manager: LioranManager);
|
|
80
|
-
|
|
81
|
-
collection<T extends { _id?: string }>(name: string): Collection<T>;
|
|
82
|
-
|
|
83
|
-
createCollection(name: string): Promise<boolean>;
|
|
84
|
-
deleteCollection(name: string): Promise<boolean>;
|
|
85
|
-
dropCollection(name: string): Promise<boolean>;
|
|
86
|
-
renameCollection(oldName: string, newName: string): Promise<boolean>;
|
|
87
|
-
|
|
88
|
-
listCollections(): Promise<string[]>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// --------------------------
|
|
92
|
-
// LioranManager
|
|
93
|
-
// --------------------------
|
|
94
|
-
|
|
95
8
|
export class LioranManager {
|
|
96
9
|
rootPath: string;
|
|
97
10
|
|
|
98
|
-
constructor();
|
|
11
|
+
constructor(options?: LioranManagerOptions);
|
|
99
12
|
|
|
100
|
-
|
|
101
|
-
db(name: string): Promise<LioranDB>;
|
|
13
|
+
db(name: string): Promise<any>;
|
|
102
14
|
|
|
103
|
-
createDatabase(name: string): Promise<
|
|
104
|
-
openDatabase(name: string): Promise<
|
|
15
|
+
createDatabase(name: string): Promise<any>;
|
|
16
|
+
openDatabase(name: string): Promise<any>;
|
|
105
17
|
closeDatabase(name: string): Promise<void>;
|
|
106
18
|
|
|
107
19
|
renameDatabase(oldName: string, newName: string): Promise<boolean>;
|
|
@@ -111,13 +23,4 @@ declare module "@liorandb/core" {
|
|
|
111
23
|
|
|
112
24
|
listDatabases(): Promise<string[]>;
|
|
113
25
|
}
|
|
114
|
-
|
|
115
|
-
// --------------------------
|
|
116
|
-
// Utils
|
|
117
|
-
// --------------------------
|
|
118
|
-
|
|
119
|
-
export function getBaseDBFolder(): string;
|
|
120
|
-
|
|
121
|
-
// Package exports
|
|
122
|
-
export { LioranManager, LioranDB, getBaseDBFolder };
|
|
123
26
|
}
|
package/src/utils/encryption.js
CHANGED
|
@@ -2,17 +2,40 @@ import crypto from "crypto";
|
|
|
2
2
|
import { getMasterKey } from "./secureKey.js";
|
|
3
3
|
|
|
4
4
|
const algorithm = "aes-256-gcm";
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
// 🔐 Runtime-configurable key
|
|
7
|
+
let ACTIVE_KEY = getMasterKey();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Allows LioranManager to inject a custom encryption key
|
|
11
|
+
*/
|
|
12
|
+
export function setEncryptionKey(key) {
|
|
13
|
+
if (!key) return;
|
|
14
|
+
|
|
15
|
+
if (typeof key === "string") {
|
|
16
|
+
ACTIVE_KEY = crypto.createHash("sha256").update(key).digest();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (Buffer.isBuffer(key)) {
|
|
21
|
+
if (key.length !== 32) {
|
|
22
|
+
throw new Error("Encryption key must be 32 bytes (256-bit)");
|
|
23
|
+
}
|
|
24
|
+
ACTIVE_KEY = key;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
throw new Error("Invalid encryption key format");
|
|
29
|
+
}
|
|
6
30
|
|
|
7
31
|
export function encryptData(plainObj) {
|
|
8
|
-
const iv = crypto.randomBytes(16);
|
|
32
|
+
const iv = crypto.randomBytes(16);
|
|
9
33
|
const data = Buffer.from(JSON.stringify(plainObj), "utf8");
|
|
10
34
|
|
|
11
|
-
const cipher = crypto.createCipheriv(algorithm,
|
|
35
|
+
const cipher = crypto.createCipheriv(algorithm, ACTIVE_KEY, iv);
|
|
12
36
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
13
37
|
const tag = cipher.getAuthTag();
|
|
14
38
|
|
|
15
|
-
// store as iv + tag + data, base64 encoded
|
|
16
39
|
return Buffer.concat([iv, tag, encrypted]).toString("base64");
|
|
17
40
|
}
|
|
18
41
|
|
|
@@ -22,9 +45,13 @@ export function decryptData(encStr) {
|
|
|
22
45
|
const tag = buf.slice(16, 32);
|
|
23
46
|
const encrypted = buf.slice(32);
|
|
24
47
|
|
|
25
|
-
const decipher = crypto.createDecipheriv(algorithm,
|
|
48
|
+
const decipher = crypto.createDecipheriv(algorithm, ACTIVE_KEY, iv);
|
|
26
49
|
decipher.setAuthTag(tag);
|
|
27
50
|
|
|
28
|
-
const decrypted = Buffer.concat([
|
|
51
|
+
const decrypted = Buffer.concat([
|
|
52
|
+
decipher.update(encrypted),
|
|
53
|
+
decipher.final()
|
|
54
|
+
]);
|
|
55
|
+
|
|
29
56
|
return JSON.parse(decrypted.toString("utf8"));
|
|
30
57
|
}
|
package/src/utils/secureKey.js
CHANGED
|
@@ -2,21 +2,20 @@ import crypto from "crypto";
|
|
|
2
2
|
import os from "os";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* No env vars. No files. Machine-bound.
|
|
5
|
+
* Default machine-bound key (used ONLY if user does not supply one)
|
|
7
6
|
*/
|
|
8
7
|
export function getMasterKey() {
|
|
9
|
-
const
|
|
8
|
+
const fingerprint = [
|
|
10
9
|
os.hostname(),
|
|
11
10
|
os.platform(),
|
|
12
11
|
os.arch(),
|
|
13
|
-
os.cpus()?.[0]?.model || "unknown
|
|
14
|
-
os.cpus()?.length
|
|
15
|
-
os.totalmem()
|
|
12
|
+
os.cpus()?.[0]?.model || "unknown",
|
|
13
|
+
os.cpus()?.length || 0,
|
|
14
|
+
os.totalmem()
|
|
16
15
|
].join("|");
|
|
17
16
|
|
|
18
17
|
return crypto
|
|
19
18
|
.createHash("sha256")
|
|
20
|
-
.update(
|
|
19
|
+
.update(fingerprint)
|
|
21
20
|
.digest(); // 32 bytes
|
|
22
21
|
}
|