inibase 1.0.0-rc.10 → 1.0.0-rc.12

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/.env.example ADDED
@@ -0,0 +1 @@
1
+ INIBASE_SECRET=
package/README.md CHANGED
@@ -385,6 +385,56 @@ await db.put("user", { isActive: false });
385
385
 
386
386
  </details>
387
387
 
388
+ <details>
389
+ <summary>SUM</summary>
390
+
391
+ ```js
392
+ import Inibase from "inibase";
393
+ const db = new Inibase("/database_name");
394
+
395
+ // get the sum of column "age" in "user" table
396
+ await db.sum("user", "age");
397
+
398
+ // get the sum of column "age" by criteria (where "isActive" is equal to "false") in "user" table
399
+ await db.sum("user", ["age", ...], { isActive: false });
400
+ ```
401
+
402
+ </details>
403
+
404
+ <details>
405
+ <summary>MAX</summary>
406
+
407
+ ```js
408
+ import Inibase from "inibase";
409
+ const db = new Inibase("/database_name");
410
+
411
+ // get the biggest number of column "age" in "user" table
412
+ await db.max("user", "age");
413
+
414
+ // get the biggest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
415
+ await db.max("user", ["age", ...], { isActive: false });
416
+ ```
417
+
418
+ </details>
419
+
420
+ <details>
421
+ <summary>MIN</summary>
422
+
423
+ ```js
424
+ import Inibase from "inibase";
425
+ const db = new Inibase("/database_name");
426
+
427
+ // get the smallest number of column "age" in "user" table
428
+ await db.min("user", "age");
429
+
430
+ // get the smallest number of column "age" by criteria (where "isActive" is equal to "false") in "user" table
431
+ await db.min("user", ["age", ...], { isActive: false });
432
+ ```
433
+
434
+ </details>
435
+
436
+
437
+
388
438
  ## Roadmap
389
439
 
390
440
  - [x] Actions:
package/file.ts CHANGED
@@ -3,8 +3,13 @@ import { open, unlink, rename, stat } from "node:fs/promises";
3
3
  import { Interface, createInterface } from "node:readline";
4
4
  import { parse } from "node:path";
5
5
  import { ComparisonOperator, FieldType } from ".";
6
- import { detectFieldType, isArrayOfArrays, isNumber } from "./utils";
7
- import { encodeID, comparePassword } from "./utils.server";
6
+ import {
7
+ detectFieldType,
8
+ isArrayOfArrays,
9
+ isNumber,
10
+ encodeID,
11
+ comparePassword,
12
+ } from "./utils";
8
13
 
9
14
  const doesSupportReadLines = () => {
10
15
  const [major, minor, patch] = process.versions.node.split(".").map(Number);
@@ -20,6 +25,8 @@ export const isExists = async (path: string) => {
20
25
  }
21
26
  };
22
27
 
28
+ const delimiters = [",", "|", "&", "$", "#", "@", "^", "%", ":", "!", ";"];
29
+
23
30
  export const encode = (
24
31
  input:
25
32
  | string
@@ -30,23 +37,42 @@ export const encode = (
30
37
  secretKey?: string | Buffer
31
38
  ) => {
32
39
  const secureString = (input: string | number | boolean | null) => {
33
- if (["true", "false"].includes(String(input))) return input ? 1 : 0;
34
- return typeof input === "string"
35
- ? decodeURIComponent(input)
36
- .replaceAll("<", "&lt;")
37
- .replaceAll(">", "&gt;")
38
- .replaceAll(",", "%2C")
39
- .replaceAll("|", "%7C")
40
- .replaceAll("\n", "\\n")
41
- .replaceAll("\r", "\\r")
42
- : input;
43
- };
40
+ if (["true", "false"].includes(String(input))) return input ? 1 : 0;
41
+ return typeof input === "string"
42
+ ? decodeURIComponent(input)
43
+ .replaceAll("<", "&lt;")
44
+ .replaceAll(">", "&gt;")
45
+ .replaceAll(",", "%2C")
46
+ .replaceAll("|", "%7C")
47
+ .replaceAll("&", "%26")
48
+ .replaceAll("$", "%24")
49
+ .replaceAll("#", "%23")
50
+ .replaceAll("@", "%40")
51
+ .replaceAll("^", "%5E")
52
+ .replaceAll("%", "%25")
53
+ .replaceAll(":", "%3A")
54
+ .replaceAll("!", "%21")
55
+ .replaceAll(";", "%3B")
56
+ .replaceAll("\n", "\\n")
57
+ .replaceAll("\r", "\\r")
58
+ : input;
59
+ },
60
+ secureArray = (arr_str: any[] | any): any[] | any =>
61
+ Array.isArray(arr_str) ? arr_str.map(secureArray) : secureString(arr_str),
62
+ joinMultidimensionalArray = (
63
+ arr: any[] | any[][],
64
+ delimiter_index = 0
65
+ ): string => {
66
+ delimiter_index++;
67
+ if (isArrayOfArrays(arr))
68
+ arr = arr.map((ar: any[]) =>
69
+ joinMultidimensionalArray(ar, delimiter_index)
70
+ );
71
+ delimiter_index--;
72
+ return arr.join(delimiters[delimiter_index]);
73
+ };
44
74
  return Array.isArray(input)
45
- ? isArrayOfArrays(input)
46
- ? (input as any[])
47
- .map((_input) => _input.map(secureString).join(","))
48
- .join("|")
49
- : input.map(secureString).join(",")
75
+ ? joinMultidimensionalArray(secureArray(input))
50
76
  : secureString(input);
51
77
  };
