inibase 1.0.0-rc.6 → 1.0.0-rc.60

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,157 @@ 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
+ ## Config (.env)
68
+
69
+ The `.env` file supports the following parameters
70
+
71
+ ```ini
72
+ # Don't add this line, it's an auto generated secret key, will be using for encrypting the IDs
73
+ INIBASE_SECRET=
74
+
75
+ INIBASE_COMPRESSION=false
76
+ INIBASE_CACHE=false
77
+
78
+ # Prepend new items to the beginning of file
79
+ INIBASE_REVERSE=false
54
80
  ```
55
81
 
56
- ## How it works?
82
+ ## Benchmark
57
83
 
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)
84
+ ### Bulk
59
85
 
60
- ## Examples
86
+ | | 10 | 100 | 1000 |
87
+ |--------|-----------------|-----------------|-----------------|
88
+ | POST | 11 ms (0.65 mb) | 19 ms (1.00 mb) | 85 ms (4.58 mb) |
89
+ | GET | 14 ms (2.77 mb) | 12 ms (3.16 mb) | 34 ms (1.38 mb) |
90
+ | PUT | 6 ms (1.11 mb) | 5 ms (1.37 mb) | 10 ms (1.12 mb) |
91
+ | DELETE | 17 ms (1.68 mb) | 14 ms (5.45 mb) | 25 ms (5.94 mb) |
92
+
93
+ ### Single
94
+
95
+ | | 10 | 100 | 1000 |
96
+ |--------|-------------------|--------------------|--------------------|
97
+ | POST | 43 ms (4.70 mb) | 387 ms (6.36 mb) | 5341 ms (24.73 mb) |
98
+ | GET | 99 ms (12.51 mb) | 846 ms (30.68 mb) | 7103 ms (30.86 mb) |
99
+ | PUT | 33 ms (10.29 mb) | 312 ms (11.06 mb) | 3539 ms (14.87 mb) |
100
+ | DELETE | 134 ms (13.50 mb) | 1224 ms (16.57 mb) | 7339 ms (11.46 mb) |
101
+
102
+ > Testing by default with `user` table, with username, email, password fields _so results include password encryption process_ <br>
103
+ > To run benchmarks, install *typescript* & *tsx* globally and run `benchmark` `benchmark:bulk` `benchmark:single`
104
+
105
+ ## Inibase CLI
106
+
107
+ ```shell
108
+ npx inibase -p <databaseFolderPath>
109
+ ```
110
+
111
+ <blockquote>
112
+ <details>
113
+ <summary>GET</summary>
114
+
115
+ ```shell
116
+ get <tableName> -w <ID|LineNumber|Criteria> -p <pageNumber> -l <perPage> -c <columnName1> -c <columnName2>
117
+ ```
118
+ </details>
61
119
 
62
120
  <details>
63
121
  <summary>POST</summary>
64
122
 
