inibase 1.0.0-rc.55 → 1.0.0-rc.57

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Inibase
3
+ Copyright (c) 2024 Inibase
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -10,8 +10,9 @@
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
17
  - **Suitable for large data** :page_with_curl: (tested with 200K row)
17
18
  - **Support Compression** :eight_spoked_asterisk: (using built-in nodejs zlib)
@@ -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");
@@ -77,6 +78,21 @@ To simplify the idea, each database has tables, each table has columns, each col
77
78
  Ps: Testing by default with `user` table, with username, email, password fields _so results include password encryption process_
78
79
 
79
80
 
81
+ ## Config (.env)
82
+
83
+ The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
84
+
85
+ ```ini
86
+ # Auto generated secret key, will be using for encrypting the IDs
87
+ INIBASE_SECRET=
88
+
89
+ INIBASE_COMPRESSION=true
90
+ INIBASE_CACHE=true
91
+
92
+ # Prepend new items to the beginning of file
93
+ INIBASE_REVERSE=true
94
+ ```
95
+
80
96
  ## Roadmap
81
97
 
82
98
  - [x] Actions:
@@ -126,7 +142,7 @@ Ps: Testing by default with `user` table, with username, email, password fields
126
142
 
127
143
  ```js
128
144
  import Inibase from "inibase";
129
- const db = new Inibase("/database_name");
145
+ const db = new Inibase("/databaseName");
130
146
 
131
147
  const user_schema = [
132
148
  {
@@ -271,7 +287,7 @@ Link two tables: "product" with "user"
271
287
 
272
288
  ```js
273
289
  import Inibase from "inibase";
274
- const db = new Inibase("/database_name");
290
+ const db = new Inibase("/databaseName");
275
291
 
