@liorandb/core 1.0.8 → 1.0.10
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/chunk-DD5XS45L.js +703 -0
- package/dist/chunk-J7OD7MXK.js +741 -0
- package/dist/chunk-JETRGCOS.js +703 -0
- package/dist/chunk-JIGKUFXC.js +741 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.js +55 -0
- package/dist/collection.d.ts +16 -0
- package/dist/collection.js +36 -0
- package/dist/db.d.ts +13 -0
- package/dist/db.js +30 -0
- package/dist/index.d.ts +49 -3
- package/dist/index.js +5 -435
- package/dist/types.d.ts +18 -0
- package/dist/types.js +2 -0
- package/dist/utils/parseUri.d.ts +7 -0
- package/dist/utils/parseUri.js +20 -0
- package/dist/worker/dbWorker.d.ts +2 -0
- package/dist/worker/dbWorker.js +40 -0
- package/package.json +40 -38
- package/src/LioranManager.ts +140 -9
- package/src/core/collection.ts +278 -112
- package/src/core/database.ts +50 -9
- package/src/ipc/index.ts +56 -0
- package/src/ipc/queue.ts +78 -0
- package/src/types/index.ts +2 -1
- package/src/utils/schema.ts +14 -0
- package/src/worker/dbWorker.ts +45 -0
package/src/core/database.ts
CHANGED
|
@@ -2,6 +2,7 @@ import path from "path";
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import { Collection } from "./collection.js";
|
|
4
4
|
import type { LioranManager } from "../LioranManager.js";
|
|
5
|
+
import type { ZodSchema } from "zod";
|
|
5
6
|
|
|
6
7
|
export class LioranDB {
|
|
7
8
|
basePath: string;
|
|
@@ -20,9 +21,13 @@ export class LioranDB {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
/* -------------------------------- COLLECTION -------------------------------- */
|
|
25
|
+
|
|
26
|
+
collection<T = any>(name: string, schema?: ZodSchema<T>): Collection<T> {
|
|
24
27
|
if (this.collections.has(name)) {
|
|
25
|
-
|
|
28
|
+
const col = this.collections.get(name)!;
|
|
29
|
+
if (schema) col.setSchema(schema);
|
|
30
|
+
return col as Collection<T>;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
const colPath = path.join(this.basePath, name);
|
|
@@ -31,12 +36,16 @@ export class LioranDB {
|
|
|
31
36
|
fs.mkdirSync(colPath, { recursive: true });
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
const col = new Collection<T>(colPath);
|
|
39
|
+
const col = new Collection<T>(colPath, schema);
|
|
35
40
|
this.collections.set(name, col);
|
|
41
|
+
|
|
36
42
|
return col;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
async createCollection
|
|
45
|
+
async createCollection<T = any>(
|
|
46
|
+
name: string,
|
|
47
|
+
schema?: ZodSchema<T>
|
|
48
|
+
): Promise<Collection<T>> {
|
|
40
49
|
const colPath = path.join(this.basePath, name);
|
|
41
50
|
|
|
42
51
|
if (fs.existsSync(colPath)) {
|
|
@@ -44,8 +53,11 @@ export class LioranDB {
|
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
await fs.promises.mkdir(colPath, { recursive: true });
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
|
|
57
|
+
const col = new Collection<T>(colPath, schema);
|
|
58
|
+
this.collections.set(name, col);
|
|
59
|
+
|
|
60
|
+
return col;
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
async deleteCollection(name: string): Promise<boolean> {
|
|
@@ -68,8 +80,13 @@ export class LioranDB {
|
|
|
68
80
|
const oldPath = path.join(this.basePath, oldName);
|
|
69
81
|
const newPath = path.join(this.basePath, newName);
|
|
70
82
|
|
|
71
|
-
if (!fs.existsSync(oldPath))
|
|
72
|
-
|
|
83
|
+
if (!fs.existsSync(oldPath)) {
|
|
84
|
+
throw new Error("Collection does not exist");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (fs.existsSync(newPath)) {
|
|
88
|
+
throw new Error("New collection name already exists");
|
|
89
|
+
}
|
|
73
90
|
|
|
74
91
|
if (this.collections.has(oldName)) {
|
|
75
92
|
await this.collections.get(oldName)!.close();
|
|
@@ -77,7 +94,10 @@ export class LioranDB {
|
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
await fs.promises.rename(oldPath, newPath);
|
|
80
|
-
|
|
97
|
+
|
|
98
|
+
const col = new Collection(newPath);
|
|
99
|
+
this.collections.set(newName, col);
|
|
100
|
+
|
|
81
101
|
return true;
|
|
82
102
|
}
|
|
83
103
|
|
|
@@ -92,4 +112,25 @@ export class LioranDB {
|
|
|
92
112
|
|
|
93
113
|
return dirs.filter(d => d.isDirectory()).map(d => d.name);
|
|
94
114
|
}
|
|
115
|
+
|
|
116
|
+
/* -------------------------------- LIFECYCLE -------------------------------- */
|
|
117
|
+
|
|
118
|
+
async close(): Promise<void> {
|
|
119
|
+
for (const col of this.collections.values()) {
|
|
120
|
+
try {
|
|
121
|
+
await col.close();
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
this.collections.clear();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* -------------------------------- DEBUG -------------------------------- */
|
|
128
|
+
|
|
129
|
+
getStats() {
|
|
130
|
+
return {
|
|
131
|
+
dbName: this.dbName,
|
|
132
|
+
basePath: this.basePath,
|
|
133
|
+
collections: [...this.collections.keys()]
|
|
134
|
+
};
|
|
135
|
+
}
|
|
95
136
|
}
|
package/src/ipc/index.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { dbQueue } from "./queue.js";
|
|
2
|
+
|
|
3
|
+
type AnyFn = (...args: any[]) => Promise<any>;
|
|
4
|
+
|
|
5
|
+
/* -------------------------------- COLLECTION PROXY -------------------------------- */
|
|
6
|
+
|
|
7
|
+
class CollectionProxy {
|
|
8
|
+
constructor(
|
|
9
|
+
private dbName: string,
|
|
10
|
+
private collectionName: string
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
private call(method: string, params: any[]): Promise<any> {
|
|
14
|
+
return dbQueue.exec("op", {
|
|
15
|
+
db: this.dbName,
|
|
16
|
+
col: this.collectionName,
|
|
17
|
+
method,
|
|
18
|
+
params
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
insertOne = (doc: any) => this.call("insertOne", [doc]);
|
|
23
|
+
insertMany = (docs: any[]) => this.call("insertMany", [docs]);
|
|
24
|
+
find = (query?: any) => this.call("find", [query]);
|
|
25
|
+
findOne = (query?: any) => this.call("findOne", [query]);
|
|
26
|
+
updateOne = (filter: any, update: any, options?: any) =>
|
|
27
|
+
this.call("updateOne", [filter, update, options]);
|
|
28
|
+
updateMany = (filter: any, update: any) =>
|
|
29
|
+
this.call("updateMany", [filter, update]);
|
|
30
|
+
deleteOne = (filter: any) => this.call("deleteOne", [filter]);
|
|
31
|
+
deleteMany = (filter: any) => this.call("deleteMany", [filter]);
|
|
32
|
+
countDocuments = (filter?: any) =>
|
|
33
|
+
this.call("countDocuments", [filter]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* -------------------------------- DATABASE PROXY -------------------------------- */
|
|
37
|
+
|
|
38
|
+
class DBProxy {
|
|
39
|
+
constructor(private dbName: string) {}
|
|
40
|
+
|
|
41
|
+
collection(name: string) {
|
|
42
|
+
return new CollectionProxy(this.dbName, name);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* -------------------------------- MANAGER PROXY -------------------------------- */
|
|
47
|
+
|
|
48
|
+
class LioranManagerIPC {
|
|
49
|
+
async db(name: string) {
|
|
50
|
+
await dbQueue.exec("db", { db: name });
|
|
51
|
+
return new DBProxy(name);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const manager = new LioranManagerIPC();
|
|
56
|
+
export type { CollectionProxy, DBProxy };
|
package/src/ipc/queue.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { fork, ChildProcess } from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
function resolveWorkerPath() {
|
|
9
|
+
// dist/ipc/queue.js → dist/worker/dbWorker.js
|
|
10
|
+
return path.resolve(__dirname, "../dist/worker/dbWorker.js");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class DBQueue {
|
|
14
|
+
private worker: ChildProcess;
|
|
15
|
+
private seq = 0;
|
|
16
|
+
private pending = new Map<number, (r: any) => void>();
|
|
17
|
+
private isShutdown = false;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
const workerPath = resolveWorkerPath();
|
|
21
|
+
|
|
22
|
+
this.worker = fork(workerPath, [], {
|
|
23
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
this.worker.on("message", (msg: any) => {
|
|
27
|
+
const cb = this.pending.get(msg.id);
|
|
28
|
+
if (cb) {
|
|
29
|
+
this.pending.delete(msg.id);
|
|
30
|
+
cb(msg);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
this.worker.on("exit", () => {
|
|
35
|
+
this.isShutdown = true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Auto cleanup
|
|
39
|
+
process.on("exit", () => this.shutdown());
|
|
40
|
+
process.on("SIGINT", () => this.shutdown());
|
|
41
|
+
process.on("SIGTERM", () => this.shutdown());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
exec(action: string, args: any) {
|
|
45
|
+
if (this.isShutdown) {
|
|
46
|
+
return Promise.reject(new Error("DBQueue is shutdown"));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const id = ++this.seq;
|
|
51
|
+
|
|
52
|
+
this.pending.set(id, (msg) => {
|
|
53
|
+
if (msg.ok) resolve(msg.result);
|
|
54
|
+
else reject(new Error(msg.error));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.worker.send({ id, action, args });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async shutdown() {
|
|
62
|
+
if (this.isShutdown) return;
|
|
63
|
+
|
|
64
|
+
this.isShutdown = true;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
this.worker.send({ action: "shutdown" });
|
|
68
|
+
} catch {}
|
|
69
|
+
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
try {
|
|
72
|
+
this.worker.kill("SIGTERM");
|
|
73
|
+
} catch {}
|
|
74
|
+
}, 200);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const dbQueue = new DBQueue();
|
package/src/types/index.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ZodSchema } from "zod";
|
|
2
|
+
|
|
3
|
+
export function validateSchema<T>(schema: ZodSchema<T>, data: any): T {
|
|
4
|
+
const result = schema.safeParse(data);
|
|
5
|
+
|
|
6
|
+
if (!result.success) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"Schema validation failed:\n" +
|
|
9
|
+
JSON.stringify(result.error.format(), null, 2)
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return result.data;
|
|
14
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LioranManager } from "../LioranManager.js";
|
|
2
|
+
|
|
3
|
+
const manager = new LioranManager();
|
|
4
|
+
|
|
5
|
+
process.on("message", async (msg: any) => {
|
|
6
|
+
const { id, action, args } = msg;
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
let result;
|
|
10
|
+
|
|
11
|
+
switch (action) {
|
|
12
|
+
case "shutdown": {
|
|
13
|
+
await manager.closeAll();
|
|
14
|
+
result = true;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
case "db": {
|
|
19
|
+
const db = await manager.db(args.db);
|
|
20
|
+
result = true;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
case "collection": {
|
|
25
|
+
const db = await manager.db(args.db);
|
|
26
|
+
result = db.collection(args.collection);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case "op": {
|
|
31
|
+
const { db, col, method, params } = args;
|
|
32
|
+
const collection = (await manager.db(db)).collection(col);
|
|
33
|
+
result = await (collection as any)[method](...params);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
throw new Error("Unknown action");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
process.send?.({ id, ok: true, result });
|
|
42
|
+
} catch (err: any) {
|
|
43
|
+
process.send?.({ id, ok: false, error: err.message });
|
|
44
|
+
}
|
|
45
|
+
});
|