inibase 1.0.0-rc.9 → 1.0.0-rc.90

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,113 @@ 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
 
56
57
  ## How it works?
57
58
 
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)
59
+ `Inibase` organizes data into databases, tables, and columns, each stored in separate files.
60
+
61
+ - **POST**: New data is appended to column files efficiently.
62
+ - **GET**: Data retrieval is optimized by reading files line-by-line.
63
+ - **PUT**: Updates are streamlined, with only the relevant file being modified.
64
+ - **DELETE**: Removes lines from column files for swift deletion.
65
+
66
+ This structure ensures efficient storage, retrieval, and updates, making our system scalable and high-performing for diverse datasets and applications.
67
+
68
+ ## Inibase CLI
69
+
70
+ ```shell
71
+ npx inibase -p <databaseFolderPath>
72
+ # by default it will diplay a list of available commands (or type 'help')
73
+ ```
59
74
 
60
75
  ## Examples
61
76
 
62
77
  <details>
63
- <summary>POST</summary>
78
+ <summary>Tables</summary>
79
+ <blockquote>
80
+
81
+ <details>
82
+ <summary>Config</summary>
83
+ <blockquote>
84
+
85
+ ```ts
86
+ interface {
87
+ compression: boolean;
88
+ cache: boolean;
89
+ prepend: boolean;
90
+ }
91
+ ```
92
+
93
+ </blockquote>
94
+ </details>
95
+
96
+ <details>
97
+ <summary>Schema</summary>
98
+ <blockquote>
99
+
100
+ ```ts
101
+ interface {
102
+ id: number; // stored as a Number but displayed as a hashed ID
103
+ key: string;
104
+ required?: boolean;
105
+ unique?: boolean;
106
+ type: "string" | "number" | "boolean" | "date" | "email" | "url" | "password" | "html" | "ip" | "json" | "id";
107
+ }
108
+ interface Table {
109
+ id: number;
110
+ key: string;
111
+ required?: boolean;
112
+ type: "table";
113
+ table: string;
114
+ }
115
+ interface Array {
116
+ id: number;
117
+ key: string;
118
+ required?: boolean;
119
+ type: "array";
120
+ children: string|string[];
121
+ }
122
+ interface ObjectOrArrayOfObjects {
123
+ id: number;
124
+ key: string;
125
+ required?: boolean;
126
+ type: "object" | "array";
127
+ children: Schema;
128
+ }
129
+ ```
130
+
131
+ </blockquote>
132
+ </details>
133
+
134
+ <details>
135
+ <summary>Create Table</summary>
136
+ <blockquote>
64
137
 
