inibase 1.0.0-rc.6 → 1.0.0-rc.61

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** 🪶 (~60kb)
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,146 @@ 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
  ```
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! 🫰
40
49
 
41
- If you like Inibase, please sponsor: [GitHub Sponsors](https://github.com/sponsors/inicontent) || [Paypal](https://paypal.me/KarimAmahtil).
50
+ ## Install
51
+
52
+ ```js
53
+ <npm|pnpm|yarn> install inibase
54
+ ```
42
55
 
43
- ## Sponsors
56
+ ## How it works?
44
57
 
45
- <br>
46
- <br>
58
+ `Inibase` organizes data into databases, tables, and columns, each stored in separate files.
47
59
 
48
- Become a sponsor and have your company logo here 👉 [GitHub Sponsors](https://github.com/sponsors/inicontent) || [paypal](https://paypal.me/KarimAmahtil).
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.
49
64
 
50
- ## Install
65
+ This structure ensures efficient storage, retrieval, and updates, making our system scalable and high-performing for diverse datasets and applications.
51
66
 
52
- ```js
53
- <npm|pnpm> install inibase
67
+ ## Inibase CLI
68
+
69
+ ```shell
70
+ npx inibase -p <databaseFolderPath>
54
71
  ```
55
72
 
56
- ## How it works?
73
+ <blockquote>
74
+ <details>
75
+ <summary>GET</summary>
76
+
77
+ ```shell
78
+ get <tableName> -w <ID|LineNumber|Criteria> -p <pageNumber> -l <perPage> -c <columnName1> -c <columnName2>
79
+ ```
80
+ </details>
57
81
 
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)
82
+ <details>
83
+ <summary>POST</summary>
84
+
85
+ ```shell
86
+ post <tableName> -d <InisonStrigifedData>
87
+ ```
88
+ </details>
89
+
90
+ <details>
91
+ <summary>PUT</summary>
92
+
93
+ ```shell
94
+ put <tableName> -d <InisonStrigifedData> -w <ID|LineNumber|Criteria>
95
+ ```
96
+ </details>
97
+
98
+ <details>
99
+ <summary>DELETE</summary>
100
+
101
+ ```shell
102
+ delete <tableName> -w <ID|LineNumber|Criteria>
103
+ ```
104
+ </details>
105
+ </blockquote>
59
106
 
60
107
  ## Examples
61
108
 
62
109
  <details>
63
- <summary>POST</summary>
110
+ <summary>Tables</summary>
111
+ <blockquote>
112
+
113
+ <details>
114
+ <summary>Config</summary>
115
+ <blockquote>
116
+
117
+ ```ts
118
+ interface {
119
+ compression: boolean;
120
+ cache: boolean;
121
+ prepend: boolean;
122
+ }
123
+ ```
124
+ </blockquote>
125
+ </details>
126
+
127
+ <details>
128
+ <summary>Schema</summary>
129
+ <blockquote>
130
+
131
+ ```ts
132
+ interface {
133
+ id: number; // stored as a Number but displayed as a hashed ID
134
+ key: string;
135
+ required?: boolean;
136
+ unique?: boolean;
137
+ type: "string" | "number" | "boolean" | "date" | "email" | "url" | "password" | "html" | "ip" | "json" | "id";
138
+ }
139
+ interface Table {
140
+ id: number;
141
+ key: string;
142
+ required?: boolean;
143
+ type: "table";
144
+ table: string;
145
+ }
146
+ interface Array {
147
+ id: number;
148
+ key: string;
149
+ required?: boolean;
150
+ type: "array";
151
+ children: string|string[];
152
+ }
153
+ interface ObjectOrArrayOfObjects {
154
+ id: number;
155
+ key: string;
156
+ required?: boolean;
157
+ type: "object" | "array";
158
+ children: Schema;
159
+ }
160
+ ```
161
+ </blockquote>
162
+ </details>
163
+
164
+ <details>
165
+ <summary>Create Table</summary>
166
+ <blockquote>
64
167
 
