inibase 1.0.0-rc.56 → 1.0.0-rc.58

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/README.md CHANGED
@@ -10,10 +10,11 @@
10
10
 
11
11
  - **Lightweight** 🪶
12
12
  - **Minimalist** :white_circle: (but powerful)
13
- - **TypeScript** :large_blue_diamond:
13
+ - **100% TypeScript** :large_blue_diamond:
14
14
  - **Super-Fast** :zap: (built-in caching system)
15
+ - **ATOMIC** :lock: File lock for writing
15
16
  - **Built-in form-validation** included :sunglasses:
16
- - **Suitable for large data** :page_with_curl: (tested with 200K row)
17
+ - **Suitable for large data** :page_with_curl: (tested with 4M records)
17
18
  - **Support Compression** :eight_spoked_asterisk: (using built-in nodejs zlib)
18
19
  - **Support Table Joins** :link:
19
20
  - **Low memory-usage** :chart_with_downwards_trend: (3-5mb)
@@ -25,7 +26,7 @@
25
26
 
26
27
  ```js
27
28
  import Inibase from "inibase";
28
- const db = new Inibase("database_name");
29
+ const db = new Inibase("databaseName");
29
30
 
30
31
  // Get all items from "user" table
31
32
  const users = await db.get("user");
@@ -54,6 +55,21 @@ If you like Inibase, please sponsor: [GitHub Sponsors](https://github.com/sponso
54
55
 
55
56
  To simplify the idea, each database has tables, each table has columns, each column will be stored in a seperated file. When **POST**ing new data, it will be appended to the _head_ of each file as new line. When **GET**ing data, the file will be readed line-by-line so it can handle large data (without consuming a lot of resources), when **PUT**ing(updating) in a specific column, only one file will be opened and updated
56
57
 
58
+ ## Config (.env)
59
+
60
+ The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
61
+
62
+ ```ini
63
+ # Auto generated secret key, will be using for encrypting the IDs
64
+ INIBASE_SECRET=
65
+
66
+ INIBASE_COMPRESSION=true
67
+ INIBASE_CACHE=true
68
+
69
+ # Prepend new items to the beginning of file
70
+ INIBASE_REVERSE=true
71
+ ```
72
+
57
73
  ## Benchmark
58
74
 
59
75
  ### Bulk
@@ -76,7 +92,6 @@ To simplify the idea, each database has tables, each table has columns, each col
76
92
 
77
93
  Ps: Testing by default with `user` table, with username, email, password fields _so results include password encryption process_
78
94
 
79
-
80
95
  ## Roadmap
81
96
 
82
97
  - [x] Actions:
@@ -84,7 +99,7 @@ Ps: Testing by default with `user` table, with username, email, password fields
84
99
  - [x] Pagination
85
100
  - [x] Criteria
86
101
  - [x] Columns
87
- - [x] Order By (using UNIX commands)
102
+ - [x] Sort (using UNIX commands)
88
103
  - [x] POST
89
104
  - [x] PUT
90
105
  - [x] DELETE
@@ -126,7 +141,7 @@ Ps: Testing by default with `user` table, with username, email, password fields
126
141
 
127
142
  ```js
128
143
  import Inibase from "inibase";
129
- const db = new Inibase("/database_name");
144
+ const db = new Inibase("/databaseName");
130
145
 