65
138
  ```js
66
139
  import Inibase from "inibase";
67
- const db = new Inibase("/database_name");
140
+ const db = new Inibase("/databaseName");
141
+
142
+ const userTableConfig = {
143
+ compression: true,
144
+ cache: true,
145
+ prepend: false
146
+ }
68
147
 
69
- const user_schema = [
148
+ const userTableSchema = [
70
149
  {
71
150
  key: "username",
72
151
  type: "string",
@@ -132,7 +211,156 @@ const user_schema = [
132
211
  },
133
212
  ];
134
213
 
135
- const user_data = [
214
+ await db.createTable("user", userTableSchema, userTableConfig);
215
+ ```
216
+
217
+ </blockquote>
218
+ </details>
219
+
220
+ <details>
221
+ <summary>Update Table</summary>
222
+ <blockquote>
223
+
224
+ <details>
225
+ <summary>Change Name</summary>
226
+ <blockquote>
227
+
228
+ ```js
229
+ import Inibase from "inibase";
230
+ const db = new Inibase("/databaseName");
231
+
232
+ // this will change table name also in joined tables
233
+ await db.updateTable("user", undefined, {name: "userV2"});
234
+ ```
235
+
236
+ </blockquote>
237
+ </details>
238
+
239
+ <details>
240
+ <summary>Update field</summary>
241
+ <blockquote>
242
+
243
+ ```js
244
+ import Inibase from "inibase";
245
+ import { setField } from "inibase/utils";
246
+
247
+ const db = new Inibase("/databaseName");
248
+
249
+ const userTableSchema = (await db.getTable("user")).schema;
250
+ setField("username", userTableSchema, {key: "fullName"});
251
+ await db.updateTable("user", newUserTableSchema);
252
+ ```
253
+
254
+ </blockquote>
255
+ </details>
256
+
257
+ <details>
258
+ <summary>Remove field</summary>
259
+ <blockquote>
260
+
261
+ ```js
262
+ import Inibase from "inibase";
263
+ import { unsetField } from "inibase/utils";
264
+
265
+ const db = new Inibase("/databaseName");
266
+
267
+ const userTableSchema = (await db.getTable("user")).schema;
268
+ unsetField("fullName", userTableSchema);
269
+ await db.updateTable("user", newUserTableSchema);
270
+ ```
271
+
272
+ </blockquote>
273
+ </details>
274
+
275
+ </blockquote>
276
+ </details>
277
+
278
+ <details>
279
+ <summary>Join Tables</summary>
280
+ <blockquote>
281
+
282
+ ```js
283
+ import Inibase from "inibase";
284
+ const db = new Inibase("/databaseName");
285
+
286
+ const productTableSchema = [
287
+ {
288
+ key: "title",
289
+ type: "string",
290
+ required: true,
291
+ },
292
+ {
293
+ key: "price",
294
+ type: "number",
295
+ },
296
+ {
297
+ key: "createdBy",
298
+ type: "table",
299
+ table: "user",
300
+ required: true,
301
+ },
302
+ ];
303
+
304
+ await db.createTable("product", productTableSchema);
305
+
306
+ const productTableData = [
307
+ {
308
+ title: "Product 1",
309
+ price: 16,
310
+ createdBy: "1d88385d4b1581f8fb059334dec30f4c",
311
+ },
312
+ {
313
+ title: "Product 2",
314
+ price: 10,
315
+ createdBy: "5011c230aa44481bf7e8dcfe0710474f",
316
+ },
317
+ ];
318
+
319
+ const product = await db.post("product", productTableData);
320
+ // [
321
+ // {
322
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
323
+ // "title": "Product 1",
324
+ // "price": 16,
325
+ // "createdBy": {
326
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
327
+ // "username": "user1",
328
+ // "email": "user1@example.com",
329
+ // ...
330
+ // }
331
+ // },
332
+ // {
333
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
334
+ // "title": "Product 2",
335
+ // "price": 10,
336
+ // "createdBy": {
337
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
338
+ // "username": "user2",
339
+ // ...
340
+ // }
341
+ // }
342
+ // ]
343
+ ```
344
+
345
+ </blockquote>
346
+ </details>
347
+
348
+ </blockquote>
349
+ </details>
350
+
351
+ <details>
352
+ <summary>Methods</summary>
353
+ <blockquote>
354
+
355
+ <details>
356
+ <summary>POST</summary>
357
+ <blockquote>
358
+
359
+ ```js
360
+ import Inibase from "inibase";
361
+ const db = new Inibase("/databaseName");
362
+
363
+ const userTableData = [
136
364
  {
137
365
  username: "user1",
138
366
  email: "user1@example.com",
@@ -167,7 +395,7 @@ const user_data = [
167
395
  },
168
396
  ];
169
397
 
170
- const users = await db.post("user", user_data);
398
+ const users = await db.post("user", userTableData);
171
399
  // [
172
400
  // {
173
401
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -205,78 +433,21 @@ const users = await db.post("user", user_data);
205
433
  // ]
206
434
  ```
207
435
 
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
-
436
+ </blockquote>
270
437
  </details>
271
438
 
272
439
  <details>
273
440
  <summary>GET</summary>
441
+ <blockquote>
442
+
443
+ <details>
444
+ <summary>GET by ID</summary>
445
+ <blockquote>
274
446
 