123
+ ```shell
124
+ post <tableName> -d <InisonStrigifedData>
125
+ ```
126
+ </details>
127
+
128
+ <details>
129
+ <summary>PUT</summary>
130
+
131
+ ```shell
132
+ put <tableName> -d <InisonStrigifedData> -w <ID|LineNumber|Criteria>
133
+ ```
134
+ </details>
135
+
136
+ <details>
137
+ <summary>DELETE</summary>
138
+
139
+ ```shell
140
+ delete <tableName> -w <ID|LineNumber|Criteria>
141
+ ```
142
+ </details>
143
+ </blockquote>
144
+
145
+ ## Examples
146
+
147
+ <details>
148
+ <summary>Schema</summary>
149
+ <blockquote>
150
+
151
+ <details>
152
+ <summary>Create Schema</summary>
153
+ <blockquote>
154
+
155
+ <details>
156
+ <summary>Using schema.json file</summary>
157
+ <blockquote>
158
+ Inside the table folder
159
+
160
+ 1. Create empty folders `.cache` `.tmp`
161
+ 2. Create `schema.json` file
162
+
163
+ ```jsonc
164
+ [
165
+ {
166
+ // Give a unique ID number for each field
167
+ "id": 1,
168
+ "key": "username",
169
+ "type": "string"
170
+ },
171
+ {
172
+ "id": 2,
173
+ "key": "email",
174
+ "type": "email"
175
+ },
176
+ ]
177
+ ```
178
+ </blockquote>
179
+ </details>
180
+
181
+ <details>
182
+ <summary>Using built-in function</summary>
183
+ <blockquote>
184
+
65
185
  ```js
66
186
  import Inibase from "inibase";
67
- const db = new Inibase("/database_name");
187
+ const db = new Inibase("/databaseName");
68
188
 
69
- const user_schema = [
189
+ const userSchema = [
70
190
  {
71
191
  key: "username",
72
192
  type: "string",
@@ -132,7 +252,128 @@ const user_schema = [
132
252
  },
133
253
  ];
134
254
 
135
- const user_data = [
255
+ await db.setTableSchema("user", userSchema);
256
+ ```
257
+ </blockquote>
258
+ </details>
259
+
260
+ </blockquote>
261
+ </details>
262
+
263
+ <details>
264
+ <summary>Add field</summary>
265
+ <blockquote>
266
+
267
+ ```js
268
+ import Inibase from "inibase";
269
+ const db = new Inibase("/databaseName");
270
+
271
+ const userSchema = await db.getTableSchema("user");
272
+ const newUserSchema = [...userSchema, {key: "phone2", type: "number", required: false}];
273
+
274
+ await db.setTableSchema("user", newUserSchema);
275
+ ```
276
+ </blockquote>
277
+ </details>
278
+
279
+ <details>
280
+ <summary>Update field</summary>
281
+ <blockquote>
282
+
283
+ ```js
284
+ import Inibase from "inibase";
285
+ import { setField } from "inibase/utils";
286
+
287
+ const db = new Inibase("/databaseName");
288
+
289
+ const userSchema = await db.getTableSchema("user");
290
+ setField("username", userSchema, {key: "full_name"});
291
+ await db.setTableSchema("user", newUserSchema);
292
+ ```
293
+ </blockquote>
294
+ </details>
295
+
296
+ <details>
297
+ <summary>Join Tables</summary>
298
+ <blockquote>
299
+
300
+ ```js
301
+ import Inibase from "inibase";
302
+ const db = new Inibase("/databaseName");
303
+
304
+ const productSchema = [
305
+ {
306
+ key: "title",
307
+ type: "string",
308
+ required: true,
309
+ },
310
+ {
311
+ key: "price",
312
+ type: "number",
313
+ },
314
+ {
315
+ key: "createdBy",
316
+ type: "table",
317
+ table: "user",
318
+ required: true,
319
+ },
320
+ ];
321
+
322
+ await db.setTableSchema("product", productSchema);
323
+
324
+ const productData = [
325
+ {
326
+ title: "Product 1",
327
+ price: 16,
328
+ createdBy: "1d88385d4b1581f8fb059334dec30f4c",
329
+ },
330
+ {
331
+ title: "Product 2",
332
+ price: 10,
333
+ createdBy: "5011c230aa44481bf7e8dcfe0710474f",
334
+ },
335
+ ];
336
+
337
+ const product = await db.post("product", productData);
338
+ // [
339
+ // {
340
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
341
+ // "title": "Product 1",
342
+ // "price": 16,
343
+ // "createdBy": {
344
+ // "id": "1d88385d4b1581f8fb059334dec30f4c",
345
+ // "username": "user1",
346
+ // "email": "user1@example.com",
347
+ // ...
348
+ // }
349
+ // },
350
+ // {
351
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
352
+ // "title": "Product 2",
353
+ // "price": 10,
354
+ // "createdBy": {
355
+ // "id": "5011c230aa44481bf7e8dcfe0710474f",
356
+ // "username": "user2",
357
+ // ...
358
+ // }
359
+ // }
360
+ // ]
361
+ ```
362
+ </blockquote>
363
+ </details>
364
+
365
+ </blockquote>
366
+ </details>
367
+
368
+ <details>
369
+ <summary>POST</summary>
370
+ <blockquote>
371
+
372
+ ```js
373
+ import Inibase from "inibase";
374
+ const db = new Inibase("/databaseName");
375
+
376
+ const userData = [
136
377
  {
137
378
  username: "user1",
138
379
  email: "user1@example.com",
@@ -167,7 +408,7 @@ const user_data = [
167
408
  },
168
409
  ];
169
410
 
170
- const users = await db.post("user", user_data);
411
+ const users = await db.post("user", userData);
171
412
  // [
172
413
  // {
173
414
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -205,78 +446,21 @@ const users = await db.post("user", user_data);
205
446
  // ]
206
447
  ```