131
146
  const user_schema = [
132
147
  {
@@ -271,7 +286,7 @@ Link two tables: "product" with "user"
271
286
 
272
287
  ```js
273
288
  import Inibase from "inibase";
274
- const db = new Inibase("/database_name");
289
+ const db = new Inibase("/databaseName");
275
290
 
276
291
  const product_schema = [
277
292
  {
@@ -337,7 +352,7 @@ const product = await db.post("product", product_data);
337
352
 
338
353
  ```js
339
354
  import Inibase from "inibase";
340
- const db = new Inibase("/database_name");
355
+ const db = new Inibase("/databaseName");
341
356
 
342
357
  // Get "user" by id
343
358
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
@@ -415,7 +430,7 @@ const users = await db.get("user", undefined, {
415
430
 
416
431
  ```js
417
432
  import Inibase from "inibase";
418
- const db = new Inibase("/database_name");
433
+ const db = new Inibase("/databaseName");
419
434
 
420
435
  // set "isActive" to "false" for all items in table "user"
421
436
  await db.put("user", { isActive: false });
@@ -434,7 +449,7 @@ await db.put("user", { isActive: false }, { isActive: true });
434
449
 
435
450
  ```js
436
451
  import Inibase from "inibase";
437
- const db = new Inibase("/database_name");
452
+ const db = new Inibase("/databaseName");
438
453
 
439
454
  // delete all items in "user" table
440
455
  await db.delete("user");
@@ -453,7 +468,7 @@ await db.put("user", { isActive: false });
453
468
 
454
469
  ```js
455
470
  import Inibase from "inibase";
456
- const db = new Inibase("/database_name");
471
+ const db = new Inibase("/databaseName");
457
472
 
458
473
  // get the sum of column "age" in "user" table
459
474
  await db.sum("user", "age");
@@ -469,7 +484,7 @@ await db.sum("user", ["age", ...], { isActive: false });
469
484
 
470
485
  ```js
471
486
  import Inibase from "inibase";
472
- const db = new Inibase("/database_name");
487
+ const db = new Inibase("/databaseName");
473
488
 
474
489
  // get the biggest number of column "age" in "user" table
475
490
  await db.max("user", "age");
@@ -485,7 +500,7 @@ await db.max("user", ["age", ...], { isActive: false });
485
500
 
486
501
  ```js
487
502
  import Inibase from "inibase";
488
- const db = new Inibase("/database_name");
503
+ const db = new Inibase("/databaseName");
489
504
 
490
505
  // get the smallest number of column "age" in "user" table
491
506
  await db.min("user", "age");
@@ -497,47 +512,22 @@ await db.min("user", ["age", ...], { isActive: false });
497
512
  </details>
498
513
 
499
514
  <details>
500
- <summary>createWorker</summary>
515
+ <summary>SORT</summary>
501
516
 
502
517
  ```js
503
518
  import Inibase from "inibase";
504
- const db = new Inibase("/database_name");
505
-
506
- // POST 10,000 USER
507
- await Promise.all(
508
- [...Array(10)]
509
- .map((x, i) => i)
510
- .map(
511
- (_index) =>
512
- db.createWorker("post", [
513
- "user",
514
- [...Array(1000)].map((_, i) => ({
515
- username: `username_${i + 1}`,
516
- email: `email_${i + 1}@test.com`,
517
- password: `password_${i + 1}`,
518
- })),
519
- ])
520
- )
521
- )
522
- ```
519
+ const db = new Inibase("/databaseName");
523
520
 
524
- </details>
525
-
526
- ## Config (.env)
527
-
528
- The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
529
-
530
- ```ini
531
- # Auto generated secret key, will be using for encrypting the IDs
532
- INIBASE_SECRET=
533
-
534
- INIBASE_COMPRESSION=true
535
- INIBASE_CACHE=true
521
+ // order users by the age column
522
+ await db.sort("user", "age");
536
523
 
537
- # Prepend new items to the beginning of file
538
- INIBASE_REVERSE=true
524
+ // order users by the age and username columns
525
+ await db.sort("user", ["age","username"]);
526
+ await db.sort("user", {age: -1, username: "asc"});
539
527
  ```
540
528
 
529
+ </details>
530
+
541
531
  ## License
542
532
 
543
533
  [MIT](./LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
package/dist/cli.js ADDED
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
3
+ import { createInterface } from "node:readline/promises";
4
+ import { parseArgs } from "node:util";
5
+ import Inibase from "./index.js";
6
+ import { basename } from "node:path";
7
+ import { isJSON, isNumber } from "./utils.js";
8
+ import Inison from "inison";
9
+ const { path } = parseArgs({
10
+ options: {
11
+ path: { type: "string", short: "p" },
12
+ },
13
+ }).values;
14
+ if (!path)
15
+ throw new Error("Please specify database folder path --path <databasePath> or -p <databasePath>");
16
+ const db = new Inibase(basename(path));
17
+ const rl = createInterface({
18
+ input: process.stdin,
19
+ output: process.stdout,
20
+ });
21
+ rl.prompt();
22
+ rl.on("line", async (input) => {
23
+ const trimedInput = input.trim();
24
+ if (trimedInput === "clear") {
25
+ console.clear();
26
+ rl.prompt();
27
+ }
28
+ if (trimedInput === "info") {
29
+ console.warn("war");
30
+ console.error("err");
31
+ }
32
+ const splitedInput = trimedInput.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g);
33
+ if (["get", "post", "delete", "put"].includes(splitedInput[0].toLocaleLowerCase())) {
34
+ const table = splitedInput[1];
35
+ if (!table)
36
+ throw new Error("Please specify table name");
37
+ let { where, page, perPage, columns, data, returnData } = parseArgs({
38
+ args: splitedInput.toSpliced(0, 2),
39
+ options: {
40
+ where: { type: "string", short: "w" },
41
+ page: { type: "string", short: "p" },
42
+ perPage: { type: "string", short: "l" },
43
+ columns: { type: "string", short: "c", multiple: true },
44
+ data: { type: "string", short: "d" },
45
+ returnData: { type: "boolean", short: "r" },
46
+ },
47
+ }).values;
48
+ if (where) {
49
+ if (isNumber(where))
50
+ where = Number(where);
51
+ else if (isJSON(where))
52
+ where = Inison.unstringify(where);
53
+ }
54
+ if (data) {
55
+ if (isJSON(data))
56
+ where = Inison.unstringify(data);
57
+ else
58
+ data = undefined;
59
+ }
60
+ switch (splitedInput[0].toLocaleLowerCase()) {
61
+ case "get":
62
+ console.log(await db.get(table, where, {
63
+ page: Number(page) ?? 1,
64
+ perPage: Number(perPage) ?? 15,
65
+ columns,
66
+ }));
67
+ break;
68
+ case "post":
69
+ {
70
+ const postReturn = await db.post(table, data, {
71
+ page: Number(page) ?? 1,
72
+ perPage: Number(perPage) ?? 15,
73
+ columns,
74
+ }, returnData);
75
+ console.log(postReturn !== null && typeof postReturn === "object"
76
+ ? "Item(s) Posted Successfully"
77
+ : postReturn);
78
+ }
79
+ break;
80
+ case "put": {
81
+ const putReturn = await db.put(table, data, where, {
82
+ page: Number(page) ?? 1,
83
+ perPage: Number(perPage) ?? 15,
84
+ columns,
85
+ }, returnData);
86
+ console.log(putReturn !== null && typeof putReturn === "object"
87
+ ? "Item(s) Updated Successfully"
88
+ : putReturn);
89
+ break;
90
+ }
91
+ case "delete":
92
+ console.log(await db.delete(table, where));
93
+ break;
94
+ default:
95
+ break;
96
+ }
97
+ }
98
+ });
package/dist/file.d.ts CHANGED
@@ -142,4 +142,3 @@ export declare const max: (filePath: string, lineNumbers?: number | number[]) =>
142
142
  * Note: Decodes each line as a number using the 'decode' function. Considers only numerical values for determining the minimum.
143
143
  */
144
144
  export declare const min: (filePath: string, lineNumbers?: number | number[]) => Promise<number>;
145
- export declare function createWorker(functionName: "get" | "remove" | "search" | "replace" | "sum" | "min" | "max" | "append" | "count", arg: any[]): Promise<any>;
package/dist/file.js CHANGED
@@ -1,12 +1,11 @@
1
- import { open, access, writeFile, readFile, constants as fsConstants, unlink, copyFile, appendFile, } from "node:fs/promises";
1
+ import { open, access, writeFile, readFile, constants as fsConstants, unlink, copyFile, appendFile, mkdir, } from "node:fs/promises";
2
2
  import { createInterface } from "node:readline";
3
3
  import { Transform } from "node:stream";
4
4
  import { pipeline } from "node:stream/promises";
5
5
  import { createGzip, createGunzip, gunzipSync, gzipSync } from "node:zlib";
6
- import { join } from "node:path";
7
- import { Worker } from "node:worker_threads";
6
+ import { dirname, join } from "node:path";
8
7
  import { detectFieldType, isArrayOfObjects, isJSON, isNumber, isObject, } from "./utils.js";
9
- import { encodeID, compare } from "./utils.server.js";
8
+ import { encodeID, compare, exec } from "./utils.server.js";
10
9
  import * as Config from "./config.js";
11
10
  import Inison from "inison";
12
11
  export const lock = async (folderPath, prefix) => {
@@ -31,15 +30,14 @@ export const unlock = async (folderPath, prefix) => {
31
30
  catch { }
32
31
  };
33
32
  export const write = async (filePath, data, disableCompression = false) => {
33
+ await mkdir(dirname(filePath), { recursive: true });
34
34
  await writeFile(filePath, Config.isCompressionEnabled && !disableCompression
35
35
  ? gzipSync(String(data))
36
36
  : String(data));
37
37
  };
38
- export const read = async (filePath, disableCompression = false) => {
39
- return Config.isCompressionEnabled && !disableCompression
40
- ? gunzipSync(await readFile(filePath)).toString()
41
- : (await readFile(filePath)).toString();
42
- };
38
+ export const read = async (filePath, disableCompression = false) => Config.isCompressionEnabled && !disableCompression
39
+ ? gunzipSync(await readFile(filePath)).toString()
40
+ : (await readFile(filePath)).toString();
43
41
  const _pipeline = async (rl, writeStream, transform) => {
44
42
  if (Config.isCompressionEnabled)
45
43
  await pipeline(rl, transform, createGzip(), writeStream);
@@ -52,18 +50,12 @@ const _pipeline = async (rl, writeStream, transform) => {
52
50
  * @param fileHandle - The file handle from which to create a read stream.
53
51
  * @returns A readline.Interface instance configured with the provided file stream.
54
52
  */
55
- const readLineInternface = (fileHandle) => {
56
- const [major, minor, patch] = process.versions.node.split(".").map(Number);
57
- return major > 18 ||
58
- (major === 18 && minor >= 11 && !Config.isCompressionEnabled)
59
- ? fileHandle.readLines()
60
- : createInterface({
61
- input: Config.isCompressionEnabled
62
- ? fileHandle.createReadStream().pipe(createGunzip())
63
- : fileHandle.createReadStream(),
64
- crlfDelay: Number.POSITIVE_INFINITY,
65
- });
66
- };
53
+ const readLineInternface = (fileHandle) => createInterface({
54
+ input: Config.isCompressionEnabled
55
+ ? fileHandle.createReadStream().pipe(createGunzip())
56
+ : fileHandle.createReadStream(),
57
+ crlfDelay: Number.POSITIVE_INFINITY,
58
+ });
67
59
  /**
68
60
  * Checks if a file or directory exists at the specified path.
69
61
  *
@@ -212,18 +204,28 @@ export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, s
212
204
  lines[linesCount] = decode(lastLine, fieldType, fieldChildrenType, secretKey);
213
205
  }
214
206
  else {
215
- const lineNumbersArray = new Set(Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers]);
216
- for await (const line of rl) {
217
- linesCount++;
218
- if (!lineNumbersArray.has(linesCount))
219
- continue;
220
- lines[linesCount] = decode(line, fieldType, fieldChildrenType, secretKey);
221
- lineNumbersArray.delete(linesCount);
222
- if (!lineNumbersArray.size && !readWholeFile)
223
- break;
207
+ lineNumbers = Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers];
208
+ if (readWholeFile) {
209
+ const lineNumbersArray = new Set(lineNumbers);
210
+ for await (const line of rl) {
211
+ linesCount++;
212
+ if (!lineNumbersArray.has(linesCount))
213
+ continue;
214
+ lines[linesCount] = decode(line, fieldType, fieldChildrenType, secretKey);
215
+ lineNumbersArray.delete(linesCount);
216
+ }
217
+ return [lines, linesCount];
218
+ }
219
+ const command = Config.isCompressionEnabled
220
+ ? `zcat ${filePath} | sed -n '${lineNumbers.join("p;")}p'`
221
+ : `sed -n '${lineNumbers.join("p;")}p' ${filePath}`, foundedLines = (await exec(command)).stdout.trim().split(/\r?\n/);
222
+ let index = 0;
223
+ for (const line of foundedLines) {
224
+ lines[lineNumbers[index]] = decode(line, fieldType, fieldChildrenType, secretKey);
225
+ index++;
224
226
  }
225
227
  }
226
- return readWholeFile ? [lines, linesCount] : lines;
228
+ return lines;
227
229
  }
228
230
  finally {
229
231
  // Ensure that file handles are closed, even if an error occurred
@@ -312,7 +314,7 @@ export const append = async (filePath, data) => {
312
314
  rl = readLineInternface(fileHandle);
313
315
  let isAppended = false;
314
316
  await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
315
- transform(line, encoding, callback) {
317
+ transform(line, _, callback) {
316
318
  if (!isAppended) {
317
319
  isAppended = true;
318
320
  return callback(null, `${Array.isArray(data) ? data.join("\n") : data}\n${line.length ? `${line}\n` : ""}`);
@@ -343,32 +345,41 @@ export const append = async (filePath, data) => {
343
345
  * Note: Creates a temporary file during the process and replaces the original file with it after removing lines.
344
346
  */
345
347
  export const remove = async (filePath, linesToDelete) => {
346
- let linesCount = 0;
347
- let deletedCount = 0;
348
- const fileHandle = await open(filePath, "r");
349
- const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
350
- const fileTempHandle = await open(fileTempPath, "w");
351
- const linesToDeleteArray = new Set(Array.isArray(linesToDelete)
348
+ linesToDelete = Array.isArray(linesToDelete)
352
349
  ? linesToDelete.map(Number)
353
- : [Number(linesToDelete)]);
354
- const rl = readLineInternface(fileHandle);
355
- await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
356
- transform(line, encoding, callback) {
357
- linesCount++;
358
- if (linesToDeleteArray.has(linesCount)) {
359
- deletedCount++;
350
+ : [Number(linesToDelete)];
351
+ const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
352
+ if (linesToDelete.length < 1000) {
353
+ const command = Config.isCompressionEnabled
354
+ ? `zcat ${filePath} | sed "${linesToDelete.join("d;")}d" | gzip > ${fileTempPath}`
355
+ : `sed "${linesToDelete.join("d;")}d" ${filePath} > ${fileTempPath}`;
356
+ await exec(command);
357
+ }
358
+ else {
359
+ let linesCount = 0;
360
+ let deletedCount = 0;
361
+ const fileHandle = await open(filePath, "r");
362
+ const fileTempHandle = await open(fileTempPath, "w");
363
+ const linesToDeleteArray = new Set(linesToDelete);
364
+ const rl = readLineInternface(fileHandle);
365
+ await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
366
+ transform(line, _, callback) {
367
+ linesCount++;
368
+ if (linesToDeleteArray.has(linesCount)) {
369
+ deletedCount++;
370
+ return callback();
371
+ }
372
+ return callback(null, `${line}\n`);
373
+ },
374
+ final(callback) {
375
+ if (deletedCount === linesCount)
376
+ this.push("\n");
360
377
  return callback();
361
- }
362
- return callback(null, `${line}\n`);
363
- },
364
- final(callback) {
365
- if (deletedCount === linesCount)
366
- this.push("\n");
367
- return callback();
368
- },
369
- }));
370
- await fileTempHandle.close();
371
- await fileHandle.close();
378
+ },
379
+ }));
380
+ await fileTempHandle.close();
381
+ await fileHandle.close();
382
+ }
372
383
  return [fileTempPath, filePath];
373
384
  };
374
385
  /**
@@ -456,7 +467,7 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
456
467
  * Note: Reads through the file line by line to count the total number of lines.
457
468
  */
458
469
  export const count = async (filePath) => {
459
- // return Number((await exec(`wc -l < ${filePath}`)).stdout.trim());
470
+ // Number((await exec(`wc -l < ${filePath}`)).stdout.trim());
460
471
  let linesCount = 0;
461
472
  if (await isExists(filePath)) {
462
473
  let fileHandle = null;
@@ -580,16 +591,3 @@ export const min = async (filePath, lineNumbers) => {
580
591
  await fileHandle.close();
581
592
  return min;
582
593
  };
583
- export function createWorker(functionName, arg) {
584
- return new Promise((resolve, reject) => {
585
- const worker = new Worker("./dist/file.thread.js", {
586
- workerData: { functionName, arg },
587
- });
588
- worker.on("message", (data) => {
589
- resolve(data);
590
- });
591
- worker.on("error", (msg) => {
592
- reject(`An error ocurred: ${msg}`);
593
- });
594
- });
595
- }
package/dist/index.d.ts CHANGED
@@ -47,13 +47,13 @@ export default class Inibase {
47
47
  database: string;
48
48
  table: string | null;
49
49
  pageInfo: Record<string, pageInfo>;
50
+ private fileExtension;
50
51
  private checkIFunique;
51
- private isThreadEnabled;
52
52
  private totalItems;
53
53
  salt: Buffer;
54
54
  constructor(database: string, mainFolder?: string, _table?: string | null, _totalItems?: Record<string, number>, _pageInfo?: Record<string, pageInfo>, _isThreadEnabled?: boolean);
55
55
  private throwError;
56
- createWorker(functionName: "get" | "post" | "put" | "delete" | "sum" | "min" | "max" | "sort", arg: any[]): Promise<any>;
56
+ private getFileExtension;
57
57
  private _schemaToIdsPath;
58
58
  setTableSchema(tableName: string, schema: Schema): Promise<void>;
59
59
  getTableSchema(tableName: string, encodeIDs?: boolean): Promise<Schema | undefined>;
@@ -82,6 +82,8 @@ export default class Inibase {
82
82
  put(tableName: string, data: Data[], where: number | string | (number | string)[] | Criteria | undefined, options: Options | undefined, returnUpdatedData: true): Promise<Data[] | null>;
83
83
  delete(tableName: string, where?: number | string, _id?: string | string[]): Promise<string | null>;
84
84
  delete(tableName: string, where?: (number | string)[] | Criteria, _id?: string | string[]): Promise<string[] | null>;
85
+ delete(tableName: string, where?: number, _id?: string | string[]): Promise<number | null>;
86
+ delete(tableName: string, where?: number[], _id?: string | string[]): Promise<number[] | null>;
85
87
  sum(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
86
88
  sum(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
87
89
  max(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
package/dist/index.js CHANGED
@@ -2,7 +2,6 @@ import { unlink, rename, mkdir, readdir } from "node:fs/promises";
2
2
  import { existsSync, appendFileSync, readFileSync } from "node:fs";
3
3
  import { join, parse } from "node:path";
4
4
  import { scryptSync, randomBytes } from "node:crypto";
5
- import { Worker } from "node:worker_threads";
6
5
  import { inspect } from "node:util";
7
6
  import Inison from "inison";
8
7
  import * as File from "./file.js";
@@ -14,8 +13,8 @@ export default class Inibase {
14
13
  database;
15
14
  table;
16
15
  pageInfo;
16
+ fileExtension = ".txt";
17
17
  checkIFunique;
18
- isThreadEnabled = false;
19
18
  totalItems;
20
19
  salt;
21
20
  constructor(database, mainFolder = ".", _table = null, _totalItems = {}, _pageInfo = {}, _isThreadEnabled = false) {
@@ -24,7 +23,6 @@ export default class Inibase {
24
23
  this.table = _table;
25
24
  this.totalItems = _totalItems;
26
25
  this.pageInfo = _pageInfo;
27
- this.isThreadEnabled = _isThreadEnabled;
28
26
  this.checkIFunique = {};
29
27
  if (!process.env.INIBASE_SECRET) {
30
28
  if (existsSync(".env") &&
@@ -35,6 +33,19 @@ export default class Inibase {
35
33
  }
36
34
  else
37
35
  this.salt = Buffer.from(process.env.INIBASE_SECRET, "hex");
36
+ // try {
37
+ // if (Config.isCompressionEnabled)
38
+ // execSync(
39
+ // `gzip -r ${join(this.folder, this.database)}/*/*.txt 2>/dev/null`,
40
+ // );
41
+ // else
42
+ // execSync(
43
+ // `gunzip -r ${join(
44
+ // this.folder,
45
+ // this.database,
46
+ // )}/*/*.txt.gz 2>/dev/null`,
47
+ // );
48
+ // } catch {}
38
49
  }
39
50
  throwError(code, variable, language = "en") {
40
51
  const errorMessages = {
@@ -62,26 +73,15 @@ export default class Inibase {
62
73
  : errorMessage.replaceAll("{variable}", `'${variable.toString()}'`)
63
74
  : errorMessage.replaceAll("{variable}", ""));
64
75
  }
65
- async createWorker(functionName, arg) {
66
- return new Promise((resolve, reject) => {
67
- const worker = new Worker("./dist/index.thread.js", {
68
- workerData: {
69
- _constructor: [
70
- this.database,
71
- this.folder,
72
- this.table,
73
- this.totalItems,
74
- this.pageInfo,
75
- true, // enable Thread
76
- ],
77
- functionName,
78
- arg,
79
- },
80
- });
81
- worker.on("message", resolve);
82
- worker.on("error", reject);
83
- });
84
- }
76
+ getFileExtension = () => {
77
+ let mainExtension = this.fileExtension;
78
+ // TODO: ADD ENCRYPTION
79
+ // if(Config.isEncryptionEnabled)
80
+ // mainExtension += ".enc"
81
+ if (Config.isCompressionEnabled)
82
+ mainExtension += ".gz";
83
+ return mainExtension;
84
+ };
85
85
  _schemaToIdsPath = (schema, prefix = "") => {
86
86
  const RETURN = {};
87
87
  for (const field of schema)
@@ -91,7 +91,7 @@ export default class Inibase {
91
91
  Utils.deepMerge(RETURN, this._schemaToIdsPath(field.children, `${(prefix ?? "") + field.key}.`));
92
92
  }
93
93
  else if (field.id)
94
- RETURN[field.id] = `${(prefix ?? "") + field.key}.inib`;
94
+ RETURN[field.id] = `${(prefix ?? "") + field.key}${this.getFileExtension()}`;
95
95
  return RETURN;
96
96
  };
97
97
  async setTableSchema(tableName, schema) {
@@ -161,7 +161,7 @@ export default class Inibase {
161
161
  schema = await this.getTableSchema(tableName);
162
162
  if (!schema)
163
163
  throw this.throwError("NO_SCHEMA", tableName);
164
- if (!(await File.isExists(join(tablePath, "id.inib"))))
164
+ if (!(await File.isExists(join(tablePath, `id${this.getFileExtension()}`))))
165
165
  throw this.throwError("NO_ITEMS", tableName);
166
166
  return schema;
167
167
  }
@@ -289,7 +289,7 @@ export default class Inibase {
289
289
  const field = Utils.getField(key, schema);
290
290
  if (!field)
291
291
  continue;
292
- const [searchResult, totalLines] = await File.search(join(tablePath, `${key}.inib`), Array.isArray(values) ? "=" : "[]", values, undefined, field.type, field.children, 1, undefined, false, this.salt);
292
+ const [searchResult, totalLines] = await File.search(join(tablePath, `${key}${this.getFileExtension()}`), Array.isArray(values) ? "=" : "[]", values, undefined, field.type, field.children, 1, undefined, false, this.salt);
293
293
  if (searchResult && totalLines > 0)
294
294
  throw this.throwError("FIELD_UNIQUE", [
295
295
  field.key,
@@ -378,7 +378,7 @@ export default class Inibase {
378
378
  _addPathToKeys = (obj, path) => {
379
379
  const newObject = {};
380
380
  for (const key in obj)
381
- newObject[join(path, `${key}.inib`)] = obj[key];
381
+ newObject[join(path, `${key}${this.getFileExtension()}`)] = obj[key];
382
382
  return newObject;
383
383
  };
384
384
  joinPathesContents(mainPath, data) {
@@ -428,7 +428,7 @@ export default class Inibase {
428
428
  async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
429
429
  const tablePath = join(this.folder, this.database, tableName);
430
430
  let RETURN = {};
431
- await Promise.all(schema.map(async (field) => {
431
+ for await (const field of schema) {
432
432
  if ((field.type === "array" ||
433
433
  (Array.isArray(field.type) && field.type.includes("array"))) &&
434
434
  field.children) {
@@ -436,83 +436,86 @@ export default class Inibase {
436
436
  if (field.children.filter((children) => children.type === "array" &&
437
437
  Utils.isArrayOfObjects(children.children)).length) {
438
438
  // one of children has array field type and has children array of object = Schema
439
- for (const [index, item] of Object.entries((await this.getItemsFromSchema(tableName, field.children.filter((children) => children.type === "array" &&
440
- Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`)) ?? {})) {
441
- this._getItemsFromSchemaHelper(RETURN, item, index, field);
442
- }
439
+ const childItems = await this.getItemsFromSchema(tableName, field.children.filter((children) => children.type === "array" &&
440
+ Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`);
441
+ if (childItems)
442
+ for (const [index, item] of Object.entries(childItems))
443
+ this._getItemsFromSchemaHelper(RETURN, item, index, field);
443
444
  field.children = field.children.filter((children) => children.type !== "array" ||
444
445
  !Utils.isArrayOfObjects(children.children));
445
446
  }
446
- for (const [index, item] of Object.entries((await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`)) ?? {})) {
447
- if (!RETURN[index])
448
- RETURN[index] = {};
449
- if (Utils.isObject(item)) {
450
- if (!Utils.isArrayOfNulls(Object.values(item))) {
451
- if (RETURN[index][field.key])
452
- Object.entries(item).forEach(([key, value], _index) => {
453
- for (let _index = 0; _index < value.length; _index++)
454
- if (RETURN[index][field.key][_index])
455
- Object.assign(RETURN[index][field.key][_index], {
456
- [key]: value[_index],
457
- });
458
- else
459
- RETURN[index][field.key][_index] = {
460
- [key]: value[_index],
461
- };
462
- });
463
- else if (Object.values(item).every((_i) => Utils.isArrayOfArrays(_i) || Array.isArray(_i)) &&
464
- prefix)
465
- RETURN[index][field.key] = item;
466
- else {
467
- RETURN[index][field.key] = [];
468
- Object.entries(item).forEach(([key, value], _ind) => {
469
- if (!Array.isArray(value)) {
470
- RETURN[index][field.key][_ind] = {};
471
- RETURN[index][field.key][_ind][key] = value;
472
- }
473
- else
474
- for (let _i = 0; _i < value.length; _i++) {
475
- if (value[_i] === null ||
476
- (Array.isArray(value[_i]) &&
477
- Utils.isArrayOfNulls(value[_i])))
478
- continue;
479
- if (!RETURN[index][field.key][_i])
480
- RETURN[index][field.key][_i] = {};
481
- RETURN[index][field.key][_i][key] = value[_i];
447
+ const fieldItems = await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
448
+ if (fieldItems)
449
+ for (const [index, item] of Object.entries(fieldItems)) {
450
+ if (!RETURN[index])
451
+ RETURN[index] = {};
452
+ if (Utils.isObject(item)) {
453
+ if (!Utils.isArrayOfNulls(Object.values(item))) {
454
+ if (RETURN[index][field.key])
455
+ Object.entries(item).forEach(([key, value], _index) => {
456
+ for (let _index = 0; _index < value.length; _index++)
457
+ if (RETURN[index][field.key][_index])
458
+ Object.assign(RETURN[index][field.key][_index], {
459
+ [key]: value[_index],
460
+ });
461
+ else
462
+ RETURN[index][field.key][_index] = {
463
+ [key]: value[_index],
464
+ };
465
+ });
466
+ else if (Object.values(item).every((_i) => Utils.isArrayOfArrays(_i) || Array.isArray(_i)) &&
467
+ prefix)
468
+ RETURN[index][field.key] = item;
469
+ else {
470
+ RETURN[index][field.key] = [];
471
+ Object.entries(item).forEach(([key, value], _ind) => {
472
+ if (!Array.isArray(value)) {
473
+ RETURN[index][field.key][_ind] = {};
474
+ RETURN[index][field.key][_ind][key] = value;
482
475
  }
483
- });
476
+ else
477
+ for (let _i = 0; _i < value.length; _i++) {
478
+ if (value[_i] === null ||
479
+ (Array.isArray(value[_i]) &&
480
+ Utils.isArrayOfNulls(value[_i])))
481
+ continue;
482
+ if (!RETURN[index][field.key][_i])
483
+ RETURN[index][field.key][_i] = {};
484
+ RETURN[index][field.key][_i][key] = value[_i];
485
+ }
486
+ });
487
+ }
484
488
  }
489
+ else
490
+ RETURN[index][field.key] = null;
485
491
  }
486
492
  else
487
- RETURN[index][field.key] = null;
493
+ RETURN[index][field.key] = item;
488
494
  }
489
- else
490
- RETURN[index][field.key] = item;
491
- }
492
495
  }
493
496
  else if (field.children === "table" ||
494
497
  (Array.isArray(field.type) && field.type.includes("table")) ||
495
498
  (Array.isArray(field.children) && field.children.includes("table"))) {
496
499
  if (field.table &&
497
500
  (await File.isExists(join(this.folder, this.database, field.table))) &&
498
- (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.inib`)))) {
501
+ (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`)))) {
499
502
  if (options.columns)
500
503
  options.columns = options.columns
501
504
  .filter((column) => column.includes(`${field.key}.`))
502
505
  .map((column) => column.replace(`${field.key}.`, ""));
503
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.inib`), linesNumber, field.type, field.children, this.salt);
506
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
504
507
  if (items)
505
- await Promise.allSettled(Object.entries(items).map(async ([index, item]) => {
508
+ for await (const [index, item] of Object.entries(items)) {
506
509
  if (!RETURN[index])
507
510
  RETURN[index] = {};
508
511
  RETURN[index][field.key] = item
509
512
  ? await this.get(field.table, item, options)
510
513
  : this.getDefaultValue(field);
511
- }));
514
+ }
512
515
  }
513
516
  }
514
- else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.inib`))) {
515
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.inib`), linesNumber, field.type, field.children, this.salt);
517
+ else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`))) {
518
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
516
519
  if (items)
517
520
  for (const [index, item] of Object.entries(items)) {
518
521
  if (!RETURN[index])
@@ -522,40 +525,42 @@ export default class Inibase {
522
525
  }
523
526
  }
524
527
  else if (field.type === "object") {
525
- for await (const [index, item] of Object.entries((await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`)) ?? {})) {
526
- if (!RETURN[index])
527
- RETURN[index] = {};
528
- if (Utils.isObject(item)) {
529
- if (!Object.values(item).every((i) => i === null))
530
- RETURN[index][field.key] = item;
528
+ const fieldItems = await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
529
+ if (fieldItems)
530
+ for (const [index, item] of Object.entries(fieldItems)) {
531
+ if (!RETURN[index])
532
+ RETURN[index] = {};
533
+ if (Utils.isObject(item)) {
534
+ if (!Object.values(item).every((i) => i === null))
535
+ RETURN[index][field.key] = item;
536
+ else
537
+ RETURN[index][field.key] = null;
538
+ }
531
539
  else
532
540
  RETURN[index][field.key] = null;
533
541
  }
534
- else
535
- RETURN[index][field.key] = null;
536
- }
537
542
  }
538
543
  else if (field.type === "table") {
539
544
  if (field.table &&
540
545
  (await File.isExists(join(this.folder, this.database, field.table))) &&
541
- (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.inib`)))) {
546
+ (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`)))) {
542
547
  if (options.columns)
543
548
  options.columns = options.columns
544
549
  .filter((column) => column.includes(`${field.key}.`))
545
550
  .map((column) => column.replace(`${field.key}.`, ""));
546
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.inib`), linesNumber, "number", undefined, this.salt);
551
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, "number", undefined, this.salt);
547
552
  if (items)
