@liorandb/core 1.0.10 → 1.0.12

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 CHANGED
@@ -8,59 +8,71 @@ declare class Collection<T = any> {
8
8
  dir: string;
9
9
  db: ClassicLevel<string, string>;
10
10
  private queue;
11
- private walPath;
12
11
  private schema?;
13
12
  constructor(dir: string, schema?: ZodSchema<T>);
14
13
  setSchema(schema: ZodSchema<T>): void;
15
14
  private validate;
16
- private writeWAL;
17
- private clearWAL;
18
- private recoverFromWAL;
19
15
  private _enqueue;
20
- private _exec;
21
16
  close(): Promise<void>;
17
+ _exec(op: string, args: any[]): Promise<any>;
22
18
  insertOne(doc: T & {
23
19
  _id?: string;
24
- }): Promise<T>;
20
+ }): Promise<any>;
25
21
  insertMany(docs?: (T & {
26
22
  _id?: string;
27
- })[]): Promise<T[]>;
28
- find(query?: any): Promise<T[]>;
29
- findOne(query?: any): Promise<T | null>;
30
- updateOne(filter?: any, update?: any, options?: UpdateOptions$1): Promise<T | null>;
31
- updateMany(filter?: any, update?: any): Promise<T[]>;
32
- deleteOne(filter?: any): Promise<boolean>;
33
- deleteMany(filter?: any): Promise<number>;
34
- countDocuments(filter?: any): Promise<number>;
23
+ })[]): Promise<any>;
24
+ find(query?: any): Promise<any>;
25
+ findOne(query?: any): Promise<any>;
26
+ updateOne(filter: any, update: any, options?: UpdateOptions$1): Promise<any>;
27
+ updateMany(filter: any, update: any): Promise<any>;
28
+ deleteOne(filter: any): Promise<any>;
29
+ deleteMany(filter: any): Promise<any>;
30
+ countDocuments(filter?: any): Promise<any>;
35
31
  private _insertOne;
36
32
  private _insertMany;
33
+ private _findOne;
37
34
  private _updateOne;
38
35
  private _updateMany;
39
36
  private _find;
40
- private _findOne;
41
37
  private _deleteOne;
42
38
  private _deleteMany;
43
39
  private _countDocuments;
44
40
  }
45
41
 
42
+ type TXOp = {
43
+ tx: number;
44
+ col: string;
45
+ op: string;
46
+ args: any[];
47
+ };
48
+ type TXCommit = {
49
+ tx: number;
50
+ commit: true;
51
+ };
52
+ type WALEntry = TXOp | TXCommit;
53
+ declare class DBTransactionContext {
54
+ private db;
55
+ readonly txId: number;
56
+ private ops;
57
+ constructor(db: LioranDB, txId: number);
58
+ collection(name: string): {};
59
+ commit(): Promise<void>;
60
+ }
46
61
  declare class LioranDB {
47
62
  basePath: string;
48
63
  dbName: string;
49
64
  manager: LioranManager;
50
65
  collections: Map<string, Collection>;
66
+ private walPath;
67
+ private static TX_SEQ;
51
68
  constructor(basePath: string, dbName: string, manager: LioranManager);
69
+ writeWAL(entries: WALEntry[]): Promise<void>;
70
+ clearWAL(): Promise<void>;
71
+ private recoverFromWAL;
72
+ applyTransaction(ops: TXOp[]): Promise<void>;
52
73
  collection<T = any>(name: string, schema?: ZodSchema<T>): Collection<T>;
53
- createCollection<T = any>(name: string, schema?: ZodSchema<T>): Promise<Collection<T>>;
54
- deleteCollection(name: string): Promise<boolean>;
55
- renameCollection(oldName: string, newName: string): Promise<boolean>;
56
- dropCollection(name: string): Promise<boolean>;
57
- listCollections(): Promise<string[]>;
74
+ transaction<T>(fn: (tx: DBTransactionContext) => Promise<T>): Promise<T>;
58
75
  close(): Promise<void>;
59
- getStats(): {
60
- dbName: string;
61
- basePath: string;
62
- collections: string[];
63
- };
64
76
  }
