inibase 1.0.0-rc.57 → 1.0.0-rc.59

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
@@ -14,7 +14,7 @@
14
14
  - **Super-Fast** :zap: (built-in caching system)
15
15
  - **ATOMIC** :lock: File lock for writing
16
16
  - **Built-in form-validation** included :sunglasses:
17
- - **Suitable for large data** :page_with_curl: (tested with 200K row)
17
+ - **Suitable for large data** :page_with_curl: (tested with 4M records)
18
18
  - **Support Compression** :eight_spoked_asterisk: (using built-in nodejs zlib)
19
19
  - **Support Table Joins** :link:
20
20
  - **Low memory-usage** :chart_with_downwards_trend: (3-5mb)
@@ -42,8 +42,10 @@ const users = await db.get("user", undefined, {
42
42
  // Get items from "user" table where "favoriteFoods" does not includes "Pizza" or "Burger"
43
43
  const users = await db.get("user", { favoriteFoods: "![]Pizza,Burger" });
44
44
  ```
45
-
46
- If you like Inibase, please sponsor: [GitHub Sponsors](https://github.com/sponsors/inicontent) || [Paypal](https://paypal.me/KarimAmahtil).
45
+ > [!NOTE]
46
+ > Enjoy using Inibase? Consider sponsoring us via [PayPal](https://paypal.me/KarimAmahtil) <br>
47
+ > Your support helps us maintain and improve our services. <br>
48
+ > Thank you! 🫰
47
49
 
48
50
  ## Install
49
51
 
@@ -53,7 +55,29 @@ If you like Inibase, please sponsor: [GitHub Sponsors](https://github.com/sponso
53
55
 
54
56
  ## How it works?
55
57
 
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
58
+ `Inibase` organizes data into databases, tables, and columns, each stored in separate files.
59
+
60
+ - **POST**: New data is appended to column files efficiently.
61
+ - **GET**: Data retrieval is optimized by reading files line-by-line.
62
+ - **PUT**: Updates are streamlined, with only the relevant file being modified.
63
+ - **DELETE**: Removes lines from column files for swift deletion.
64
+
65
+ This structure ensures efficient storage, retrieval, and updates, making our system scalable and high-performing for diverse datasets and applications.
66
+
67
+ ## Config (.env)
68
+
69
+ The `.env` file supports the following parameters
70
+
71
+ ```ini
72
+ # Don't add this line, it's an auto generated secret key, will be using for encrypting the IDs
73
+ INIBASE_SECRET=
74
+
75
+ INIBASE_COMPRESSION=false
76
+ INIBASE_CACHE=false
77
+
78
+ # Prepend new items to the beginning of file
79
+ INIBASE_REVERSE=false
80
+ ```
57
81
 
58
82
  ## Benchmark
59
83
 
@@ -75,76 +99,94 @@ To simplify the idea, each database has tables, each table has columns, each col
75
99
  | PUT | 33 ms (10.29 mb) | 312 ms (11.06 mb) | 3539 ms (14.87 mb) |
76
100
  | DELETE | 134 ms (13.50 mb) | 1224 ms (16.57 mb) | 7339 ms (11.46 mb) |
77
101
 
78
- Ps: Testing by default with `user` table, with username, email, password fields _so results include password encryption process_
102
+ > Testing by default with `user` table, with username, email, password fields _so results include password encryption process_
103
+ > To run benchmarks, install *typescript* & *tsx* globally and run `benchmark` `benchmark:bulk` `benchmark:single`
79
104
 
105
+ ## Inibase CLI
80
106
 
81
- ## Config (.env)
107
+ ```shell
108
+ npx inibase -p <databaseFolderPath>
109
+ ```
82
110
 
83
- The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
111
+ <blockquote>
112
+ <details>
113
+ <summary>GET</summary>
84
114
 
85
- ```ini
86
- # Auto generated secret key, will be using for encrypting the IDs
87
- INIBASE_SECRET=
115
+ ```shell
116
+ get <tableName> -w <ID|LineNumber|Criteria> -p <pageNumber> -l <perPage> -c <columnName1> -c <columnName2>
117
+ ```
118
+ </details>
88
119
 
89
- INIBASE_COMPRESSION=true
90
- INIBASE_CACHE=true
120
+ <details>
121
+ <summary>POST</summary>
91
122
 
92
- # Prepend new items to the beginning of file
93
- INIBASE_REVERSE=true
123
+ ```shell
124
+ post <tableName> -d <InisonStrigifedData>
94
125
  ```
126
+ </details>
95
127
 
96
- ## Roadmap
128
+ <details>
129
+ <summary>PUT</summary>
97
130
 
98
- - [x] Actions:
99
- - [x] GET:
100
- - [x] Pagination
101
- - [x] Criteria
102
- - [x] Columns
103
- - [x] Order By (using UNIX commands)
104
- - [x] POST
105
- - [x] PUT
106
- - [x] DELETE
107
- - [x] SUM
108
- - [x] MAX
109
- - [x] MIN
110
- - [ ] Schema supported types:
111
- - [x] String
112
- - [x] Number
113
- - [x] Boolean
114
- - [x] Date
115
- - [x] Email
116
- - [x] Url
117
- - [x] Table
118
- - [x] Object
119
- - [x] Array
120
- - [x] Password
121
- - [x] IP
122
- - [x] HTML
123
- - [x] Id
124
- - [x] JSON
125
- - [ ] TO-DO:
126
- - [x] Improve caching
127
- - [ ] Commenting the code
128
- - [x] Add property "unique" for schema fields
129
- - [ ] Add Backup feature (generate a tar.gz)
130
- - [ ] Add Custom field validation property to schema (using RegEx?)
131
- - [ ] Features:
132
- - [ ] Encryption
133
- - [x] Data Compression
134
- - [x] Caching System
135
- - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
131
+ ```shell
132
+ put <tableName> -d <InisonStrigifedData> -w <ID|LineNumber|Criteria>
133
+ ```
134
+ </details>
136
135
 
136
+ <details>
137
+ <summary>DELETE</summary>
138
+
139
+ ```shell
140
+ delete <tableName> -w <ID|LineNumber|Criteria>
141
+ ```
142
+ </details>
143
+ </blockquote>
137
144
 
138
145
  ## Examples
139
146
 
140
147
  <details>
141
- <summary>POST</summary>
148
+ <summary>Schema</summary>
149
+ <blockquote>
150
+
151
+ <details>
152
+ <summary>Create Schema</summary>
153
+ <blockquote>
154
+
155
+ <details>
156
+ <summary>Using schema.json file</summary>
157
+ <blockquote>
158
+ Inside the table folder
159
+
160
+ 1. Create empty folders `.cache` `.tmp`
161
+ 2. Create `schema.json` file
162
+
163
+ ```jsonc
164
+ [
165
+ {
166
+ // Give a unique ID number for each field
167
+ "id": 1,
168
+ "key": "username",
169
+ "type": "string"
170
+ },
171
+ {
172
+ "id": 2,
173
+ "key": "email",
174
+ "type": "email"
175
+ },
176
+ ]
177
+ ```
178
+ </blockquote>
179
+ </details>
180
+
181
+ <details>
182
+ <summary>Using built-in function</summary>
183
+ <blockquote>
142
184
 
143
185
  ```js
144
186
  import Inibase from "inibase";
145
187
  const db = new Inibase("/databaseName");
146
188
 
147
- const user_schema = [
189
+ const userSchema = [
148
190
  {
149
191
  key: "username",
150
192
  type: "string",
@@ -210,7 +252,128 @@ const user_schema = [
210
252
  },
211
253
  ];
212
254
 
213
- const user_data = [
255
+ await db.setTableSchema("user", userSchema);
256
+ ```
257
+ </blockquote>
258
+ </details>
259
+
260
+ </blockquote>
261
+ </details>
262
+
263
+ <details>
264
+ <summary>Add field</summary>
265
+ <blockquote>
266
+
267
+ ```js
268
+ import Inibase from "inibase";
269
+ const db = new Inibase("/databaseName");
270
+
271
+ const userSchema = await db.getTableSchema("user");
272
+ const newUserSchema = [...userSchema, {key: "phone2", type: "number", required: false}];
273
+
274
+ await db.setTableSchema("user", newUserSchema);
275
+ ```
276
+ </blockquote>
277
+ </details>
278
+
279
+ <details>
280
+ <summary>Update field</summary>
281
+ <blockquote>
282
+
283
+ ```js
284
+ import Inibase from "inibase";
285
+ import { setField } from "inibase/utils";
286
+
287
+ const db = new Inibase("/databaseName");
288
+
289
+ const userSchema = await db.getTableSchema("user");
290
+ setField("username", userSchema, {key: "full_name"});
291
+ await db.setTableSchema("user", newUserSchema);
292
+ ```
293
+ </blockquote>
294
+ </details>
295
+
296
+ <details>
297
+ <summary>Join Tables</summary>
298
+ <blockquote>
299
+
300
+ ```js
301
+ import Inibase from "inibase";
302
+ const db = new Inibase("/databaseName");
303
+
304
+ const productSchema = [
305
+ {
306
+ key: "title",
307
+ type: "string",
308
+ required: true,
309
+ },
310
+ {
311
+ key: "price",
312
+ type: "number",
313
+ },
314
+ {
315
+ key: "createdBy",
316
+ type: "table",
317
+ table: "user",
318
+ required: true,
319
+ },
320
+ ];
321
+
322
+ await db.setTableSchema("product", productSchema);
323
+
324
+ const productData = [
325
+ {
326
+ title: "Product 1",
327
+ price: 16,
328
+ createdBy: "1d88385d4b1581f8fb059334dec30f4c",
329
+ },
330
+ {
331
+ title: "Product 2",
332
+ price: 10,
333
+ createdBy: "5011c230aa44481bf7e8dcfe0710474f",
334
+ },
335
+ ];
336
+
337
+ const product = await db.post("product", productData);
338
+ // [
339
+ // {
340
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
341
+ // "title": "Product 1",
342
+ // "price": 16,
343
+ // "createdBy": {
344
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
345
+ // "username": "user1",
346
+ // "email": "user1@example.com",
347
+ // ...
348
+ // }
349
+ // },
350
+ // {
351
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
352
+ // "title": "Product 2",
353
+ // "price": 10,
354
+ // "createdBy": {
355
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
356
+ // "username": "user2",
357
+ // ...
358
+ // }
359
+ // }
360
+ // ]
361
+ ```
362
+ </blockquote>
363
+ </details>
364
+
365
+ </blockquote>
366
+ </details>
367
+
368
+ <details>
369
+ <summary>POST</summary>
370
+ <blockquote>
371
+
372
+ ```js
373
+ import Inibase from "inibase";
374
+ const db = new Inibase("/databaseName");
375
+
376
+ const userData = [
214
377
  {
215
378
  username: "user1",
216
379
  email: "user1@example.com",
@@ -245,7 +408,7 @@ const user_data = [
245
408
  },
246
409
  ];
247
410
 
248
- const users = await db.post("user", user_data);
411
+ const users = await db.post("user", userData);
249
412
  // [
250
413
  // {
251
414
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -283,79 +446,21 @@ const users = await db.post("user", user_data);
283
446
  // ]
284
447
  ```
285
448
 
286
- Link two tables: "product" with "user"
287
-
288
- ```js
289
- import Inibase from "inibase";
290
- const db = new Inibase("/databaseName");
291
-
292
- const product_schema = [
293
- {
294
- key: "title",
295
- type: "string",
296
- required: true,
297
- },
298
- {
299
- key: "price",
300
- type: "number",
301
- },
302
- {
303
- key: "createdBy",
304
- type: "table",
305
- table: "user",
306
- required: true,
307
- },
308
- ];
309
-
310
- const product_data = [
311
- {
312
- title: "Product 1",
313
- price: 16,
314
- createdBy: "1d88385d4b1581f8fb059334dec30f4c",
315
- },
316
- {
317
- title: "Product 2",
318
- price: 10,
319
- createdBy: "5011c230aa44481bf7e8dcfe0710474f",
320
- },
321
- ];
322
-
323
- const product = await db.post("product", product_data);
324
- // [
325
- // {
326
- // "id": "1d88385d4b1581f8fb059334dec30f4c",
327
- // "title": "Product 1",
328
- // "price": 16,
329
- // "createdBy": {
330
- // "id": "1d88385d4b1581f8fb059334dec30f4c",
331
- // "username": "user1",
332
- // "email": "user1@example.com",
333
- // ...
334
- // }
335
- // },
336
- // {
337
- // "id": "5011c230aa44481bf7e8dcfe0710474f",
338
- // "title": "Product 2",
339
- // "price": 10,
340
- // "createdBy": {
341
- // "id": "5011c230aa44481bf7e8dcfe0710474f",
342
- // "username": "user2",
343
- // ...
344
- // }
345
- // }
346
- // ]
347
- ```
348
-
449
+ </blockquote>
349
450
  </details>
350
451
 
351
452
  <details>
352
453
  <summary>GET</summary>
454
+ <blockquote>
455
+
456
+ <details>
457
+ <summary>GET by ID</summary>
458
+ <blockquote>
353
459
 
354
460
  ```js
355
461
  import Inibase from "inibase";
356
462
  const db = new Inibase("/databaseName");
357
463
 
358
- // Get "user" by id
359
464
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
360
465
  // {
361
466
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -384,8 +489,18 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
384
489
  // "country": "Sampleland"
385
490
  // }
386
491
  // }
492
+ ```
493
+ </blockquote>
494
+ </details>
495
+
496
+ <details>
497
+ <summary>GET by criteria</summary>
498
+ <blockquote>
499
+
500
+ ```js
501
+ import Inibase from "inibase";
502
+ const db = new Inibase("/databaseName");
387
503
 
388
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
389
504
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
390
505
  // [
391
506
  // {
@@ -417,17 +532,32 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
417
532
  // },
418
533
  // ...
419
534
  // ]
535
+ ```
536
+ </blockquote>
537
+ </details>
538
+
539
+ <details>
540
+ <summary>GET with columns</summary>
541
+ <blockquote>
542
+
543
+ ```js
544
+ import Inibase from "inibase";
545
+ const db = new Inibase("/databaseName");
420
546
 
421
547
  // Get all "user" columns except "username" & "address.street"
422
548
  const users = await db.get("user", undefined, {
423
549
  columns: ["!username", "!address.street"],
424
550
  });
425
551
  ```
552
+ </blockquote>
553
+ </details>
426
554
 
555
+ </blockquote>
427
556
  </details>
428
557
 
429
558
  <details>
430
559
  <summary>PUT</summary>
560
+ <blockquote>
431
561
 
432
562
  ```js
433
563
  import Inibase from "inibase";
@@ -442,11 +572,12 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
442
572
  // set "isActive" to "true" in table "user" by criteria (where "isActive" is equal to "true")
443
573
  await db.put("user", { isActive: false }, { isActive: true });
444
574
  ```
445
-
575
+ </blockquote>
446
576
  </details>
447
577
 
448
578
  <details>
449
579
  <summary>DELETE</summary>
580
+ <blockquote>
450
581
 
451
582
  ```js
452
583
  import Inibase from "inibase";
@@ -461,11 +592,12 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
461
592
  // delete "user" by criteria (where "isActive" is equal to "false")
462
593
  await db.put("user", { isActive: false });
463
594
  ```
464
-
595
+ </blockquote>
465
596
  </details>
466
597
 
467
598
  <details>
468
599
  <summary>SUM</summary>
600
+ <blockquote>
469
601
 
470
602
  ```js
471
603
  import Inibase from "inibase";
@@ -477,11 +609,12 @@ await db.sum("user", "age");
477
609
  // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
478
610
  await db.sum("user", ["age", ...], { isActive: false });
479
611
  ```
480
-
612
+ </blockquote>
481
613
  </details>
482
614
 
483
615
  <details>
484
616
  <summary>MAX</summary>
617
+ <blockquote>
485
618
 
486
619
  ```js
487
620
  import Inibase from "inibase";
@@ -493,11 +626,12 @@ await db.max("user", "age");
493
626
  // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
494
627
  await db.max("user", ["age", ...], { isActive: false });
495
628
  ```
496
-
629
+ </blockquote>
497
630
  </details>
498
631
 
499
632
  <details>
500
633
  <summary>MIN</summary>
634
+ <blockquote>
501
635
 
502
636
  ```js
503
637
  import Inibase from "inibase";
@@ -509,9 +643,68 @@ await db.min("user", "age");
509
643
  // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
510
644
  await db.min("user", ["age", ...], { isActive: false });
511
645
  ```
646
+ </blockquote>
647
+ </details>
512
648
 
649
+ <details>
650
+ <summary>SORT</summary>
651
+ <blockquote>
652
+
653
+ ```js
654
+ import Inibase from "inibase";
655
+ const db = new Inibase("/databaseName");
656
+
657
+ // order users by the age column
658
+ await db.sort("user", "age");
659
+
660
+ // order users by the age and username columns
661
+ await db.sort("user", ["age","username"]);
662
+ await db.sort("user", {age: -1, username: "asc"});
663
+ ```
664
+ </blockquote>
513
665
  </details>
514
666
 
667
+ ## Roadmap
668
+
669
+ - [x] Actions:
670
+ - [x] GET:
671
+ - [x] Pagination
672
+ - [x] Criteria
673
+ - [x] Columns
674
+ - [x] Sort (using UNIX commands)
675
+ - [x] POST
676
+ - [x] PUT
677
+ - [x] DELETE
678
+ - [x] SUM
679
+ - [x] MAX
680
+ - [x] MIN
681
+ - [ ] Schema supported types:
682
+ - [x] String
683
+ - [x] Number
684
+ - [x] Boolean
685
+ - [x] Date
686
+ - [x] Email
687
+ - [x] Url
688
+ - [x] Table
689
+ - [x] Object
690
+ - [x] Array
691
+ - [x] Password
692
+ - [x] IP
693
+ - [x] HTML
694
+ - [x] Id
695
+ - [x] JSON
696
+ - [ ] TO-DO:
697
+ - [x] Improve caching
698
+ - [ ] Commenting the code
699
+ - [x] Add property "unique" for schema fields
700
+ - [ ] Add Backup feature (generate a tar.gz)
701
+ - [ ] Add Custom field validation property to schema (using RegEx?)
702
+ - [ ] Features:
703
+ - [ ] Encryption
704
+ - [x] Data Compression
705
+ - [x] Caching System
706
+ - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
707
+
515
708
  ## License
516
709
 
517
- [MIT](./LICENSE)
710
+ [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,88 @@
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 } = 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
+ },
46
+ }).values;
47
+ if (where) {
48
+ if (isNumber(where))
49
+ where = Number(where);
50
+ else if (isJSON(where))
51
+ where = Inison.unstringify(where);
52
+ }
53
+ if (data) {
54
+ if (isJSON(data))
55
+ where = Inison.unstringify(data);
56
+ else
57
+ data = undefined;
58
+ }
59
+ switch (splitedInput[0].toLocaleLowerCase()) {
60
+ case "get":
61
+ console.log(await db.get(table, where, {
62
+ page: Number(page) ?? 1,
63
+ perPage: Number(perPage) ?? 15,
64
+ columns,
65
+ }));
66
+ break;
67
+ case "post":
68
+ console.log(await db.post(table, data, {
69
+ page: Number(page) ?? 1,
70
+ perPage: Number(perPage) ?? 15,
71
+ columns,
72
+ }, true));
73
+ break;
74
+ case "put":
75
+ console.log(await db.put(table, data, where, {
76
+ page: Number(page) ?? 1,
77
+ perPage: Number(perPage) ?? 15,
78
+ columns,
79
+ }, true));
80
+ break;
81
+ case "delete":
82
+ console.log(await db.delete(table, where));
83
+ break;
84
+ default:
85
+ break;
86
+ }
87
+ }
88
+ });