275
447
  ```js
276
448
  import Inibase from "inibase";
277
- const db = new Inibase("/database_name");
449
+ const db = new Inibase("/databaseName");
278
450
 
279
- // Get "user" by id
280
451
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
281
452
  // {
282
453
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -305,8 +476,19 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
305
476
  // "country": "Sampleland"
306
477
  // }
307
478
  // }
479
+ ```
480
+
481
+ </blockquote>
482
+ </details>
483
+
484
+ <details>
485
+ <summary>GET by criteria</summary>
486
+ <blockquote>
487
+
488
+ ```js
489
+ import Inibase from "inibase";
490
+ const db = new Inibase("/databaseName");
308
491
 
309
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
310
492
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
311
493
  // [
312
494
  // {
@@ -338,6 +520,18 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
338
520
  // },
339
521
  // ...
340
522
  // ]
523
+ ```
524
+
525
+ </blockquote>
526
+ </details>
527
+
528
+ <details>
529
+ <summary>GET with columns</summary>
530
+ <blockquote>
531
+
532
+ ```js
533
+ import Inibase from "inibase";
534
+ const db = new Inibase("/databaseName");
341
535
 
342
536
  // Get all "user" columns except "username" & "address.street"
343
537
  const users = await db.get("user", undefined, {
@@ -345,14 +539,19 @@ const users = await db.get("user", undefined, {
345
539
  });
346
540
  ```
347
541
 
542
+ </blockquote>
543
+ </details>
544
+
545
+ </blockquote>
348
546
  </details>
349
547
 
350
548
  <details>
351
549
  <summary>PUT</summary>
550
+ <blockquote>
352
551
 
353
552
  ```js
354
553
  import Inibase from "inibase";
355
- const db = new Inibase("/database_name");
554
+ const db = new Inibase("/databaseName");
356
555
 
357
556
  // set "isActive" to "false" for all items in table "user"
358
557
  await db.put("user", { isActive: false });
@@ -364,14 +563,16 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
364
563
  await db.put("user", { isActive: false }, { isActive: true });
365
564
  ```
366
565
 
566
+ </blockquote>
367
567
  </details>
368
568
 
369
569
  <details>
370
570
  <summary>DELETE</summary>
571
+ <blockquote>
371
572
 
372
573
  ```js
373
574
  import Inibase from "inibase";
374
- const db = new Inibase("/database_name");
575
+ const db = new Inibase("/databaseName");
375
576
 
376
577
  // delete all items in "user" table
377
578
  await db.delete("user");
@@ -383,8 +584,108 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
383
584
  await db.put("user", { isActive: false });
384
585
  ```
385
586
 
587
+ </blockquote>
588
+ </details>
589
+
590
+ <details>
591
+ <summary>SUM</summary>
592
+ <blockquote>
593
+
594
+ ```js
595
+ import Inibase from "inibase";
596
+ const db = new Inibase("/databaseName");
597
+
598
+ // get the sum of column "age" in "user" table
599
+ await db.sum("user", "age");
600
+
601
+ // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
602
+ await db.sum("user", ["age", ...], { isActive: false });
603
+ ```
604
+
605
+ </blockquote>
606
+ </details>
607
+
608
+ <details>
609
+ <summary>MAX</summary>
610
+ <blockquote>
611
+
612
+ ```js
613
+ import Inibase from "inibase";
614
+ const db = new Inibase("/databaseName");
615
+
616
+ // get the biggest number of column "age" in "user" table
617
+ await db.max("user", "age");
618
+
619
+ // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
620
+ await db.max("user", ["age", ...], { isActive: false });
621
+ ```
622
+
623
+ </blockquote>
624
+ </details>
625
+
626
+ <details>
627
+ <summary>MIN</summary>
628
+ <blockquote>
629
+
630
+ ```js
631
+ import Inibase from "inibase";
632
+ const db = new Inibase("/databaseName");
633
+
634
+ // get the smallest number of column "age" in "user" table
635
+ await db.min("user", "age");
636
+
637
+ // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
638
+ await db.min("user", ["age", ...], { isActive: false });
639
+ ```
640
+
641
+ </blockquote>
386
642
  </details>
