inibase 1.0.0-rc.0 → 1.0.0-rc.100

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,26 +1,32 @@
1
- ![Inibase banner](./.github/assets/banner.jpg)
1
+ # Inibase :pencil:
2
2
 
3
- # Inibase
3
+ > A file-based & memory-efficient, serverless, ACID compliant, relational database management system :fire:
4
4
 
5
- [![npmjs](https://img.shields.io/npm/dm/inibase.svg?style=flat)](https://www.npmjs.org/package/inibase) [![Node.js Version](https://img.shields.io/badge/node-18.11.0-blue)](https://nodejs.org/) [![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)
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** 🪶 (~50kb)
12
- - **Minimalist** :white_circle:
13
- - **TypeScript** :large_blue_diamond:
14
- - **Super-Fast** :turtle:
15
- - **Suitable for large data** :page_with_curl:
16
- - **Safe** :lock:
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:
17
23
  - **...** and much more :rocket:
18
24
 
19
25
  ## Usage
20
26
 
21
27
  ```js
22
28
  import Inibase from "inibase";
23
- const db = new Inibase("/database_name");
29
+ const db = new Inibase("databaseName");
24
30
 
25
31
  // Get all items from "user" table
26
32
  const users = await db.get("user");
@@ -29,45 +35,117 @@ const users = await db.get("user");
29
35
  const users = await db.get("user", undefined, { page: 2, per_page: 15 });
30
36
 
31
37
  // Get only required columns to improve speed
32
- const users = await InibaseDB.get("user", undefined, {
33
- columns: ["username", "address.street", "hobbies.*.name"],
38
+ const users = await db.get("user", undefined, {
39
+ columns: ["username", "address.street", "hobbies.name"],
34
40
  });
35
41
 
36
- // Get items from "user" table where "favoriteFoods" does not includes "Pizza"
37
- const users = await InibaseDB.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" });
38
44
  ```
39
45
 
40
- If you like Inibase, please sponsor: [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! 🫰
41
50
 
42
- ## Sponsors
51
+ ## Install
43
52
 
44
- <br>
45
- <br>
53
+ ```js
54
+ <npm|pnpm|yarn|bun> install inibase
55
+ ```
46
56
 
47
- Become a sponsor and have your company logo here 👉 [GitHub Sponsors](https://github.com/sponsors/inicontent) || [paypal](https://paypal.me/KarimAmahtil).
57
+ ## How it works?
48
58
 
49
- ## Install
59
+ `Inibase` organizes data into databases, tables, and columns, each stored in separate files.
50
60
 
51
- ```js
52
- // npm
53
- npm install inibase
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.
54
67
 
55
- // pnpm
56
- pnpm add inibase
68
+ ## Inibase CLI
57
69
 
58
- // yarn
59
- yarn add inibase
70
+ ```shell
71
+ npx inibase -p <databaseFolderPath>
72
+ # by default it will diplay a list of available commands (or type 'help')
60
73
  ```
61
74
 
62
75
  ## Examples
63
76
 
64
- #### POST
77
+ <details>
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>
65
137
 
66
138
  ```js
67
139
  import Inibase from "inibase";
68
- const db = new Inibase("/database_name");
140
+ const db = new Inibase("/databaseName");
69
141
 
70
- const user_schema = [
142
+ const userTableConfig = {
143
+ compression: true,
144
+ cache: true,
145
+ prepend: false
146
+ }
147
+
148
+ const userTableSchema = [
71
149
  {
72
150
  key: "username",
73
151
  type: "string",
@@ -133,7 +211,156 @@ const user_schema = [
133
211
  },
134
212
  ];
135
213
 
136
- 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 = [
137
364
  {
138
365
  username: "user1",
139
366
  email: "user1@example.com",
@@ -168,7 +395,7 @@ const user_data = [
168
395
  },
169
396
  ];
170
397
 
171
- const users = await db.post("user", user_data);
398
+ const users = await db.post("user", userTableData);
172
399
  // [
173
400
  // {
174
401
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -206,75 +433,21 @@ const users = await db.post("user", user_data);
206
433
  // ]
207
434
  ```
208
435
 
209
- Link two tables: "product" with "user"
436
+ </blockquote>
437
+ </details>
210
438
 
211
- ```js
212
- import Inibase from "inibase";
213
- const db = new Inibase("/database_name");
214
-
215
- const product_schema = [
216
- {
217
- key: "title",
218
- type: "string",
219
- required: true,
220
- },
221
- {
222
- key: "price",
223
- type: "number",
224
- },
225
- {
226
- key: "user",
227
- type: "table",
228
- required: true,
229
- },
230
- ];
439
+ <details>
440
+ <summary>GET</summary>
441
+ <blockquote>
231
442
 
232
- const product_data = [
233
- {
234
- title: "Product 1",
235
- price: 16,
236
- user: "1d88385d4b1581f8fb059334dec30f4c",
237
- },
238
- {
239
- title: "Product 2",
240
- price: 10,
241
- user: "5011c230aa44481bf7e8dcfe0710474f",
242
- },
243
- ];
244
-
245
- const product = await db.post("product", product_data);
246
- // [
247
- // {
248
- // "id": "1d88385d4b1581f8fb059334dec30f4c",
249
- // "title": "Product 1",
250
- // "price": 16,
251
- // "user": {
252
- // "id": "1d88385d4b1581f8fb059334dec30f4c",
253
- // "username": "user1",
254
- // "email": "user1@example.com",
255
- // ...
256
- // }
257
- // },
258
- // {
259
- // "id": "5011c230aa44481bf7e8dcfe0710474f",
260
- // "title": "Product 2",
261
- // "price": 10,
262
- // "user": {
263
- // "id": "5011c230aa44481bf7e8dcfe0710474f",
264
- // "username": "user2",
265
- // ...
266
- // }
267
- // }
268
- // ]
269
- ```
270
-
271
- #### GET
443
+ <details>
444
+ <summary>GET by ID</summary>
445
+ <blockquote>
272
446
 
273
447
  ```js
274
448
  import Inibase from "inibase";
275
- const db = new Inibase("/database_name");
449
+ const db = new Inibase("/databaseName");
276
450
 
277
- // Get "user" by id
278
451
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
279
452
  // {
280
453
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -303,8 +476,19 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
303
476
  // "country": "Sampleland"
304
477
  // }
305
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");
306
491
 
307
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
308
492
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
309
493
  // [
310
494
  // {
@@ -338,11 +522,36 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
338
522
  // ]
339
523
  ```
340
524
 
341
- #### PUT
525
+ </blockquote>
526
+ </details>
527
+
528
+ <details>
529
+ <summary>GET with columns</summary>
530
+ <blockquote>
342
531
 
343
532
  ```js
344
533
  import Inibase from "inibase";
345
- const db = new Inibase("/database_name");
534
+ const db = new Inibase("/databaseName");
535
+
536
+ // Get all "user" columns except "username" & "address.street"
537
+ const users = await db.get("user", undefined, {
538
+ columns: ["!username", "!address.street"],
539
+ });
540
+ ```
541
+
542
+ </blockquote>
543
+ </details>
544
+
545
+ </blockquote>
546
+ </details>
547
+
548
+ <details>
549
+ <summary>PUT</summary>
550
+ <blockquote>
551
+
552
+ ```js
553
+ import Inibase from "inibase";
554
+ const db = new Inibase("/databaseName");
346
555
 
347
556
  // set "isActive" to "false" for all items in table "user"
348
557
  await db.put("user", { isActive: false });
@@ -354,11 +563,16 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
354
563
  await db.put("user", { isActive: false }, { isActive: true });
355
564
  ```
356
565
 
357
- #### DELETE
566
+ </blockquote>
567
+ </details>
568
+
569
+ <details>
570
+ <summary>DELETE</summary>
571
+ <blockquote>
358
572
 
359
573
  ```js
360
574
  import Inibase from "inibase";
361
- const db = new Inibase("/database_name");
575
+ const db = new Inibase("/databaseName");
362
576
 
363
577
  // delete all items in "user" table
364
578
  await db.delete("user");
@@ -370,46 +584,108 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
370
584
  await db.put("user", { isActive: false });
371
585
  ```
372
586
 
373
- ## Typescript
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>
374
625
 
375
- #### Schema
626
+ <details>
627
+ <summary>MIN</summary>
628
+ <blockquote>
376
629
 
377
630
  ```js
378
- type FieldType =
379
- | "string"
380
- | "number"
381
- | "boolean"
382
- | "date"
383
- | "email"
384
- | "url"
385
- | "table"
386
- | "object"
387
- | "array";
388
- type Field =
389
- | {
390
- id?: string | number | null | undefined,
391
- key: string,
392
- type: Exclude<FieldType, "array" | "object">,
393
- required?: boolean,
394
- }
395
- | {
396
- id?: string | number | null | undefined,
397
- key: string,
398
- type: "array",
399
- required?: boolean,
400
- children: FieldType | FieldType[] | Schema,
401
- }
402
- | {
403
- id?: string | number | null | undefined,
404
- key: string,
405
- type: "object",
406
- required?: boolean,
407
- children: Schema,
408
- };
409
-
410
- type Schema = Field[];
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 });
411
639
  ```
412
640
 
641
+ </blockquote>
642
+ </details>
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
+
413
689
  ## Roadmap
414
690
 
415
691
  - [x] Actions:
@@ -417,11 +693,14 @@ type Schema = Field[];
417
693
  - [x] Pagination
418
694
  - [x] Criteria
419
695
  - [x] Columns
420
- - [ ] Order
696
+ - [x] Sort
421
697
  - [x] POST
422
698
  - [x] PUT
423
699
  - [x] DELETE
424
- - [ ] Schema supported types:
700
+ - [x] SUM
701
+ - [x] MAX
702
+ - [x] MIN
703
+ - [x] Schema supported types:
425
704
  - [x] String
426
705
  - [x] Number
427
706
  - [x] Boolean
@@ -431,9 +710,23 @@ type Schema = Field[];
431
710
  - [x] Table
432
711
  - [x] Object
433
712
  - [x] Array
434
- - [ ] Password
435
- - [ ] IP
436
- - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
713
+ - [x] Password
714
+ - [x] IP
715
+ - [x] HTML
716
+ - [x] Id
717
+ - [x] JSON
718
+ - [ ] TO-DO:
719
+ - [ ] Ability to search in JSON fields
720
+ - [ ] Re-check used exec functions
721
+ - [ ] Use smart caching (based on N° of queries)
722
+ - [ ] Commenting the code
723
+ - [ ] Add Backup feature (generate a tar.gz)
724
+ - [ ] Add Custom field validation property to schema (using RegEx?)
725
+ - [ ] Features:
726
+ - [ ] Encryption
727
+ - [x] Data Compression
728
+ - [x] Caching System
729
+ - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
437
730
 
438
731
  ## License
439
732