inibase 1.0.0-rc.0 → 1.0.0-rc.11

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/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.0",
3
+ "version": "1.0.0-rc.11",
4
4
  "description": "File-based Relational Database for large data",
5
5
  "main": "index.ts",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "npx tsx watch ./index.test.ts"
9
+ },
6
10
  "repository": {
7
11
  "type": "git",
8
12
  "url": "git+https://github.com/inicontent/inibase.git"
@@ -29,9 +33,6 @@
29
33
  },
30
34
  "homepage": "https://github.com/inicontent/inibase#readme",
31
35
  "devDependencies": {
32
- "@types/node": "^20.6.0"
33
- },
34
- "scripts": {
35
- "test": "echo \"Error: no test specified\" && exit 1"
36
+ "@types/node": "^20.8.6"
36
37
  }
37
- }
38
+ }
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2021",
3
+ "module": "ES2022",
4
+ "target": "ES2022",
4
5
  "moduleResolution": "node"
5
6
  }
6
7
  }
@@ -0,0 +1,79 @@
1
+ import {
2
+ scryptSync,
3
+ randomBytes,
4
+ timingSafeEqual,
5
+ createDecipheriv,
6
+ createCipheriv,
7
+ Cipher,
8
+ Decipher,
9
+ } from "node:crypto";
10
+
11
+ export const hashPassword = (password: string) => {
12
+ const salt = randomBytes(16).toString("hex");
13
+ const buf = scryptSync(password, salt, 64);
14
+ // return "161" length string
15
+ return `${buf.toString("hex")}.${salt}`;
16
+ };
17
+
18
+ export const comparePassword = (
19
+ storedPassword: string,
20
+ suppliedPassword: string
21
+ ) => {
22
+ // split() returns array
23
+ const [hashedPassword, salt] = storedPassword.split(".");
24
+ // we need to pass buffer values to timingSafeEqual
25
+ const hashedPasswordBuf = Buffer.from(hashedPassword, "hex");
26
+ // we hash the new sign-in password
27
+ const suppliedPasswordBuf = scryptSync(suppliedPassword, salt, 64);
28
+ // compare the new supplied password with the stored hashed password
29
+ return timingSafeEqual(hashedPasswordBuf, suppliedPasswordBuf);
30
+ };
31
+
32
+ export const encodeID = (
33
+ id: number,
34
+ secretKey: string | number | Buffer
35
+ ): string => {
36
+ let cipher: Cipher, ret: string;
37
+
38
+ if (Buffer.isBuffer(secretKey))
39
+ cipher = createCipheriv(
40
+ "aes-256-cbc",
41
+ secretKey,
42
+ secretKey.subarray(0, 16)
43
+ );
44
+ else {
45
+ const salt = scryptSync(secretKey.toString(), "salt", 32);
46
+ cipher = createCipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
47
+ }
48
+
49
+ return cipher.update(id.toString(), "utf8", "hex") + cipher.final("hex");
50
+ };
51
+
52
+ export const decodeID = (
53
+ input: string,
54
+ secretKey: string | number | Buffer
55
+ ): number => {
56
+ let decipher: Decipher;
57
+
58
+ if (Buffer.isBuffer(secretKey))
59
+ decipher = createDecipheriv(
60
+ "aes-256-cbc",
61
+ secretKey,
62
+ secretKey.subarray(0, 16)
63
+ );
64
+ else {
65
+ const salt = scryptSync(secretKey.toString(), "salt", 32);
66
+ decipher = createDecipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
67
+ }
68
+
69
+ return Number(
70
+ decipher.update(input as string, "hex", "utf8") + decipher.final("utf8")
71
+ );
72
+ };
73
+
74
+ export default class Utils {
75
+ static encodeID = encodeID;
76
+ static decodeID = decodeID;
77
+ static hashPassword = hashPassword;
78
+ static comparePassword = comparePassword;
79
+ }
package/utils.ts CHANGED
@@ -1,49 +1,11 @@
1
- import { FieldType } from ".";
2
- import { scryptSync, randomBytes, timingSafeEqual } from "crypto";
3
-
4
- export const encode = (
5
- input: string | number | boolean | null | (string | number | boolean | null)[]
6
- ) => {
7
- const secureString = (input: string | number | boolean | null) => {
8
- if (["true", "false"].includes((input ?? "").toString()))
9
- return input ? 1 : 0;
10
- return typeof input === "string"
11
- ? decodeURIComponent(input)
12
- .replaceAll("<", "&lt;")
13
- .replaceAll(">", "&gt;")
14
- .replaceAll(",", "%2C")
15
- .replaceAll("\n", "\\n")
16
- .replaceAll("\r", "\\r")
17
- : input;
18
- };
19
- return Array.isArray(input)
20
- ? input.map(secureString).join(",")
21
- : secureString(input);
22
- };
23
-
24
- export const decode = (
25
- input: string | null | number,
26
- fieldType?: FieldType
27
- ): string | number | boolean | null | (string | number | null | boolean)[] => {
28
- const unSecureString = (input: string) =>
29
- decodeURIComponent(input)
30
- .replaceAll("&lt;", "<")
31
- .replaceAll("&gt;", ">")
32
- .replaceAll("%2C", ",")
33
- .replaceAll("\\n", "\n")
34
- .replaceAll("\\r", "\r") || null;
35
-
36
- if (input === null || input === "") return null;
37
- if (!isNaN(Number(input)) && isFinite(Number(input)))
38
- return fieldType === "boolean" ? Boolean(Number(input)) : Number(input);
39
- return (input as string).includes(",")
40
- ? (input as string).split(",").map(unSecureString)
41
- : unSecureString(input as string);
42
- };
1
+ import { FieldType, Data } from ".";
43
2
 