65
168
  ```js
66
169
  import Inibase from "inibase";
67
- const db = new Inibase("/database_name");
170
+ const db = new Inibase("/databaseName");
68
171
 
69
- const user_schema = [
172
+ const userTableConfig = {
173
+ compression: true,
174
+ cache: true,
175
+ prepend: false
176
+ }
177
+
178
+ const userTableSchema = [
70
179
  {
71
180
  key: "username",
72
181
  type: "string",
@@ -132,7 +241,149 @@ const user_schema = [
132
241
  },
133
242
  ];
134
243
 
135
- const user_data = [
244
+ await db.createTable("user", userTableSchema, userTableConfig);
245
+ ```
246
+ </blockquote>
247
+ </details>
248
+
249
+ <details>
250
+ <summary>Update Table</summary>
251
+ <blockquote>
252
+
253
+ <details>
254
+ <summary>Add field</summary>
255
+ <blockquote>
256
+
257
+ ```js
258
+ import Inibase from "inibase";
259
+ const db = new Inibase("/databaseName");
260
+
261
+ const userTableSchema = (await db.getTable("user")).schema;
262
+ const newUserTableSchema = [...userTableSchema, {key: "phone2", type: "number", required: false}];
263
+
264
+ await db.updateTable("user", newUserTableSchema);
265
+ ```
266
+ </blockquote>
267
+ </details>
268
+
269
+ <details>
270
+ <summary>Update field</summary>
271
+ <blockquote>
272
+
273
+ ```js
274
+ import Inibase from "inibase";
275
+ import { setField } from "inibase/utils";
276
+
277
+ const db = new Inibase("/databaseName");
278
+
279
+ const userTableSchema = (await db.getTable("user")).schema;
280
+ setField("username", userTableSchema, {key: "fullName"});
281
+ await db.updateTable("user", newUserTableSchema);
282
+ ```
283
+ </blockquote>
284
+ </details>
285
+
286
+ <details>
287
+ <summary>Remove field</summary>
288
+ <blockquote>
289
+
290
+ ```js
291
+ import Inibase from "inibase";
292
+ import { unsetField } from "inibase/utils";
293
+
294
+ const db = new Inibase("/databaseName");
295
+
296
+ const userTableSchema = (await db.getTable("user")).schema;
297
+ unsetField("fullName", userTableSchema);
298
+ await db.updateTable("user", newUserTableSchema);
299
+ ```
300
+ </blockquote>
301
+ </details>
302
+
303
+ </blockquote>
304
+ </details>
305
+
306
+ <details>
307
+ <summary>Join Tables</summary>
308
+ <blockquote>
309
+
310
+ ```js
311
+ import Inibase from "inibase";
312
+ const db = new Inibase("/databaseName");
313
+
314
+ const productTableSchema = [
315
+ {
316
+ key: "title",
317
+ type: "string",
318
+ required: true,
319
+ },
320
+ {
321
+ key: "price",
322
+ type: "number",
323
+ },
324
+ {
325
+ key: "createdBy",
326
+ type: "table",
327
+ table: "user",
328
+ required: true,
329
+ },
330
+ ];
331
+
332
+ await db.createTable("product", productTableSchema);
333
+
334
+ const productTableData = [
335
+ {
336
+ title: "Product 1",
337
+ price: 16,
338
+ createdBy: "1d88385d4b1581f8fb059334dec30f4c",
339
+ },
340
+ {
341
+ title: "Product 2",
342
+ price: 10,
343
+ createdBy: "5011c230aa44481bf7e8dcfe0710474f",
344
+ },
345
+ ];
346
+
347
+ const product = await db.post("product", productTableData);
348
+ // [
349
+ // {
350
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
351
+ // "title": "Product 1",
352
+ // "price": 16,
353
+ // "createdBy": {
354
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
355
+ // "username": "user1",
356
+ // "email": "user1@example.com",
357
+ // ...
358
+ // }
359
+ // },
360
+ // {
361
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
362
+ // "title": "Product 2",
363
+ // "price": 10,
364
+ // "createdBy": {
365
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
366
+ // "username": "user2",
367
+ // ...
368
+ // }
369
+ // }
370
+ // ]
371
+ ```
372
+ </blockquote>
373
+ </details>
374
+
375
+ </blockquote>
376
+ </details>
377
+
378
+ <details>
379
+ <summary>POST</summary>
380
+ <blockquote>
381
+
382
+ ```js
383
+ import Inibase from "inibase";
384
+ const db = new Inibase("/databaseName");
385
+
386
+ const userTableData = [
136
387
  {
137
388
  username: "user1",
138
389
  email: "user1@example.com",
@@ -167,7 +418,7 @@ const user_data = [
167
418
  },
168
419
  ];
169
420
 
170
- const users = await db.post("user", user_data);
421
+ const users = await db.post("user", userTableData);
171
422
  // [
172
423
  // {
173
424
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -205,78 +456,21 @@ const users = await db.post("user", user_data);
205
456
  // ]
206
457
  ```
207
458
 
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
-
459
+ </blockquote>
270
460
  </details>
271
461
 
272
462
  <details>