207
448
 
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
-
449
+ </blockquote>
270
450
  </details>
271
451
 
272
452
  <details>
273
453
  <summary>GET</summary>
454
+ <blockquote>
455
+
456
+ <details>
457
+ <summary>GET by ID</summary>
458
+ <blockquote>
274
459
 
275
460
  ```js
276
461
  import Inibase from "inibase";
277
- const db = new Inibase("/database_name");
462
+ const db = new Inibase("/databaseName");
278
463
 
279
- // Get "user" by id
280
464
  const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
281
465
  // {
282
466
  // "id": "1d88385d4b1581f8fb059334dec30f4c",
@@ -305,8 +489,18 @@ const user = await db.get("user", "1d88385d4b1581f8fb059334dec30f4c");
305
489
  // "country": "Sampleland"
306
490
  // }
307
491
  // }
492
+ ```
493
+ </blockquote>
494
+ </details>
495
+
496
+ <details>
497
+ <summary>GET by criteria</summary>
498
+ <blockquote>
499
+
500
+ ```js
501
+ import Inibase from "inibase";
502
+ const db = new Inibase("/databaseName");
308
503
 
309
- // Get "user" by Criteria: where "favoriteFoods" includes "Pizza"
310
504
  const users = await db.get("user", { favoriteFoods: "[]Pizza" });
311
505
  // [
312
506
  // {
@@ -338,21 +532,36 @@ const users = await db.get("user", { favoriteFoods: "[]Pizza" });
338
532
  // },
339
533
  // ...
340
534
  // ]
535
+ ```
536
+ </blockquote>
537
+ </details>
538
+
539
+ <details>
540
+ <summary>GET with columns</summary>
541
+ <blockquote>
542
+
543
+ ```js
544
+ import Inibase from "inibase";
545
+ const db = new Inibase("/databaseName");
341
546
 
342
547
  // Get all "user" columns except "username" & "address.street"
343
548
  const users = await db.get("user", undefined, {
344
549
  columns: ["!username", "!address.street"],
345
550
  });
346
551
  ```
552
+ </blockquote>
553
+ </details>
347
554
 
555
+ </blockquote>
348
556
  </details>
349
557
 
350
558
  <details>
351
559
  <summary>PUT</summary>
560
+ <blockquote>
352
561
 
353
562
  ```js
354
563
  import Inibase from "inibase";
355
- const db = new Inibase("/database_name");
564
+ const db = new Inibase("/databaseName");
356
565
 
357
566
  // set "isActive" to "false" for all items in table "user"
358
567
  await db.put("user", { isActive: false });
@@ -363,15 +572,16 @@ await db.put("user", { isActive: false }, "1d88385d4b1581f8fb059334dec30f4c");
363
572
  // set "isActive" to "true" in table "user" by criteria (where "isActive" is equal to "true")
364
573
  await db.put("user", { isActive: false }, { isActive: true });
365
574
  ```
