inibase 1.0.0-rc.5 → 1.0.0-rc.7
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/README.md +4 -62
- package/file.ts +114 -74
- package/index.test.ts +141 -179
- package/index.ts +295 -324
- package/package.json +2 -2
- package/utils.server.ts +79 -0
- package/utils.ts +124 -77
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inibase",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.7",
|
|
4
4
|
"description": "File-based Relational Database for large data",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/inicontent/inibase#readme",
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "^20.6
|
|
33
|
+
"@types/node": "^20.8.6"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"test": "npx tsx watch ./index.test.ts"
|
package/utils.server.ts
ADDED
|
@@ -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,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
scryptSync,
|
|
3
|
-
randomBytes,
|
|
4
|
-
timingSafeEqual,
|
|
5
|
-
createDecipheriv,
|
|
6
|
-
createCipheriv,
|
|
7
|
-
} from "crypto";
|
|
8
|
-
import { FieldType } from ".";
|
|
1
|
+
import { FieldType, Data } from ".";
|
|
9
2
|
|
|
10
3
|
export const isArrayOfObjects = (arr: any) => {
|
|
11
4
|
return Array.isArray(arr) && (arr.length === 0 || arr.every(isObject));
|
|
@@ -40,7 +33,7 @@ export const combineObjects = (objectArray: Record<string, any>[]) => {
|
|
|
40
33
|
return combinedValues;
|
|
41
34
|
};
|
|
42
35
|
|
|
43
|
-
export const isNumber = (input: any): boolean =>
|
|
36
|
+
export const isNumber = (input: any | any[]): boolean =>
|
|
44
37
|
Array.isArray(input)
|
|
45
38
|
? input.every(isNumber)
|
|
46
39
|
: !isNaN(parseFloat(input)) && !isNaN(input - 0);
|
|
@@ -48,52 +41,35 @@ export const isNumber = (input: any): boolean =>
|
|
|
48
41
|
export const isEmail = (input: any) =>
|
|
49
42
|
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(input));
|
|
50
43
|
|
|
51
|
-
export const isURL = (input: any) =>
|
|
52
|
-
input[0] === "#" ||
|
|
53
|
-
/^((https?|www):\/\/)?[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]+(\/[^\s]*)?$/.test(
|
|
54
|
-
input
|
|
55
|
-
);
|
|
44
|
+
export const isURL = (input: any) => input[0] === "#" || URL.canParse(input);
|
|
56
45
|
|
|
57
|
-
export const
|
|
46
|
+
export const isHTML = (input: any) =>
|
|
47
|
+
/<\/?\s*[a-z-][^>]*\s*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);)/g.test(input);
|
|
58
48
|
|
|
59
|
-
export const
|
|
60
|
-
|
|
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);
|
|
61
58
|
|
|
62
|
-
export const
|
|
63
|
-
|
|
64
|
-
const buf = scryptSync(password, salt, 64);
|
|
65
|
-
// return "161" length string
|
|
66
|
-
return `${buf.toString("hex")}.${salt}`;
|
|
67
|
-
};
|
|
59
|
+
export const isIP = (input: any) =>
|
|
60
|
+
/^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/.test(input);
|
|
68
61
|
|
|
69
|
-
export const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// we need to pass buffer values to timingSafeEqual
|
|
76
|
-
const hashedPasswordBuf = Buffer.from(hashedPassword, "hex");
|
|
77
|
-
// we hash the new sign-in password
|
|
78
|
-
const suppliedPasswordBuf = scryptSync(suppliedPassword, salt, 64);
|
|
79
|
-
// compare the new supplied password with the stored hashed password
|
|
80
|
-
return timingSafeEqual(hashedPasswordBuf, suppliedPasswordBuf);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export const encodeID = (id: number, secretKey: string | number): string => {
|
|
84
|
-
const salt = scryptSync(secretKey.toString(), "salt", 32),
|
|
85
|
-
cipher = createCipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
|
|
62
|
+
export const isBoolean = (input: any) =>
|
|
63
|
+
typeof input === "boolean" ||
|
|
64
|
+
input === "true" ||
|
|
65
|
+
input === "false" ||
|
|
66
|
+
input === true ||
|
|
67
|
+
input === false;
|
|
86
68
|
|
|
87
|
-
|
|
88
|
-
};
|
|
69
|
+
export const isPassword = (input: any) => input.length === 161;
|
|
89
70
|
|
|
90
|
-
export const
|
|
91
|
-
|
|
92
|
-
decipher = createDecipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
|
|
93
|
-
return Number(
|
|
94
|
-
decipher.update(input as string, "hex", "utf8") + decipher.final("utf8")
|
|
95
|
-
);
|
|
96
|
-
};
|
|
71
|
+
export const isDate = (input: any) =>
|
|
72
|
+
!isNaN(Date.parse(String(input))) && Date.parse(String(input)) >= 0;
|
|
97
73
|
|
|
98
74
|
export const isValidID = (input: any): boolean => {
|
|
99
75
|
return Array.isArray(input)
|
|
@@ -118,35 +94,103 @@ export const detectFieldType = (
|
|
|
118
94
|
input: any,
|
|
119
95
|
availableTypes: FieldType[]
|
|
120
96
|
): FieldType | undefined => {
|
|
121
|
-
if (
|
|
122
|
-
(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return "email";
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return "
|
|
144
|
-
else return
|
|
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
|
+
}
|
|
145
191
|
};
|
|
146
192
|
|
|
147
193
|
export default class Utils {
|
|
148
|
-
static encodeID = encodeID;
|
|
149
|
-
static decodeID = decodeID;
|
|
150
194
|
static isNumber = isNumber;
|
|
151
195
|
static isObject = isObject;
|
|
152
196
|
static isEmail = isEmail;
|
|
@@ -154,12 +198,15 @@ export default class Utils {
|
|
|
154
198
|
static isURL = isURL;
|
|
155
199
|
static isValidID = isValidID;
|
|
156
200
|
static isPassword = isPassword;
|
|
157
|
-
static hashPassword = hashPassword;
|
|
158
201
|
static deepMerge = deepMerge;
|
|
159
202
|
static combineObjects = combineObjects;
|
|
160
|
-
static comparePassword = comparePassword;
|
|
161
203
|
static isArrayOfObjects = isArrayOfObjects;
|
|
162
204
|
static findChangedProperties = findChangedProperties;
|
|
163
205
|
static detectFieldType = detectFieldType;
|
|
164
206
|
static isArrayOfArrays = isArrayOfArrays;
|
|
207
|
+
static isBoolean = isBoolean;
|
|
208
|
+
static isString = isString;
|
|
209
|
+
static isHTML = isHTML;
|
|
210
|
+
static isIP = isIP;
|
|
211
|
+
static validateFieldType = validateFieldType;
|
|
165
212
|
}
|