52
78
 
@@ -58,65 +84,87 @@ export const decode = (
58
84
  ): string | number | boolean | null | (string | number | null | boolean)[] => {
59
85
  if (!fieldType) return null;
60
86
  const unSecureString = (input: string) =>
61
- decodeURIComponent(input)
62
- .replaceAll("&lt;", "<")
63
- .replaceAll("&gt;", ">")
64
- .replaceAll("%2C", ",")
65
- .replaceAll("%7C", "|")
66
- .replaceAll("\\n", "\n")
67
- .replaceAll("\\r", "\r") || null;
87
+ decodeURIComponent(input)
88
+ .replaceAll("&lt;", "<")
89
+ .replaceAll("&gt;", ">")
90
+ .replaceAll("%2C", ",")
91
+ .replaceAll("%7C", "|")
92
+ .replaceAll("%26", "&")
93
+ .replaceAll("%24", "$")
94
+ .replaceAll("%23", "#")
95
+ .replaceAll("%40", "@")
96
+ .replaceAll("%5E", "^")
97
+ .replaceAll("%25", "%")
98
+ .replaceAll("%3A", ":")
99
+ .replaceAll("%21", "!")
100
+ .replaceAll("%3B", ";")
101
+ .replaceAll("\\n", "\n")
102
+ .replaceAll("\\r", "\r") || null,
103
+ unSecureArray = (arr_str: any[] | any): any[] | any =>
104
+ Array.isArray(arr_str)
105
+ ? arr_str.map(unSecureArray)
106
+ : unSecureString(arr_str),
107
+ reverseJoinMultidimensionalArray = (
108
+ joinedString: string | any[] | any[][]
109
+ ): any | any[] | any[][] => {
110
+ const reverseJoinMultidimensionalArrayHelper = (
111
+ arr: any | any[] | any[][],
112
+ delimiter: string
113
+ ) =>
114
+ Array.isArray(arr)
115
+ ? arr.map((ar: any) =>
116
+ reverseJoinMultidimensionalArrayHelper(ar, delimiter)
117
+ )
118
+ : arr.split(delimiter);
119
+
120
+ const availableDelimiters = delimiters.filter((delimiter) =>
121
+ joinedString.includes(delimiter)
122
+ );
123
+ for (const delimiter of availableDelimiters) {
124
+ joinedString = Array.isArray(joinedString)
125
+ ? reverseJoinMultidimensionalArrayHelper(joinedString, delimiter)
126
+ : joinedString.split(delimiter);
127
+ }
128
+ return joinedString;
129
+ },
130
+ decodeHelper = (value: string | number | any[]) => {
131
+ if (Array.isArray(value) && fieldType !== "array")
132
+ return value.map(decodeHelper);
133
+ switch (fieldType as FieldType) {
134
+ case "table":
135
+ case "number":
136
+ return isNumber(value) ? Number(value) : null;
137
+ case "boolean":
138
+ return typeof value === "string" ? value === "true" : Boolean(value);
139
+ case "array":
140
+ if (!Array.isArray(value)) return [value];
141
+
142
+ if (fieldChildrenType)
143
+ return value.map(
144
+ (v) =>
145
+ decode(
146
+ v,
147
+ Array.isArray(fieldChildrenType)
148
+ ? detectFieldType(v, fieldChildrenType)
149
+ : fieldChildrenType,
150
+ undefined,
151
+ secretKey
152
+ ) as string | number | boolean | null
153
+ );
154
+ else return value;
155
+ case "id":
156
+ return isNumber(value) ? encodeID(value as number, secretKey) : value;
157
+ default:
158
+ return value;
159
+ }
160
+ };
68
161
  if (input === null || input === "") return null;
69
162
  if (Array.isArray(fieldType))
70
163
  fieldType = detectFieldType(String(input), fieldType);
71
- const decodeHelper = (value: string | number | any[]) => {
72
- if (Array.isArray(value) && fieldType !== "array")
73
- return value.map(decodeHelper);
74
- switch (fieldType as FieldType) {
75
- case "table":
76
- case "number":
77
- return isNumber(value) ? Number(value) : null;
78
- case "boolean":
79
- return typeof value === "string" ? value === "true" : Boolean(value);
80
- case "array":
81
- if (!Array.isArray(value)) return [value];
82
-
83
- if (fieldChildrenType)
84
- return value.map(
85
- (v) =>
86
- decode(
87
- v,
88
- Array.isArray(fieldChildrenType)
89
- ? detectFieldType(v, fieldChildrenType)
90
- : fieldChildrenType,
91
- undefined,
92
- secretKey
93
- ) as string | number | boolean | null
94
- );
95
- else return value;
96
- case "id":
97
- return isNumber(value) ? encodeID(value as number, secretKey) : value;
98
- default:
99
- return value;
100
- }
101
- };
102
164
  return decodeHelper(
103
165
  typeof input === "string"
104
166
  ? input.includes(",")
105
- ? input.includes("|")
106
- ? input
107
- .split("|")
108
- .map((_input) => _input.split(",").map(unSecureString))
109
- : input.split(",").map(unSecureString)
110
- : input.includes("|")
111
- ? input
112
- .split("|")
113
- .map((_input) => [
114
- _input
115
- ? fieldType === "array"
116
- ? [unSecureString(_input)]
117
- : unSecureString(_input)
118
- : null,
119
- ])
167
+ ? unSecureArray(reverseJoinMultidimensionalArray(input))
120
168
  : unSecureString(input)
121
169
  : input
122
170
  );
@@ -180,9 +228,8 @@ export const get = async (
180
228
  for await (const line of rl) {
181
229
  lineCount++;
182
230
  if (!lineNumbersArray.includes(lineCount)) continue;
183
- const indexOfLineCount = lineNumbersArray.indexOf(lineCount);
184
231
  lines[lineCount] = decode(line, fieldType, fieldChildrenType, secretKey);
185
- lineNumbersArray[indexOfLineCount] = 0;
232
+ lineNumbersArray[lineNumbersArray.indexOf(lineCount)] = 0;
186
233
  if (!lineNumbersArray.filter((lineN) => lineN !== 0).length) break;
187
234
  }
188
235
  }
@@ -489,6 +536,129 @@ export const search = async (
489
536
  } else return [null, 0];
490
537
  };