548
- await Promise.allSettled(Object.entries(items).map(async ([index, item]) => {
553
+ for await (const [index, item] of Object.entries(items)) {
549
554
  if (!RETURN[index])
550
555
  RETURN[index] = {};
551
556
  RETURN[index][field.key] = item
552
557
  ? await this.get(field.table, item, options)
553
558
  : this.getDefaultValue(field);
554
- }));
559
+ }
555
560
  }
556
561
  }
557
- else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.inib`))) {
558
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.inib`), linesNumber, field.type, field.children, this.salt);
562
+ else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`))) {
563
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
559
564
  if (items)
560
565
  for (const [index, item] of Object.entries(items)) {
561
566
  if (!RETURN[index])
@@ -568,7 +573,7 @@ export default class Inibase {
568
573
  { ...data, [field.key]: this.getDefaultValue(field) },
569
574
  ]));
570
575
  }
571
- }));
576
+ }
572
577
  return RETURN;
573
578
  }
574
579
  async applyCriteria(tableName, schema, options, criteria, allTrue) {
@@ -656,7 +661,7 @@ export default class Inibase {
656
661
  searchOperator = "=";
657
662
  searchComparedAtValue = value;
658
663
  }
659
- const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}.inib`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
664
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension()}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
660
665
  if (searchResult) {
661
666
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
662
667
  id,
@@ -701,7 +706,7 @@ export default class Inibase {
701
706
  }
702
707
  async clearCache(tablePath) {
703
708
  await Promise.all((await readdir(join(tablePath, ".cache")))
704
- ?.filter((fileName) => fileName !== "pagination.inib")
709
+ ?.filter((fileName) => fileName !== `pagination${this.fileExtension}`)
705
710
  .map(async (file) => unlink(join(tablePath, ".cache", file))));
706
711
  }
707
712
  async get(tableName, where, options = {
@@ -735,16 +740,14 @@ export default class Inibase {
735
740
  RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
736
741
  index +
737
742
  1), options));
738
- if (Config.isCacheEnabled &&
739
- (await File.isExists(join(tablePath, ".cache", "pagination.inib"))))
740
- this.totalItems[`${tableName}-*`] = Number((await File.read(join(tablePath, ".cache", "pagination.inib"), true)).split(",")[1]);
743
+ if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
744
+ this.totalItems[`${tableName}-*`] = Number((await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true)).split(",")[1]);
741
745
  else {
742
- let [lastId, totalItems] = await File.get(join(tablePath, "id.inib"), -1, "number", undefined, this.salt, true);
746
+ let [lastId, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
743
747
  if (lastId)
744
748
  lastId = Number(Object.keys(lastId)?.[0] ?? 0);
745
749
  this.totalItems[`${tableName}-*`] = totalItems;
746
- if (Config.isCacheEnabled)
747
- await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems}`, true);
750
+ await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
748
751
  }
749
752
  }
750
753
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -767,7 +770,7 @@ export default class Inibase {
767
770
  let Ids = where;
768
771
  if (!Array.isArray(Ids))
769
772
  Ids = [Ids];
770
- const [lineNumbers, countItems] = await File.search(join(tablePath, "id.inib"), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[`${tableName}-*`], this.salt);
773
+ const [lineNumbers, countItems] = await File.search(join(tablePath, `id${this.getFileExtension()}`), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[`${tableName}-*`], this.salt);
771
774
  if (!lineNumbers)
772
775
  throw this.throwError("NO_RESULTS", tableName);
773
776
  if (onlyLinesNumbers)
@@ -784,7 +787,7 @@ export default class Inibase {
784
787
  let cachedFilePath = "";
785
788
  // Criteria
786
789
  if (Config.isCacheEnabled)
787
- cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}.inib`);
790
+ cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}${this.fileExtension}`);
788
791
  if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
789
792
  const cachedItems = (await File.read(cachedFilePath, true)).split(",");
790
793
  this.totalItems[`${tableName}-*`] = cachedItems.length;
@@ -837,15 +840,14 @@ export default class Inibase {
837
840
  let lastId = 0, totalItems = 0, renameList = [];
838
841
  try {
839
842
  await File.lock(join(tablePath, ".tmp"), keys);
840
- if (await File.isExists(join(tablePath, "id.inib"))) {
841
- if (Config.isCacheEnabled &&
842
- (await File.isExists(join(tablePath, ".cache", "pagination.inib"))))
843
- [lastId, totalItems] = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
843
+ if (await File.isExists(join(tablePath, `id${this.getFileExtension()}`))) {
844
+ if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
845
+ [lastId, totalItems] = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
844
846
  .split(",")
845
847
  .map(Number);
846
848
  else {
847
849
  let lastIdObj = null;
848
- [lastIdObj, totalItems] = await File.get(join(tablePath, "id.inib"), -1, "number", undefined, this.salt, true);
850
+ [lastIdObj, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
849
851
  if (lastIdObj)
850
852
  lastId = Number(Object.keys(lastIdObj)?.[0] ?? 0);
851
853
  }
@@ -870,16 +872,13 @@ export default class Inibase {
870
872
  ? RETURN.toReversed()
871
873
  : RETURN
872
874
  : RETURN);
873
- await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
874
- ? await File.createWorker("append", [path, content])
875
- : await File.append(path, content))));
875
+ await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content))));
876
876
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
877
877
  renameList = [];
878
878
  totalItems += Array.isArray(RETURN) ? RETURN.length : 1;
879
- if (Config.isCacheEnabled) {
879
+ if (Config.isCacheEnabled)
880
880
  await this.clearCache(tablePath);
881
- await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems}`, true);
882
- }
881
+ await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
883
882
  if (returnPostedData)
