inibase 1.0.0-rc.30 → 1.0.0-rc.32
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/dist/file.d.ts +4 -0
- package/dist/file.js +39 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +134 -146
- package/package.json +1 -1
package/dist/file.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
import { ComparisonOperator, FieldType } from "./index.js";
|
|
3
|
+
export declare const lock: (folderPath: string) => Promise<void>;
|
|
4
|
+
export declare const unlock: (folderPath: string) => Promise<void>;
|
|
3
5
|
export declare const write: (filePath: string, data: any, disableCompression?: boolean) => Promise<void>;
|
|
4
6
|
export declare const read: (filePath: string, disableCompression?: boolean) => Promise<string>;
|
|
5
7
|
/**
|
|
@@ -168,4 +170,6 @@ export default class File {
|
|
|
168
170
|
static count: (filePath: string) => Promise<number>;
|
|
169
171
|
static write: (filePath: string, data: any, disableCompression?: boolean) => Promise<void>;
|
|
170
172
|
static read: (filePath: string, disableCompression?: boolean) => Promise<string>;
|
|
173
|
+
static lock: (folderPath: string) => Promise<void>;
|
|
174
|
+
static unlock: (folderPath: string) => Promise<void>;
|
|
171
175
|
}
|
package/dist/file.js
CHANGED
|
@@ -1,16 +1,39 @@
|
|
|
1
|
-
import { open, access, writeFile, readFile, constants as fsConstants, } from "node:fs/promises";
|
|
1
|
+
import { open, access, writeFile, readFile, constants as fsConstants, unlink, } from "node:fs/promises";
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
3
|
import { Transform } from "node:stream";
|
|
4
4
|
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
|
+
import { join } from "node:path";
|
|
7
8
|
import { detectFieldType, isArrayOfArrays, isNumber, isObject, } from "./utils.js";
|
|
8
9
|
import { encodeID, comparePassword } from "./utils.server.js";
|
|
9
10
|
import Config from "./config.js";
|
|
10
11
|
const gzip = promisify(gzipAsync);
|
|
11
12
|
const gunzip = promisify(gunzipAsync);
|
|
13
|
+
export const lock = async (folderPath) => {
|
|
14
|
+
let lockFile, lockFilePath = join(folderPath, "locked.inib");
|
|
15
|
+
try {
|
|
16
|
+
lockFile = await open(lockFilePath, "wx");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
catch ({ message }) {
|
|
20
|
+
if (message.split(":")[0] === "EEXIST")
|
|
21
|
+
return await new Promise((resolve, reject) => setTimeout(() => resolve(lock(folderPath)), 13));
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
await lockFile?.close();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export const unlock = async (folderPath) => {
|
|
28
|
+
try {
|
|
29
|
+
await unlink(join(folderPath, "locked.inib"));
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
};
|
|
12
33
|
export const write = async (filePath, data, disableCompression = false) => {
|
|
13
|
-
await writeFile(filePath, Config.isCompressionEnabled && !disableCompression
|
|
34
|
+
await writeFile(filePath, Config.isCompressionEnabled && !disableCompression
|
|
35
|
+
? await gzip(String(data))
|
|
36
|
+
: String(data));
|
|
14
37
|
};
|
|
15
38
|
export const read = async (filePath, disableCompression = false) => {
|
|
16
39
|
return Config.isCompressionEnabled && !disableCompression
|
|
@@ -30,12 +53,16 @@ const _pipeline = async (rl, writeStream, transform) => {
|
|
|
30
53
|
* @returns A readline.Interface instance configured with the provided file stream.
|
|
31
54
|
*/
|
|
32
55
|
const readLineInternface = (fileHandle) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
const [major, minor, patch] = process.versions.node.split(".").map(Number);
|
|
57
|
+
return major > 18 ||
|
|
58
|
+
(major === 18 && minor >= 11 && !Config.isCompressionEnabled)
|
|
59
|
+
? fileHandle.readLines()
|
|
60
|
+
: createInterface({
|
|
61
|
+
input: Config.isCompressionEnabled
|
|
62
|
+
? fileHandle.createReadStream().pipe(createGunzip())
|
|
63
|
+
: fileHandle.createReadStream(),
|
|
64
|
+
crlfDelay: Infinity,
|
|
65
|
+
});
|
|
39
66
|
};
|
|
40
67
|
/**
|
|
41
68
|
* Checks if a file or directory exists at the specified path.
|
|
@@ -190,7 +217,6 @@ const decodeHelper = (value, fieldType, fieldChildrenType, secretKey) => {
|
|
|
190
217
|
if (Array.isArray(value) && fieldType !== "array")
|
|
191
218
|
return value.map((v) => decodeHelper(v, fieldType, fieldChildrenType, secretKey));
|
|
192
219
|
switch (fieldType) {
|
|
193
|
-
case "table":
|
|
194
220
|
case "number":
|
|
195
221
|
return isNumber(value) ? Number(value) : null;
|
|
196
222
|
case "boolean":
|
|
@@ -204,6 +230,7 @@ const decodeHelper = (value, fieldType, fieldChildrenType, secretKey) => {
|
|
|
204
230
|
? detectFieldType(v, fieldChildrenType)
|
|
205
231
|
: fieldChildrenType, undefined, secretKey))
|
|
206
232
|
: value;
|
|
233
|
+
case "table":
|
|
207
234
|
case "id":
|
|
208
235
|
return isNumber(value) && secretKey
|
|
209
236
|
? encodeID(value, secretKey)
|
|
@@ -227,8 +254,8 @@ export const decode = (input, fieldType, fieldChildrenType, secretKey) => {
|
|
|
227
254
|
return null;
|
|
228
255
|
if (input === null || input === "")
|
|
229
256
|
return null;
|
|
257
|
+
// Detect the fieldType based on the input and the provided array of possible types.
|
|
230
258
|
if (Array.isArray(fieldType))
|
|
231
|
-
// Detect the fieldType based on the input and the provided array of possible types.
|
|
232
259
|
fieldType = detectFieldType(String(input), fieldType);
|
|
233
260
|
// Decode the input using the decodeHelper function.
|
|
234
261
|
return decodeHelper(typeof input === "string"
|
|
@@ -761,4 +788,6 @@ export default class File {
|
|
|
761
788
|
static count = count;
|
|
762
789
|
static write = write;
|
|
763
790
|
static read = read;
|
|
791
|
+
static lock = lock;
|
|
792
|
+
static unlock = unlock;
|
|
764
793
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ export default class Inibase {
|
|
|
86
86
|
put(tableName: string, data: Data, where: number | string | (number | string)[] | Criteria | undefined, options: Options | undefined, returnPostedData: true): Promise<Data | null>;
|
|
87
87
|
put(tableName: string, data: Data[], where: number | string | (number | string)[] | Criteria | undefined, options: Options | undefined, returnPostedData: true): Promise<Data[] | null>;
|
|
88
88
|
delete(tableName: string, where?: number | string, _id?: string | string[]): Promise<string | null>;
|
|
89
|
-
delete(tableName: string, where?: (number | string)[], _id?: string | string[]): Promise<string[] | null>;
|
|
89
|
+
delete(tableName: string, where?: (number | string)[] | Criteria, _id?: string | string[]): Promise<string[] | null>;
|
|
90
90
|
sum(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
|
91
91
|
sum(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
|
|
92
92
|
max(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { unlink, rename, mkdir, readdir
|
|
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
4
|
import { cpus } from "node:os";
|
|
@@ -66,12 +66,12 @@ export default class Inibase {
|
|
|
66
66
|
// remove id from schema
|
|
67
67
|
schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
|
|
68
68
|
schema = UtilsServer.addIdToSchema(schema, UtilsServer.findLastIdNumber(schema, this.salt), this.salt);
|
|
69
|
-
const
|
|
70
|
-
if (!(await File.isExists(
|
|
71
|
-
await mkdir(
|
|
72
|
-
if (!(await File.isExists(join(
|
|
73
|
-
await mkdir(join(
|
|
74
|
-
if (await File.isExists(
|
|
69
|
+
const tablePath = join(this.folder, this.database, tableName), tableSchemaPath = join(tablePath, "schema.json");
|
|
70
|
+
if (!(await File.isExists(tablePath)))
|
|
71
|
+
await mkdir(tablePath, { recursive: true });
|
|
72
|
+
if (!(await File.isExists(join(tablePath, ".tmp"))))
|
|
73
|
+
await mkdir(join(tablePath, ".tmp"));
|
|
74
|
+
if (await File.isExists(tableSchemaPath)) {
|
|
75
75
|
// update columns files names based on field id
|
|
76
76
|
const schemaToIdsPath = (schema, prefix = "") => {
|
|
77
77
|
let RETURN = {};
|
|
@@ -88,17 +88,17 @@ export default class Inibase {
|
|
|
88
88
|
}, replaceOldPathes = Utils.findChangedProperties(schemaToIdsPath((await this.getTableSchema(tableName)) ?? []), schemaToIdsPath(schema));
|
|
89
89
|
if (replaceOldPathes)
|
|
90
90
|
await Promise.all(Object.entries(replaceOldPathes).map(async ([oldPath, newPath]) => {
|
|
91
|
-
if (await File.isExists(join(
|
|
92
|
-
await rename(join(
|
|
91
|
+
if (await File.isExists(join(tablePath, oldPath)))
|
|
92
|
+
await rename(join(tablePath, oldPath), join(tablePath, newPath));
|
|
93
93
|
}));
|
|
94
94
|
}
|
|
95
|
-
await File.write(join(
|
|
95
|
+
await File.write(join(tablePath, "schema.json"), JSON.stringify(decodeIdFromSchema(schema), null, 2), true);
|
|
96
96
|
}
|
|
97
97
|
async getTableSchema(tableName) {
|
|
98
|
-
const
|
|
99
|
-
if (!(await File.isExists(
|
|
98
|
+
const tableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
|
|
99
|
+
if (!(await File.isExists(tableSchemaPath)))
|
|
100
100
|
return undefined;
|
|
101
|
-
const schemaFile = await File.read(
|
|
101
|
+
const schemaFile = await File.read(tableSchemaPath, true);
|
|
102
102
|
if (!schemaFile)
|
|
103
103
|
return undefined;
|
|
104
104
|
const schema = JSON.parse(schemaFile), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
|
|
@@ -301,7 +301,7 @@ export default class Inibase {
|
|
|
301
301
|
]));
|
|
302
302
|
}
|
|
303
303
|
async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
|
|
304
|
-
const
|
|
304
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
305
305
|
let RETURN = {};
|
|
306
306
|
await Promise.all(schema.map(async (field) => {
|
|
307
307
|
if ((field.type === "array" ||
|
|
@@ -392,7 +392,7 @@ export default class Inibase {
|
|
|
392
392
|
options.columns = options.columns
|
|
393
393
|
.filter((column) => column.includes(`${field.key}.`))
|
|
394
394
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
395
|
-
const items = await File.get(join(
|
|
395
|
+
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
|
|
396
396
|
if (items)
|
|
397
397
|
await Promise.all(Object.entries(items).map(async ([index, item]) => {
|
|
398
398
|
if (!RETURN[index])
|
|
@@ -402,8 +402,8 @@ export default class Inibase {
|
|
|
402
402
|
: this.getDefaultValue(field);
|
|
403
403
|
}));
|
|
404
404
|
}
|
|
405
|
-
else if (await File.isExists(join(
|
|
406
|
-
const items = await File.get(join(
|
|
405
|
+
else if (await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib"))) {
|
|
406
|
+
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
407
407
|
if (items)
|
|
408
408
|
for (const [index, item] of Object.entries(items)) {
|
|
409
409
|
if (!RETURN[index])
|
|
@@ -428,13 +428,13 @@ export default class Inibase {
|
|
|
428
428
|
}
|
|
429
429
|
else if (field.type === "table") {
|
|
430
430
|
if ((await File.isExists(join(this.folder, this.database, field.key))) &&
|
|
431
|
-
(await File.isExists(join(
|
|
431
|
+
(await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib")))) {
|
|
432
432
|
if (options.columns)
|
|
433
433
|
options.columns = options.columns
|
|
434
434
|
.filter((column) => column.includes(`${field.key}.`) &&
|
|
435
435
|
!column.includes(`${field.key}.`))
|
|
436
436
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
437
|
-
const items = await File.get(join(
|
|
437
|
+
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
|
|
438
438
|
if (items)
|
|
439
439
|
for await (const [index, item] of Object.entries(items)) {
|
|
440
440
|
if (!RETURN[index])
|
|
@@ -445,8 +445,8 @@ export default class Inibase {
|
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
|
-
else if (await File.isExists(join(
|
|
449
|
-
const items = await File.get(join(
|
|
448
|
+
else if (await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib"))) {
|
|
449
|
+
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
450
450
|
if (items)
|
|
451
451
|
for (const [index, item] of Object.entries(items)) {
|
|
452
452
|
if (!RETURN[index])
|
|
@@ -463,6 +463,7 @@ export default class Inibase {
|
|
|
463
463
|
return RETURN;
|
|
464
464
|
}
|
|
465
465
|
async applyCriteria(tableName, schema, options, criteria, allTrue) {
|
|
466
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
466
467
|
let RETURN = {}, RETURN_LineNumbers = null;
|
|
467
468
|
if (!criteria)
|
|
468
469
|
return [null, null];
|
|
@@ -545,7 +546,7 @@ export default class Inibase {
|
|
|
545
546
|
searchOperator = "=";
|
|
546
547
|
searchComparedAtValue = value;
|
|
547
548
|
}
|
|
548
|
-
const [searchResult, totalLines, linesNumbers] = await File.search(join(
|
|
549
|
+
const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, key + ".inib"), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
|
|
549
550
|
if (searchResult) {
|
|
550
551
|
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
|
|
551
552
|
id,
|
|
@@ -592,6 +593,7 @@ export default class Inibase {
|
|
|
592
593
|
page: 1,
|
|
593
594
|
perPage: 15,
|
|
594
595
|
}, onlyOne, onlyLinesNumbers, tableSchema) {
|
|
596
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
595
597
|
// Ensure options.columns is an array
|
|
596
598
|
if (options.columns) {
|
|
597
599
|
options.columns = Array.isArray(options.columns)
|
|
@@ -607,8 +609,7 @@ export default class Inibase {
|
|
|
607
609
|
let schema = tableSchema ?? (await this.getTableSchema(tableName));
|
|
608
610
|
if (!schema)
|
|
609
611
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
610
|
-
|
|
611
|
-
if (!(await File.isExists(idFilePath)))
|
|
612
|
+
if (!(await File.isExists(join(tablePath, "id.inib"))))
|
|
612
613
|
return null;
|
|
613
614
|
if (options.columns && options.columns.length)
|
|
614
615
|
schema = this._filterSchemaByColumns(schema, options.columns);
|
|
@@ -618,12 +619,15 @@ export default class Inibase {
|
|
|
618
619
|
index +
|
|
619
620
|
1), options));
|
|
620
621
|
if (Config.isCacheEnabled &&
|
|
621
|
-
(await File.isExists(join(
|
|
622
|
-
this.totalItems[tableName + "-*"] = Number(await File.read(join(
|
|
622
|
+
(await File.isExists(join(tablePath, ".tmp", "pagination.inib"))))
|
|
623
|
+
this.totalItems[tableName + "-*"] = Number((await File.read(join(tablePath, ".tmp", "pagination.inib"), true)).split(",")[1]);
|
|
623
624
|
else {
|
|
624
|
-
|
|
625
|
+
let [lastId, totalItems] = await File.get(join(tablePath, "id.inib"), -1, "number", undefined, this.salt, true);
|
|
626
|
+
if (lastId)
|
|
627
|
+
lastId = Number(Object.keys(lastId)[0] ?? 0);
|
|
628
|
+
this.totalItems[tableName + "-*"] = totalItems;
|
|
625
629
|
if (Config.isCacheEnabled)
|
|
626
|
-
await File.write(join(
|
|
630
|
+
await File.write(join(tablePath, ".tmp", "pagination.inib"), `${lastId},${totalItems}`, true);
|
|
627
631
|
}
|
|
628
632
|
}
|
|
629
633
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
@@ -644,7 +648,7 @@ export default class Inibase {
|
|
|
644
648
|
let Ids = where;
|
|
645
649
|
if (!Array.isArray(Ids))
|
|
646
650
|
Ids = [Ids];
|
|
647
|
-
const [lineNumbers, countItems] = await File.search(
|
|
651
|
+
const [lineNumbers, countItems] = await File.search(join(tablePath, "id.inib"), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[tableName + "-*"], this.salt);
|
|
648
652
|
if (!lineNumbers)
|
|
649
653
|
throw this.throwError("INVALID_ID", where);
|
|
650
654
|
if (onlyLinesNumbers)
|
|
@@ -652,16 +656,8 @@ export default class Inibase {
|
|
|
652
656
|
? Object.keys(lineNumbers).map(Number)
|
|
653
657
|
: null;
|
|
654
658
|
RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
|
|
655
|
-
if (!this.totalItems[tableName + "-*"])
|
|
656
|
-
|
|
657
|
-
(await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
|
|
658
|
-
this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
|
|
659
|
-
else {
|
|
660
|
-
this.totalItems[tableName + "-*"] = await File.count(idFilePath);
|
|
661
|
-
if (Config.isCacheEnabled)
|
|
662
|
-
await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
659
|
+
if (!this.totalItems[tableName + "-*"])
|
|
660
|
+
this.totalItems[tableName + "-*"] = countItems;
|
|
665
661
|
if (RETURN && RETURN.length && !Array.isArray(where))
|
|
666
662
|
RETURN = RETURN[0];
|
|
667
663
|
}
|
|
@@ -669,10 +665,12 @@ export default class Inibase {
|
|
|
669
665
|
let cachedFilePath = "";
|
|
670
666
|
// Criteria
|
|
671
667
|
if (Config.isCacheEnabled)
|
|
672
|
-
cachedFilePath = join(
|
|
668
|
+
cachedFilePath = join(tablePath, ".tmp", `${UtilsServer.hashObject(where)}.inib`);
|
|
673
669
|
if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
|
|
674
670
|
const cachedItems = (await File.read(cachedFilePath, true)).split(",");
|
|
675
671
|
this.totalItems[tableName + "-*"] = cachedItems.length;
|
|
672
|
+
if (onlyLinesNumbers)
|
|
673
|
+
return cachedItems.map(Number);
|
|
676
674
|
return this.get(tableName, cachedItems
|
|
677
675
|
.slice((options.page - 1) * options.perPage, options.page * options.perPage)
|
|
678
676
|
.map(Number), options, undefined, undefined, schema);
|
|
@@ -712,37 +710,26 @@ export default class Inibase {
|
|
|
712
710
|
page: 1,
|
|
713
711
|
perPage: 15,
|
|
714
712
|
};
|
|
713
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
714
|
+
await File.lock(join(tablePath, ".tmp"));
|
|
715
715
|
if (!returnPostedData)
|
|
716
716
|
returnPostedData = false;
|
|
717
717
|
const schema = await this.getTableSchema(tableName);
|
|
718
718
|
let RETURN;
|
|
719
719
|
if (!schema)
|
|
720
720
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
return await new Promise((resolve, reject) => setTimeout(() => resolve(this.post(tableName, data, options, returnPostedData)), 13));
|
|
729
|
-
}
|
|
730
|
-
finally {
|
|
731
|
-
await testFileHandle?.close();
|
|
732
|
-
}
|
|
733
|
-
let lastId = 0, totalItems = 0, lastIdObj;
|
|
734
|
-
if (await File.isExists(idFilePath)) {
|
|
735
|
-
if (await File.isExists(join(cashFolderPath, "lastId.inib"))) {
|
|
736
|
-
lastId = Number(await File.read(join(cashFolderPath, "lastId.inib"), true));
|
|
737
|
-
if (await File.isExists(join(cashFolderPath, "totalItems.inib")))
|
|
738
|
-
totalItems = Number(await File.read(join(cashFolderPath, "totalItems.inib"), true));
|
|
739
|
-
else
|
|
740
|
-
totalItems = await File.count(join(this.folder, this.database, tableName, "id.inib"));
|
|
741
|
-
}
|
|
721
|
+
let lastId = 0, totalItems = 0, renameList = [];
|
|
722
|
+
if (await File.isExists(join(tablePath, "id.inib"))) {
|
|
723
|
+
if (Config.isCacheEnabled &&
|
|
724
|
+
(await File.isExists(join(tablePath, ".tmp", "pagination.inib"))))
|
|
725
|
+
[lastId, totalItems] = (await File.read(join(tablePath, ".tmp", "pagination.inib"), true))
|
|
726
|
+
.split(",")
|
|
727
|
+
.map(Number);
|
|
742
728
|
else {
|
|
743
|
-
|
|
729
|
+
let lastIdObj;
|
|
730
|
+
[lastIdObj, totalItems] = await File.get(join(tablePath, "id.inib"), -1, "number", undefined, this.salt, true);
|
|
744
731
|
if (lastIdObj)
|
|
745
|
-
lastId = Number(Object.
|
|
732
|
+
lastId = Number(Object.keys(lastIdObj)[0] ?? 0);
|
|
746
733
|
}
|
|
747
734
|
}
|
|
748
735
|
if (Utils.isArrayOfObjects(data))
|
|
@@ -759,29 +746,39 @@ export default class Inibase {
|
|
|
759
746
|
}))(data);
|
|
760
747
|
if (!RETURN)
|
|
761
748
|
throw this.throwError("NO_DATA");
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
749
|
+
try {
|
|
750
|
+
RETURN = this.formatData(RETURN, schema);
|
|
751
|
+
const pathesContents = this.joinPathesContents(join(tablePath), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
|
|
752
|
+
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content))));
|
|
753
|
+
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
754
|
+
renameList = [];
|
|
755
|
+
if (Config.isCacheEnabled)
|
|
756
|
+
await File.write(join(tablePath, ".tmp", "pagination.inib"), `${lastId},${totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)}`, true);
|
|
757
|
+
if (returnPostedData)
|
|
758
|
+
return this.get(tableName, Utils.isArrayOfObjects(RETURN)
|
|
759
|
+
? RETURN.map((data) => Number(data.id))
|
|
760
|
+
: RETURN.id, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
|
|
761
|
+
undefined, schema);
|
|
762
|
+
}
|
|
763
|
+
finally {
|
|
764
|
+
await File.unlock(join(tablePath, ".tmp"));
|
|
765
|
+
if (renameList.length)
|
|
766
|
+
await Promise.all(renameList.map(async ([tempPath, _]) => (await File.isExists(tempPath)) ? unlink(tempPath) : undefined));
|
|
767
|
+
}
|
|
773
768
|
}
|
|
774
769
|
async put(tableName, data, where, options = {
|
|
775
770
|
page: 1,
|
|
776
771
|
perPage: 15,
|
|
777
772
|
}, returnPostedData) {
|
|
773
|
+
let renameList = [];
|
|
774
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
778
775
|
const schema = await this.getTableSchema(tableName);
|
|
779
776
|
if (!schema)
|
|
780
777
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
781
|
-
|
|
782
|
-
if (!(await File.isExists(idFilePath)))
|
|
778
|
+
if (!(await File.isExists(join(tablePath, "id.inib"))))
|
|
783
779
|
throw this.throwError("NO_ITEMS", tableName);
|
|
784
780
|
data = this.formatData(data, schema, true);
|
|
781
|
+
await File.lock(join(tablePath, ".tmp"));
|
|
785
782
|
if (!where) {
|
|
786
783
|
if (Utils.isArrayOfObjects(data)) {
|
|
787
784
|
if (!data.every((item) => item.hasOwnProperty("id") && Utils.isValidID(item.id)))
|
|
@@ -796,7 +793,7 @@ export default class Inibase {
|
|
|
796
793
|
return this.put(tableName, data, UtilsServer.decodeID(data.id, this.salt));
|
|
797
794
|
}
|
|
798
795
|
else {
|
|
799
|
-
const pathesContents = this.joinPathesContents(join(
|
|
796
|
+
const pathesContents = this.joinPathesContents(join(tablePath), Utils.isArrayOfObjects(data)
|
|
800
797
|
? data.map((item) => ({
|
|
801
798
|
...(({ id, ...restOfData }) => restOfData)(item),
|
|
802
799
|
updatedAt: Date.now(),
|
|
@@ -805,26 +802,21 @@ export default class Inibase {
|
|
|
805
802
|
...(({ id, ...restOfData }) => restOfData)(data),
|
|
806
803
|
updatedAt: Date.now(),
|
|
807
804
|
});
|
|
808
|
-
let testFileHandle;
|
|
809
805
|
try {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
806
|
+
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
|
|
807
|
+
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
808
|
+
if (Config.isCacheEnabled)
|
|
809
|
+
await Promise.all((await readdir(join(tablePath, ".tmp")))
|
|
810
|
+
?.filter((fileName) => !["pagination.inib", "locked.inib"].includes(fileName))
|
|
811
|
+
.map(async (file) => unlink(join(tablePath, ".tmp", file))));
|
|
812
|
+
if (returnPostedData)
|
|
813
|
+
return this.get(tableName, where, options, undefined, undefined, schema);
|
|
815
814
|
}
|
|
816
815
|
finally {
|
|
817
|
-
|
|
816
|
+
if (renameList.length)
|
|
817
|
+
await Promise.all(renameList.map(async ([tempPath, _]) => (await File.isExists(tempPath)) ? unlink(tempPath) : undefined));
|
|
818
|
+
await File.unlock(join(tablePath, ".tmp"));
|
|
818
819
|
}
|
|
819
|
-
const renameList = await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => File.replace(path, content)));
|
|
820
|
-
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
821
|
-
if (Config.isCacheEnabled) {
|
|
822
|
-
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
823
|
-
if (cacheFiles.length)
|
|
824
|
-
await Promise.all(cacheFiles.map(async (file) => unlink(join(this.folder, this.database, tableName, ".tmp", file))));
|
|
825
|
-
}
|
|
826
|
-
if (returnPostedData)
|
|
827
|
-
return this.get(tableName, where, options, undefined, undefined, schema);
|
|
828
820
|
}
|
|
829
821
|
}
|
|
830
822
|
else if ((Array.isArray(where) &&
|
|
@@ -839,7 +831,7 @@ export default class Inibase {
|
|
|
839
831
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
840
832
|
Utils.isNumber(where)) {
|
|
841
833
|
// "where" in this case, is the line(s) number(s) and not id(s)
|
|
842
|
-
const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(join(
|
|
834
|
+
const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(join(tablePath), Utils.isArrayOfObjects(data)
|
|
843
835
|
? data.map((item) => ({
|
|
844
836
|
...item,
|
|
845
837
|
updatedAt: Date.now(),
|
|
@@ -851,26 +843,22 @@ export default class Inibase {
|
|
|
851
843
|
[lineNum]: Array.isArray(content) ? content[index] : content,
|
|
852
844
|
}), {}),
|
|
853
845
|
]));
|
|
854
|
-
let testFileHandle;
|
|
855
846
|
try {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (
|
|
860
|
-
|
|
847
|
+
await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
|
|
848
|
+
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
849
|
+
renameList = [];
|
|
850
|
+
if (Config.isCacheEnabled)
|
|
851
|
+
await Promise.all((await readdir(join(tablePath, ".tmp")))
|
|
852
|
+
?.filter((fileName) => !["pagination.inib", "locked.inib"].includes(fileName))
|
|
853
|
+
.map(async (file) => unlink(join(tablePath, ".tmp", file))));
|
|
854
|
+
if (returnPostedData)
|
|
855
|
+
return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
|
|
861
856
|
}
|
|
862
857
|
finally {
|
|
863
|
-
|
|
858
|
+
if (renameList.length)
|
|
859
|
+
await Promise.all(renameList.map(async ([tempPath, _]) => (await File.isExists(tempPath)) ? unlink(tempPath) : undefined));
|
|
860
|
+
await File.unlock(join(tablePath, ".tmp"));
|
|
864
861
|
}
|
|
865
|
-
const renameList = await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => File.replace(path, content)));
|
|
866
|
-
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
867
|
-
if (Config.isCacheEnabled) {
|
|
868
|
-
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
869
|
-
if (cacheFiles.length)
|
|
870
|
-
await Promise.all(cacheFiles.map(async (file) => unlink(join(this.folder, this.database, tableName, ".tmp", file))));
|
|
871
|
-
}
|
|
872
|
-
if (returnPostedData)
|
|
873
|
-
return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
|
|
874
862
|
}
|
|
875
863
|
}
|
|
876
864
|
else if (Utils.isObject(where)) {
|
|
@@ -883,21 +871,22 @@ export default class Inibase {
|
|
|
883
871
|
throw this.throwError("INVALID_PARAMETERS", tableName);
|
|
884
872
|
}
|
|
885
873
|
async delete(tableName, where, _id) {
|
|
874
|
+
let renameList = [];
|
|
875
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
876
|
+
await File.lock(join(tablePath, ".tmp"));
|
|
886
877
|
const schema = await this.getTableSchema(tableName);
|
|
887
878
|
if (!schema)
|
|
888
879
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
889
|
-
|
|
890
|
-
if (!(await File.isExists(idFilePath)))
|
|
880
|
+
if (!(await File.isExists(join(tablePath, "id.inib"))))
|
|
891
881
|
throw this.throwError("NO_ITEMS", tableName);
|
|
892
882
|
if (!where) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
if (Config.isCacheEnabled)
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
}
|
|
883
|
+
await Promise.all((await readdir(join(tablePath)))
|
|
884
|
+
?.filter((fileName) => fileName.endsWith(".inib"))
|
|
885
|
+
.map(async (file) => unlink(join(tablePath, file))));
|
|
886
|
+
if (Config.isCacheEnabled)
|
|
887
|
+
await Promise.all((await readdir(join(tablePath, ".tmp")))
|
|
888
|
+
?.filter((fileName) => !["pagination.inib", "locked.inib"].includes(fileName))
|
|
889
|
+
.map(async (file) => unlink(join(tablePath, ".tmp", file))));
|
|
901
890
|
return "*";
|
|
902
891
|
}
|
|
903
892
|
else if ((Array.isArray(where) &&
|
|
@@ -914,34 +903,33 @@ export default class Inibase {
|
|
|
914
903
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
915
904
|
Utils.isNumber(where)) {
|
|
916
905
|
// "where" in this case, is the line(s) number(s) and not id(s)
|
|
917
|
-
const files = (await readdir(join(
|
|
906
|
+
const files = (await readdir(join(tablePath)))?.filter((fileName) => fileName.endsWith(".inib"));
|
|
918
907
|
if (files.length) {
|
|
919
908
|
if (!_id)
|
|
920
|
-
_id = Object.entries((await File.get(join(
|
|
909
|
+
_id = Object.entries((await File.get(join(tablePath, "id.inib"), where, "number", undefined, this.salt)) ?? {}).map(([_key, id]) => UtilsServer.encodeID(Number(id), this.salt));
|
|
921
910
|
if (!_id.length)
|
|
922
911
|
throw this.throwError("NO_ITEMS", tableName);
|
|
923
|
-
let testFileHandle;
|
|
924
912
|
try {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
913
|
+
await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
|
|
914
|
+
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
915
|
+
if (Config.isCacheEnabled) {
|
|
916
|
+
await Promise.all((await readdir(join(tablePath, ".tmp")))
|
|
917
|
+
?.filter((fileName) => !["pagination.inib", "locked.inib"].includes(fileName))
|
|
918
|
+
.map(async (file) => unlink(join(tablePath, ".tmp", file))));
|
|
919
|
+
if (await File.isExists(join(tablePath, ".tmp", "pagination.inib"))) {
|
|
920
|
+
let [lastId, totalItems] = (await File.read(join(tablePath, ".tmp", "pagination.inib"), true))
|
|
921
|
+
.split(",")
|
|
922
|
+
.map(Number);
|
|
923
|
+
await File.write(join(tablePath, ".tmp", "pagination.inib"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
930
927
|
}
|
|
931
928
|
finally {
|
|
932
|
-
|
|
929
|
+
if (renameList.length)
|
|
930
|
+
await Promise.all(renameList.map(async ([tempPath, _]) => (await File.isExists(tempPath)) ? unlink(tempPath) : undefined));
|
|
931
|
+
await File.unlock(join(tablePath, ".tmp"));
|
|
933
932
|
}
|
|
934
|
-
const renameList = await Promise.all(files.map(async (file) => File.remove(join(this.folder, this.database, tableName, file), where)));
|
|
935
|
-
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
936
|
-
if (Config.isCacheEnabled) {
|
|
937
|
-
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
938
|
-
if (cacheFiles.length)
|
|
939
|
-
await Promise.all(cacheFiles.map(async (file) => unlink(join(this.folder, this.database, tableName, ".tmp", file))));
|
|
940
|
-
await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(((await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib")))
|
|
941
|
-
? Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true))
|
|
942
|
-
: await File.count(join(this.folder, this.database, tableName, "id.inib"))) - (Array.isArray(where) ? where.length : 1)), true);
|
|
943
|
-
}
|
|
944
|
-
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
945
933
|
}
|
|
946
934
|
}
|
|
947
935
|
}
|
|
@@ -957,10 +945,10 @@ export default class Inibase {
|
|
|
957
945
|
}
|
|
958
946
|
async sum(tableName, columns, where) {
|
|
959
947
|
let RETURN = {};
|
|
960
|
-
const schema = await this.getTableSchema(tableName);
|
|
948
|
+
const tablePath = join(this.folder, this.database, tableName), schema = await this.getTableSchema(tableName);
|
|
961
949
|
if (!schema)
|
|
962
950
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
963
|
-
if (!(await File.isExists(join(
|
|
951
|
+
if (!(await File.isExists(join(tablePath, "id.inib"))))
|
|
964
952
|
throw this.throwError("NO_ITEMS", tableName);
|
|
965
953
|
if (!Array.isArray(columns))
|
|
966
954
|
columns = [columns];
|
|
@@ -981,10 +969,10 @@ export default class Inibase {
|
|
|
981
969
|
}
|
|
982
970
|
async max(tableName, columns, where) {
|
|
983
971
|
let RETURN = {};
|
|
984
|
-
const schema = await this.getTableSchema(tableName);
|
|
972
|
+
const tablePath = join(this.folder, this.database, tableName), schema = await this.getTableSchema(tableName);
|
|
985
973
|
if (!schema)
|
|
986
974
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
987
|
-
if (!(await File.isExists(join(
|
|
975
|
+
if (!(await File.isExists(join(tablePath, "id.inib"))))
|
|
988
976
|
throw this.throwError("NO_ITEMS", tableName);
|
|
989
977
|
if (!Array.isArray(columns))
|
|
990
978
|
columns = [columns];
|
|
@@ -1005,10 +993,10 @@ export default class Inibase {
|
|
|
1005
993
|
}
|
|
1006
994
|
async min(tableName, columns, where) {
|
|
1007
995
|
let RETURN = {};
|
|
1008
|
-
const schema = await this.getTableSchema(tableName);
|
|
996
|
+
const tablePath = join(this.folder, this.database, tableName), schema = await this.getTableSchema(tableName);
|
|
1009
997
|
if (!schema)
|
|
1010
998
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
1011
|
-
if (!(await File.isExists(join(
|
|
999
|
+
if (!(await File.isExists(join(tablePath, "id.inib"))))
|
|
1012
1000
|
throw this.throwError("NO_ITEMS", tableName);
|
|
1013
1001
|
if (!Array.isArray(columns))
|
|
1014
1002
|
columns = [columns];
|