65
77
 
66
78
  interface LioranManagerOptions$1 {
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  LioranDB,
3
3
  LioranManager,
4
4
  getBaseDBFolder
5
- } from "./chunk-J7OD7MXK.js";
5
+ } from "./chunk-ST6KMJQJ.js";
6
6
  export {
7
7
  LioranDB,
8
8
  LioranManager,
@@ -1,37 +1,35 @@
1
1
  import {
2
2
  LioranManager
3
- } from "../chunk-J7OD7MXK.js";
3
+ } from "../chunk-ST6KMJQJ.js";
4
4
 
5
5
  // src/worker/dbWorker.ts
6
- var manager = new LioranManager();
6
+ var manager = new LioranManager({ ipc: false });
7
7
  process.on("message", async (msg) => {
8
8
  const { id, action, args } = msg;
9
9
  try {
10
10
  let result;
11
11
  switch (action) {
12
- case "shutdown": {
12
+ case "shutdown":
13
13
  await manager.closeAll();
14
14
  result = true;
15
15
  break;
16
- }
17
- case "db": {
18
- const db = await manager.db(args.db);
16
+ case "db":
17
+ await manager.db(args.db);
19
18
  result = true;
20
19
  break;
21
- }
22
- case "collection": {
23
- const db = await manager.db(args.db);
24
- result = db.collection(args.collection);
25
- break;
26
- }
27
20
  case "op": {
28
21
  const { db, col, method, params } = args;
29
22
  const collection = (await manager.db(db)).collection(col);
30
23
  result = await collection[method](...params);
31
24
  break;
32
25
  }
26
+ case "tx": {
27
+ const db = await manager.db(args.db);
28
+ result = await db.transaction(args.fn);
29
+ break;
30
+ }
33
31
  default:
34
- throw new Error("Unknown action");
32
+ throw new Error("Unknown IPC action");
35
33
  }
36
34
  process.send?.({ id, ok: true, result });
37
35
  } catch (err) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@liorandb/core",
3
- "version": "1.0.10",
4
- "description": "**LioranDB Core Module** – Lightweight, local-first, peer-to-peer database management for Node.js.",
3
+ "version": "1.0.12",
4
+ "description": "LioranDB Core Module – Lightweight, local-first, peer-to-peer database management for Node.js.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
@@ -1,5 +1,3 @@
1
- import fs from "fs";
2
- import path from "path";
3
1
  import { ClassicLevel } from "classic-level";
4
2
  import { matchDocument, applyUpdate } from "./query.js";
5
3
  import { v4 as uuid } from "uuid";
@@ -14,18 +12,13 @@ export interface UpdateOptions {
14
12
  export class Collection<T = any> {
15
13
  dir: string;
16
14
  db: ClassicLevel<string, string>;
17
- private queue: Promise<any>;
18
- private walPath: string;
15
+ private queue: Promise<any> = Promise.resolve();
19
16
  private schema?: ZodSchema<T>;
20
17
 
21
18
  constructor(dir: string, schema?: ZodSchema<T>) {
22
19
  this.dir = dir;
23
- this.db = new ClassicLevel(dir);
24
- this.queue = Promise.resolve();
25
- this.walPath = path.join(dir, "__wal.log");
20
+ this.db = new ClassicLevel(dir, { valueEncoding: "utf8" });
26
21
  this.schema = schema;
27
-
28
- this.recoverFromWAL().catch(console.error);
29
22
  }
30
23
 
31
24
  setSchema(schema: ZodSchema<T>) {
@@ -36,294 +29,187 @@ export class Collection<T = any> {
36
29
  return this.schema ? validateSchema(this.schema, doc) : doc;
37
30
  }
38
31
 
39
- /* ---------------- WAL ---------------- */
40
-
41
- private async writeWAL(entry: any) {
42
- await fs.promises.appendFile(
43
- this.walPath,
44
- JSON.stringify(entry) + "\n"
45
- );
46
- }
47
-
48
- private async clearWAL() {
49
- if (fs.existsSync(this.walPath)) {
50
- await fs.promises.unlink(this.walPath);
51
- }
52
- }
53
-
54
- private async recoverFromWAL() {
55
- if (!fs.existsSync(this.walPath)) return;
56
-
57
- const lines = (await fs.promises.readFile(this.walPath, "utf8"))
58
- .split("\n")
59
- .filter(Boolean);
60
-
61
- for (const line of lines) {
62
- try {
63
- const { op, args } = JSON.parse(line);
64
- await this._exec(op, args, false);
65
- } catch (err) {
66
- console.error("WAL recovery failed:", err);
67
- }
68
- }
69
-
70
- await this.clearWAL();
71
- }
72
-
73
- /* ---------------- Queue ---------------- */
74
-
75
32
  private _enqueue<R>(task: () => Promise<R>): Promise<R> {
76
33
  this.queue = this.queue.then(task).catch(console.error);
77
34
  return this.queue;
78
35
  }
79
36
 
80
- /* ---------------- Core Executor ---------------- */
81
-
82
- private async _exec(op: string, args: any[], log = true) {
83
- if (log) await this.writeWAL({ op, args });
84
-
85
- let result: any;
37
+ async close(): Promise<void> {
38
+ try { await this.db.close(); } catch {}
39
+ }
86
40
 
41
+ async _exec(op: string, args: any[]) {
87
42
  switch (op) {
88
- case "insertOne":
89
- result = await this._insertOne(args[0]);
90
- break;
91
-
92
- case "insertMany":
93
- result = await this._insertMany(args[0]);
94
- break;
95
-
96
- case "find":
97
- result = await this._find(args[0]);
98
- break;
99
-
100
- case "findOne":
101
- result = await this._findOne(args[0]);
102
- break;
103
-
104
- case "updateOne":
105
- result = await this._updateOne(args[0], args[1], args[2]);
106
- break;
107
-
108
- case "updateMany":
109
- result = await this._updateMany(args[0], args[1]);
110
- break;
111
-
112
- case "deleteOne":
113
- result = await this._deleteOne(args[0]);
114
- break;
115
-
116
- case "deleteMany":
117
- result = await this._deleteMany(args[0]);
118
- break;
119
-
120
- case "countDocuments":
121
- result = await this._countDocuments(args[0]);
122
- break;
123
-
124
- default:
125
- throw new Error(`Unknown operation: ${op}`);
43
+ case "insertOne": return this._insertOne(args[0]);
44
+ case "insertMany": return this._insertMany(args[0]);
45
+ case "find": return this._find(args[0]);
46
+ case "findOne": return this._findOne(args[0]);
47
+ case "updateOne": return this._updateOne(args[0], args[1], args[2]);
48
+ case "updateMany": return this._updateMany(args[0], args[1]);
49
+ case "deleteOne": return this._deleteOne(args[0]);
50
+ case "deleteMany": return this._deleteMany(args[0]);
51
+ case "countDocuments": return this._countDocuments(args[0]);
52
+ default: throw new Error(`Unknown operation: ${op}`);
126
53
  }
127
-
128
- if (log) await this.clearWAL();
129
- return result;
130
54
  }
131
55
 
132
- /* ---------------- Public API ---------------- */
133
-
134
- async close(): Promise<void> {
135
- try {
136
- await this.db.close();
137
- } catch {}
138
- }
139
-
140
- insertOne(doc: T & { _id?: string }): Promise<T> {
56
+ insertOne(doc: T & { _id?: string }) {
141
57
  return this._enqueue(() => this._exec("insertOne", [doc]));
142
58
  }
143
59
 
144
- insertMany(docs: (T & { _id?: string })[] = []): Promise<T[]> {
60
+ insertMany(docs: (T & { _id?: string })[] = []) {
145
61
  return this._enqueue(() => this._exec("insertMany", [docs]));
146
62
  }
147
63
 
148
- find(query: any = {}): Promise<T[]> {
64
+ find(query: any = {}) {
149
65
  return this._enqueue(() => this._exec("find", [query]));
150
66
  }
151
67
 
152
- findOne(query: any = {}): Promise<T | null> {
68
+ findOne(query: any = {}) {
153
69
  return this._enqueue(() => this._exec("findOne", [query]));
154
70
  }
155
71
 
156
- updateOne(
157
- filter: any = {},
158
- update: any = {},
159
- options: UpdateOptions = { upsert: false }
160
- ): Promise<T | null> {
72
+ updateOne(filter: any, update: any, options: UpdateOptions = {}) {
161
73
  return this._enqueue(() =>
162
74
  this._exec("updateOne", [filter, update, options])
163
75
  );
164
76
  }
165
77
 
166
- updateMany(filter: any = {}, update: any = {}): Promise<T[]> {
78
+ updateMany(filter: any, update: any) {
167
79
  return this._enqueue(() =>
168
80
  this._exec("updateMany", [filter, update])
169
81
  );
170
82
  }
171
83
 
172
- deleteOne(filter: any = {}): Promise<boolean> {
173
- return this._enqueue(() => this._exec("deleteOne", [filter]));
84
+ deleteOne(filter: any) {
85
+ return this._enqueue(() =>
86
+ this._exec("deleteOne", [filter])
87
+ );
174
88
  }
175
89
 
176
- deleteMany(filter: any = {}): Promise<number> {
177
- return this._enqueue(() => this._exec("deleteMany", [filter]));
90
+ deleteMany(filter: any) {
91
+ return this._enqueue(() =>
92
+ this._exec("deleteMany", [filter])
93
+ );
178
94
  }
179
95
 
180
- countDocuments(filter: any = {}): Promise<number> {
96
+ countDocuments(filter: any = {}) {
181
97
  return this._enqueue(() =>
182
98
  this._exec("countDocuments", [filter])
183
99
  );
184
100
  }
185
101
 
186
- /* ---------------- Internal Ops ---------------- */
102
+ /* ---------------- Storage ---------------- */
187
103
 
188
- private async _insertOne(doc: T & { _id?: string }): Promise<T> {
104
+ private async _insertOne(doc: any) {
189
105
  const _id = doc._id ?? uuid();
190
106
  const final = this.validate({ _id, ...doc });
191
-
192
107
  await this.db.put(String(_id), encryptData(final));
193
108
  return final;
194
109
  }
195
110
 
196
- private async _insertMany(
197
- docs: (T & { _id?: string })[]
198
- ): Promise<T[]> {
199
- const ops: Array<{ type: "put"; key: string; value: string }> = [];
200
- const out: T[] = [];
111
+ private async _insertMany(docs: any[]) {
112
+ const batch: Array<{ type: "put"; key: string; value: string }> = [];
113
+ const out = [];
201
114
 
202
115
  for (const d of docs) {
203
116
  const _id = d._id ?? uuid();
204
117
  const final = this.validate({ _id, ...d });
205
-
206
- ops.push({
207
- type: "put",
208
- key: String(_id),
209
- value: encryptData(final)
210
- });
211
-
118
+ batch.push({ type: "put", key: String(_id), value: encryptData(final) });
212
119
  out.push(final);
213
120
  }
214
121
 
215
- await this.db.batch(ops);
122
+ await this.db.batch(batch);
216
123
  return out;
217
124
  }
218
125
 
219
- private async _updateOne(
220
- filter: any,
221
- update: any,
222
- options: UpdateOptions
223
- ): Promise<T | null> {
126
+ private async _findOne(query: any) {
127
+ if (query?._id) {
128
+ try {
129
+ const enc = await this.db.get(String(query._id));
130
+ return enc ? decryptData(enc) : null;
131
+ } catch { return null; }
132
+ }
133
+
134
+ for await (const [, enc] of this.db.iterator()) {
135
+ const v = decryptData(enc);
136
+ if (matchDocument(v, query)) return v;
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ private async _updateOne(filter: any, update: any, options: UpdateOptions) {
224
143
  for await (const [key, enc] of this.db.iterator()) {
225
144
  const value = decryptData(enc);
226
-
227
145
  if (matchDocument(value, filter)) {
228
- const updated = applyUpdate(value, update);
146
+ const updated = this.validate(applyUpdate(value, update)) as any;
229
147
  updated._id = value._id;
230
-
231
- const validated = this.validate(updated);
232
- await this.db.put(key, encryptData(validated));
233
-
234
- return validated;
148
+ await this.db.put(key, encryptData(updated));
149
+ return updated;
235
150
  }
236
151
  }
237
152
 
238
153
  if (options?.upsert) {
239
- const doc = applyUpdate(filter, update);
240
- doc._id ??= uuid();
241
-
242
- const validated = this.validate(doc);
243
- await this.db.put(String(doc._id), encryptData(validated));
244
-
245
- return validated;
154
+ const doc = this.validate({ _id: uuid(), ...applyUpdate({}, update) }) as any;
155
+ await this.db.put(String(doc._id), encryptData(doc));
156
+ return doc;
246
157
  }
247
158
 
248
159
  return null;
249
160
  }
250
161
 
251
- private async _updateMany(filter: any, update: any): Promise<T[]> {
252
- const updated: T[] = [];
162
+ private async _updateMany(filter: any, update: any) {
163
+ const out = [];
253
164
 
254
165
  for await (const [key, enc] of this.db.iterator()) {
255
166
  const value = decryptData(enc);
256
-
257
167
  if (matchDocument(value, filter)) {
258
- const doc = applyUpdate(value, update);
259
- doc._id = value._id;
260
-
261
- const validated = this.validate(doc);
262
- await this.db.put(key, encryptData(validated));
263
-
264
- updated.push(validated);
168
+ const updated = this.validate(applyUpdate(value, update)) as any;
169
+ updated._id = value._id;
170
+ await this.db.put(key, encryptData(updated));
171
+ out.push(updated);
265
172
  }
266
173
  }
267
174
 
268
- return updated;
269
- }
270
-
271
- private async _find(query: any): Promise<T[]> {
272
- const out: T[] = [];
273
-
274
- for await (const [, enc] of this.db.iterator()) {
275
- const value = decryptData(enc);
276
- if (matchDocument(value, query)) out.push(value);
277
- }
278
-
279
175
  return out;
280
176
  }
281
177
 
282
- private async _findOne(query: any): Promise<T | null> {
178
+ private async _find(query: any) {
179
+ const out = [];
283
180
  for await (const [, enc] of this.db.iterator()) {
284
- const value = decryptData(enc);
285
- if (matchDocument(value, query)) return value;
181
+ const v = decryptData(enc);
182
+ if (matchDocument(v, query)) out.push(v);
286
183
  }
287
-
288
- return null;
184
+ return out;
289
185
  }
290
186
 
291
- private async _deleteOne(filter: any): Promise<boolean> {
187
+ private async _deleteOne(filter: any) {
292
188
  for await (const [key, enc] of this.db.iterator()) {
293
- const value = decryptData(enc);
294
-
295
- if (matchDocument(value, filter)) {
189
+ if (matchDocument(decryptData(enc), filter)) {
296
190
  await this.db.del(key);
297
191
  return true;
298
192
  }
299
193
  }
300
-
301
194
  return false;
302
195
  }
303
196
 
304
- private async _deleteMany(filter: any): Promise<number> {
197
+ private async _deleteMany(filter: any) {
305
198
  let count = 0;
306
-
307
199
  for await (const [key, enc] of this.db.iterator()) {
308
- const value = decryptData(enc);
309
-
310
- if (matchDocument(value, filter)) {
200
+ if (matchDocument(decryptData(enc), filter)) {
311
201
  await this.db.del(key);
312
202
  count++;
313
203
  }
314
204
  }
315
-
316
205
  return count;
317
206
  }
318
207
 
319
- private async _countDocuments(filter: any): Promise<number> {
208
+ private async _countDocuments(filter: any) {
320
209
  let c = 0;
321
-
322
210
  for await (const [, enc] of this.db.iterator()) {
323
- const value = decryptData(enc);
324
- if (matchDocument(value, filter)) c++;
211
+ if (matchDocument(decryptData(enc), filter)) c++;
325
212
  }
326
-
327
213
  return c;
328
214
  }
329
- }
215
+ }