276
292
  const product_schema = [
277
293
  {
@@ -337,7 +353,7 @@ const product = await db.post("product", product_data);
337
353
 
338
354
  ```js
339
355
  import Inibase from "inibase";
340
- const db = new Inibase("/database_name");
356
+ const db = new Inibase("/databaseName");
341
357
 
342
358
  // Get "user" by id
343
359
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
@@ -415,7 +431,7 @@ const users = await db.get("user", undefined, {
415
431
 
416
432
  ```js
417
433
  import Inibase from "inibase";
418
- const db = new Inibase("/database_name");
434
+ const db = new Inibase("/databaseName");
419
435
 
420
436
  // set "isActive" to "false" for all items in table "user"
421
437
  await db.put("user", { isActive: false });
@@ -434,7 +450,7 @@ await db.put("user", { isActive: false }, { isActive: true });
434
450
 
435
451
  ```js
436
452
  import Inibase from "inibase";
437
- const db = new Inibase("/database_name");
453
+ const db = new Inibase("/databaseName");
438
454
 
439
455
  // delete all items in "user" table
440
456
  await db.delete("user");
@@ -453,7 +469,7 @@ await db.put("user", { isActive: false });
453
469
 
454
470
  ```js
455
471
  import Inibase from "inibase";
456
- const db = new Inibase("/database_name");
472
+ const db = new Inibase("/databaseName");
457
473
 
458
474
  // get the sum of column "age" in "user" table
459
475
  await db.sum("user", "age");
@@ -469,7 +485,7 @@ await db.sum("user", ["age", ...], { isActive: false });
469
485
 
470
486
  ```js
471
487
  import Inibase from "inibase";
472
- const db = new Inibase("/database_name");
488
+ const db = new Inibase("/databaseName");
473
489
 
474
490
  // get the biggest number of column "age" in "user" table
475
491
  await db.max("user", "age");
@@ -485,7 +501,7 @@ await db.max("user", ["age", ...], { isActive: false });
485
501
 
486
502
  ```js
487
503
  import Inibase from "inibase";
488
- const db = new Inibase("/database_name");
504
+ const db = new Inibase("/databaseName");
489
505
 
490
506
  // get the smallest number of column "age" in "user" table
491
507
  await db.min("user", "age");
@@ -496,48 +512,6 @@ await db.min("user", ["age", ...], { isActive: false });
496
512
 
497
513
  </details>
498
514
 
499
- <details>
500
- <summary>createWorker</summary>
501
-
502
- ```js
503
- 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
- ```
523
-
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
536
-
537
- # Prepend new items to the beginning of file
538
- INIBASE_REVERSE=true
539
- ```
540
-
541
515
  ## License
542
516
 
543
517
  [MIT](./LICENSE)
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
@@ -4,7 +4,6 @@ import { Transform } from "node:stream";
4
4
  import { pipeline } from "node:stream/promises";
5
5
  import { createGzip, createGunzip, gunzipSync, gzipSync } from "node:zlib";
6
6
  import { join } from "node:path";
7
- import { Worker } from "node:worker_threads";
8
7
  import { detectFieldType, isArrayOfObjects, isJSON, isNumber, isObject, } from "./utils.js";
9
8
  import { encodeID, compare } from "./utils.server.js";
10
9
  import * as Config from "./config.js";
@@ -35,11 +34,9 @@ export const write = async (filePath, data, disableCompression = false) => {
35
34
  ? gzipSync(String(data))
36
35
  : String(data));
37
36
  };
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
- };
37
+ export const read = async (filePath, disableCompression = false) => Config.isCompressionEnabled && !disableCompression
38
+ ? gunzipSync(await readFile(filePath)).toString()
39
+ : (await readFile(filePath)).toString();
43
40
  const _pipeline = async (rl, writeStream, transform) => {
44
41
  if (Config.isCompressionEnabled)
45
42
  await pipeline(rl, transform, createGzip(), writeStream);
@@ -52,18 +49,12 @@ const _pipeline = async (rl, writeStream, transform) => {
52
49
  * @param fileHandle - The file handle from which to create a read stream.
53
50
  * @returns A readline.Interface instance configured with the provided file stream.
54
51
  */
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
- };
52
+ const readLineInternface = (fileHandle) => createInterface({
53
+ input: Config.isCompressionEnabled
54
+ ? fileHandle.createReadStream().pipe(createGunzip())
55
+ : fileHandle.createReadStream(),
56
+ crlfDelay: Number.POSITIVE_INFINITY,
57
+ });
67
58
  /**
68
59
  * Checks if a file or directory exists at the specified path.
69
60
  *
@@ -312,7 +303,7 @@ export const append = async (filePath, data) => {
312
303
  rl = readLineInternface(fileHandle);
313
304
  let isAppended = false;
314
305
  await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
315
- transform(line, encoding, callback) {
306
+ transform(line, _, callback) {
316
307
  if (!isAppended) {
317
308
  isAppended = true;
318
309
  return callback(null, `${Array.isArray(data) ? data.join("\n") : data}\n${line.length ? `${line}\n` : ""}`);
@@ -580,16 +571,3 @@ export const min = async (filePath, lineNumbers) => {
580
571
  await fileHandle.close();
581
572
  return min;
582
573
  };
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
@@ -53,7 +53,6 @@ export default class Inibase {
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>;
57
56
  private _schemaToIdsPath;
58
57
  setTableSchema(tableName: string, schema: Schema): Promise<void>;
59
58
  getTableSchema(tableName: string, encodeIDs?: boolean): Promise<Schema | undefined>;
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";
@@ -62,26 +61,6 @@ export default class Inibase {
62
61
  : errorMessage.replaceAll("{variable}", `'${variable.toString()}'`)
63
62
  : errorMessage.replaceAll("{variable}", ""));
64
63
  }
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
- }
85
64
  _schemaToIdsPath = (schema, prefix = "") => {
86
65
  const RETURN = {};
87
66
  for (const field of schema)
@@ -437,9 +416,8 @@ export default class Inibase {
437
416
  Utils.isArrayOfObjects(children.children)).length) {
438
417
  // one of children has array field type and has children array of object = Schema
439
418
  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}.`)) ?? {})) {
419
+ Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`)) ?? {}))
441
420
  this._getItemsFromSchemaHelper(RETURN, item, index, field);
442
- }
443
421
  field.children = field.children.filter((children) => children.type !== "array" ||
444
422
  !Utils.isArrayOfObjects(children.children));
445
423
  }
@@ -536,7 +514,8 @@ export default class Inibase {
536
514
  }
537
515
  }
538
516
  else if (field.type === "table") {
539
- if ((await File.isExists(join(this.folder, this.database, field.key))) &&
517
+ if (field.table &&
518
+ (await File.isExists(join(this.folder, this.database, field.table))) &&
540
519
  (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.inib`)))) {
541
520
  if (options.columns)
542
521
  options.columns = options.columns
@@ -548,7 +527,7 @@ export default class Inibase {
548
527
  if (!RETURN[index])
549
528
  RETURN[index] = {};
550
529
  RETURN[index][field.key] = item
551
- ? await this.get(field.key, item, options)
530
+ ? await this.get(field.table, item, options)
552
531
  : this.getDefaultValue(field);
553
532
  }));