884
883
  return this.get(tableName, Config.isReverseEnabled
885
884
  ? Array.isArray(RETURN)
@@ -919,13 +918,12 @@ export default class Inibase {
919
918
  return this.put(tableName, data, data.id);
920
919
  }
921
920
  let totalItems;
922
- if (Config.isCacheEnabled &&
923
- (await File.isExists(join(tablePath, ".cache", "pagination.inib"))))
924
- totalItems = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
921
+ if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
922
+ totalItems = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
925
923
  .split(",")
926
924
  .map(Number)[1];
927
925
  else
928
- totalItems = await File.count(join(tablePath, "id.inib"));
926
+ totalItems = await File.count(join(tablePath, `id${this.getFileExtension()}`));
929
927
  const pathesContents = this.joinPathesContents(tablePath, {
930
928
  ...(({ id, ...restOfData }) => restOfData)(data),
931
929
  updatedAt: Date.now(),
@@ -936,9 +934,7 @@ export default class Inibase {
936
934
  const replacementObject = {};
937
935
  for (const index of [...Array(totalItems)].keys())
938
936
  replacementObject[`${index + 1}`] = content;
939
- renameList.push(this.isThreadEnabled
940
- ? await File.createWorker("replace", [path, replacementObject])
941
- : await File.replace(path, replacementObject));
937
+ renameList.push(await File.replace(path, replacementObject));
942
938
  }));
943
939
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
944
940
  if (Config.isCacheEnabled)
@@ -976,9 +972,7 @@ export default class Inibase {
976
972
  .join("."));
977
973
  try {
978
974
  await File.lock(join(tablePath, ".tmp"), keys);
979
- await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
980
- ? await File.createWorker("replace", [path, content])
981
- : await File.replace(path, content))));
975
+ await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
982
976
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
983
977
  renameList = [];
