inibase 1.0.0-rc.8 → 1.0.0-rc.80

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
@@ -4,24 +4,29 @@
4
4
 
5
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)
6
6
 
7
- > File-based relational database, simple to use and can handle large data :fire:
7
+ > A file-based & memory-efficient, serverless, ACID compliant, relational database management system :fire:
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** included :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");
@@ -31,42 +36,113 @@ const users = await db.get("user", undefined, { page: 2, per_page: 15 });
31
36
 
32
37
  // Get only required columns to improve speed
33
38
  const users = await db.get("user", undefined, {
34
- columns: ["username", "address.street", "hobbies.*.name"],
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
-
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).
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! 🫰
49
49
 
50
50
  ## Install
51
51
 
52
52
  ```js
53
- <npm|pnpm> install inibase
53
+ <npm|pnpm|yarn> install inibase
54
54
  ```
55
55
 
56
56
  ## How it works?
57
57
 
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)
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
+ ## Inibase CLI
68
+
69
+ ```shell
70
+ npx inibase -p <databaseFolderPath>
71
+ # by default it will diplay a list of available commands (or type 'help')
72
+ ```
59
73
 
60
74
  ## Examples
61
75
 
62
76
  <details>
63
- <summary>POST</summary>
77
+ <summary>Tables</summary>
78
+ <blockquote>
79
+
80
+ <details>
81
+ <summary>Config</summary>
82
+ <blockquote>
83
+
84
+ ```ts
85
+ interface {
86
+ compression: boolean;
87
+ cache: boolean;
88
+ prepend: boolean;
89
+ }
90
+ ```
91
+ </blockquote>
92
+ </details>
93
+
94
+ <details>
95
+ <summary>Schema</summary>
96
+ <blockquote>
97
+
98
+ ```ts
99
+ interface {
100
+ id: number; // stored as a Number but displayed as a hashed ID
101
+ key: string;
102
+ required?: boolean;
103
+ unique?: boolean;
104
+ type: "string" | "number" | "boolean" | "date" | "email" | "url" | "password" | "html" | "ip" | "json" | "id";
105
+ }
106
+ interface Table {
107
+ id: number;
108
+ key: string;
109
+ required?: boolean;
110
+ type: "table";
111
+ table: string;
112
+ }
113
+ interface Array {
114
+ id: number;
115
+ key: string;
116
+ required?: boolean;
117
+ type: "array";
118
+ children: string|string[];
119
+ }
120
+ interface ObjectOrArrayOfObjects {
121
+ id: number;
122
+ key: string;
123
+ required?: boolean;
124
+ type: "object" | "array";
125
+ children: Schema;
126
+ }
127
+ ```
128
+ </blockquote>
129
+ </details>
130
+
131
+ <details>
132
+ <summary>Create Table</summary>
133
+ <blockquote>
64
134
 