273
463
  <summary>GET</summary>
464
+ <blockquote>
465
+
466
+ <details>
467
+ <summary>GET by ID</summary>
468
+ <blockquote>
274
469
 
275
470
  ```js
276
471
  import Inibase from "inibase";
277
- const db = new Inibase("/database_name");
472
+ const db = new Inibase("/databaseName");
278
473
 
279
- // Get "user" by id
280
474
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
281
475
  // {
282
476
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -305,8 +499,18 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
305
499
  // "country": "Sampleland"
306
500
  // }
307
501
  // }
502
+ ```
503
+ </blockquote>
504
+ </details>
505
+
506
+ <details>
507
+ <summary>GET by criteria</summary>
508
+ <blockquote>
509
+
510
+ ```js
511
+ import Inibase from "inibase";
512
+ const db = new Inibase("/databaseName");
308
513
 
309
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
310
514
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
311
515
  // [
312
516
  // {
@@ -338,21 +542,36 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
338
542
  // },
339
543
  // ...
340
544
  // ]
545
+ ```
546
+ </blockquote>
547
+ </details>
548
+
549
+ <details>
550
+ <summary>GET with columns</summary>
551
+ <blockquote>
552
+
553
+ ```js
554
+ import Inibase from "inibase";
555
+ const db = new Inibase("/databaseName");
341
556
 
342
557
  // Get all "user" columns except "username" & "address.street"
343
558
  const users = await db.get("user", undefined, {
344
559
  columns: ["!username", "!address.street"],
345
560
  });
346
561
  ```
562
+ </blockquote>
563
+ </details>
347
564
 
565
+ </blockquote>
348
566
  </details>
349
567
 
350
568
  <details>
351
569
  <summary>PUT</summary>
570
+ <blockquote>
352
571
 
353
572
  ```js
354
573
  import Inibase from "inibase";
355
- const db = new Inibase("/database_name");
574
+ const db = new Inibase("/databaseName");
356
575
 
357
576
  // set "isActive" to "false" for all items in table "user"
358
577
  await db.put("user", { isActive: false });
@@ -363,15 +582,16 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
363
582
  // set "isActive" to "true" in table "user" by criteria (where "isActive" is equal to "true")
364
583
  await db.put("user", { isActive: false }, { isActive: true });
365
584
  ```
366
-
585
+ </blockquote>
367
586
  </details>
368
587
 
369
588
  <details>
370
589
  <summary>DELETE</summary>
590
+ <blockquote>
371
591
 
372
592
  ```js
373
593
  import Inibase from "inibase";
374
- const db = new Inibase("/database_name");
594
+ const db = new Inibase("/databaseName");
375
595
 
376
596
  // delete all items in "user" table
377
597
  await db.delete("user");
@@ -382,63 +602,101 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
382
602
  // delete "user" by criteria (where "isActive" is equal to "false")
383
603
  await db.put("user", { isActive: false });
384
604
  ```
385
-
605
+ </blockquote>
386
606
  </details>
387
607
 
388
- ## Typescript
389
-
390
608
  <details>
391
- <summary>Schema</summary>
609
+ <summary>SUM</summary>
610
+ <blockquote>
392
611
 
393
612
  ```js
394
- type Schema = Field[];
395
- type Field = {
396
- id?: string | number | null | undefined,
397
- key: string,
398
- required?: boolean,
399
- } & (
400
- | {
401
- type: Exclude<FieldType, "array" | "object">,
402
- required?: boolean,
403
- }
404
- | {
405
- type: "array",
406
- children: FieldType | FieldType[] | Schema,
407
- }
408
- | {
409
- type: "object",
410
- children: Schema,
411
- }
412
- );
413
- type FieldType =
414
- | "string"
415
- | "number"
416
- | "boolean"
417
- | "date"
418
- | "email"
419
- | "url"
420
- | "table"
421
- | "object"
422
- | "array"
423
- | "password";
613
+ import Inibase from "inibase";
614
+ const db = new Inibase("/databaseName");
615
+
616
+ // get the sum of column "age" in "user" table
617
+ await db.sum("user", "age");
618
+
619
+ // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
620
+ await db.sum("user", ["age", ...], { isActive: false });
424
621
  ```
622
+ </blockquote>
623
+ </details>
624
+
625
+ <details>
626
+ <summary>MAX</summary>
627
+ <blockquote>
425
628
 
629
+ ```js
630
+ import Inibase from "inibase";
631
+ const db = new Inibase("/databaseName");
632
+
633
+ // get the biggest number of column "age" in "user" table
634
+ await db.max("user", "age");
635
+
636
+ // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
637
+ await db.max("user", ["age", ...], { isActive: false });
638
+ ```
639
+ </blockquote>
426
640
  </details>
