@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liorandb/core",
3
- "version": "1.0.3",
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",
@@ -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
- this.rootPath = path.join(
8
- process.env.LIORANDB_PATH ||
9
- path.join(process.env.HOME || process.env.USERPROFILE),
10
- "LioranDB",
11
- "db"
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)) throw new Error(`Database "${oldName}" not found`);
87
- if (fs.existsSync(newPath)) throw new Error(`Database "${newName}" already exists`);
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
- // Update Operators
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
- // MongoDB-style
101
- db(name: string): Promise<LioranDB>;
13
+ db(name: string): Promise<any>;
102
14
 
103
- createDatabase(name: string): Promise<LioranDB>;
104
- openDatabase(name: string): Promise<LioranDB>;
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
  }
@@ -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
- const key = Buffer.from(getMasterKey(), "hex");
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); // random 16-byte IV
32
+ const iv = crypto.randomBytes(16);
9
33
  const data = Buffer.from(JSON.stringify(plainObj), "utf8");
10
34
 
11
- const cipher = crypto.createCipheriv(algorithm, key, iv);
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, key, iv);
48
+ const decipher = crypto.createDecipheriv(algorithm, ACTIVE_KEY, iv);
26
49
  decipher.setAuthTag(tag);
27
50
 
28
- const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
51
+ const decrypted = Buffer.concat([
52
+ decipher.update(encrypted),
53
+ decipher.final()
54
+ ]);
55
+
29
56
  return JSON.parse(decrypted.toString("utf8"));
30
57
  }
@@ -2,21 +2,20 @@ import crypto from "crypto";
2
2
  import os from "os";
3
3
 
4
4
  /**
5
- * Returns a 256-bit master key derived from machine hardware.
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 hardwareFingerprint = [
8
+ const fingerprint = [
10
9
  os.hostname(),
11
10
  os.platform(),
12
11
  os.arch(),
13
- os.cpus()?.[0]?.model || "unknown-cpu",
14
- os.cpus()?.length.toString() || "0",
15
- os.totalmem().toString()
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(hardwareFingerprint)
19
+ .update(fingerprint)
21
20
  .digest(); // 32 bytes
22
21
  }