inibase 1.0.0-rc.11 → 1.0.0-rc.110

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
@@ -1,27 +1,32 @@
1
- [![Inibase banner](./.github/assets/banner.jpg)](https://github.com/inicontent/inibase)
2
-
3
1
  # Inibase :pencil:
4
2
 
5
- [![npmjs](https://img.shields.io/npm/dm/inibase.svg?style=flat)](https://www.npmjs.org/package/inibase) [![License](https://img.shields.io/github/license/inicontent/inibase.svg?style=flat&colorA=18181B&colorB=28CF8D)](./LICENSE) [![Activity](https://img.shields.io/github/commit-activity/m/inicontent/inibase)](https://github.com/inicontent/inibase/pulse) [![GitHub stars](https://img.shields.io/github/stars/inicontent/inibase?style=social)](https://github.com/inicontent/inibase)
3
+ > A file-based & memory-efficient, serverless, ACID compliant, relational database management system :fire:
4
+
5
+ [![Inibase banner](./.github/assets/banner.jpg)](https://github.com/inicontent/inibase)
6
6
 
7
- > File-based relational database, simple to use and can handle large data :fire:
7
+ [![npmjs](https://img.shields.io/npm/dm/inibase.svg?style=flat)](https://www.npmjs.org/package/inibase) [![License](https://img.shields.io/github/license/inicontent/inibase.svg?style=flat&colorA=18181B&colorB=28CF8D)](./LICENSE) [![Activity](https://img.shields.io/github/commit-activity/m/inicontent/inibase)](https://github.com/inicontent/inibase/pulse) [![GitHub stars](https://img.shields.io/github/stars/inicontent/inibase?style=social)](https://github.com/inicontent/inibase)
8
8
 
9
9
  ## Features
10
10
 
11
- - **Lightweight** 🪶 (~80kb)
12
- - **Minimalist** :white_circle:
13
- - **TypeScript** :large_blue_diamond:
14
- - **Super-Fast** :turtle:
15
- - **Suitable for large data** :page_with_curl:
16
- - **Safe** :lock:
17
- - **Easy to use** :hourglass:
11
+ - **Lightweight** 🪶
12
+ - **Minimalist** :white_circle: (but powerful)
13
+ - **100% TypeScript** :large_blue_diamond:
14
+ - **Super-Fast** :zap: (built-in caching system)
15
+ - **ATOMIC** :lock: File lock for writing
16
+ - **Built-in** form validation (+unique values :new: ) :sunglasses:
17
+ - **Suitable for large data** :page_with_curl: (tested with 4M records)
18
+ - **Support Compression** :eight_spoked_asterisk: (using built-in nodejs zlib)
19
+ - **Support Table Joins** :link:
20
+ - **Low memory-usage** :chart_with_downwards_trend: (3-5mb)
21
+ - **Safe** :lock: (no sql or javascript injections)
22
+ - **Easy to use** :bread:
18
23
  - **...** and much more :rocket:
19
24
 
20
25
  ## Usage
21
26
 
22
27
  ```js
23
28
  import Inibase from "inibase";
24
- const db = new Inibase("database_name");
29
+ const db = new Inibase("databaseName");
25
30
 
26
31
  // Get all items from "user" table
27
32
  const users = await db.get("user");
@@ -34,39 +39,122 @@ const users = await db.get("user", undefined, {
34
39
  columns: ["username", "address.street", "hobbies.name"],
35
40
  });
36
41
 
37
- // Get items from "user" table where "favoriteFoods" does not includes "Pizza"
38
- const users = await db.get("user", { favoriteFoods: "![]Pizza" });
42
+ // Get items from "user" table where "favoriteFoods" does not includes "Pizza" or "Burger"
43
+ const users = await db.get("user", { favoriteFoods: "![]Pizza,Burger" });
39
44
  ```
40
45
 
41
- If you like Inibase, please sponsor: [GitHub Sponsors](https://github.com/sponsors/inicontent) || [Paypal](https://paypal.me/KarimAmahtil).
42
-
43
- ## Sponsors
44
-
45
- <br>
46
- <br>
47
-
48
- Become a sponsor and have your company logo here 👉 [GitHub Sponsors](https://github.com/sponsors/inicontent) || [paypal](https://paypal.me/KarimAmahtil).
46
+ > [!NOTE]
47
+ > Enjoy using Inibase? Consider sponsoring us via [PayPal](https://paypal.me/KarimAmahtil) <br>
48
+ > Your support helps us maintain and improve our services. <br>
49
+ > Thank you! 🫰
49
50
 
50
51
  ## Install
51
52
 
52
53
  ```js
53
- <npm|pnpm> install inibase
54
+ <npm|pnpm|yarn|bun> install inibase
54
55
  ```
55
56
 
57
+ > [!WARNING]
58
+ > If you're using **Windows**, the following Unix commands are required: `zcat`, `sed`, `gzip`, and `echo`.
59
+ >
60
+ > To use the missing commands, you need to install additional tools:
61
+ > - **[GnuWin32](http://gnuwin32.sourceforge.net/)**: Provides individual GNU utilities for Windows.
62
+ > - **[Cygwin](https://www.cygwin.com/)**: Offers a full Unix-like environment for Windows.
63
+ >
64
+ > Alternatively, consider using the **Windows Subsystem for Linux (WSL)** to run a Linux environment on Windows. Learn more [here](https://learn.microsoft.com/en-us/windows/wsl/).
65
+
56
66
  ## How it works?
57
67
 
58
- To semplify the idea, each database has tables, each table has columns, each column will be stored in a seperated file. When POSTing new data, it will be appended to each columns file as new line. When GETing data, the file will be readed line-by-line so it can handle large data (without consuming a lot of resources)
68
+ `Inibase` organizes data into databases, tables, and columns, each stored in separate files.
69
+
70
+ - **POST**: New data is appended to column files efficiently.
71
+ - **GET**: Data retrieval is optimized by reading files line-by-line.
72
+ - **PUT**: Updates are streamlined, with only the relevant file being modified.
73
+ - **DELETE**: Removes lines from column files for swift deletion.
74
+
75
+ This structure ensures efficient storage, retrieval, and updates, making our system scalable and high-performing for diverse datasets and applications.
76
+
77
+ ## Inibase CLI
78
+
79
+ ```shell
80
+ npx inibase -p <databaseFolderPath>
81
+ # by default it will diplay a list of available commands (or type 'help')
82
+ ```
59
83
 
60
84
  ## Examples
61
85
 
62
86
  <details>
63
- <summary>POST</summary>
87
+ <summary>Tables</summary>
88
+ <blockquote>
89
+
90
+ <details>
91
+ <summary>Config</summary>
92
+ <blockquote>
93
+
94
+ ```ts
95
+ interface {
96
+ compression: boolean;
97
+ cache: boolean;
98
+ prepend: boolean;
99
+ }
100
+ ```
101
+
102
+ </blockquote>
103
+ </details>
104
+
105
+ <details>
106
+ <summary>Schema</summary>
107
+ <blockquote>
108
+
109
+ ```ts
110
+ interface {
111
+ id: number; // stored as a Number but displayed as a hashed ID
112
+ key: string;
113
+ required?: boolean;
114
+ unique?: boolean;
115
+ type: "string" | "number" | "boolean" | "date" | "email" | "url" | "password" | "html" | "ip" | "json" | "id";
116
+ }
117
+ interface Table {
118
+ id: number;
119
+ key: string;
120
+ required?: boolean;
121
+ type: "table";
122
+ table: string;
123
+ }
124
+ interface Array {
125
+ id: number;
126
+ key: string;
127
+ required?: boolean;
128
+ type: "array";
129
+ children: string|string[];
130
+ }
131
+ interface ObjectOrArrayOfObjects {
132
+ id: number;
133
+ key: string;
134
+ required?: boolean;
135
+ type: "object" | "array";
136
+ children: Schema;
137
+ }
138
+ ```
139
+
140
+ </blockquote>
141
+ </details>
142
+
143
+ <details>
144
+ <summary>Create Table</summary>
145
+ <blockquote>
64
146
 
65
147
  ```js
66
148
  import Inibase from "inibase";
67
- const db = new Inibase("/database_name");
149
+ const db = new Inibase("/databaseName");
68
150
 
69
- const user_schema = [
151
+ const userTableConfig = {
152
+ compression: true,
153
+ cache: true,
154
+ prepend: false
155
+ }
156
+
157
+ const userTableSchema = [
70
158
  {
71
159
  key: "username",
72
160
  type: "string",
@@ -132,7 +220,156 @@ const user_schema = [
132
220
  },
133
221
  ];
134
222
 
135
- const user_data = [
223
+ await db.createTable("user", userTableSchema, userTableConfig);
224
+ ```
225
+
226
+ </blockquote>
227
+ </details>
228
+
229
+ <details>
230
+ <summary>Update Table</summary>
231
+ <blockquote>
232
+
233
+ <details>
234
+ <summary>Change Name</summary>
235
+ <blockquote>
236
+
237
+ ```js
238
+ import Inibase from "inibase";
239
+ const db = new Inibase("/databaseName");
240
+
241
+ // this will change table name also in joined tables
242
+ await db.updateTable("user", undefined, {name: "userV2"});
243
+ ```
244
+
245
+ </blockquote>
246
+ </details>
247
+
248
+ <details>
249
+ <summary>Update field</summary>
250
+ <blockquote>
251
+
252
+ ```js
253
+ import Inibase from "inibase";
254
+ import { setField } from "inibase/utils";
255
+
256
+ const db = new Inibase("/databaseName");
257
+
258
+ const userTableSchema = (await db.getTable("user")).schema;
259
+ setField("username", userTableSchema, {key: "fullName"});
260
+ await db.updateTable("user", newUserTableSchema);
261
+ ```
262
+
263
+ </blockquote>
264
+ </details>
265
+
266
+ <details>
267
+ <summary>Remove field</summary>
268
+ <blockquote>
269
+
270
+ ```js
271
+ import Inibase from "inibase";
272
+ import { unsetField } from "inibase/utils";
273
+
274
+ const db = new Inibase("/databaseName");
275
+
276
+ const userTableSchema = (await db.getTable("user")).schema;
277
+ unsetField("fullName", userTableSchema);
278
+ await db.updateTable("user", newUserTableSchema);
279
+ ```
280
+
281
+ </blockquote>
282
+ </details>
283
+
284
+ </blockquote>
285
+ </details>
286
+
287
+ <details>
288
+ <summary>Join Tables</summary>
289
+ <blockquote>
290
+
291
+ ```js
292
+ import Inibase from "inibase";
293
+ const db = new Inibase("/databaseName");
294
+
295
+ const productTableSchema = [
296
+ {
297
+ key: "title",
298
+ type: "string",
299
+ required: true,
300
+ },
301
+ {
302
+ key: "price",
303
+ type: "number",
304
+ },
305
+ {
306
+ key: "createdBy",
307
+ type: "table",
308
+ table: "user",
309
+ required: true,
310
+ },
311
+ ];
312
+
313
+ await db.createTable("product", productTableSchema);
314
+
315
+ const productTableData = [
316
+ {
317
+ title: "Product 1",
318
+ price: 16,
319
+ createdBy: "1d88385d4b1581f8fb059334dec30f4c",
320
+ },
321
+ {
322
+ title: "Product 2",
323
+ price: 10,
324
+ createdBy: "5011c230aa44481bf7e8dcfe0710474f",
325
+ },
326
+ ];
327
+
328
+ const product = await db.post("product", productTableData);
329
+ // [
330
+ // {
331
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
332
+ // "title": "Product 1",
333
+ // "price": 16,
334
+ // "createdBy": {
335
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
336
+ // "username": "user1",
337
+ // "email": "user1@example.com",
338
+ // ...
339
+ // }
340
+ // },
341
+ // {
342
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
343
+ // "title": "Product 2",
344
+ // "price": 10,
345
+ // "createdBy": {
346
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
347
+ // "username": "user2",
348
+ // ...
349
+ // }
350
+ // }
351
+ // ]
352
+ ```
353
+
354
+ </blockquote>
355
+ </details>
356
+
357
+ </blockquote>
358
+ </details>
359
+
360
+ <details>
361
+ <summary>Methods</summary>
362
+ <blockquote>
363
+
364
+ <details>
365
+ <summary>POST</summary>
366
+ <blockquote>
367
+
368
+ ```js
369
+ import Inibase from "inibase";
370
+ const db = new Inibase("/databaseName");
371
+
372
+ const userTableData = [
136
373
  {
137
374
  username: "user1",
138
375
  email: "user1@example.com",
@@ -167,7 +404,7 @@ const user_data = [
167
404
  },
168
405
  ];
169
406
 
170
- const users = await db.post("user", user_data);
407
+ const users = await db.post("user", userTableData);
171
408
  // [
172
409
  // {
173
410
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -205,78 +442,21 @@ const users = await db.post("user", user_data);
205
442
  // ]
206
443
  ```
207
444
 
208
- Link two tables: "product" with "user"
209
-
210
- ```js
211
- import Inibase from "inibase";
212
- const db = new Inibase("/database_name");
213
-
214
- const product_schema = [
215
- {
216
- key: "title",
217
- type: "string",
218
- required: true,
219
- },
220
- {
221
- key: "price",
222
- type: "number",
223
- },
224
- {
225
- key: "user",
226
- type: "table",
227
- required: true,
228
- },
229
- ];
230
-
231
- const product_data = [
232
- {
233
- title: "Product 1",
234
- price: 16,
235
- user: "1d88385d4b1581f8fb059334dec30f4c",
236
- },
237
- {
238
- title: "Product 2",
239
- price: 10,
240
- user: "5011c230aa44481bf7e8dcfe0710474f",
241
- },
242
- ];
243
-
244
- const product = await db.post("product", product_data);
245
- // [
246
- // {
247
- // "id": "1d88385d4b1581f8fb059334dec30f4c",
248
- // "title": "Product 1",
249
- // "price": 16,
250
- // "user": {
251
- // "id": "1d88385d4b1581f8fb059334dec30f4c",
252
- // "username": "user1",
253
- // "email": "user1@example.com",
254
- // ...
255
- // }
256
- // },
257
- // {
258
- // "id": "5011c230aa44481bf7e8dcfe0710474f",
259
- // "title": "Product 2",
260
- // "price": 10,
261
- // "user": {
262
- // "id": "5011c230aa44481bf7e8dcfe0710474f",
263
- // "username": "user2",
264
- // ...
265
- // }
266
- // }
267
- // ]
268
- ```
269
-
445
+ </blockquote>
270
446
  </details>
271
447
 
272
448
  <details>
273
449
  <summary>GET</summary>
450
+ <blockquote>
451
+
452
+ <details>
453
+ <summary>GET by ID</summary>
454
+ <blockquote>
274
455
 
275
456
  ```js
276
457
  import Inibase from "inibase";
277
- const db = new Inibase("/database_name");
458
+ const db = new Inibase("/databaseName");
278
459
 
279
- // Get "user" by id
280
460
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
281
461
  // {
282
462
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -305,8 +485,19 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
305
485
  // "country": "Sampleland"
306
486
  // }
307
487
  // }
488
+ ```
489
+
490
+ </blockquote>
491
+ </details>
492
+
493
+ <details>
494
+ <summary>GET by criteria</summary>
495
+ <blockquote>
496
+
497
+ ```js
498
+ import Inibase from "inibase";
499
+ const db = new Inibase("/databaseName");
308
500
 
309
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
310
501
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
311
502
  // [
312
503
  // {
@@ -338,6 +529,18 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
338
529
  // },
339
530
  // ...
340
531
  // ]
532
+ ```
533
+
534
+ </blockquote>
535
+ </details>
536
+
537
+ <details>
538
+ <summary>GET with columns</summary>
539
+ <blockquote>
540
+
541
+ ```js
542
+ import Inibase from "inibase";
543
+ const db = new Inibase("/databaseName");
341
544
 
342
545
  // Get all "user" columns except "username" & "address.street"
343
546
  const users = await db.get("user", undefined, {
@@ -345,14 +548,19 @@ const users = await db.get("user", undefined, {
345
548
  });
346
549
  ```
347
550
 
551
+ </blockquote>
552
+ </details>
553
+
554
+ </blockquote>
348
555
  </details>
349
556
 
350
557
  <details>
351
558
  <summary>PUT</summary>
559
+ <blockquote>
352
560
 
353
561
  ```js
354
562
  import Inibase from "inibase";
355
- const db = new Inibase("/database_name");
563
+ const db = new Inibase("/databaseName");
356
564
 
357
565
  // set "isActive" to "false" for all items in table "user"
358
566
  await db.put("user", { isActive: false });
@@ -364,14 +572,16 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
364
572
  await db.put("user", { isActive: false }, { isActive: true });
365
573
  ```
366
574
 
575
+ </blockquote>
367
576
  </details>
368
577
 
369
578
  <details>
370
579
  <summary>DELETE</summary>
580
+ <blockquote>
371
581
 
372
582
  ```js
373
583
  import Inibase from "inibase";
374
- const db = new Inibase("/database_name");
584
+ const db = new Inibase("/databaseName");
375
585
 
376
586
  // delete all items in "user" table
377
587
  await db.delete("user");
@@ -383,8 +593,109 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
383
593
  await db.put("user", { isActive: false });
384
594
  ```
385
595
 
596
+ </blockquote>
597
+ </details>
598
+
599
+ <details>
600
+ <summary>SUM</summary>
601
+ <blockquote>
602
+
603
+ ```js
604
+ import Inibase from "inibase";
605
+ const db = new Inibase("/databaseName");
606
+
607
+ // get the sum of column "age" in "user" table
608
+ await db.sum("user", "age");
609
+
610
+ // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
611
+ await db.sum("user", ["age", ...], { isActive: false });
612
+ ```
613
+
614
+ </blockquote>
386
615
  </details>
387
616
 
617
+ <details>
618
+ <summary>MAX</summary>
619
+ <blockquote>
620
+
621
+ ```js
622
+ import Inibase from "inibase";
623
+ const db = new Inibase("/databaseName");
624
+
625
+ // get the biggest number of column "age" in "user" table
626
+ await db.max("user", "age");
627
+
628
+ // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
629
+ await db.max("user", ["age", ...], { isActive: false });
630
+ ```
631
+
632
+ </blockquote>
633
+ </details>
634
+
635
+ <details>
636
+ <summary>MIN</summary>
637
+ <blockquote>
638
+
639
+ ```js
640
+ import Inibase from "inibase";
641
+ const db = new Inibase("/databaseName");
642
+
643
+ // get the smallest number of column "age" in "user" table
644
+ await db.min("user", "age");
645
+
646
+ // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
647
+ await db.min("user", ["age", ...], { isActive: false });
648
+ ```
649
+
650
+ </blockquote>
651
+ </details>
652
+
653
+ <details>
654
+ <summary>SORT</summary>
655
+ <blockquote>
656
+
657
+ ```js
658
+ import Inibase from "inibase";
659
+ const db = new Inibase("/databaseName");
660
+
661
+ // order users by the age column
662
+ await db.get("user", undefined, { sort: "age" });
663
+
664
+ // order users by the age and username columns
665
+ await db.get("user", undefined, { sort: ["age", "username"] });
666
+ await db.get("user", undefined, { sort: {age: -1, username: "asc"} });
667
+ ```
668
+
669
+ </blockquote>
670
+ </details>
671
+
672
+ </blockquote>
673
+ </details>
674
+
675
+ ## Benchmark
676
+
677
+ ### Bulk
678
+
679
+ | | 10 | 100 | 1000 |
680
+ |--------|-------------------|-------------------|-------------------|
681
+ | POST | 11 ms (0.66 mb) | 5 ms (1.02 mb) | 24 ms (1.44 mb) |
682
+ | GET | 29 ms (2.86 mb) | 24 ms (2.81 mb) | 36 ms (0.89 mb) |
683
+ | PUT | 21 ms (2.68 mb) | 16 ms (2.90 mb) | 12 ms (0.63 mb) |
684
+ | DELETE | 14 ms (0.82 mb) | 13 ms (0.84 mb) | 2 ms (0.17 mb) |
685
+
686
+
687
+ ### Single
688
+
689
+ | | 10 | 100 | 1000 |
690
+ |--------|---------------------|--------------------|--------------------|
691
+ | POST | 45 ms (1.07 mb) | 12 ms (0.52 mb) | 11 ms (0.37 mb) |
692
+ | GET | 200 ms (2.15 mb) | 192 ms (2.72 mb) | 190 ms (2.31 mb) |
693
+ | PUT | 49 ms (3.22 mb) | 17 ms (2.98 mb) | 17 ms (3.06 mb) |
694
+ | DELETE | 118 ms (0.59 mb) | 113 ms (0.51 mb) | 103 ms (3.14 mb) |
695
+
696
+ > Default testing uses a table with username, email, and password fields, ensuring password encryption is included in the process<br>
697
+ > To run benchmarks, install _typescript_ & _[tsx](https://github.com/privatenumber/tsx)_ globally and run `benchmark` by default bulk, for single use `benchmark --single|-s`
698
+
388
699
  ## Roadmap
389
700
 
390
701
  - [x] Actions:
@@ -392,11 +703,14 @@ await db.put("user", { isActive: false });
392
703
  - [x] Pagination
393
704
  - [x] Criteria
394
705
  - [x] Columns
395
- - [ ] Order By
706
+ - [x] Sort
396
707
  - [x] POST
397
708
  - [x] PUT
398
709
  - [x] DELETE
399
- - [ ] Schema supported types:
710
+ - [x] SUM
711
+ - [x] MAX
712
+ - [x] MIN
713
+ - [x] Schema supported types:
400
714
  - [x] String
401
715
  - [x] Number
402
716
  - [x] Boolean
@@ -410,12 +724,19 @@ await db.put("user", { isActive: false });
410
724
  - [x] IP
411
725
  - [x] HTML
412
726
  - [x] Id
727
+ - [x] JSON
413
728
  - [ ] TO-DO:
414
- - [ ] Improve caching
729
+ - [ ] Use new Map() instead of Object
730
+ - [ ] Ability to search in JSON fields
731
+ - [ ] Re-check used exec functions
732
+ - [ ] Use smart caching (based on N° of queries)
415
733
  - [ ] Commenting the code
734
+ - [ ] Add Backup feature (generate a tar.gz)
735
+ - [ ] Add Custom field validation property to schema (using RegEx?)
416
736
  - [ ] Features:
417
737
  - [ ] Encryption
418
- - [ ] Compress data
738
+ - [x] Data Compression
739
+ - [x] Caching System
419
740
  - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
420
741
 
421
742
  ## License
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";