554
533
  }
@@ -869,9 +848,7 @@ export default class Inibase {
869
848
  ? RETURN.toReversed()
870
849
  : RETURN
871
850
  : RETURN);
872
- await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
873
- ? await File.createWorker("append", [path, content])
874
- : await File.append(path, content))));
851
+ await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content))));
875
852
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
876
853
  renameList = [];
877
854
  totalItems += Array.isArray(RETURN) ? RETURN.length : 1;
@@ -935,9 +912,7 @@ export default class Inibase {
935
912
  const replacementObject = {};
936
913
  for (const index of [...Array(totalItems)].keys())
937
914
  replacementObject[`${index + 1}`] = content;
938
- renameList.push(this.isThreadEnabled
939
- ? await File.createWorker("replace", [path, replacementObject])
940
- : await File.replace(path, replacementObject));
915
+ renameList.push(await File.replace(path, replacementObject));
941
916
  }));
942
917
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
943
918
  if (Config.isCacheEnabled)
@@ -975,9 +950,7 @@ export default class Inibase {
975
950
  .join("."));
976
951
  try {
977
952
  await File.lock(join(tablePath, ".tmp"), keys);
978
- await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
979
- ? await File.createWorker("replace", [path, content])
980
- : await File.replace(path, content))));
953
+ await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
981
954
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
982
955
  renameList = [];
983
956
  if (Config.isCacheEnabled)
@@ -1033,12 +1006,7 @@ export default class Inibase {
1033
1006
  throw this.throwError("NO_RESULTS", tableName);
1034
1007
  try {
1035
1008
  await File.lock(join(tablePath, ".tmp"));
1036
- await Promise.all(files.map(async (file) => renameList.push(this.isThreadEnabled
1037
- ? await File.createWorker("remove", [
1038
- join(tablePath, file),
1039
- where,
1040
- ])
1041
- : await File.remove(join(tablePath, file), where))));
1009
+ await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
1042
1010
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1043
1011
  if (Config.isCacheEnabled) {
1044
1012
  await this.clearCache(tablePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.55",
3
+ "version": "1.0.0-rc.57",
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"
@@ -56,15 +54,9 @@
56
54
  ".": [
57
55
  "./dist/index.d.ts"
58
56
  ],
59
- "thread": [
60
- "./dist/index.thread.d.ts"
61
- ],
62
57
  "file": [
63
58
  "./dist/file.d.ts"
64
59
  ],
65
- "file.thread": [
66
- "./dist/file.thread.d.ts"
67
- ],
68
60
  "utils": [
69
61
  "./dist/utils.d.ts"
70
62
  ],
@@ -77,15 +69,14 @@
77
69
  }
78
70
  },
79
71
  "devDependencies": {
80
- "@types/node": "^20.10.6",
81
- "tinybench": "^2.6.0",
82
- "typescript": "^5.3.3"
72
+ "@types/node": "^20.12.11",
73
+ "tinybench": "^2.6.0"
83
74
  },
84
75
  "dependencies": {
85
76
  "inison": "^1.0.0-rc.2"
86
77
  },
87
78
  "scripts": {
88
- "build": "npx tsc",
79
+ "build": "tsc",
89
80
  "test": "npx tsx watch --expose-gc --env-file=.env ./index.test",
90
81
  "benchmark": "npx tsx --env-file=.env ./benchmark/index",
91
82
  "benchmark:single": "npx tsx --expose-gc --env-file=.env ./benchmark/single",
@@ -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));