44
3
  export const isArrayOfObjects = (arr: any) => {
45
4
  return Array.isArray(arr) && (arr.length === 0 || arr.every(isObject));
46
5
  };
6
+ export const isArrayOfArrays = (arr: any) => {
7
+ return Array.isArray(arr) && (arr.length === 0 || arr.every(Array.isArray));
8
+ };
47
9
 
48
10
  export const isObject = (obj: any) =>
49
11
  obj != null &&
@@ -71,40 +33,180 @@ export const combineObjects = (objectArray: Record<string, any>[]) => {
71
33
  return combinedValues;
72
34
  };
73
35
 
74
- export const isNumber = (input: any): boolean =>
36
+ export const isNumber = (input: any | any[]): boolean =>
75
37
  Array.isArray(input)
76
38
  ? input.every(isNumber)
77
39
  : !isNaN(parseFloat(input)) && !isNaN(input - 0);
78
40
 
79
- export const hashPassword = (password: string) => {
80
- const salt = randomBytes(16).toString("hex");
81
- const buf = scryptSync(password, salt, 64);
82
- // return "161" length string
83
- return `${buf.toString("hex")}.${salt}`;
41
+ export const isEmail = (input: any) =>
42
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(input));
43
+
44
+ export const isURL = (input: any) => input[0] === "#" || URL.canParse(input);
45
+
46
+ export const isHTML = (input: any) =>
47
+ /<\/?\s*[a-z-][^>]*\s*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);)/g.test(input);
48
+
49
+ export const isString = (input: any) =>
50
+ Object.prototype.toString.call(input) === "[object String]" &&
51
+ !isNumber(input) &&
52
+ !isBoolean(input) &&
53
+ !isEmail(input) &&
54
+ !isDate(input) &&
55
+ !isURL(input) &&
56
+ !isIP(input) &&
57
+ !isHTML(input);
58
+
59
+ export const isIP = (input: any) =>
60
+ /^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/.test(input);
61
+
62
+ export const isBoolean = (input: any) =>
63
+ typeof input === "boolean" ||
64
+ input === "true" ||
65
+ input === "false" ||
66
+ input === true ||
67
+ input === false;
68
+
69
+ export const isPassword = (input: any) => input.length === 161;
70
+
71
+ export const isDate = (input: any) =>
72
+ !isNaN(Date.parse(String(input))) && Date.parse(String(input)) >= 0;
73
+
74
+ export const isValidID = (input: any): boolean => {
75
+ return Array.isArray(input)
76
+ ? input.every(isValidID)
77
+ : typeof input === "string" && input.length === 32;
78
+ };
79
+
80
+ export const findChangedProperties = (
81
+ obj1: Record<string, string>,
82
+ obj2: Record<string, string>
83
+ ): Record<string, string> | null => {
84
+ const result: Record<string, string> = {};
85
+
86
+ for (const key1 in obj1)
87
+ if (obj2.hasOwnProperty(key1) && obj1[key1] !== obj2[key1])
88
+ result[obj1[key1]] = obj2[key1];
89
+
90
+ return Object.keys(result).length ? result : null;
84
91
  };
85
92
 