65
135
  ```js
66
136
  import Inibase from "inibase";
67
- const db = new Inibase("/database_name");
137
+ const db = new Inibase("/databaseName");
138
+
139
+ const userTableConfig = {
140
+ compression: true,
141
+ cache: true,
142
+ prepend: false
143
+ }
68
144
 
69
- const user_schema = [
145
+ const userTableSchema = [
70
146
  {
71
147
  key: "username",
72
148
  type: "string",
@@ -132,7 +208,149 @@ const user_schema = [
132
208
  },
133
209
  ];
134
210
 
135
- const user_data = [
211
+ await db.createTable("user", userTableSchema, userTableConfig);
212
+ ```
213
+ </blockquote>
214
+ </details>
215
+
216
+ <details>
217
+ <summary>Update Table</summary>
218
+ <blockquote>
219
+
220
+ <details>
221
+ <summary>Add field</summary>
222
+ <blockquote>
223
+
224
+ ```js
225
+ import Inibase from "inibase";
226
+ const db = new Inibase("/databaseName");
227
+
228
+ const userTableSchema = (await db.getTable("user")).schema;
229
+ const newUserTableSchema = [...userTableSchema, {key: "phone2", type: "number", required: false}];
230
+
231
+ await db.updateTable("user", newUserTableSchema);
232
+ ```
233
+ </blockquote>
234
+ </details>
235
+
236
+ <details>
237
+ <summary>Update field</summary>
238
+ <blockquote>
239
+
240
+ ```js
241
+ import Inibase from "inibase";
242
+ import { setField } from "inibase/utils";
243
+
244
+ const db = new Inibase("/databaseName");
245
+
246
+ const userTableSchema = (await db.getTable("user")).schema;
247
+ setField("username", userTableSchema, {key: "fullName"});
248
+ await db.updateTable("user", newUserTableSchema);
249
+ ```
250
+ </blockquote>
251
+ </details>
252
+
253
+ <details>
254
+ <summary>Remove field</summary>
255
+ <blockquote>
256
+
257
+ ```js
258
+ import Inibase from "inibase";
259
+ import { unsetField } from "inibase/utils";
260
+
261
+ const db = new Inibase("/databaseName");
262
+
263
+ const userTableSchema = (await db.getTable("user")).schema;
264
+ unsetField("fullName", userTableSchema);
265
+ await db.updateTable("user", newUserTableSchema);
266
+ ```
267
+ </blockquote>
268
+ </details>
269
+
270
+ </blockquote>
271
+ </details>
272
+
273
+ <details>
274
+ <summary>Join Tables</summary>
275
+ <blockquote>
276
+
277
+ ```js
278
+ import Inibase from "inibase";
279
+ const db = new Inibase("/databaseName");
280
+
281
+ const productTableSchema = [
282
+ {
283
+ key: "title",
284
+ type: "string",
285
+ required: true,
286
+ },
287
+ {
288
+ key: "price",
289
+ type: "number",
290
+ },
291
+ {
292
+ key: "createdBy",
293
+ type: "table",
294
+ table: "user",
295
+ required: true,
296
+ },
297
+ ];
298
+
299
+ await db.createTable("product", productTableSchema);
300
+
301
+ const productTableData = [
302
+ {
303
+ title: "Product 1",
304
+ price: 16,
305
+ createdBy: "1d88385d4b1581f8fb059334dec30f4c",
306
+ },
307
+ {
308
+ title: "Product 2",
309
+ price: 10,
310
+ createdBy: "5011c230aa44481bf7e8dcfe0710474f",
311
+ },
312
+ ];
313
+
314
+ const product = await db.post("product", productTableData);
315
+ // [
316
+ // {
317
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
318
+ // "title": "Product 1",
319
+ // "price": 16,
320
+ // "createdBy": {
321
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
322
+ // "username": "user1",
323
+ // "email": "user1@example.com",
324
+ // ...
325
+ // }
326
+ // },
327
+ // {
328
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
329
+ // "title": "Product 2",
330
+ // "price": 10,
331
+ // "createdBy": {
332
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
333
+ // "username": "user2",
334
+ // ...
335
+ // }
336
+ // }
337
+ // ]
338
+ ```
339
+ </blockquote>
340
+ </details>
341
+
342
+ </blockquote>
343
+ </details>
344
+
345
+ <details>
346
+ <summary>POST</summary>
347
+ <blockquote>
348
+
349
+ ```js
350
+ import Inibase from "inibase";
351
+ const db = new Inibase("/databaseName");
352
+
353
+ const userTableData = [
136
354
  {
137
355
  username: "user1",
138
356
  email: "user1@example.com",
@@ -167,7 +385,7 @@ const user_data = [
167
385
  },
168
386
  ];
169
387
 
170
- const users = await db.post("user", user_data);
388
+ const users = await db.post("user", userTableData);
171
389
  // [
172
390
  // {
173
391
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -205,78 +423,21 @@ const users = await db.post("user", user_data);
205
423
  // ]
206
424
  ```
207
425
 
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
-
426
+ </blockquote>
270
427
  </details>
271
428
 
272
429
  <details>
