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/README.md
CHANGED
|
@@ -50,11 +50,7 @@ Become a sponsor and have your company logo here 👉 [GitHub Sponsors](https://
|
|
|
50
50
|
## Install
|
|
51
51
|
|
|
52
52
|
```js
|
|
53
|
-
|
|
54
|
-
npm install inibase
|
|
55
|
-
|
|
56
|
-
// pnpm
|
|
57
|
-
pnpm add inibase
|
|
53
|
+
<npm|pnpm> install inibase
|
|
58
54
|
```
|
|
59
55
|
|
|
60
56
|
## How it works?
|
|
@@ -389,60 +385,6 @@ await db.put("user", { isActive: false });
|
|
|
389
385
|
|
|
390
386
|
</details>
|
|
391
387
|
|
|
392
|
-
## Typescript
|
|
393
|
-
|
|
394
|
-
<details>
|
|
395
|
-
<summary>Schema</summary>
|
|
396
|
-
|
|
397
|
-
```js
|
|
398
|
-
type Schema = Field[];
|
|
399
|
-
type Field = {
|
|
400
|
-
id?: string | number | null | undefined,
|
|
401
|
-
key: string,
|
|
402
|
-
required?: boolean,
|
|
403
|
-
} & (
|
|
404
|
-
| {
|
|
405
|
-
type: Exclude<FieldType, "array" | "object">,
|
|
406
|
-
required?: boolean,
|
|
407
|
-
}
|
|
408
|
-
| {
|
|
409
|
-
type: "array",
|
|
410
|
-
children: FieldType | FieldType[] | Schema,
|
|
411
|
-
}
|
|
412
|
-
| {
|
|
413
|
-
type: "object",
|
|
414
|
-
children: Schema,
|
|
415
|
-
}
|
|
416
|
-
);
|
|
417
|
-
type FieldType =
|
|
418
|
-
| "string"
|
|
419
|
-
| "number"
|
|
420
|
-
| "boolean"
|
|
421
|
-
| "date"
|
|
422
|
-
| "email"
|
|
423
|
-
| "url"
|
|
424
|
-
| "table"
|
|
425
|
-
| "object"
|
|
426
|
-
| "array"
|
|
427
|
-
| "password";
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
</details>
|
|
431
|
-
|
|
432
|
-
<details>
|
|
433
|
-
<summary>Data</summary>
|
|
434
|
-
|
|
435
|
-
```js
|
|
436
|
-
type Data = {
|
|
437
|
-
id?: number | string,
|
|
438
|
-
[key: string]: any,
|
|
439
|
-
created_at?: Date,
|
|
440
|
-
updated_at?: Date,
|
|
441
|
-
};
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
</details>
|
|
445
|
-
|
|
446
388
|
## Roadmap
|
|
447
389
|
|
|
448
390
|
- [x] Actions:
|
|
@@ -465,9 +407,9 @@ type Data = {
|
|
|
465
407
|
- [x] Object
|
|
466
408
|
- [x] Array
|
|
467
409
|
- [x] Password
|
|
468
|
-
- [
|
|
469
|
-
- [
|
|
470
|
-
- [
|
|
410
|
+
- [x] IP
|
|
411
|
+
- [x] HTML
|
|
412
|
+
- [x] Id
|
|
471
413
|
- [ ] TO-DO:
|
|
472
414
|
- [ ] Improve caching
|
|
473
415
|
- [ ] Commenting the code
|
package/file.ts
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
existsSync,
|
|
6
|
-
createReadStream,
|
|
7
|
-
WriteStream,
|
|
8
|
-
} from "fs";
|
|
9
|
-
import { open } from "fs/promises";
|
|
10
|
-
import { Interface, createInterface } from "readline";
|
|
11
|
-
import { parse } from "path";
|
|
1
|
+
import { createWriteStream, createReadStream, WriteStream } from "node:fs";
|
|
2
|
+
import { open, unlink, rename, stat } from "node:fs/promises";
|
|
3
|
+
import { Interface, createInterface } from "node:readline";
|
|
4
|
+
import { parse } from "node:path";
|
|
12
5
|
import { ComparisonOperator, FieldType } from ".";
|
|
13
|
-
import
|
|
6
|
+
import { detectFieldType, isArrayOfArrays, isNumber } from "./utils";
|
|
7
|
+
import { encodeID, decodeID, comparePassword } from "./utils.server";
|
|
14
8
|
|
|
15
9
|
const doesSupportReadLines = () => {
|
|
16
10
|
const [major, minor, patch] = process.versions.node.split(".").map(Number);
|
|
17
11
|
return major >= 18 && minor >= 11;
|
|
18
12
|
};
|
|
19
13
|
|
|
14
|
+
export const isExists = async (path: string) => {
|
|
15
|
+
try {
|
|
16
|
+
await stat(path);
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
20
22
|
export const encodeFileName = (fileName: string, extension?: string) => {
|
|
21
23
|
return (
|
|
22
24
|
fileName.replaceAll("%", "%25").replaceAll("*", "%") +
|
|
@@ -29,7 +31,13 @@ export const decodeFileName = (fileName: string) => {
|
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
export const encode = (
|
|
32
|
-
input:
|
|
34
|
+
input:
|
|
35
|
+
| string
|
|
36
|
+
| number
|
|
37
|
+
| boolean
|
|
38
|
+
| null
|
|
39
|
+
| (string | number | boolean | null)[],
|
|
40
|
+
secretKey?: string | Buffer
|
|
33
41
|
) => {
|
|
34
42
|
const secureString = (input: string | number | boolean | null) => {
|
|
35
43
|
if (["true", "false"].includes(String(input))) return input ? 1 : 0;
|
|
@@ -44,7 +52,7 @@ export const encode = (
|
|
|
44
52
|
: input;
|
|
45
53
|
};
|
|
46
54
|
return Array.isArray(input)
|
|
47
|
-
?
|
|
55
|
+
? isArrayOfArrays(input)
|
|
48
56
|
? (input as any[])
|
|
49
57
|
.map((_input) => _input.map(secureString).join(","))
|
|
50
58
|
.join("|")
|
|
@@ -55,7 +63,8 @@ export const encode = (
|
|
|
55
63
|
export const decode = (
|
|
56
64
|
input: string | null | number,
|
|
57
65
|
fieldType?: FieldType | FieldType[],
|
|
58
|
-
fieldChildrenType?: FieldType | FieldType[]
|
|
66
|
+
fieldChildrenType?: FieldType | FieldType[],
|
|
67
|
+
secretKey?: string | Buffer
|
|
59
68
|
): string | number | boolean | null | (string | number | null | boolean)[] => {
|
|
60
69
|
if (!fieldType) return null;
|
|
61
70
|
const unSecureString = (input: string) =>
|
|
@@ -68,29 +77,34 @@ export const decode = (
|
|
|
68
77
|
.replaceAll("\\r", "\r") || null;
|
|
69
78
|
if (input === null || input === "") return null;
|
|
70
79
|
if (Array.isArray(fieldType))
|
|
71
|
-
fieldType =
|
|
80
|
+
fieldType = detectFieldType(String(input), fieldType);
|
|
72
81
|
const decodeHelper = (value: string | number | any[]) => {
|
|
73
82
|
if (Array.isArray(value) && fieldType !== "array")
|
|
74
83
|
return value.map(decodeHelper);
|
|
75
84
|
switch (fieldType as FieldType) {
|
|
76
85
|
case "table":
|
|
77
86
|
case "number":
|
|
78
|
-
return
|
|
87
|
+
return isNumber(value) ? Number(value) : null;
|
|
79
88
|
case "boolean":
|
|
80
89
|
return typeof value === "string" ? value === "true" : Boolean(value);
|
|
81
90
|
case "array":
|
|
82
|
-
if (!Array.isArray(value)) return
|
|
91
|
+
if (!Array.isArray(value)) return [value];
|
|
92
|
+
|
|
83
93
|
if (fieldChildrenType)
|
|
84
94
|
return value.map(
|
|
85
95
|
(v) =>
|
|
86
96
|
decode(
|
|
87
97
|
v,
|
|
88
98
|
Array.isArray(fieldChildrenType)
|
|
89
|
-
?
|
|
90
|
-
: fieldChildrenType
|
|
99
|
+
? detectFieldType(v, fieldChildrenType)
|
|
100
|
+
: fieldChildrenType,
|
|
101
|
+
undefined,
|
|
102
|
+
secretKey
|
|
91
103
|
) as string | number | boolean | null
|
|
92
104
|
);
|
|
93
105
|
else return value;
|
|
106
|
+
case "id":
|
|
107
|
+
return isNumber(value) ? encodeID(value as number, secretKey) : value;
|
|
94
108
|
default:
|
|
95
109
|
return value;
|
|
96
110
|
}
|
|
@@ -103,6 +117,16 @@ export const decode = (
|
|
|
103
117
|
.split("|")
|
|
104
118
|
.map((_input) => _input.split(",").map(unSecureString))
|
|
105
119
|
: input.split(",").map(unSecureString)
|
|
120
|
+
: input.includes("|")
|
|
121
|
+
? input
|
|
122
|
+
.split("|")
|
|
123
|
+
.map((_input) => [
|
|
124
|
+
_input
|
|
125
|
+
? fieldType === "array"
|
|
126
|
+
? [unSecureString(_input)]
|
|
127
|
+
: unSecureString(_input)
|
|
128
|
+
: null,
|
|
129
|
+
])
|
|
106
130
|
: unSecureString(input)
|
|
107
131
|
: input
|
|
108
132
|
);
|
|
@@ -112,8 +136,20 @@ export const get = async (
|
|
|
112
136
|
filePath: string,
|
|
113
137
|
lineNumbers?: number | number[],
|
|
114
138
|
fieldType?: FieldType | FieldType[],
|
|
115
|
-
fieldChildrenType?: FieldType | FieldType[]
|
|
116
|
-
|
|
139
|
+
fieldChildrenType?: FieldType | FieldType[],
|
|
140
|
+
secretKey?: string | Buffer
|
|
141
|
+
): Promise<
|
|
142
|
+
[
|
|
143
|
+
Record<
|
|
144
|
+
number,
|
|
145
|
+
| string
|
|
146
|
+
| number
|
|
147
|
+
| boolean
|
|
148
|
+
| (string | number | boolean | (string | number | boolean)[])[]
|
|
149
|
+
> | null,
|
|
150
|
+
number
|
|
151
|
+
]
|
|
152
|
+
> => {
|
|
117
153
|
let rl: Interface;
|
|
118
154
|
if (doesSupportReadLines()) rl = (await open(filePath)).readLines();
|
|
119
155
|
else
|
|
@@ -134,12 +170,19 @@ export const get = async (
|
|
|
134
170
|
if (!lineNumbers) {
|
|
135
171
|
for await (const line of rl)
|
|
136
172
|
lineCount++,
|
|
137
|
-
(lines[lineCount] = decode(
|
|
173
|
+
(lines[lineCount] = decode(
|
|
174
|
+
line,
|
|
175
|
+
fieldType,
|
|
176
|
+
fieldChildrenType,
|
|
177
|
+
secretKey
|
|
178
|
+
));
|
|
138
179
|
} else if (lineNumbers === -1) {
|
|
139
180
|
let lastLine: string;
|
|
140
181
|
for await (const line of rl) lineCount++, (lastLine = line);
|
|
141
182
|
if (lastLine)
|
|
142
|
-
lines = {
|
|
183
|
+
lines = {
|
|
184
|
+
[lineCount]: decode(lastLine, fieldType, fieldChildrenType, secretKey),
|
|
185
|
+
};
|
|
143
186
|
} else {
|
|
144
187
|
let lineNumbersArray = [
|
|
145
188
|
...(Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers]),
|
|
@@ -148,13 +191,13 @@ export const get = async (
|
|
|
148
191
|
lineCount++;
|
|
149
192
|
if (!lineNumbersArray.includes(lineCount)) continue;
|
|
150
193
|
const indexOfLineCount = lineNumbersArray.indexOf(lineCount);
|
|
151
|
-
lines[lineCount] = decode(line, fieldType, fieldChildrenType);
|
|
194
|
+
lines[lineCount] = decode(line, fieldType, fieldChildrenType, secretKey);
|
|
152
195
|
lineNumbersArray[indexOfLineCount] = 0;
|
|
153
196
|
if (!lineNumbersArray.filter((lineN) => lineN !== 0).length) break;
|
|
154
197
|
}
|
|
155
198
|
}
|
|
156
199
|
|
|
157
|
-
return lines ?? null;
|
|
200
|
+
return [lines ?? null, lineCount];
|
|
158
201
|
};
|
|
159
202
|
|
|
160
203
|
export const replace = async (
|
|
@@ -168,9 +211,10 @@ export const replace = async (
|
|
|
168
211
|
| Record<
|
|
169
212
|
number,
|
|
170
213
|
string | boolean | number | null | (string | boolean | number | null)[]
|
|
171
|
-
|
|
214
|
+
>,
|
|
215
|
+
secretKey?: string | Buffer
|
|
172
216
|
) => {
|
|
173
|
-
if (
|
|
217
|
+
if (await isExists(filePath)) {
|
|
174
218
|
let rl: Interface, writeStream: WriteStream;
|
|
175
219
|
if (doesSupportReadLines()) {
|
|
176
220
|
const file = await open(filePath, "w+");
|
|
@@ -188,13 +232,14 @@ export const replace = async (
|
|
|
188
232
|
for await (const line of rl) {
|
|
189
233
|
lineCount++;
|
|
190
234
|
writeStream.write(
|
|
191
|
-
(lineCount in replacements
|
|
192
|
-
|
|
235
|
+
(lineCount in replacements
|
|
236
|
+
? encode(replacements[lineCount], secretKey)
|
|
237
|
+
: line) + "\n"
|
|
193
238
|
);
|
|
194
239
|
}
|
|
195
240
|
} else
|
|
196
241
|
for await (const _line of rl)
|
|
197
|
-
writeStream.write(encode(replacements) + "\n");
|
|
242
|
+
writeStream.write(encode(replacements, secretKey) + "\n");
|
|
198
243
|
|
|
199
244
|
writeStream.end();
|
|
200
245
|
} else if (typeof replacements === "object" && !Array.isArray(replacements)) {
|
|
@@ -206,8 +251,9 @@ export const replace = async (
|
|
|
206
251
|
Math.max(...Object.keys(replacements).map(Number)) + 1;
|
|
207
252
|
for (let lineCount = 1; lineCount < largestLinesNumbers; lineCount++) {
|
|
208
253
|
writeStream.write(
|
|
209
|
-
(lineCount in replacements
|
|
210
|
-
|
|
254
|
+
(lineCount in replacements
|
|
255
|
+
? encode(replacements[lineCount], secretKey)
|
|
256
|
+
: "") + "\n"
|
|
211
257
|
);
|
|
212
258
|
}
|
|
213
259
|
writeStream.end();
|
|
@@ -220,7 +266,7 @@ export const remove = async (
|
|
|
220
266
|
): Promise<void> => {
|
|
221
267
|
let lineCount = 0;
|
|
222
268
|
|
|
223
|
-
const tempFilePath = `${filePath}.tmp`,
|
|
269
|
+
const tempFilePath = `${filePath}-${Date.now()}.tmp`,
|
|
224
270
|
linesToDeleteArray = [
|
|
225
271
|
...(Array.isArray(linesToDelete) ? linesToDelete : [linesToDelete]),
|
|
226
272
|
];
|
|
@@ -243,10 +289,9 @@ export const remove = async (
|
|
|
243
289
|
writeStream.write(`${line}\n`);
|
|
244
290
|
}
|
|
245
291
|
}
|
|
246
|
-
writeStream.end()
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
renameSync(tempFilePath, filePath); // Rename the temp file to the original file name
|
|
292
|
+
writeStream.end(async () => {
|
|
293
|
+
await unlink(filePath); // Remove the original file
|
|
294
|
+
await rename(tempFilePath, filePath); // Rename the temp file to the original file name
|
|
250
295
|
});
|
|
251
296
|
};
|
|
252
297
|
|
|
@@ -279,7 +324,8 @@ export const search = async (
|
|
|
279
324
|
fieldChildrenType?: FieldType | FieldType[],
|
|
280
325
|
limit?: number,
|
|
281
326
|
offset?: number,
|
|
282
|
-
readWholeFile?: boolean
|
|
327
|
+
readWholeFile?: boolean,
|
|
328
|
+
secretKey?: string | Buffer
|
|
283
329
|
): Promise<
|
|
284
330
|
[
|
|
285
331
|
Record<
|
|
@@ -310,7 +356,16 @@ export const search = async (
|
|
|
310
356
|
fieldChildrenType?: FieldType | FieldType[]
|
|
311
357
|
): boolean => {
|
|
312
358
|
if (Array.isArray(fieldType))
|
|
313
|
-
fieldType =
|
|
359
|
+
fieldType = detectFieldType(String(originalValue), fieldType);
|
|
360
|
+
if (Array.isArray(comparedAtValue) && !["[]", "![]"].includes(operator))
|
|
361
|
+
return comparedAtValue.some((comparedAtValueSingle) =>
|
|
362
|
+
handleComparisonOperator(
|
|
363
|
+
operator,
|
|
364
|
+
originalValue,
|
|
365
|
+
comparedAtValueSingle,
|
|
366
|
+
fieldType
|
|
367
|
+
)
|
|
368
|
+
);
|
|
314
369
|
// check if not array or object // it can't be array or object!
|
|
315
370
|
switch (operator) {
|
|
316
371
|
case "=":
|
|
@@ -318,14 +373,16 @@ export const search = async (
|
|
|
318
373
|
case "password":
|
|
319
374
|
return typeof originalValue === "string" &&
|
|
320
375
|
typeof comparedAtValue === "string"
|
|
321
|
-
?
|
|
376
|
+
? comparePassword(originalValue, comparedAtValue)
|
|
322
377
|
: false;
|
|
323
378
|
case "boolean":
|
|
324
379
|
return Number(originalValue) - Number(comparedAtValue) === 0;
|
|
380
|
+
case "id":
|
|
381
|
+
return secretKey && typeof comparedAtValue === "string"
|
|
382
|
+
? decodeID(comparedAtValue as string, secretKey) === originalValue
|
|
383
|
+
: comparedAtValue === originalValue;
|
|
325
384
|
default:
|
|
326
|
-
return
|
|
327
|
-
? comparedAtValue.some((value) => originalValue === value)
|
|
328
|
-
: originalValue === comparedAtValue;
|
|
385
|
+
return originalValue === comparedAtValue;
|
|
329
386
|
}
|
|
330
387
|
case "!=":
|
|
331
388
|
return !handleComparisonOperator(
|
|
@@ -335,21 +392,13 @@ export const search = async (
|
|
|
335
392
|
fieldType
|
|
336
393
|
);
|
|
337
394
|
case ">":
|
|
338
|
-
return
|
|
339
|
-
? comparedAtValue.some((value) => originalValue > value)
|
|
340
|
-
: originalValue > comparedAtValue;
|
|
395
|
+
return originalValue > comparedAtValue;
|
|
341
396
|
case "<":
|
|
342
|
-
return
|
|
343
|
-
? comparedAtValue.some((value) => originalValue < value)
|
|
344
|
-
: originalValue < comparedAtValue;
|
|
397
|
+
return originalValue < comparedAtValue;
|
|
345
398
|
case ">=":
|
|
346
|
-
return
|
|
347
|
-
? comparedAtValue.some((value) => originalValue >= value)
|
|
348
|
-
: originalValue >= comparedAtValue;
|
|
399
|
+
return originalValue >= comparedAtValue;
|
|
349
400
|
case "<=":
|
|
350
|
-
return
|
|
351
|
-
? comparedAtValue.some((value) => originalValue <= value)
|
|
352
|
-
: originalValue <= comparedAtValue;
|
|
401
|
+
return originalValue <= comparedAtValue;
|
|
353
402
|
case "[]":
|
|
354
403
|
return (
|
|
355
404
|
(Array.isArray(originalValue) &&
|
|
@@ -370,23 +419,13 @@ export const search = async (
|
|
|
370
419
|
fieldType
|
|
371
420
|
);
|
|
372
421
|
case "*":
|
|
373
|
-
return
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
"i"
|
|
381
|
-
).test(String(originalValue))
|
|
382
|
-
)
|
|
383
|
-
: new RegExp(
|
|
384
|
-
`^${(String(comparedAtValue).includes("%")
|
|
385
|
-
? String(comparedAtValue)
|
|
386
|
-
: "%" + String(comparedAtValue) + "%"
|
|
387
|
-
).replace(/%/g, ".*")}$`,
|
|
388
|
-
"i"
|
|
389
|
-
).test(String(originalValue));
|
|
422
|
+
return new RegExp(
|
|
423
|
+
`^${(String(comparedAtValue).includes("%")
|
|
424
|
+
? String(comparedAtValue)
|
|
425
|
+
: "%" + String(comparedAtValue) + "%"
|
|
426
|
+
).replace(/%/g, ".*")}$`,
|
|
427
|
+
"i"
|
|
428
|
+
).test(String(originalValue));
|
|
390
429
|
case "!*":
|
|
391
430
|
return !handleComparisonOperator(
|
|
392
431
|
"*",
|
|
@@ -420,7 +459,7 @@ export const search = async (
|
|
|
420
459
|
|
|
421
460
|
for await (const line of rl) {
|
|
422
461
|
lineCount++;
|
|
423
|
-
const decodedLine = decode(line, fieldType, fieldChildrenType);
|
|
462
|
+
const decodedLine = decode(line, fieldType, fieldChildrenType, secretKey);
|
|
424
463
|
if (
|
|
425
464
|
(Array.isArray(operator) &&
|
|
426
465
|
Array.isArray(comparedAtValue) &&
|
|
@@ -474,4 +513,5 @@ export default class File {
|
|
|
474
513
|
static decode = decode;
|
|
475
514
|
static encodeFileName = encodeFileName;
|
|
476
515
|
static decodeFileName = decodeFileName;
|
|
516
|
+
static isExists = isExists;
|
|
477
517
|
}
|