387
643
 
644
+ <details>
645
+ <summary>SORT</summary>
646
+ <blockquote>
647
+
648
+ ```js
649
+ import Inibase from "inibase";
650
+ const db = new Inibase("/databaseName");
651
+
652
+ // order users by the age column
653
+ await db.get("user", undefined, { sort: "age" });
654
+
655
+ // order users by the age and username columns
656
+ await db.get("user", undefined, { sort: ["age", "username"] });
657
+ await db.get("user", undefined, { sort: {age: -1, username: "asc"} });
658
+ ```
659
+
660
+ </blockquote>
661
+ </details>
662
+
663
+ </blockquote>
664
+ </details>
665
+
666
+ ## Benchmark
667
+
668
+ ### Bulk
669
+
670
+ | | 10 | 100 | 1000 |
671
+ |--------|-----------------|-----------------|-----------------|
672
+ | POST | 11 ms (0.65 mb) | 19 ms (1.00 mb) | 85 ms (4.58 mb) |
673
+ | GET | 14 ms (2.77 mb) | 12 ms (3.16 mb) | 34 ms (1.38 mb) |
674
+ | PUT | 6 ms (1.11 mb) | 5 ms (1.37 mb) | 10 ms (1.12 mb) |
675
+ | DELETE | 17 ms (1.68 mb) | 14 ms (5.45 mb) | 25 ms (5.94 mb) |
676
+
677
+ ### Single
678
+
679
+ | | 10 | 100 | 1000 |
680
+ |--------|-------------------|--------------------|--------------------|
681
+ | POST | 43 ms (4.70 mb) | 387 ms (6.36 mb) | 5341 ms (24.73 mb) |
682
+ | GET | 99 ms (12.51 mb) | 846 ms (30.68 mb) | 7103 ms (30.86 mb) |
683
+ | PUT | 33 ms (10.29 mb) | 312 ms (11.06 mb) | 3539 ms (14.87 mb) |
684
+ | DELETE | 134 ms (13.50 mb) | 1224 ms (16.57 mb) | 7339 ms (11.46 mb) |
685
+
686
+ > Default testing uses a table with username, email, and password fields, ensuring password encryption is included in the process<br>
687
+ > To run benchmarks, install _typescript_ & _[tsx](https://github.com/privatenumber/tsx)_ globally and run `benchmark` by default bulk, for single use `benchmark --single|-s`
688
+
388
689
  ## Roadmap
389
690
 
390
691
  - [x] Actions:
@@ -392,11 +693,14 @@ await db.put("user", { isActive: false });
392
693
  - [x] Pagination
393
694
  - [x] Criteria
394
695
  - [x] Columns
395
- - [ ] Order By
696
+ - [x] Sort
396
697
  - [x] POST
397
698
  - [x] PUT
398
699
  - [x] DELETE
399
- - [ ] Schema supported types:
700
+ - [x] SUM
701
+ - [x] MAX
702
+ - [x] MIN
703
+ - [x] Schema supported types:
400
704
  - [x] String
401
705
  - [x] Number
402
706
  - [x] Boolean
@@ -410,12 +714,18 @@ await db.put("user", { isActive: false });
410
714
  - [x] IP
411
715
  - [x] HTML
412
716
  - [x] Id
717
+ - [x] JSON
413
718
  - [ ] TO-DO:
414
- - [ ] Improve caching
719
+ - [ ] Ability to search in JSON fields
720
+ - [ ] Re-check used exec functions
721
+ - [ ] Use smart caching (based on N° of queries)
415
722
  - [ ] Commenting the code
723
+ - [ ] Add Backup feature (generate a tar.gz)
724
+ - [ ] Add Custom field validation property to schema (using RegEx?)
416
725
  - [ ] Features:
417
726
  - [ ] Encryption
418
- - [ ] Compress data
727
+ - [x] Data Compression
728
+ - [x] Caching System
419
729
  - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
420
730
 
421
731
  ## License
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";