273
430
  <summary>GET</summary>
431
+ <blockquote>
432
+
433
+ <details>
434
+ <summary>GET by ID</summary>
435
+ <blockquote>
274
436
 
275
437
  ```js
276
438
  import Inibase from "inibase";
277
- const db = new Inibase("/database_name");
439
+ const db = new Inibase("/databaseName");
278
440
 
279
- // Get "user" by id
280
441
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
281
442
  // {
282
443
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -305,8 +466,18 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
305
466
  // "country": "Sampleland"
306
467
  // }
307
468
  // }
469
+ ```
470
+ </blockquote>
471
+ </details>
472
+
473
+ <details>
474
+ <summary>GET by criteria</summary>
475
+ <blockquote>
476
+
477
+ ```js
478
+ import Inibase from "inibase";
479
+ const db = new Inibase("/databaseName");
308
480
 
309
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
310
481
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
311
482
  // [
312
483
  // {
@@ -338,21 +509,36 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
338
509
  // },
339
510
  // ...
340
511
  // ]
512
+ ```
513
+ </blockquote>
514
+ </details>
515
+
516
+ <details>
517
+ <summary>GET with columns</summary>
518
+ <blockquote>
519
+
520
+ ```js
521
+ import Inibase from "inibase";
522
+ const db = new Inibase("/databaseName");
341
523
 
342
524
  // Get all "user" columns except "username" & "address.street"
343
525
  const users = await db.get("user", undefined, {
344
526
  columns: ["!username", "!address.street"],
345
527
  });
346
528
  ```
529
+ </blockquote>
530
+ </details>
347
531
 
532
+ </blockquote>
348
533
  </details>
349
534
 
350
535
  <details>
351
536
  <summary>PUT</summary>
537
+ <blockquote>
352
538
 
353
539
  ```js
354
540
  import Inibase from "inibase";
355
- const db = new Inibase("/database_name");
541
+ const db = new Inibase("/databaseName");
356
542
 
357
543
  // set "isActive" to "false" for all items in table "user"
358
544
  await db.put("user", { isActive: false });
@@ -363,15 +549,16 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
363
549
  // set "isActive" to "true" in table "user" by criteria (where "isActive" is equal to "true")
364
550
  await db.put("user", { isActive: false }, { isActive: true });
365
551
  ```
366
-
552
+ </blockquote>
367
553
  </details>
368
554
 
369
555
  <details>
370
556
  <summary>DELETE</summary>
557
+ <blockquote>
371
558
 
372
559
  ```js
373
560
  import Inibase from "inibase";
374
- const db = new Inibase("/database_name");
561
+ const db = new Inibase("/databaseName");
375
562
 
376
563
  // delete all items in "user" table
377
564
  await db.delete("user");
@@ -382,9 +569,101 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
382
569
  // delete "user" by criteria (where "isActive" is equal to "false")
383
570
  await db.put("user", { isActive: false });
384
571
  ```
572
+ </blockquote>
573
+ </details>
574
+
575
+ <details>
576
+ <summary>SUM</summary>
577
+ <blockquote>
578
+
579
+ ```js
580
+ import Inibase from "inibase";
581
+ const db = new Inibase("/databaseName");
385
582
 
583
+ // get the sum of column "age" in "user" table
584
+ await db.sum("user", "age");
585
+
586
+ // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
587
+ await db.sum("user", ["age", ...], { isActive: false });
588
+ ```
589
+ </blockquote>
386
590
  </details>
387
591
 