366
-
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");
@@ -382,61 +592,76 @@ await db.put("user", "1d88385d4b1581f8fb059334dec30f4c");
382
592
  // delete "user" by criteria (where "isActive" is equal to "false")
383
593
  await db.put("user", { isActive: false });
384
594
  ```
385
-
595
+ </blockquote>
386
596
  </details>
387
597
 
388
- ## Typescript
389
-
390
598
  <details>
391
- <summary>Schema</summary>
599
+ <summary>SUM</summary>
600
+ <blockquote>
392
601
 
393
602
  ```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";
603
+ import Inibase from "inibase";
604
+ const db = new Inibase("/databaseName");
605
+
606
+ // get the sum of column "age" in "user" table
607
+ await db.sum("user", "age");
608
+
609
+ // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
610
+ await db.sum("user", ["age", ...], { isActive: false });
424
611
  ```
612
+ </blockquote>
613
+ </details>
614
+
615
+ <details>
616
+ <summary>MAX</summary>
617
+ <blockquote>
618
+
619
+ ```js
620
+ import Inibase from "inibase";
621
+ const db = new Inibase("/databaseName");
425
622
 
623
+ // get the biggest number of column "age" in "user" table
624
+ await db.max("user", "age");
625
+
626
+ // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
627
+ await db.max("user", ["age", ...], { isActive: false });
628
+ ```
629
+ </blockquote>
426
630
  </details>
427
631
 
428
632
  <details>
429
- <summary>Data</summary>
633
+ <summary>MIN</summary>
634
+ <blockquote>
430
635
 
431
636
  ```js
432
- type Data = {
433
- id?: number | string,
434
- [key: string]: any,
435
- created_at?: Date,
436
- updated_at?: Date,
437
- };
637
+ import Inibase from "inibase";
638
+ const db = new Inibase("/databaseName");
639
+
640
+ // get the smallest number of column "age" in "user" table
641
+ await db.min("user", "age");
642
+
643
+ // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
644
+ await db.min("user", ["age", ...], { isActive: false });
438
645
  ```
646
+ </blockquote>
647
+ </details>
439
648
 
649
+ <details>
650
+ <summary>SORT</summary>
651
+ <blockquote>
652
+
653
+ ```js
654
+ import Inibase from "inibase";
655
+ const db = new Inibase("/databaseName");
656
+
657
+ // order users by the age column
658
+ await db.sort("user", "age");
659
+
660
+ // order users by the age and username columns
661
+ await db.sort("user", ["age","username"]);
662
+ await db.sort("user", {age: -1, username: "asc"});
663
+ ```
664
+ </blockquote>
440
665
  </details>
441
666
 
442
667
  ## Roadmap
@@ -446,10 +671,13 @@ type Data = {
446
671
  - [x] Pagination
447
672
  - [x] Criteria
448
673
  - [x] Columns
449
- - [ ] Order By
674
+ - [x] Sort (using UNIX commands)
450
675
  - [x] POST
451
676
  - [x] PUT
452
677
  - [x] DELETE
678
+ - [x] SUM
679
+ - [x] MAX
680
+ - [x] MIN
453
681
  - [ ] Schema supported types:
454
682
  - [x] String
455
683
  - [x] Number
@@ -464,14 +692,19 @@ type Data = {
464
692
  - [x] IP
465
693
  - [x] HTML
466
694
  - [x] Id
695
+ - [x] JSON
467
696
  - [ ] TO-DO:
468
- - [ ] Improve caching
697
+ - [x] Improve caching
469
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?)
470
702
  - [ ] Features:
471
703
  - [ ] Encryption
472
- - [ ] Compress data
704
+ - [x] Data Compression
705
+ - [x] Caching System
473
706
  - [ ] Suggest [new feature +](https://github.com/inicontent/inibase/discussions/new?category=ideas)
474
707
 
475
708
  ## License
476
709
 
477
- [MIT](./LICENSE)
710
+ [MIT](./LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";