491
538
 
539
+ export const sum = async (
540
+ filePath: string,
541
+ lineNumbers?: number | number[]
542
+ ): Promise<number> => {
543
+ let rl: Interface;
544
+ if (doesSupportReadLines()) rl = (await open(filePath)).readLines();
545
+ else
546
+ rl = createInterface({
547
+ input: createReadStream(filePath),
548
+ crlfDelay: Infinity,
549
+ });
550
+ let sum = 0;
551
+
552
+ if (lineNumbers) {
553
+ let lineCount = 0;
554
+
555
+ let lineNumbersArray = [
556
+ ...(Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers]),
557
+ ];
558
+ for await (const line of rl) {
559
+ lineCount++;
560
+ if (!lineNumbersArray.includes(lineCount)) continue;
561
+ sum += +decode(line, "number");
562
+ lineNumbersArray[lineNumbersArray.indexOf(lineCount)] = 0;
563
+ if (!lineNumbersArray.filter((lineN) => lineN !== 0).length) break;
564
+ }
565
+ } else for await (const line of rl) sum += +decode(line, "number");
566
+
567
+ return sum;
568
+ };
569
+
570
+ export const max = async (
571
+ filePath: string,
572
+ lineNumbers?: number | number[]
573
+ ): Promise<number> => {
574
+ let rl: Interface;
575
+ if (doesSupportReadLines()) rl = (await open(filePath)).readLines();
576
+ else
577
+ rl = createInterface({
578
+ input: createReadStream(filePath),
579
+ crlfDelay: Infinity,
580
+ });
581
+ let max = 0;
582
+
583
+ if (lineNumbers) {
584
+ let lineCount = 0;
585
+
586
+ let lineNumbersArray = [
587
+ ...(Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers]),
588
+ ];
589
+ for await (const line of rl) {
590
+ lineCount++;
591
+ if (!lineNumbersArray.includes(lineCount)) continue;
592
+ const lineContentNum = +decode(line, "number");
593
+ if (lineContentNum > max) max = lineContentNum;
594
+ lineNumbersArray[lineNumbersArray.indexOf(lineCount)] = 0;
595
+ if (!lineNumbersArray.filter((lineN) => lineN !== 0).length) break;
596
+ }
597
+ } else
598
+ for await (const line of rl) {
599
+ const lineContentNum = +decode(line, "number");
600
+ if (lineContentNum > max) max = lineContentNum;
601
+ }
602
+
603
+ return max;
604
+ };
605
+
606
+ export const min = async (
607
+ filePath: string,
608
+ lineNumbers?: number | number[]
609
+ ): Promise<number> => {
610
+ let rl: Interface;
611
+ if (doesSupportReadLines()) rl = (await open(filePath)).readLines();
612
+ else
613
+ rl = createInterface({
614
+ input: createReadStream(filePath),
615
+ crlfDelay: Infinity,
616
+ });
617
+ let min = 0;
618
+
619
+ if (lineNumbers) {
620
+ let lineCount = 0;
621
+
622
+ let lineNumbersArray = [
623
+ ...(Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers]),
624
+ ];
625
+ for await (const line of rl) {
626
+ lineCount++;
627
+ if (!lineNumbersArray.includes(lineCount)) continue;
628
+ const lineContentNum = +decode(line, "number");
629
+ if (lineContentNum < min) min = lineContentNum;
630
+ lineNumbersArray[lineNumbersArray.indexOf(lineCount)] = 0;
631
+ if (!lineNumbersArray.filter((lineN) => lineN !== 0).length) break;
632
+ }
633
+ } else
634
+ for await (const line of rl) {
635
+ const lineContentNum = +decode(line, "number");
636
+ if (lineContentNum < min) min = lineContentNum;
637
+ }
638
+
639
+ return min;
640
+ };
641
+
642
+ export const sort = async (
643
+ filePath: string,
644
+ sortDirection: 1 | -1 | "asc" | "desc",
645
+ lineNumbers?: number | number[],
646
+ _lineNumbersPerChunk: number = 100000
647
+ ): Promise<void> => {
648
+ let rl: Interface;
649
+ if (doesSupportReadLines()) rl = (await open(filePath)).readLines();
650
+ else
651
+ rl = createInterface({
652
+ input: createReadStream(filePath),
653
+ crlfDelay: Infinity,
654
+ });
655
+ let lineCount = 0;
656
+
657
+ for await (const line of rl) {
658
+ lineCount++;
659
+ }
660
+ };
661
+
492
662
  export default class File {
493
663
  static get = get;
494
664
  static remove = remove;
@@ -498,4 +668,7 @@ export default class File {
498
668
  static encode = encode;
499
669
  static decode = decode;
500
670
  static isExists = isExists;
671
+ static sum = sum;
672
+ static min = min;
673
+ static max = max;
501
674
  }
package/index.test.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { decode } from "./file";
1
2
  import Inibase, { Schema, Data } from "./index";
2
3
  import { join } from "node:path";
3
4
  // import os from "os";
@@ -197,9 +198,50 @@ const data_2 = [
197
198
  try {
198
199
  // db.setTableSchema("database", schema_2);
199
200
  // const DATA = await db.post("database", data_2);
200
- const DATA = await db.get("user", {
201
- or: { username: "admin", email: "demo" },
202
- });
201
+ // const DATA = await db.delete("database", 2);
202
+ // const DATA = await db.post("database", {
203
+ // slug: "iptv",
204
+ // allowed_domains: ['https://iptv.kamatil.com'],
205
+ // tables: [
206
+ // {
207
+ // id: 1,
208
+ // slug: "user",
209
+ // allowed_methods: [
210
+ // {
211
+ // role: "user",
212
+ // methods: ["c", "r", "u"],
213
+ // },
214
+ // {
215
+ // role: "guest",
216
+ // methods: ["c"],
217
+ // },
218
+ // {
219
+ // role: "admin",
220
+ // methods: ["d", "u"],
221
+ // },
222
+ // ],
223
+ // },
224
+ // {
225
+ // id: 1,
226
+ // slug: "user",
227
+ // allowed_methods: [
228
+ // {
229
+ // role: "user",
230
+ // methods: ["c", "r", "u"],
231
+ // },
232
+ // {
233
+ // role: "guest",
234
+ // methods: ["c"],
235
+ // },
236
+ // {
237
+ // role: "admin",
238
+ // methods: ["d", "u"],
239
+ // },
240
+ // ],
241
+ // },
242
+ // ],
243
+ // });
244
+ const DATA = await db.get("database");
203
245
  console.log(JSON.stringify(DATA, null, 4));
204
246
  } catch (er) {
205
247
  console.log(er);