984
978
  if (Config.isCacheEnabled)
@@ -1026,31 +1020,23 @@ export default class Inibase {
1026
1020
  if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1027
1021
  Utils.isNumber(where)) {
1028
1022
  // "where" in this case, is the line(s) number(s) and not id(s)
1029
- const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(".inib"));
1023
+ const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension()));
1030
1024
  if (files.length) {
1031
- if (!_id)
1032
- _id = Object.entries((await File.get(join(tablePath, "id.inib"), where, "number", undefined, this.salt)) ?? {}).map(([_, id]) => UtilsServer.encodeID(id, this.salt));
1033
- if (!_id.length)
1034
- throw this.throwError("NO_RESULTS", tableName);
1035
1025
  try {
1036
1026
  await File.lock(join(tablePath, ".tmp"));
1037
- await Promise.all(files.map(async (file) => renameList.push(this.isThreadEnabled
1038
- ? await File.createWorker("remove", [
1039
- join(tablePath, file),
1040
- where,
1041
- ])
1042
- : await File.remove(join(tablePath, file), where))));
1027
+ await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
1043
1028
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1044
- if (Config.isCacheEnabled) {
1029
+ if (Config.isCacheEnabled)
1045
1030
  await this.clearCache(tablePath);
1046
- if (await File.isExists(join(tablePath, ".cache", "pagination.inib"))) {
1047
- const [lastId, totalItems] = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
1048
- .split(",")
1049
- .map(Number);
1050
- await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
1051
- }
1031
+ if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`))) {
1032
+ const [lastId, totalItems] = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
1033
+ .split(",")
1034
+ .map(Number);
1035
+ await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
1052
1036
  }
1053
- return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
1037
+ if (_id)
1038
+ return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
1039
+ return where;
1054
1040
  }
1055
1041
  finally {
1056
1042
  if (renameList.length)
@@ -1073,7 +1059,7 @@ export default class Inibase {
1073
1059
  if (!Array.isArray(columns))
1074
1060
  columns = [columns];
1075
1061
  for await (const column of columns) {
1076
- const columnPath = join(tablePath, `${column}.inib`);
1062
+ const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1077
1063
  if (await File.isExists(columnPath)) {
1078
1064
  if (where) {
1079
1065
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
@@ -1093,7 +1079,7 @@ export default class Inibase {
1093
1079
  if (!Array.isArray(columns))
1094
1080
  columns = [columns];
1095
1081
  for await (const column of columns) {
1096
- const columnPath = join(tablePath, `${column}.inib`);
1082
+ const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1097
1083
  if (await File.isExists(columnPath)) {
1098
1084
  if (where) {
1099
1085
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
@@ -1113,7 +1099,7 @@ export default class Inibase {
1113
1099
  if (!Array.isArray(columns))
1114
1100
  columns = [columns];
1115
1101
  for await (const column of columns) {
1116
- const columnPath = join(tablePath, `${column}.inib`);
1102
+ const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1117
1103
  if (await File.isExists(columnPath)) {
1118
1104
  if (where) {
1119
1105
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
@@ -1131,7 +1117,6 @@ export default class Inibase {
1131
1117
  page: 1,
1132
1118
  perPage: 15,
1133
1119
  }) {
1134
- // TO-DO: Cache Results based on "Columns and Sort Direction"
1135
1120
  const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1136
1121
  // Default values for page and perPage
1137
1122
  options.page = options.page || 1;
@@ -1155,7 +1140,7 @@ export default class Inibase {
1155
1140
  cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
1156
1141
  if (where) {
1157
1142
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1158
- keepItems = Object.values((await File.get(join(tablePath, "id.inib"), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
1143
+ keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension()}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
1159
1144
  isLineNumbers = false;
1160
1145
  if (!keepItems.length)
1161
1146
  throw this.throwError("NO_RESULTS", tableName);
@@ -1165,7 +1150,7 @@ export default class Inibase {
1165
1150
  keepItems = Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
1166
1151
  index +
1167
1152
  1);
1168
- const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}.inib`));
1153
+ const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension()}`));
1169
1154
  // Construct the paste command to merge files and filter lines by IDs
1170
1155
  const pasteCommand = `paste ${filesPathes.join(" ")}`;
1171
1156
  // Construct the sort command dynamically based on the number of files for sorting
@@ -1190,10 +1175,10 @@ export default class Inibase {
1190
1175
  await File.lock(join(tablePath, ".tmp"), cacheKey);
1191
1176
  // Combine the commands
1192
1177
  // Execute the command synchronously
1193
- const { stdout, stderr } = await UtilsServer.exec(Config.isCacheEnabled
1194
- ? (await File.isExists(join(tablePath, ".cache", `${cacheKey}.inib`)))
1195
- ? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}.inib`)}`
1196
- : `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}.inib`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}.inib`)}`
1178
+ const { stdout } = await UtilsServer.exec(Config.isCacheEnabled
1179
+ ? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
1180
+ ? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
1181
+ : `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
1197
1182
  : `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
1198
1183
  encoding: "utf-8",
1199
1184
  });
@@ -1202,7 +1187,7 @@ export default class Inibase {
1202
1187
  const outputArray = lines.map((line) => {
1203
1188
  const splitedFileColumns = line.split("\t"); // Assuming tab-separated columns
1204
1189
  const outputObject = {};
1205
- // Extract values for each file, including "id.inib"
1190
+ // Extract values for each file, including `id${this.getFileExtension()}`
1206
1191
  filesPathes.forEach((fileName, index) => {
1207
1192
  const Field = Utils.getField(parse(fileName).name, schema);
1208
1193
  if (Field)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.56",
3
+ "version": "1.0.0-rc.58",
4
4
  "author": {
5
5
  "name": "Karim Amahtil",
6
6
  "email": "karim.amahtil@gmail.com"
@@ -9,9 +9,7 @@
9
9
  "main": "./dist/index.js",
10
10
  "exports": {
11
11
  ".": "./dist/index.js",
12
- "./thread": "./dist/index.thread.js",
13
12
  "./file": "./dist/file.js",
14
- "./file.thread": "./dist/file.thread.js",
15
13
  "./config": "./dist/config.js",
16
14
  "./utils": "./dist/utils.js",
17
15
  "./utils.server": "./dist/utils.server.js"
@@ -49,6 +47,9 @@
49
47
  "pocketbase"
50
48
  ],
51
49
  "license": "MIT",
50
+ "bin": {
51
+ "inibase": "./dist/cli.js"
52
+ },
52
53
  "type": "module",
53
54
  "types": "./dist",
54
55
  "typesVersions": {
@@ -56,15 +57,9 @@
56
57
  ".": [
57
58
  "./dist/index.d.ts"
58
59
  ],
59
- "thread": [
60
- "./dist/index.thread.d.ts"
61
- ],
62
60
  "file": [
63
61
  "./dist/file.d.ts"
64
62
  ],
65
- "file.thread": [
66
- "./dist/file.thread.d.ts"
67
- ],
68
63
  "utils": [
69
64
  "./dist/utils.d.ts"
70
65
  ],
@@ -77,18 +72,18 @@
77
72
  }
78
73
  },
79
74
  "devDependencies": {
80
- "@types/node": "^20.10.6",
81
- "tinybench": "^2.6.0",
82
- "typescript": "^5.3.3"
75
+ "@types/node": "^20.12.11",
76
+ "tinybench": "^2.6.0"
83
77
  },
84
78
  "dependencies": {
79
+ "commander": "^12.0.0",
80
+ "dotenv": "^16.4.5",
85
81
  "inison": "^1.0.0-rc.2"
86
82
  },
87
83
  "scripts": {
88
- "build": "npx tsc",
89
- "test": "npx tsx watch --expose-gc --env-file=.env ./index.test",
90
- "benchmark": "npx tsx --env-file=.env ./benchmark/index",
91
- "benchmark:single": "npx tsx --expose-gc --env-file=.env ./benchmark/single",
92
- "benchmark:bulk": "npx tsx --expose-gc --env-file=.env ./benchmark/bulk"
84
+ "build": "tsc",
85
+ "benchmark": "tsx --env-file=.env ./benchmark/index",
86
+ "benchmark:single": "tsx --expose-gc --env-file=.env ./benchmark/single",
87
+ "benchmark:bulk": "tsx --expose-gc --env-file=.env ./benchmark/bulk"
93
88
  }
94
89
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1,5 +0,0 @@
1
- import { parentPort, workerData } from "node:worker_threads";
2
- import * as File from "./file.js";
3
- const { functionName, arg } = workerData;
4
- // @ts-ignore
5
- File[functionName](...arg).then((res) => parentPort.postMessage(res));
@@ -1 +0,0 @@
1
- export {};
@@ -1,6 +0,0 @@
1
- import Inibase from "./index.js";
2
- import { parentPort, workerData } from "node:worker_threads";
3
- const { _constructor, functionName, arg } = workerData;
4
- // @ts-ignore
5
- new Inibase(..._constructor)[functionName](...arg)
6
- .then((res) => parentPort?.postMessage(res));