86
- export const comparePassword = (
87
- storedPassword: string,
88
- suppliedPassword: string
89
- ) => {
90
- // split() returns array
91
- const [hashedPassword, salt] = storedPassword.split(".");
92
- // we need to pass buffer values to timingSafeEqual
93
- const hashedPasswordBuf = Buffer.from(hashedPassword, "hex");
94
- // we hash the new sign-in password
95
- const suppliedPasswordBuf = scryptSync(suppliedPassword, salt, 64);
96
- // compare the new supplied password with the stored hashed password
97
- return timingSafeEqual(hashedPasswordBuf, suppliedPasswordBuf);
93
+ export const detectFieldType = (
94
+ input: any,
95
+ availableTypes: FieldType[]
96
+ ): FieldType | undefined => {
97
+ if (!Array.isArray(input)) {
98
+ if (
99
+ (input === "0" ||
100
+ input === "1" ||
101
+ input === "true" ||
102
+ input === "false") &&
103
+ availableTypes.includes("boolean")
104
+ )
105
+ return "boolean";
106
+ else if (isNumber(input)) {
107
+ if (availableTypes.includes("table")) return "table";
108
+ else if (availableTypes.includes("date")) return "date";
109
+ else if (availableTypes.includes("number")) return "number";
110
+ } else if (input.includes(",") && availableTypes.includes("array"))
111
+ return "array";
112
+ else if (availableTypes.includes("email") && isEmail(input)) return "email";
113
+ else if (availableTypes.includes("url") && isURL(input)) return "url";
114
+ else if (availableTypes.includes("password") && isPassword(input))
115
+ return "password";
116
+ else if (availableTypes.includes("date") && isDate(input)) return "date";
117
+ else if (availableTypes.includes("string") && isString(input))
118
+ return "string";
119
+ else if (availableTypes.includes("ip") && isIP(input)) return "ip";
120
+ } else return "array";
121
+
122
+ return undefined;
123
+ };
124
+
125
+ export const validateFieldType = (
126
+ value: any,
127
+ fieldType: FieldType | FieldType[],
128
+ fieldChildrenType?: FieldType | FieldType[]
129
+ ): boolean => {
130
+ if (value === null) return true;
131
+ if (Array.isArray(fieldType))
132
+ return detectFieldType(value, fieldType) !== undefined;
133
+ if (fieldType === "array" && fieldChildrenType && Array.isArray(value))
134
+ return value.some(
135
+ (v) =>
136
+ detectFieldType(
137
+ v,
138
+ Array.isArray(fieldChildrenType)
139
+ ? fieldChildrenType
140
+ : [fieldChildrenType]
141
+ ) !== undefined
142
+ );
143
+
144
+ switch (fieldType) {
145
+ case "string":
146
+ return isString(value);
147
+ case "password":
148
+ return isNumber(value) || isString(value) || isPassword(value);
149
+ case "number":
150
+ return isNumber(value);
151
+ case "html":
152
+ return isHTML(value);
153
+ case "ip":
154
+ return isIP(value);
155
+ case "boolean":
156
+ return isBoolean(value);
157
+ case "date":
158
+ return isDate(value);
159
+ case "object":
160
+ return isObject(value);
161
+ case "array":
162
+ return Array.isArray(value);
163
+ case "email":
164
+ return isEmail(value);
165
+ case "url":
166
+ return isURL(value);
167
+ case "table":
168
+ // feat: check if id exists
169
+ if (Array.isArray(value))
170
+ return (
171
+ (isArrayOfObjects(value) &&
172
+ value.every(
173
+ (element: Data) =>
174
+ element.hasOwnProperty("id") &&
175
+ (isValidID(element.id) || isNumber(element.id))
176
+ )) ||
177
+ value.every(isNumber) ||
178
+ isValidID(value)
179
+ );
180
+ else if (isObject(value))
181
+ return (
182
+ value.hasOwnProperty("id") &&
183
+ (isValidID((value as Data).id) || isNumber((value as Data).id))
184
+ );
185
+ else return isNumber(value) || isValidID(value);
186
+ case "id":
187
+ return isNumber(value) || isValidID(value);
188
+ default:
189
+ return false;
190
+ }
98
191
  };
99
192
 
100
193
  export default class Utils {
101
- static encode = encode;
102
- static decode = decode;
103
194
  static isNumber = isNumber;
104
195
  static isObject = isObject;
196
+ static isEmail = isEmail;
197
+ static isDate = isDate;
198
+ static isURL = isURL;
199
+ static isValidID = isValidID;
200
+ static isPassword = isPassword;
105
201
  static deepMerge = deepMerge;
106
- static hashPassword = hashPassword;
107
202
  static combineObjects = combineObjects;
108
- static comparePassword = comparePassword;
109
203
  static isArrayOfObjects = isArrayOfObjects;
204
+ static findChangedProperties = findChangedProperties;
205
+ static detectFieldType = detectFieldType;
206
+ static isArrayOfArrays = isArrayOfArrays;
207
+ static isBoolean = isBoolean;
208
+ static isString = isString;
209
+ static isHTML = isHTML;
210
+ static isIP = isIP;
211
+ static validateFieldType = validateFieldType;
110
212
  }