427
641
 
428
642
  <details>
429
- <summary>Data</summary>
643
+ <summary>MIN</summary>
644
+ <blockquote>
430
645
 
431
646
  ```js
432
- type Data = {
433
- id?: number | string,
434
- [key: string]: any,
435
- created_at?: Date,
436
- updated_at?: Date,
437
- };
647
+ import Inibase from "inibase";
648
+ const db = new Inibase("/databaseName");
649
+
650
+ // get the smallest number of column "age" in "user" table
651
+ await db.min("user", "age");
652
+
653
+ // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
654
+ await db.min("user", ["age", ...], { isActive: false });
438
655
  ```
656
+ </blockquote>
657
+ </details>
658
+
659
+ <details>
660
+ <summary>SORT</summary>
661
+ <blockquote>
662
+
663
+ ```js
664
+ import Inibase from "inibase";
665
+ const db = new Inibase("/databaseName");
666
+
667
+ // order users by the age column
668
+ await db.sort("user", "age");
439
669
 
670
+ // order users by the age and username columns
671
+ await db.sort("user", ["age","username"]);
672
+ await db.sort("user", {age: -1, username: "asc"});
673
+ ```
674
+ </blockquote>
440
675
  </details>
441
676
 
677
+ ## Benchmark
678
+
679
+ ### Bulk
680
+
681
+ | | 10 | 100 | 1000 |
682
+ |--------|-----------------|-----------------|-----------------|
683
+ | POST | 11 ms (0.65 mb) | 19 ms (1.00 mb) | 85 ms (4.58 mb) |
684
+ | GET | 14 ms (2.77 mb) | 12 ms (3.16 mb) | 34 ms (1.38 mb) |
685
+ | PUT | 6 ms (1.11 mb) | 5 ms (1.37 mb) | 10 ms (1.12 mb) |
686
+ | DELETE | 17 ms (1.68 mb) | 14 ms (5.45 mb) | 25 ms (5.94 mb) |
687
+
688
+ ### Single
689
+
690
+ | | 10 | 100 | 1000 |
691
+ |--------|-------------------|--------------------|--------------------|
692
+ | POST | 43 ms (4.70 mb) | 387 ms (6.36 mb) | 5341 ms (24.73 mb) |
693
+ | GET | 99 ms (12.51 mb) | 846 ms (30.68 mb) | 7103 ms (30.86 mb) |
694
+ | PUT | 33 ms (10.29 mb) | 312 ms (11.06 mb) | 3539 ms (14.87 mb) |
695
+ | DELETE | 134 ms (13.50 mb) | 1224 ms (16.57 mb) | 7339 ms (11.46 mb) |
696
+
697
+ > Testing by default with `user` table, with username, email, password fields _so results include password encryption process_ <br>
698
+ > To run benchmarks, install *typescript* & *[tsx](https://github.com/privatenumber/tsx)* globally and run `benchmark` `benchmark:bulk` `benchmark:single`
699
+
442
700
  ## Roadmap
443
701
 
444
702
  - [x] Actions:
@@ -446,10 +704,13 @@ type Data = {
446
704
  - [x] Pagination
447
705
  - [x] Criteria
448
706
  - [x] Columns
449
- - [ ] Order By
707
+ - [x] Sort
450
708
  - [x] POST
451
709
  - [x] PUT
452
710
  - [x] DELETE
711
+ - [x] SUM
712
+ - [x] MAX
713
+ - [x] MIN
453
714
  - [ ] Schema supported types:
454
715
  - [x] String
455
716
  - [x] Number
@@ -464,14 +725,19 @@ type Data = {
464
725
  - [x] IP
465
726
  - [x] HTML
466
727
  - [x] Id
728
+ - [x] JSON
467
729
  - [ ] TO-DO:
468
- - [ ] Improve caching
730
+ - [x] Improve caching
469
731
  - [ ] Commenting the code
732
+ - [x] Add property "unique" for schema fields
733
+ - [ ] Add Backup feature (generate a tar.gz)
734
+ - [ ] Add Custom field validation property to schema (using RegEx?)
470
735
  - [ ] Features:
471
736
  - [ ] Encryption
472
- - [ ] Compress data
737
+ - [x] Data Compression
738
+ - [x] Caching System
473
739
  - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
474
740
 
475
741
  ## License
476
742
 
477
- [MIT](./LICENSE)
743
+ [MIT](./LICENSE)