592
+ <details>
593
+ <summary>MAX</summary>
594
+ <blockquote>
595
+
596
+ ```js
597
+ import Inibase from "inibase";
598
+ const db = new Inibase("/databaseName");
599
+
600
+ // get the biggest number of column "age" in "user" table
601
+ await db.max("user", "age");
602
+
603
+ // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
604
+ await db.max("user", ["age", ...], { isActive: false });
605
+ ```
606
+ </blockquote>
607
+ </details>
608
+
609
+ <details>
610
+ <summary>MIN</summary>
611
+ <blockquote>
612
+
613
+ ```js
614
+ import Inibase from "inibase";
615
+ const db = new Inibase("/databaseName");
616
+
617
+ // get the smallest number of column "age" in "user" table
618
+ await db.min("user", "age");
619
+
620
+ // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
621
+ await db.min("user", ["age", ...], { isActive: false });
622
+ ```
623
+ </blockquote>
624
+ </details>
625
+
626
+ <details>
627
+ <summary>SORT</summary>
628
+ <blockquote>
629
+
630
+ ```js
631
+ import Inibase from "inibase";
632
+ const db = new Inibase("/databaseName");
633
+
634
+ // order users by the age column
635
+ await db.get("user", undefined, { sort: "age" });
636
+
637
+ // order users by the age and username columns
638
+ await db.get("user", undefined, { sort: ["age", "username"] });
639
+ await db.get("user", undefined, { sort: {age: -1, username: "asc"} });
640
+ ```
641
+ </blockquote>
642
+ </details>
643
+
644
+ ## Benchmark
645
+
646
+ ### Bulk
647
+
648
+ | | 10 | 100 | 1000 |
649
+ |--------|-----------------|-----------------|-----------------|
650
+ | POST | 11 ms (0.65 mb) | 19 ms (1.00 mb) | 85 ms (4.58 mb) |
651
+ | GET | 14 ms (2.77 mb) | 12 ms (3.16 mb) | 34 ms (1.38 mb) |
652
+ | PUT | 6 ms (1.11 mb) | 5 ms (1.37 mb) | 10 ms (1.12 mb) |
653
+ | DELETE | 17 ms (1.68 mb) | 14 ms (5.45 mb) | 25 ms (5.94 mb) |
654
+
655
+ ### Single
656
+
657
+ | | 10 | 100 | 1000 |
658
+ |--------|-------------------|--------------------|--------------------|
659
+ | POST | 43 ms (4.70 mb) | 387 ms (6.36 mb) | 5341 ms (24.73 mb) |
660
+ | GET | 99 ms (12.51 mb) | 846 ms (30.68 mb) | 7103 ms (30.86 mb) |
661
+ | PUT | 33 ms (10.29 mb) | 312 ms (11.06 mb) | 3539 ms (14.87 mb) |
662
+ | DELETE | 134 ms (13.50 mb) | 1224 ms (16.57 mb) | 7339 ms (11.46 mb) |
663
+
664
+ > Testing by default with `user` table, with username, email, password fields _so results include password encryption process_ <br>
665
+ > To run benchmarks, install *typescript* & *[tsx](https://github.com/privatenumber/tsx)* globally and run `benchmark` `benchmark:bulk` `benchmark:single`
666
+
388
667
  ## Roadmap
389
668
 
390
669
  - [x] Actions:
@@ -392,10 +671,13 @@ await db.put("user", { isActive: false });
392
671
  - [x] Pagination
393
672
  - [x] Criteria
394
673
  - [x] Columns
395
- - [ ] Order By
674
+ - [x] Sort
396
675
  - [x] POST
397
676
  - [x] PUT
398
677
  - [x] DELETE
678
+ - [x] SUM
679
+ - [x] MAX
680
+ - [x] MIN
399
681
  - [ ] Schema supported types:
400
682
  - [x] String
401
683
  - [x] Number
@@ -410,14 +692,19 @@ await db.put("user", { isActive: false });
410
692
  - [x] IP
411
693
  - [x] HTML
412
694
  - [x] Id
695
+ - [x] JSON
413
696
  - [ ] TO-DO:
414
- - [ ] Improve caching
697
+ - [x] Improve caching
415
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?)
416
702
  - [ ] Features:
417
703
  - [ ] Encryption
418
- - [ ] Compress data
704
+ - [x] Data Compression
705
+ - [x] Caching System
419
706
  - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
420
707
 
421
708
  ## License
422
709
 
423
- [MIT](./LICENSE)
710
+ [MIT](./LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";