inibase 1.0.0-rc.37 → 1.0.0-rc.39
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 +28 -0
- package/dist/file.d.ts +4 -3
- package/dist/file.js +20 -5
- package/dist/file.thread.d.ts +1 -0
- package/dist/file.thread.js +5 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +106 -43
- package/dist/index.thread.d.ts +1 -0
- package/dist/index.thread.js +6 -0
- package/dist/utils.d.ts +0 -8
- package/dist/utils.js +27 -42
- package/dist/utils.server.d.ts +7 -6
- package/dist/utils.server.js +5 -4
- package/package.json +9 -1
- package/dist/Archive.zip +0 -0
package/README.md
CHANGED
|
@@ -105,6 +105,7 @@ Ps: Testing by default with `user` table, with username, email, password fields
|
|
|
105
105
|
- [x] IP
|
|
106
106
|
- [x] HTML
|
|
107
107
|
- [x] Id
|
|
108
|
+
- [x] JSON
|
|
108
109
|
- [ ] TO-DO:
|
|
109
110
|
- [x] Improve caching
|
|
110
111
|
- [x] Commenting the code
|
|
@@ -491,6 +492,33 @@ await db.min("user", ["age", ...], { isActive: false });
|
|
|
491
492
|
|
|
492
493
|
</details>
|
|
493
494
|
|
|
495
|
+
<details>
|
|
496
|
+
<summary>createWorker</summary>
|
|
497
|
+
|
|
498
|
+
```js
|
|
499
|
+
import Inibase from "inibase";
|
|
500
|
+
const db = new Inibase("/database_name");
|
|
501
|
+
|
|
502
|
+
// POST 10,000 USER
|
|
503
|
+
await Promise.all(
|
|
504
|
+
[...Array(10)]
|
|
505
|
+
.map((x, i) => i)
|
|
506
|
+
.map(
|
|
507
|
+
(_index) =>
|
|
508
|
+
db.createWorker("post", [
|
|
509
|
+
"user",
|
|
510
|
+
[...Array(1000)].map((_, i) => ({
|
|
511
|
+
username: `username_${i + 1}`,
|
|
512
|
+
email: `email_${i + 1}@test.com`,
|
|
513
|
+
password: `password_${i + 1}`,
|
|
514
|
+
})),
|
|
515
|
+
])
|
|
516
|
+
)
|
|
517
|
+
)
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
</details>
|
|
521
|
+
|
|
494
522
|
## Config
|
|
495
523
|
|
|
496
524
|
The `.env` file supports the following parameters (make sure to run command with flag --env-file=.env)
|
package/dist/file.d.ts
CHANGED
|
@@ -17,10 +17,9 @@ export declare const isExists: (path: string) => Promise<boolean>;
|
|
|
17
17
|
* If the input is a single value, it is directly secured.
|
|
18
18
|
*
|
|
19
19
|
* @param input - A value or array of values (string, number, boolean, null).
|
|
20
|
-
* @param secretKey - Optional secret key for encoding, can be a string or Buffer.
|
|
21
20
|
* @returns The secured and/or joined string.
|
|
22
21
|
*/
|
|
23
|
-
export declare const encode: (input: string | number | boolean | null | (string | number | boolean | null)[]
|
|
22
|
+
export declare const encode: (input: string | number | boolean | null | (string | number | boolean | null)[]) => string | number | boolean | null;
|
|
24
23
|
/**
|
|
25
24
|
* Decodes the input based on the specified field type(s) and an optional secret key.
|
|
26
25
|
* Handles different formats of input, including strings, numbers, and their array representations.
|
|
@@ -143,6 +142,7 @@ export declare const max: (filePath: string, lineNumbers?: number | number[]) =>
|
|
|
143
142
|
* Note: Decodes each line as a number using the 'decode' function. Considers only numerical values for determining the minimum.
|
|
144
143
|
*/
|
|
145
144
|
export declare const min: (filePath: string, lineNumbers?: number | number[]) => Promise<number>;
|
|
145
|
+
export declare function createWorker(functionName: "get" | "remove" | "search" | "replace" | "sum" | "min" | "max" | "append" | "count", arg: any[]): Promise<any>;
|
|
146
146
|
/**
|
|
147
147
|
* Asynchronously sorts the lines in a file in the specified direction.
|
|
148
148
|
*
|
|
@@ -160,7 +160,7 @@ export default class File {
|
|
|
160
160
|
static remove: (filePath: string, linesToDelete: number | number[]) => Promise<string[]>;
|
|
161
161
|
static search: (filePath: string, operator: ComparisonOperator | ComparisonOperator[], comparedAtValue: string | number | boolean | (string | number | boolean | null)[] | null, logicalOperator?: "and" | "or" | undefined, fieldType?: FieldType | FieldType[] | undefined, fieldChildrenType?: FieldType | FieldType[] | undefined, limit?: number | undefined, offset?: number | undefined, readWholeFile?: boolean | undefined, secretKey?: string | Buffer | undefined) => Promise<[Record<number, string | number | boolean | (string | number | boolean | null)[] | null> | null, number, Set<number> | null]>;
|
|
162
162
|
static replace: (filePath: string, replacements: string | number | boolean | (string | number | boolean | null)[] | Record<number, string | number | boolean | (string | number | boolean | null)[] | null> | null) => Promise<string[]>;
|
|
163
|
-
static encode: (input: string | number | boolean | (string | number | boolean | null)[] | null
|
|
163
|
+
static encode: (input: string | number | boolean | (string | number | boolean | null)[] | null) => string | number | boolean | null;
|
|
164
164
|
static decode: (input: string | number | null, fieldType?: FieldType | FieldType[] | undefined, fieldChildrenType?: FieldType | FieldType[] | undefined, secretKey?: string | Buffer | undefined) => string | number | boolean | (string | number | boolean | null)[] | null;
|
|
165
165
|
static isExists: (path: string) => Promise<boolean>;
|
|
166
166
|
static sum: (filePath: string, lineNumbers?: number | number[] | undefined) => Promise<number>;
|
|
@@ -172,4 +172,5 @@ export default class File {
|
|
|
172
172
|
static read: (filePath: string, disableCompression?: boolean) => Promise<string>;
|
|
173
173
|
static lock: (folderPath: string) => Promise<void>;
|
|
174
174
|
static unlock: (folderPath: string) => Promise<void>;
|
|
175
|
+
static createWorker: typeof createWorker;
|
|
175
176
|
}
|
package/dist/file.js
CHANGED
|
@@ -5,7 +5,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
5
5
|
import { createGzip, createGunzip, gzip as gzipAsync, gunzip as gunzipAsync, } from "node:zlib";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import { Worker } from "node:worker_threads";
|
|
9
|
+
import { detectFieldType, isArrayOfArrays, isNumber, isObject, isPassword, } from "./utils.js";
|
|
9
10
|
import { encodeID, comparePassword } from "./utils.server.js";
|
|
10
11
|
import Config from "./config.js";
|
|
11
12
|
const gzip = promisify(gzipAsync);
|
|
@@ -144,10 +145,9 @@ const joinMultidimensionalArray = (arr, delimiter_index = 0) => {
|
|
|
144
145
|
* If the input is a single value, it is directly secured.
|
|
145
146
|
*
|
|
146
147
|
* @param input - A value or array of values (string, number, boolean, null).
|
|
147
|
-
* @param secretKey - Optional secret key for encoding, can be a string or Buffer.
|
|
148
148
|
* @returns The secured and/or joined string.
|
|
149
149
|
*/
|
|
150
|
-
export const encode = (input
|
|
150
|
+
export const encode = (input) => {
|
|
151
151
|
// Use the optimized secureArray and joinMultidimensionalArray functions.
|
|
152
152
|
return Array.isArray(input)
|
|
153
153
|
? joinMultidimensionalArray(secureArray(input))
|
|
@@ -217,6 +217,8 @@ const decodeHelper = (value, fieldType, fieldChildrenType, secretKey) => {
|
|
|
217
217
|
if (Array.isArray(value) && fieldType !== "array")
|
|
218
218
|
return value.map((v) => decodeHelper(v, fieldType, fieldChildrenType, secretKey));
|
|
219
219
|
switch (fieldType) {
|
|
220
|
+
case "json":
|
|
221
|
+
return JSON.parse(value);
|
|
220
222
|
case "number":
|
|
221
223
|
return isNumber(value) ? Number(value) : null;
|
|
222
224
|
case "boolean":
|
|
@@ -510,8 +512,7 @@ const isEqual = (originalValue, comparedAtValue, fieldType) => {
|
|
|
510
512
|
switch (fieldType) {
|
|
511
513
|
// Password comparison.
|
|
512
514
|
case "password":
|
|
513
|
-
return typeof
|
|
514
|
-
typeof comparedAtValue === "string"
|
|
515
|
+
return isPassword(originalValue) && typeof comparedAtValue === "string"
|
|
515
516
|
? comparePassword(originalValue, comparedAtValue)
|
|
516
517
|
: false;
|
|
517
518
|
// Boolean comparison.
|
|
@@ -762,6 +763,19 @@ export const min = async (filePath, lineNumbers) => {
|
|
|
762
763
|
await fileHandle.close();
|
|
763
764
|
return min;
|
|
764
765
|
};
|
|
766
|
+
export function createWorker(functionName, arg) {
|
|
767
|
+
return new Promise(function (resolve, reject) {
|
|
768
|
+
const worker = new Worker("./dist/file.thread.js", {
|
|
769
|
+
workerData: { functionName, arg },
|
|
770
|
+
});
|
|
771
|
+
worker.on("message", (data) => {
|
|
772
|
+
resolve(data);
|
|
773
|
+
});
|
|
774
|
+
worker.on("error", (msg) => {
|
|
775
|
+
reject(`An error ocurred: ${msg}`);
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
}
|
|
765
779
|
/**
|
|
766
780
|
* Asynchronously sorts the lines in a file in the specified direction.
|
|
767
781
|
*
|
|
@@ -791,4 +805,5 @@ export default class File {
|
|
|
791
805
|
static read = read;
|
|
792
806
|
static lock = lock;
|
|
793
807
|
static unlock = unlock;
|
|
808
|
+
static createWorker = createWorker;
|
|
794
809
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface Data {
|
|
|
5
5
|
createdAt?: number;
|
|
6
6
|
updatedAt?: number;
|
|
7
7
|
}
|
|
8
|
-
export type FieldType = "string" | "number" | "boolean" | "date" | "email" | "url" | "table" | "object" | "array" | "password" | "html" | "ip" | "id";
|
|
8
|
+
export type FieldType = "string" | "number" | "boolean" | "date" | "email" | "url" | "table" | "object" | "array" | "password" | "html" | "ip" | "json" | "id";
|
|
9
9
|
type FieldDefault = {
|
|
10
10
|
id?: string | number;
|
|
11
11
|
key: string;
|
|
@@ -62,10 +62,14 @@ export default class Inibase {
|
|
|
62
62
|
database: string;
|
|
63
63
|
table: string | null;
|
|
64
64
|
pageInfo: Record<string, pageInfo>;
|
|
65
|
+
private isThreadEnabled;
|
|
65
66
|
private totalItems;
|
|
66
67
|
salt: Buffer;
|
|
67
|
-
constructor(database: string, mainFolder?: string);
|
|
68
|
+
constructor(database: string, mainFolder?: string, _table?: string | null, _totalItems?: Record<string, number>, _pageInfo?: Record<string, pageInfo>, _isThreadEnabled?: boolean);
|
|
68
69
|
private throwError;
|
|
70
|
+
createWorker(functionName: "get" | "post" | "put" | "delete" | "sum" | "min" | "max", arg: any[]): Promise<any>;
|
|
71
|
+
private _decodeIdFromSchema;
|
|
72
|
+
private _schemaToIdsPath;
|
|
69
73
|
setTableSchema(tableName: string, schema: Schema): Promise<void>;
|
|
70
74
|
getTableSchema(tableName: string): Promise<Schema | undefined>;
|
|
71
75
|
getField(keyPath: string, schema: Schema): (FieldDefault & FieldStringType) | (FieldDefault & FieldObjectType) | (FieldDefault & FieldArrayType) | null;
|
|
@@ -73,7 +77,12 @@ export default class Inibase {
|
|
|
73
77
|
private formatField;
|
|
74
78
|
private formatData;
|
|
75
79
|
private getDefaultValue;
|
|
76
|
-
private
|
|
80
|
+
private _combineObjectsToArray;
|
|
81
|
+
private _CombineData;
|
|
82
|
+
private _addPathToKeys;
|
|
83
|
+
joinPathesContents(mainPath: string, data: Data | Data[]): {
|
|
84
|
+
[key: string]: string[];
|
|
85
|
+
};
|
|
77
86
|
private getItemsFromSchema;
|
|
78
87
|
private applyCriteria;
|
|
79
88
|
private _filterSchemaByColumns;
|
package/dist/index.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { unlink, rename, mkdir, readdir } from "node:fs/promises";
|
|
2
2
|
import { existsSync, appendFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { cpus } from "node:os";
|
|
5
4
|
import { scryptSync, randomBytes } from "node:crypto";
|
|
5
|
+
import { Worker } from "node:worker_threads";
|
|
6
6
|
import File from "./file.js";
|
|
7
7
|
import Utils from "./utils.js";
|
|
8
8
|
import UtilsServer from "./utils.server.js";
|
|
9
9
|
import Config from "./config.js";
|
|
10
|
-
process.env.UV_THREADPOOL_SIZE = cpus().length.toString();
|
|
11
10
|
export default class Inibase {
|
|
12
11
|
folder;
|
|
13
12
|
database;
|
|
14
13
|
table;
|
|
15
14
|
pageInfo;
|
|
15
|
+
isThreadEnabled = false;
|
|
16
16
|
totalItems;
|
|
17
17
|
salt;
|
|
18
|
-
constructor(database, mainFolder = ".") {
|
|
18
|
+
constructor(database, mainFolder = ".", _table = null, _totalItems = {}, _pageInfo = {}, _isThreadEnabled = false) {
|
|
19
19
|
this.database = database;
|
|
20
20
|
this.folder = mainFolder;
|
|
21
|
-
this.table =
|
|
22
|
-
this.totalItems =
|
|
23
|
-
this.pageInfo =
|
|
21
|
+
this.table = _table;
|
|
22
|
+
this.totalItems = _totalItems;
|
|
23
|
+
this.pageInfo = _pageInfo;
|
|
24
|
+
this.isThreadEnabled = _isThreadEnabled;
|
|
24
25
|
if (!existsSync(".env") || !process.env.INIBASE_SECRET) {
|
|
25
26
|
this.salt = scryptSync(randomBytes(16), randomBytes(16), 32);
|
|
26
27
|
appendFileSync(".env", `\nINIBASE_SECRET=${this.salt.toString("hex")}\n`);
|
|
@@ -53,46 +54,67 @@ export default class Inibase {
|
|
|
53
54
|
}
|
|
54
55
|
return new Error(errorMessage);
|
|
55
56
|
}
|
|
56
|
-
async
|
|
57
|
-
|
|
57
|
+
async createWorker(functionName, arg) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const worker = new Worker("./dist/index.thread.js", {
|
|
60
|
+
workerData: {
|
|
61
|
+
_constructor: [
|
|
62
|
+
this.database,
|
|
63
|
+
this.folder,
|
|
64
|
+
this.table,
|
|
65
|
+
this.totalItems,
|
|
66
|
+
this.pageInfo,
|
|
67
|
+
true, // enable Thread
|
|
68
|
+
],
|
|
69
|
+
functionName,
|
|
70
|
+
arg,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
worker.on("message", resolve);
|
|
74
|
+
worker.on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
_decodeIdFromSchema = (schema) => schema.map((field) => {
|
|
78
|
+
if ((field.type === "array" || field.type === "object") &&
|
|
79
|
+
field.children &&
|
|
80
|
+
Utils.isArrayOfObjects(field.children))
|
|
81
|
+
field.children = this._decodeIdFromSchema(field.children);
|
|
82
|
+
if (field.id && !Utils.isNumber(field.id))
|
|
83
|
+
field.id = UtilsServer.decodeID(field.id, this.salt);
|
|
84
|
+
return field;
|
|
85
|
+
});
|
|
86
|
+
_schemaToIdsPath = (schema, prefix = "") => {
|
|
87
|
+
let RETURN = {};
|
|
88
|
+
for (const field of schema)
|
|
58
89
|
if ((field.type === "array" || field.type === "object") &&
|
|
59
90
|
field.children &&
|
|
60
|
-
Utils.isArrayOfObjects(field.children))
|
|
61
|
-
field.children
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
Utils.isArrayOfObjects(field.children)) {
|
|
92
|
+
Utils.deepMerge(RETURN, this._schemaToIdsPath(field.children, (prefix ?? "") + field.key + "."));
|
|
93
|
+
}
|
|
94
|
+
else if (Utils.isValidID(field.id))
|
|
95
|
+
RETURN[UtilsServer.decodeID(field.id, this.salt)] =
|
|
96
|
+
(prefix ?? "") + field.key + ".inib";
|
|
97
|
+
return RETURN;
|
|
98
|
+
};
|
|
99
|
+
async setTableSchema(tableName, schema) {
|
|
100
|
+
const tablePath = join(this.folder, this.database, tableName), tableSchemaPath = join(tablePath, "schema.json"), isTablePathExists = await File.isExists(tablePath);
|
|
66
101
|
// remove id from schema
|
|
67
102
|
schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
|
|
68
|
-
schema = UtilsServer.addIdToSchema(schema, UtilsServer.findLastIdNumber(schema, this.salt), this.salt);
|
|
69
|
-
|
|
70
|
-
if (!(await File.isExists(tablePath)))
|
|
103
|
+
schema = UtilsServer.addIdToSchema(schema, UtilsServer.findLastIdNumber(schema, this.salt), this.salt, isTablePathExists);
|
|
104
|
+
if (!isTablePathExists)
|
|
71
105
|
await mkdir(tablePath, { recursive: true });
|
|
72
106
|
if (!(await File.isExists(join(tablePath, ".tmp"))))
|
|
73
107
|
await mkdir(join(tablePath, ".tmp"));
|
|
74
108
|
if (await File.isExists(tableSchemaPath)) {
|
|
75
109
|
// update columns files names based on field id
|
|
76
|
-
const
|
|
77
|
-
let RETURN = {};
|
|
78
|
-
for (const field of schema)
|
|
79
|
-
if ((field.type === "array" || field.type === "object") &&
|
|
80
|
-
field.children &&
|
|
81
|
-
Utils.isArrayOfObjects(field.children)) {
|
|
82
|
-
Utils.deepMerge(RETURN, schemaToIdsPath(field.children, (prefix ?? "") + field.key + "."));
|
|
83
|
-
}
|
|
84
|
-
else if (Utils.isValidID(field.id))
|
|
85
|
-
RETURN[UtilsServer.decodeID(field.id, this.salt)] =
|
|
86
|
-
(prefix ?? "") + field.key + ".inib";
|
|
87
|
-
return RETURN;
|
|
88
|
-
}, replaceOldPathes = Utils.findChangedProperties(schemaToIdsPath((await this.getTableSchema(tableName)) ?? []), schemaToIdsPath(schema));
|
|
110
|
+
const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath((await this.getTableSchema(tableName)) ?? []), this._schemaToIdsPath(schema));
|
|
89
111
|
if (replaceOldPathes)
|
|
90
112
|
await Promise.all(Object.entries(replaceOldPathes).map(async ([oldPath, newPath]) => {
|
|
91
113
|
if (await File.isExists(join(tablePath, oldPath)))
|
|
92
114
|
await rename(join(tablePath, oldPath), join(tablePath, newPath));
|
|
93
115
|
}));
|
|
94
116
|
}
|
|
95
|
-
await File.write(join(tablePath, "schema.json"), JSON.stringify(
|
|
117
|
+
await File.write(join(tablePath, "schema.json"), JSON.stringify(isTablePathExists ? this._decodeIdFromSchema(schema) : schema, null, 2), true);
|
|
96
118
|
}
|
|
97
119
|
async getTableSchema(tableName) {
|
|
98
120
|
const tableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
|
|
@@ -158,7 +180,7 @@ export default class Inibase {
|
|
|
158
180
|
!Utils.isArrayOfObjects(field.children)
|
|
159
181
|
? field.children
|
|
160
182
|
: undefined))
|
|
161
|
-
throw this.throwError("INVALID_TYPE", field.key
|
|
183
|
+
throw this.throwError("INVALID_TYPE", [field.key, field.type]);
|
|
162
184
|
if ((field.type === "array" || field.type === "object") &&
|
|
163
185
|
field.children &&
|
|
164
186
|
Utils.isArrayOfObjects(field.children))
|
|
@@ -225,7 +247,7 @@ export default class Inibase {
|
|
|
225
247
|
case "password":
|
|
226
248
|
if (Array.isArray(value))
|
|
227
249
|
value = value[0];
|
|
228
|
-
return
|
|
250
|
+
return Utils.isPassword(value)
|
|
229
251
|
? value
|
|
230
252
|
: UtilsServer.hashPassword(String(value));
|
|
231
253
|
case "number":
|
|
@@ -238,6 +260,8 @@ export default class Inibase {
|
|
|
238
260
|
return Utils.isNumber(value)
|
|
239
261
|
? value
|
|
240
262
|
: UtilsServer.decodeID(value, this.salt);
|
|
263
|
+
case "json":
|
|
264
|
+
return JSON.stringify(value);
|
|
241
265
|
default:
|
|
242
266
|
return value;
|
|
243
267
|
}
|
|
@@ -292,13 +316,41 @@ export default class Inibase {
|
|
|
292
316
|
return null;
|
|
293
317
|
}
|
|
294
318
|
}
|
|
319
|
+
_combineObjectsToArray = (input) => input.reduce((result, current) => {
|
|
320
|
+
for (const [key, value] of Object.entries(current))
|
|
321
|
+
if (!result[key])
|
|
322
|
+
result[key] = [value];
|
|
323
|
+
else
|
|
324
|
+
result[key].push(value);
|
|
325
|
+
return result;
|
|
326
|
+
}, {});
|
|
327
|
+
_CombineData = (_data, prefix) => {
|
|
328
|
+
let RETURN = {};
|
|
329
|
+
if (Utils.isArrayOfObjects(_data))
|
|
330
|
+
RETURN = this._combineObjectsToArray(_data.map((single_data) => this._CombineData(single_data)));
|
|
331
|
+
else
|
|
332
|
+
for (const [key, value] of Object.entries(_data)) {
|
|
333
|
+
if (Utils.isObject(value))
|
|
334
|
+
Object.assign(RETURN, this._CombineData(value, `${key}.`));
|
|
335
|
+
else if (Utils.isArrayOfObjects(value)) {
|
|
336
|
+
Object.assign(RETURN, this._CombineData(this._combineObjectsToArray(value), (prefix ?? "") + key + "."));
|
|
337
|
+
}
|
|
338
|
+
else if (Utils.isArrayOfArrays(value) &&
|
|
339
|
+
value.every(Utils.isArrayOfObjects))
|
|
340
|
+
Object.assign(RETURN, this._CombineData(this._combineObjectsToArray(value.map(this._combineObjectsToArray)), (prefix ?? "") + key + "."));
|
|
341
|
+
else
|
|
342
|
+
RETURN[(prefix ?? "") + key] = File.encode(value);
|
|
343
|
+
}
|
|
344
|
+
return RETURN;
|
|
345
|
+
};
|
|
346
|
+
_addPathToKeys = (obj, path) => {
|
|
347
|
+
const newObject = {};
|
|
348
|
+
for (const key in obj)
|
|
349
|
+
newObject[join(path, key + ".inib")] = obj[key];
|
|
350
|
+
return newObject;
|
|
351
|
+
};
|
|
295
352
|
joinPathesContents(mainPath, data) {
|
|
296
|
-
return
|
|
297
|
-
? Utils.combineObjects(data.map((single_data) => this.joinPathesContents(mainPath, single_data)))
|
|
298
|
-
: Object.fromEntries(Object.entries(Utils.objectToDotNotation(data)).map(([key, value]) => [
|
|
299
|
-
join(mainPath, key + ".inib"),
|
|
300
|
-
File.encode(value, this.salt),
|
|
301
|
-
]));
|
|
353
|
+
return this._addPathToKeys(this._CombineData(data), mainPath);
|
|
302
354
|
}
|
|
303
355
|
async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
|
|
304
356
|
const tablePath = join(this.folder, this.database, tableName);
|
|
@@ -755,7 +807,9 @@ export default class Inibase {
|
|
|
755
807
|
throw this.throwError("NO_DATA");
|
|
756
808
|
RETURN = this.formatData(RETURN, schema);
|
|
757
809
|
const pathesContents = this.joinPathesContents(join(tablePath), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
|
|
758
|
-
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(
|
|
810
|
+
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
|
|
811
|
+
? await File.createWorker("append", [path, content])
|
|
812
|
+
: await File.append(path, content))));
|
|
759
813
|
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
760
814
|
renameList = [];
|
|
761
815
|
if (Config.isCacheEnabled) {
|
|
@@ -809,7 +863,9 @@ export default class Inibase {
|
|
|
809
863
|
});
|
|
810
864
|
try {
|
|
811
865
|
await File.lock(join(tablePath, ".tmp"));
|
|
812
|
-
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(
|
|
866
|
+
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
|
|
867
|
+
? await File.createWorker("replace", [path, content])
|
|
868
|
+
: await File.replace(path, content))));
|
|
813
869
|
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
814
870
|
if (Config.isCacheEnabled)
|
|
815
871
|
await this.clearCache(join(tablePath, ".tmp"));
|
|
@@ -849,7 +905,9 @@ export default class Inibase {
|
|
|
849
905
|
]));
|
|
850
906
|
try {
|
|
851
907
|
await File.lock(join(tablePath, ".tmp"));
|
|
852
|
-
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(
|
|
908
|
+
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
|
|
909
|
+
? await File.createWorker("replace", [path, content])
|
|
910
|
+
: await File.replace(path, content))));
|
|
853
911
|
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
854
912
|
renameList = [];
|
|
855
913
|
if (Config.isCacheEnabled)
|
|
@@ -917,7 +975,12 @@ export default class Inibase {
|
|
|
917
975
|
throw this.throwError("NO_ITEMS", tableName);
|
|
918
976
|
try {
|
|
919
977
|
await File.lock(join(tablePath, ".tmp"));
|
|
920
|
-
await Promise.all(files.map(async (file) => renameList.push(
|
|
978
|
+
await Promise.all(files.map(async (file) => renameList.push(this.isThreadEnabled
|
|
979
|
+
? await File.createWorker("remove", [
|
|
980
|
+
join(tablePath, file),
|
|
981
|
+
where,
|
|
982
|
+
])
|
|
983
|
+
: await File.remove(join(tablePath, file), where))));
|
|
921
984
|
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
922
985
|
if (Config.isCacheEnabled) {
|
|
923
986
|
await this.clearCache(tablePath);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import Inibase from "./index.js";
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
const { _constructor, functionName, arg } = workerData;
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
new Inibase(..._constructor)[functionName](...arg)
|
|
6
|
+
.then((res) => parentPort?.postMessage(res));
|
package/dist/utils.d.ts
CHANGED
|
@@ -167,13 +167,6 @@ export declare const detectFieldType: (input: any, availableTypes: FieldType[])
|
|
|
167
167
|
* @returns A boolean indicating whether the value matches the specified field type(s).
|
|
168
168
|
*/
|
|
169
169
|
export declare const validateFieldType: (value: any, fieldType: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[]) => boolean;
|
|
170
|
-
/**
|
|
171
|
-
* Converts a nested object to dot notation, flattening the object's structure.
|
|
172
|
-
*
|
|
173
|
-
* @param input - The input object to be converted.
|
|
174
|
-
* @returns A flattened object using dot notation for keys.
|
|
175
|
-
*/
|
|
176
|
-
export declare const objectToDotNotation: (input: Record<string, any>) => Record<string, string | number | (string | number)[]>;
|
|
177
170
|
export declare function FormatObjectCriteriaValue(value: string, isParentArray?: boolean): [
|
|
178
171
|
ComparisonOperator,
|
|
179
172
|
string | number | boolean | null | (string | number | null)[]
|
|
@@ -197,7 +190,6 @@ export default class Utils {
|
|
|
197
190
|
static isHTML: (input: any) => boolean;
|
|
198
191
|
static isIP: (input: any) => boolean;
|
|
199
192
|
static validateFieldType: (value: any, fieldType: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[] | undefined) => boolean;
|
|
200
|
-
static objectToDotNotation: (input: Record<string, any>) => Record<string, string | number | (string | number)[]>;
|
|
201
193
|
static isArrayOfNulls: (input: any) => input is null[] | null[][];
|
|
202
194
|
static FormatObjectCriteriaValue: typeof FormatObjectCriteriaValue;
|
|
203
195
|
}
|
package/dist/utils.js
CHANGED
|
@@ -135,6 +135,7 @@ export const isURL = (input) => {
|
|
|
135
135
|
else {
|
|
136
136
|
var pattern = new RegExp("^(https?:\\/\\/)?" + // protocol
|
|
137
137
|
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
|
|
138
|
+
"localhost|" + // OR localhost
|
|
138
139
|
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
|
|
139
140
|
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
|
|
140
141
|
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
|
|
@@ -192,7 +193,7 @@ export const isBoolean = (input) => typeof input === "boolean" ||
|
|
|
192
193
|
*
|
|
193
194
|
* Note: Specifically checks for string length to determine if it matches the defined password length criterion.
|
|
194
195
|
*/
|
|
195
|
-
export const isPassword = (input) => input.length ===
|
|
196
|
+
export const isPassword = (input) => typeof input === "string" && input.length === 97;
|
|
196
197
|
/**
|
|
197
198
|
* Checks if the input can be converted to a valid date.
|
|
198
199
|
*
|
|
@@ -209,6 +210,24 @@ export const isDate = (input) => !isNaN(new Date(input).getTime()) || !isNaN(Dat
|
|
|
209
210
|
export const isValidID = (input) => {
|
|
210
211
|
return typeof input === "string" && input.length === 32;
|
|
211
212
|
};
|
|
213
|
+
/**
|
|
214
|
+
* Checks if a given string is a valid JSON.
|
|
215
|
+
*
|
|
216
|
+
* @param {string} str - The string to be checked.
|
|
217
|
+
* @returns {boolean} Returns true if the string is valid JSON, otherwise false.
|
|
218
|
+
*/
|
|
219
|
+
function isJSON(str) {
|
|
220
|
+
try {
|
|
221
|
+
// Attempt to parse the string as JSON
|
|
222
|
+
JSON.parse(str);
|
|
223
|
+
// If parsing succeeds, return true
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
// If an error occurs during parsing, return false
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
212
231
|
/**
|
|
213
232
|
* Identifies and returns properties that have changed between two objects.
|
|
214
233
|
*
|
|
@@ -256,8 +275,10 @@ export const detectFieldType = (input, availableTypes) => {
|
|
|
256
275
|
return "url";
|
|
257
276
|
else if (availableTypes.includes("password") && isPassword(input))
|
|
258
277
|
return "password";
|
|
259
|
-
else if (availableTypes.includes("
|
|
260
|
-
return "
|
|
278
|
+
else if (availableTypes.includes("json") && isJSON(input))
|
|
279
|
+
return "json";
|
|
280
|
+
else if (availableTypes.includes("json") && isDate(input))
|
|
281
|
+
return "json";
|
|
261
282
|
else if (availableTypes.includes("string") && isString(input))
|
|
262
283
|
return "string";
|
|
263
284
|
else if (availableTypes.includes("ip") && isIP(input))
|
|
@@ -288,7 +309,7 @@ export const validateFieldType = (value, fieldType, fieldChildrenType) => {
|
|
|
288
309
|
case "string":
|
|
289
310
|
return isString(value);
|
|
290
311
|
case "password":
|
|
291
|
-
return isNumber(value) ||
|
|
312
|
+
return isNumber(value) || isPassword(value) || isString(value);
|
|
292
313
|
case "number":
|
|
293
314
|
return isNumber(value);
|
|
294
315
|
case "html":
|
|
@@ -322,47 +343,12 @@ export const validateFieldType = (value, fieldType, fieldChildrenType) => {
|
|
|
322
343
|
return isNumber(value) || isValidID(value);
|
|
323
344
|
case "id":
|
|
324
345
|
return isNumber(value) || isValidID(value);
|
|
346
|
+
case "json":
|
|
347
|
+
return isJSON(value) || Array.isArray(value) || isObject(value);
|
|
325
348
|
default:
|
|
326
349
|
return false;
|
|
327
350
|
}
|
|
328
351
|
};
|
|
329
|
-
/**
|
|
330
|
-
* Converts a nested object to dot notation, flattening the object's structure.
|
|
331
|
-
*
|
|
332
|
-
* @param input - The input object to be converted.
|
|
333
|
-
* @returns A flattened object using dot notation for keys.
|
|
334
|
-
*/
|
|
335
|
-
export const objectToDotNotation = (input) => {
|
|
336
|
-
const result = {};
|
|
337
|
-
const stack = [
|
|
338
|
-
{ obj: input },
|
|
339
|
-
];
|
|
340
|
-
while (stack.length > 0) {
|
|
341
|
-
const { obj, parentKey } = stack.pop();
|
|
342
|
-
for (const key in obj) {
|
|
343
|
-
if (obj.hasOwnProperty(key)) {
|
|
344
|
-
const newKey = parentKey ? `${parentKey}.${key}` : key;
|
|
345
|
-
const value = obj[key];
|
|
346
|
-
const isArray = Array.isArray(value);
|
|
347
|
-
const isStringOrNumberArray = isArray &&
|
|
348
|
-
value.every((item) => typeof item === "string" || typeof item === "number");
|
|
349
|
-
if (isStringOrNumberArray) {
|
|
350
|
-
// If the property is an array of strings or numbers, keep the array as is
|
|
351
|
-
result[newKey] = value;
|
|
352
|
-
}
|
|
353
|
-
else if (isObject(value)) {
|
|
354
|
-
// If the property is an object, push it onto the stack for further processing
|
|
355
|
-
stack.push({ obj: value, parentKey: newKey });
|
|
356
|
-
}
|
|
357
|
-
else {
|
|
358
|
-
// Otherwise, assign the value to the dot notation key
|
|
359
|
-
result[newKey] = value;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return result;
|
|
365
|
-
};
|
|
366
352
|
export function FormatObjectCriteriaValue(value, isParentArray = false) {
|
|
367
353
|
switch (value[0]) {
|
|
368
354
|
case ">":
|
|
@@ -436,7 +422,6 @@ export default class Utils {
|
|
|
436
422
|
static isHTML = isHTML;
|
|
437
423
|
static isIP = isIP;
|
|
438
424
|
static validateFieldType = validateFieldType;
|
|
439
|
-
static objectToDotNotation = objectToDotNotation;
|
|
440
425
|
static isArrayOfNulls = isArrayOfNulls;
|
|
441
426
|
static FormatObjectCriteriaValue = FormatObjectCriteriaValue;
|
|
442
427
|
}
|
package/dist/utils.server.d.ts
CHANGED
|
@@ -45,14 +45,15 @@ export declare const findLastIdNumber: (schema: Schema, secretKeyOrSalt: string
|
|
|
45
45
|
* @param schema - The schema to update, defined as an array of schema objects.
|
|
46
46
|
* @param oldIndex - The starting index for generating new IDs, defaults to 0.
|
|
47
47
|
* @param secretKeyOrSalt - The secret key or salt for encoding IDs, can be a string, number, or Buffer.
|
|
48
|
+
* @param encodeIDs - If true, IDs will be encoded, else they will remain as numbers.
|
|
48
49
|
* @returns The updated schema with encoded IDs.
|
|
49
50
|
*/
|
|
50
|
-
export declare const addIdToSchema: (schema: Schema, oldIndex: number | undefined, secretKeyOrSalt: string | number | Buffer) => (({
|
|
51
|
+
export declare const addIdToSchema: (schema: Schema, oldIndex: number | undefined, secretKeyOrSalt: string | number | Buffer, encodeIDs?: boolean) => (({
|
|
51
52
|
id?: string | number | undefined;
|
|
52
53
|
key: string;
|
|
53
54
|
required?: boolean | undefined;
|
|
54
55
|
} & {
|
|
55
|
-
type: "string" | "number" | "boolean" | "id" | "url" | "html" | "table" | "email" | "date" | "password" | "ip";
|
|
56
|
+
type: "string" | "number" | "boolean" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip";
|
|
56
57
|
children?: undefined;
|
|
57
58
|
}) | ({
|
|
58
59
|
id?: string | number | undefined;
|
|
@@ -67,7 +68,7 @@ export declare const addIdToSchema: (schema: Schema, oldIndex: number | undefine
|
|
|
67
68
|
required?: boolean | undefined;
|
|
68
69
|
} & {
|
|
69
70
|
type: "array";
|
|
70
|
-
children: "string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "date" | "password" | "ip" | Schema | ("string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "date" | "password" | "ip")[];
|
|
71
|
+
children: "string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip" | Schema | ("string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip")[];
|
|
71
72
|
}))[];
|
|
72
73
|
export declare const hashObject: (obj: any) => string;
|
|
73
74
|
export default class UtilsServer {
|
|
@@ -76,12 +77,12 @@ export default class UtilsServer {
|
|
|
76
77
|
static hashPassword: (password: string) => string;
|
|
77
78
|
static comparePassword: (hashedPassword: string, inputPassword: string) => boolean;
|
|
78
79
|
static findLastIdNumber: (schema: Schema, secretKeyOrSalt: string | number | Buffer) => number;
|
|
79
|
-
static addIdToSchema: (schema: Schema, oldIndex: number | undefined, secretKeyOrSalt: string | number | Buffer) => (({
|
|
80
|
+
static addIdToSchema: (schema: Schema, oldIndex: number | undefined, secretKeyOrSalt: string | number | Buffer, encodeIDs?: boolean | undefined) => (({
|
|
80
81
|
id?: string | number | undefined;
|
|
81
82
|
key: string;
|
|
82
83
|
required?: boolean | undefined;
|
|
83
84
|
} & {
|
|
84
|
-
type: "string" | "number" | "boolean" | "id" | "url" | "html" | "table" | "email" | "date" | "password" | "ip";
|
|
85
|
+
type: "string" | "number" | "boolean" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip";
|
|
85
86
|
children?: undefined;
|
|
86
87
|
}) | ({
|
|
87
88
|
id?: string | number | undefined;
|
|
@@ -96,7 +97,7 @@ export default class UtilsServer {
|
|
|
96
97
|
required?: boolean | undefined;
|
|
97
98
|
} & {
|
|
98
99
|
type: "array";
|
|
99
|
-
children: "string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "date" | "password" | "ip" | Schema | ("string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "date" | "password" | "ip")[];
|
|
100
|
+
children: "string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip" | Schema | ("string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip")[];
|
|
100
101
|
}))[];
|
|
101
102
|
static hashObject: (obj: any) => string;
|
|
102
103
|
}
|
package/dist/utils.server.js
CHANGED
|
@@ -87,24 +87,25 @@ export const findLastIdNumber = (schema, secretKeyOrSalt) => {
|
|
|
87
87
|
* @param schema - The schema to update, defined as an array of schema objects.
|
|
88
88
|
* @param oldIndex - The starting index for generating new IDs, defaults to 0.
|
|
89
89
|
* @param secretKeyOrSalt - The secret key or salt for encoding IDs, can be a string, number, or Buffer.
|
|
90
|
+
* @param encodeIDs - If true, IDs will be encoded, else they will remain as numbers.
|
|
90
91
|
* @returns The updated schema with encoded IDs.
|
|
91
92
|
*/
|
|
92
|
-
export const addIdToSchema = (schema, oldIndex = 0, secretKeyOrSalt) => schema.map((field) => {
|
|
93
|
+
export const addIdToSchema = (schema, oldIndex = 0, secretKeyOrSalt, encodeIDs) => schema.map((field) => {
|
|
93
94
|
if (!field.id) {
|
|
94
95
|
oldIndex++;
|
|
95
|
-
field.id = encodeID(oldIndex, secretKeyOrSalt);
|
|
96
|
+
field.id = encodeIDs ? encodeID(oldIndex, secretKeyOrSalt) : oldIndex;
|
|
96
97
|
}
|
|
97
98
|
else {
|
|
98
99
|
if (!isNumber(field.id))
|
|
99
100
|
oldIndex = decodeID(field.id, secretKeyOrSalt);
|
|
100
101
|
else {
|
|
101
102
|
oldIndex = field.id;
|
|
102
|
-
field.id = encodeID(field.id, secretKeyOrSalt);
|
|
103
|
+
field.id = encodeIDs ? encodeID(field.id, secretKeyOrSalt) : field.id;
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
if ((field.type === "array" || field.type === "object") &&
|
|
106
107
|
isArrayOfObjects(field.children)) {
|
|
107
|
-
field.children = addIdToSchema(field.children, oldIndex, secretKeyOrSalt);
|
|
108
|
+
field.children = addIdToSchema(field.children, oldIndex, secretKeyOrSalt, encodeIDs);
|
|
108
109
|
oldIndex += field.children.length;
|
|
109
110
|
}
|
|
110
111
|
return field;
|
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.39",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Karim Amahtil",
|
|
6
6
|
"email": "karim.amahtil@gmail.com"
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./dist/index.js",
|
|
12
|
+
"./thread": "./dist/index.thread.js",
|
|
12
13
|
"./file": "./dist/file.js",
|
|
14
|
+
"./file.thread": "./dist/file.thread.js",
|
|
13
15
|
"./config": "./dist/config.js",
|
|
14
16
|
"./utils": "./dist/utils.js",
|
|
15
17
|
"./utils.server": "./dist/utils.server.js"
|
|
@@ -51,9 +53,15 @@
|
|
|
51
53
|
"types": "./dist",
|
|
52
54
|
"typesVersions": {
|
|
53
55
|
"*": {
|
|
56
|
+
"thread": [
|
|
57
|
+
"./dist/index.thread.d.ts"
|
|
58
|
+
],
|
|
54
59
|
"file": [
|
|
55
60
|
"./dist/file.d.ts"
|
|
56
61
|
],
|
|
62
|
+
"file.thread": [
|
|
63
|
+
"./dist/file.thread.d.ts"
|
|
64
|
+
],
|
|
57
65
|
"utils": [
|
|
58
66
|
"./dist/utils.d.ts"
|
|
59
67
|
],
|
package/dist/Archive.zip
DELETED
|
Binary file
|