inibase 1.0.0-rc.26 → 1.0.0-rc.27
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 +59 -40
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -0
- package/dist/file.d.ts +22 -16
- package/dist/file.js +395 -261
- package/dist/index.d.ts +11 -12
- package/dist/index.js +213 -130
- package/dist/utils.server.d.ts +2 -0
- package/dist/utils.server.js +30 -0
- package/package.json +3 -5
package/dist/index.js
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
|
-
import { unlink, rename,
|
|
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";
|
|
5
5
|
import { scryptSync, randomBytes } from "node:crypto";
|
|
6
6
|
import File from "./file.js";
|
|
7
7
|
import Utils from "./utils.js";
|
|
8
8
|
import UtilsServer from "./utils.server.js";
|
|
9
|
+
import Config from "./config.js";
|
|
10
|
+
process.env.UV_THREADPOOL_SIZE = cpus().length.toString();
|
|
9
11
|
export default class Inibase {
|
|
10
12
|
folder;
|
|
11
13
|
database;
|
|
12
14
|
table;
|
|
13
15
|
pageInfo;
|
|
14
|
-
cache;
|
|
15
16
|
totalItems;
|
|
16
17
|
salt;
|
|
17
18
|
constructor(database, mainFolder = ".") {
|
|
18
19
|
this.database = database;
|
|
19
20
|
this.folder = mainFolder;
|
|
20
21
|
this.table = null;
|
|
21
|
-
this.cache = new Map();
|
|
22
22
|
this.totalItems = {};
|
|
23
|
-
this.pageInfo = {
|
|
23
|
+
this.pageInfo = {};
|
|
24
24
|
if (!existsSync(".env") || !process.env.INIBASE_SECRET) {
|
|
25
25
|
this.salt = scryptSync(randomBytes(16), randomBytes(16), 32);
|
|
26
26
|
appendFileSync(".env", `INIBASE_SECRET=${this.salt.toString("hex")}\n`);
|
|
27
27
|
}
|
|
28
|
-
else
|
|
28
|
+
else
|
|
29
29
|
this.salt = Buffer.from(process.env.INIBASE_SECRET, "hex");
|
|
30
|
-
}
|
|
31
30
|
}
|
|
32
31
|
throwError(code, variable, language = "en") {
|
|
33
32
|
const errorMessages = {
|
|
@@ -56,7 +55,9 @@ export default class Inibase {
|
|
|
56
55
|
}
|
|
57
56
|
async setTableSchema(tableName, schema) {
|
|
58
57
|
const decodeIdFromSchema = (schema) => schema.map((field) => {
|
|
59
|
-
if (field.
|
|
58
|
+
if ((field.type === "array" || field.type === "object") &&
|
|
59
|
+
field.children &&
|
|
60
|
+
Utils.isArrayOfObjects(field.children))
|
|
60
61
|
field.children = decodeIdFromSchema(field.children);
|
|
61
62
|
if (field.id && !Utils.isNumber(field.id))
|
|
62
63
|
field.id = UtilsServer.decodeID(field.id, this.salt);
|
|
@@ -75,7 +76,9 @@ export default class Inibase {
|
|
|
75
76
|
const schemaToIdsPath = (schema, prefix = "") => {
|
|
76
77
|
let RETURN = {};
|
|
77
78
|
for (const field of schema)
|
|
78
|
-
if (field.
|
|
79
|
+
if ((field.type === "array" || field.type === "object") &&
|
|
80
|
+
field.children &&
|
|
81
|
+
Utils.isArrayOfObjects(field.children)) {
|
|
79
82
|
Utils.deepMerge(RETURN, schemaToIdsPath(field.children, (prefix ?? "") + field.key + "."));
|
|
80
83
|
}
|
|
81
84
|
else if (Utils.isValidID(field.id))
|
|
@@ -88,17 +91,16 @@ export default class Inibase {
|
|
|
88
91
|
if (await File.isExists(join(TablePath, oldPath)))
|
|
89
92
|
await rename(join(TablePath, oldPath), join(TablePath, newPath));
|
|
90
93
|
}
|
|
91
|
-
await
|
|
94
|
+
await File.write(join(TablePath, "schema.json"), JSON.stringify(decodeIdFromSchema(schema), null, 2), true);
|
|
92
95
|
}
|
|
93
96
|
async getTableSchema(tableName) {
|
|
94
97
|
const TableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
|
|
95
98
|
if (!(await File.isExists(TableSchemaPath)))
|
|
96
99
|
return undefined;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (!this.cache.get(TableSchemaPath))
|
|
100
|
+
const schemaFile = await File.read(TableSchemaPath, true);
|
|
101
|
+
if (!schemaFile)
|
|
100
102
|
return undefined;
|
|
101
|
-
const schema = JSON.parse(
|
|
103
|
+
const schema = JSON.parse(schemaFile), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
|
|
102
104
|
return [
|
|
103
105
|
{
|
|
104
106
|
id: UtilsServer.encodeID(0, this.salt),
|
|
@@ -121,49 +123,45 @@ export default class Inibase {
|
|
|
121
123
|
},
|
|
122
124
|
];
|
|
123
125
|
}
|
|
124
|
-
getField(keyPath, schema
|
|
126
|
+
getField(keyPath, schema) {
|
|
127
|
+
let RETURN = null;
|
|
125
128
|
const keyPathSplited = keyPath.split(".");
|
|
126
129
|
for (const [index, key] of keyPathSplited.entries()) {
|
|
127
|
-
if (key === "*")
|
|
128
|
-
continue;
|
|
129
130
|
const foundItem = schema.find((item) => item.key === key);
|
|
130
131
|
if (!foundItem)
|
|
131
132
|
return null;
|
|
132
133
|
if (index === keyPathSplited.length - 1)
|
|
133
|
-
|
|
134
|
+
RETURN = foundItem;
|
|
134
135
|
if ((foundItem.type === "array" || foundItem.type === "object") &&
|
|
135
136
|
foundItem.children &&
|
|
136
137
|
Utils.isArrayOfObjects(foundItem.children))
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
if (property) {
|
|
140
|
-
switch (property) {
|
|
141
|
-
case "type":
|
|
142
|
-
return schema.type;
|
|
143
|
-
case "children":
|
|
144
|
-
return schema.children;
|
|
145
|
-
default:
|
|
146
|
-
return schema[property];
|
|
147
|
-
}
|
|
138
|
+
RETURN = foundItem.children;
|
|
148
139
|
}
|
|
149
|
-
|
|
150
|
-
return
|
|
140
|
+
if (!RETURN)
|
|
141
|
+
return null;
|
|
142
|
+
return Utils.isArrayOfObjects(RETURN) ? RETURN[0] : RETURN;
|
|
151
143
|
}
|
|
152
144
|
validateData(data, schema, skipRequiredField = false) {
|
|
153
145
|
if (Utils.isArrayOfObjects(data))
|
|
154
146
|
for (const single_data of data)
|
|
155
147
|
this.validateData(single_data, schema, skipRequiredField);
|
|
156
148
|
else if (Utils.isObject(data)) {
|
|
157
|
-
for (const
|
|
158
|
-
if (!data.hasOwnProperty(key) &&
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
149
|
+
for (const field of schema) {
|
|
150
|
+
if (!data.hasOwnProperty(field.key) &&
|
|
151
|
+
field.required &&
|
|
152
|
+
!skipRequiredField)
|
|
153
|
+
throw this.throwError("FIELD_REQUIRED", field.key);
|
|
154
|
+
if (data.hasOwnProperty(field.key) &&
|
|
155
|
+
!Utils.validateFieldType(data[field.key], field.type, (field.type === "array" || field.type === "object") &&
|
|
156
|
+
field.children &&
|
|
157
|
+
!Utils.isArrayOfObjects(field.children)
|
|
158
|
+
? field.children
|
|
159
|
+
: undefined))
|
|
160
|
+
throw this.throwError("INVALID_TYPE", field.key + " " + field.type + " " + data[field.key]);
|
|
161
|
+
if ((field.type === "array" || field.type === "object") &&
|
|
162
|
+
field.children &&
|
|
163
|
+
Utils.isArrayOfObjects(field.children))
|
|
164
|
+
this.validateData(data[field.key], field.children, skipRequiredField);
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
}
|
|
@@ -172,10 +170,8 @@ export default class Inibase {
|
|
|
172
170
|
field.type = Utils.detectFieldType(value, field.type) ?? field.type[0];
|
|
173
171
|
switch (field.type) {
|
|
174
172
|
case "array":
|
|
175
|
-
if (!Array.isArray(value))
|
|
176
|
-
value = [value];
|
|
177
173
|
if (typeof field.children === "string") {
|
|
178
|
-
if (field.
|
|
174
|
+
if (field.children === "table") {
|
|
179
175
|
if (Array.isArray(value)) {
|
|
180
176
|
if (Utils.isArrayOfObjects(value)) {
|
|
181
177
|
if (value.every((item) => item.hasOwnProperty("id") &&
|
|
@@ -197,8 +193,8 @@ export default class Inibase {
|
|
|
197
193
|
else if (Utils.isNumber(value))
|
|
198
194
|
return [Number(value)];
|
|
199
195
|
}
|
|
200
|
-
else
|
|
201
|
-
return value;
|
|
196
|
+
else
|
|
197
|
+
return Array.isArray(value) ? value : [value];
|
|
202
198
|
}
|
|
203
199
|
else if (Utils.isArrayOfObjects(field.children))
|
|
204
200
|
return this.formatData(value, field.children, formatOnlyAvailiableKeys);
|
|
@@ -307,10 +303,7 @@ export default class Inibase {
|
|
|
307
303
|
const path = join(this.folder, this.database, tableName);
|
|
308
304
|
let RETURN = {};
|
|
309
305
|
for await (const field of schema) {
|
|
310
|
-
if (
|
|
311
|
-
(Array.isArray(field.type) &&
|
|
312
|
-
field.type.includes("array"))) &&
|
|
313
|
-
field.children) {
|
|
306
|
+
if (field.type === "array" && field.children) {
|
|
314
307
|
if (Utils.isArrayOfObjects(field.children)) {
|
|
315
308
|
if (field.children.filter((children) => children.type === "array" &&
|
|
316
309
|
Utils.isArrayOfObjects(children.children)).length) {
|
|
@@ -392,8 +385,7 @@ export default class Inibase {
|
|
|
392
385
|
options.columns = options.columns
|
|
393
386
|
.filter((column) => column.includes(`${field.key}.`))
|
|
394
387
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
395
|
-
const
|
|
396
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
388
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
|
|
397
389
|
if (items)
|
|
398
390
|
for await (const [index, item] of Object.entries(items)) {
|
|
399
391
|
if (!RETURN[index])
|
|
@@ -404,8 +396,7 @@ export default class Inibase {
|
|
|
404
396
|
}
|
|
405
397
|
}
|
|
406
398
|
else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
|
|
407
|
-
const
|
|
408
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
399
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
409
400
|
if (items)
|
|
410
401
|
for (const [index, item] of Object.entries(items)) {
|
|
411
402
|
if (!RETURN[index])
|
|
@@ -436,8 +427,7 @@ export default class Inibase {
|
|
|
436
427
|
.filter((column) => column.includes(`${field.key}.`) &&
|
|
437
428
|
!column.includes(`${field.key}.`))
|
|
438
429
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
439
|
-
const
|
|
440
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
430
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
|
|
441
431
|
if (items)
|
|
442
432
|
for await (const [index, item] of Object.entries(items)) {
|
|
443
433
|
if (!RETURN[index])
|
|
@@ -449,8 +439,7 @@ export default class Inibase {
|
|
|
449
439
|
}
|
|
450
440
|
}
|
|
451
441
|
else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
|
|
452
|
-
const
|
|
453
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
442
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
454
443
|
if (items)
|
|
455
444
|
for (const [index, item] of Object.entries(items)) {
|
|
456
445
|
if (!RETURN[index])
|
|
@@ -521,23 +510,26 @@ export default class Inibase {
|
|
|
521
510
|
}
|
|
522
511
|
}
|
|
523
512
|
async applyCriteria(tableName, schema, options, criteria, allTrue) {
|
|
524
|
-
let RETURN = {};
|
|
513
|
+
let RETURN = {}, RETURN_LineNumbers = null;
|
|
525
514
|
if (!criteria)
|
|
526
|
-
return null;
|
|
515
|
+
return [null, null];
|
|
527
516
|
if (criteria.and && Utils.isObject(criteria.and)) {
|
|
528
|
-
const searchResult = await this.applyCriteria(tableName, schema, options, criteria.and, true);
|
|
517
|
+
const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.and, true);
|
|
529
518
|
if (searchResult) {
|
|
530
519
|
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).length === Object.keys(criteria.and ?? {}).length)));
|
|
531
520
|
delete criteria.and;
|
|
521
|
+
RETURN_LineNumbers = lineNumbers;
|
|
532
522
|
}
|
|
533
523
|
else
|
|
534
|
-
return null;
|
|
524
|
+
return [null, null];
|
|
535
525
|
}
|
|
536
526
|
if (criteria.or && Utils.isObject(criteria.or)) {
|
|
537
|
-
const searchResult = await this.applyCriteria(tableName, schema, options, criteria.or, false);
|
|
527
|
+
const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.or, false);
|
|
538
528
|
delete criteria.or;
|
|
539
|
-
if (searchResult)
|
|
529
|
+
if (searchResult) {
|
|
540
530
|
RETURN = Utils.deepMerge(RETURN, searchResult);
|
|
531
|
+
RETURN_LineNumbers = lineNumbers;
|
|
532
|
+
}
|
|
541
533
|
}
|
|
542
534
|
if (Object.keys(criteria).length > 0) {
|
|
543
535
|
if (allTrue === undefined)
|
|
@@ -600,7 +592,7 @@ export default class Inibase {
|
|
|
600
592
|
searchOperator = "=";
|
|
601
593
|
searchComparedAtValue = value;
|
|
602
594
|
}
|
|
603
|
-
const [searchResult,
|
|
595
|
+
const [searchResult, totalLines, linesNumbers] = await File.search(join(this.folder, this.database, tableName, key + ".inib"), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
|
|
604
596
|
if (searchResult) {
|
|
605
597
|
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
|
|
606
598
|
id,
|
|
@@ -608,7 +600,8 @@ export default class Inibase {
|
|
|
608
600
|
[key]: value,
|
|
609
601
|
},
|
|
610
602
|
])));
|
|
611
|
-
this.totalItems[tableName + "-" + key] =
|
|
603
|
+
this.totalItems[tableName + "-" + key] = totalLines;
|
|
604
|
+
RETURN_LineNumbers = linesNumbers;
|
|
612
605
|
}
|
|
613
606
|
if (allTrue && index > 0) {
|
|
614
607
|
if (!Object.keys(RETURN).length)
|
|
@@ -619,31 +612,10 @@ export default class Inibase {
|
|
|
619
612
|
}
|
|
620
613
|
}
|
|
621
614
|
}
|
|
622
|
-
return Object.keys(RETURN).length ? RETURN : null;
|
|
615
|
+
return [Object.keys(RETURN).length ? RETURN : null, RETURN_LineNumbers];
|
|
623
616
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
per_page: 15,
|
|
627
|
-
}, onlyOne, onlyLinesNumbers) {
|
|
628
|
-
if (options.columns) {
|
|
629
|
-
if (!Array.isArray(options.columns))
|
|
630
|
-
options.columns = [options.columns];
|
|
631
|
-
if (options.columns.length &&
|
|
632
|
-
!options.columns.includes("id"))
|
|
633
|
-
options.columns.push("id");
|
|
634
|
-
}
|
|
635
|
-
if (!options.page)
|
|
636
|
-
options.page = 1;
|
|
637
|
-
if (!options.per_page)
|
|
638
|
-
options.per_page = 15;
|
|
639
|
-
let RETURN;
|
|
640
|
-
let schema = await this.getTableSchema(tableName);
|
|
641
|
-
if (!schema)
|
|
642
|
-
throw this.throwError("NO_SCHEMA", tableName);
|
|
643
|
-
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
644
|
-
if (!(await File.isExists(idFilePath)))
|
|
645
|
-
return null;
|
|
646
|
-
const filterSchemaByColumns = (schema, columns) => schema
|
|
617
|
+
_filterSchemaByColumns(schema, columns) {
|
|
618
|
+
return schema
|
|
647
619
|
.map((field) => {
|
|
648
620
|
if (columns.some((column) => column.startsWith("!")))
|
|
649
621
|
return columns.includes("!" + field.key) ? null : field;
|
|
@@ -653,7 +625,7 @@ export default class Inibase {
|
|
|
653
625
|
Utils.isArrayOfObjects(field.children) &&
|
|
654
626
|
columns.filter((column) => column.startsWith(field.key + ".") ||
|
|
655
627
|
column.startsWith("!" + field.key + ".")).length) {
|
|
656
|
-
field.children =
|
|
628
|
+
field.children = this._filterSchemaByColumns(field.children, columns
|
|
657
629
|
.filter((column) => column.startsWith(field.key + ".") ||
|
|
658
630
|
column.startsWith("!" + field.key + "."))
|
|
659
631
|
.map((column) => column.replace(field.key + ".", "")));
|
|
@@ -662,13 +634,55 @@ export default class Inibase {
|
|
|
662
634
|
return null;
|
|
663
635
|
})
|
|
664
636
|
.filter((i) => i);
|
|
637
|
+
}
|
|
638
|
+
async get(tableName, where, options = {
|
|
639
|
+
page: 1,
|
|
640
|
+
perPage: 15,
|
|
641
|
+
}, onlyOne, onlyLinesNumbers, tableSchema) {
|
|
642
|
+
// Ensure options.columns is an array
|
|
643
|
+
if (options.columns) {
|
|
644
|
+
options.columns = Array.isArray(options.columns)
|
|
645
|
+
? options.columns
|
|
646
|
+
: [options.columns];
|
|
647
|
+
if (options.columns.length && !options.columns.includes("id"))
|
|
648
|
+
options.columns.push("id");
|
|
649
|
+
}
|
|
650
|
+
// Default values for page and perPage
|
|
651
|
+
options.page = options.page || 1;
|
|
652
|
+
options.perPage = options.perPage || 15;
|
|
653
|
+
let RETURN;
|
|
654
|
+
let schema = tableSchema ?? (await this.getTableSchema(tableName));
|
|
655
|
+
if (!schema)
|
|
656
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
657
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
658
|
+
if (!(await File.isExists(idFilePath)))
|
|
659
|
+
return null;
|
|
665
660
|
if (options.columns && options.columns.length)
|
|
666
|
-
schema =
|
|
661
|
+
schema = this._filterSchemaByColumns(schema, options.columns);
|
|
667
662
|
if (!where) {
|
|
668
663
|
// Display all data
|
|
669
|
-
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.
|
|
664
|
+
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
670
665
|
index +
|
|
671
666
|
1), options));
|
|
667
|
+
if (Config.isCacheEnabled &&
|
|
668
|
+
(await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
|
|
669
|
+
this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
|
|
670
|
+
else {
|
|
671
|
+
this.totalItems[tableName + "-*"] = await File.count(idFilePath);
|
|
672
|
+
if (Config.isCacheEnabled)
|
|
673
|
+
await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
677
|
+
(Utils.isNumber(where) && !onlyLinesNumbers)) {
|
|
678
|
+
let lineNumbers = where;
|
|
679
|
+
if (!Array.isArray(lineNumbers))
|
|
680
|
+
lineNumbers = [lineNumbers];
|
|
681
|
+
RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, lineNumbers, options)) ?? {});
|
|
682
|
+
if (!this.totalItems[tableName + "-*"])
|
|
683
|
+
this.totalItems[tableName + "-*"] = lineNumbers.length;
|
|
684
|
+
if (RETURN && RETURN.length && !Array.isArray(where))
|
|
685
|
+
RETURN = RETURN[0];
|
|
672
686
|
}
|
|
673
687
|
else if ((Array.isArray(where) &&
|
|
674
688
|
(where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
|
|
@@ -685,29 +699,56 @@ export default class Inibase {
|
|
|
685
699
|
? Object.keys(lineNumbers).map(Number)
|
|
686
700
|
: null;
|
|
687
701
|
RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
|
|
702
|
+
if (!this.totalItems[tableName + "-*"]) {
|
|
703
|
+
if (Config.isCacheEnabled &&
|
|
704
|
+
(await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
|
|
705
|
+
this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
|
|
706
|
+
else {
|
|
707
|
+
this.totalItems[tableName + "-*"] = await File.count(idFilePath);
|
|
708
|
+
if (Config.isCacheEnabled)
|
|
709
|
+
await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
688
712
|
if (RETURN && RETURN.length && !Array.isArray(where))
|
|
689
713
|
RETURN = RETURN[0];
|
|
690
714
|
}
|
|
691
715
|
else if (Utils.isObject(where)) {
|
|
716
|
+
let cachedFilePath = "";
|
|
692
717
|
// Criteria
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
718
|
+
if (Config.isCacheEnabled)
|
|
719
|
+
cachedFilePath = join(this.folder, this.database, tableName, ".tmp", `${UtilsServer.hashObject(where)}.inib`);
|
|
720
|
+
if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
|
|
721
|
+
const cachedItems = (await File.read(cachedFilePath, true)).split(",");
|
|
722
|
+
this.totalItems[tableName + "-*"] = cachedItems.length;
|
|
723
|
+
return this.get(tableName, cachedItems
|
|
724
|
+
.slice((options.page - 1) * options.perPage, options.page * options.perPage)
|
|
725
|
+
.map(Number), options, undefined, undefined, schema);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
let linesNumbers;
|
|
729
|
+
[RETURN, linesNumbers] = await this.applyCriteria(tableName, schema, options, where);
|
|
730
|
+
if (RETURN && linesNumbers) {
|
|
731
|
+
if (onlyLinesNumbers)
|
|
732
|
+
return Object.keys(RETURN).map(Number);
|
|
733
|
+
const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
|
|
734
|
+
RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter((field) => !alreadyExistsColumns.includes(field.key)), Object.keys(RETURN).map(Number), options)));
|
|
735
|
+
if (Config.isCacheEnabled)
|
|
736
|
+
await File.write(cachedFilePath, Array.from(linesNumbers).join(","), true);
|
|
737
|
+
}
|
|
699
738
|
}
|
|
700
739
|
}
|
|
701
740
|
if (!RETURN ||
|
|
702
741
|
(Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
|
|
703
742
|
(Array.isArray(RETURN) && !RETURN.length))
|
|
704
743
|
return null;
|
|
705
|
-
const greatestTotalItems =
|
|
706
|
-
.
|
|
707
|
-
|
|
708
|
-
|
|
744
|
+
const greatestTotalItems = this.totalItems[tableName + "-*"] ??
|
|
745
|
+
Math.max(...Object.entries(this.totalItems)
|
|
746
|
+
.filter(([k]) => k.startsWith(tableName + "-"))
|
|
747
|
+
.map(([, v]) => v));
|
|
748
|
+
this.pageInfo[tableName] = {
|
|
709
749
|
...(({ columns, ...restOfOptions }) => restOfOptions)(options),
|
|
710
|
-
|
|
750
|
+
perPage: Array.isArray(RETURN) ? RETURN.length : 1,
|
|
751
|
+
totalPages: Math.ceil(greatestTotalItems / options.perPage),
|
|
711
752
|
total: greatestTotalItems,
|
|
712
753
|
};
|
|
713
754
|
return onlyOne && Array.isArray(RETURN) ? RETURN[0] : RETURN;
|
|
@@ -716,7 +757,7 @@ export default class Inibase {
|
|
|
716
757
|
if (!options)
|
|
717
758
|
options = {
|
|
718
759
|
page: 1,
|
|
719
|
-
|
|
760
|
+
perPage: 15,
|
|
720
761
|
};
|
|
721
762
|
if (!returnPostedData)
|
|
722
763
|
returnPostedData = false;
|
|
@@ -724,12 +765,22 @@ export default class Inibase {
|
|
|
724
765
|
let RETURN;
|
|
725
766
|
if (!schema)
|
|
726
767
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
727
|
-
const idFilePath = join(this.folder, this.database, tableName, "id.inib"),
|
|
728
|
-
let lastId =
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
768
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib"), cashFolderPath = join(this.folder, this.database, tableName, ".tmp");
|
|
769
|
+
let lastId = 0, totalItems = 0, lastIdObj;
|
|
770
|
+
if (await File.isExists(idFilePath)) {
|
|
771
|
+
if (await File.isExists(join(cashFolderPath, "lastId.inib"))) {
|
|
772
|
+
lastId = Number(await File.read(join(cashFolderPath, "lastId.inib"), true));
|
|
773
|
+
if (await File.isExists(join(cashFolderPath, "totalItems.inib")))
|
|
774
|
+
totalItems = Number(await File.read(join(cashFolderPath, "totalItems.inib"), true));
|
|
775
|
+
else
|
|
776
|
+
totalItems = await File.count(join(this.folder, this.database, tableName, "id.inib"));
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
[lastIdObj, totalItems] = await File.get(idFilePath, -1, "number", undefined, this.salt, true);
|
|
780
|
+
if (lastIdObj)
|
|
781
|
+
lastId = Number(Object.entries(lastIdObj)[0][1]) ?? 0;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
733
784
|
if (Utils.isArrayOfObjects(data))
|
|
734
785
|
RETURN = data.map(({ id, updatedAt, createdAt, ...rest }) => ({
|
|
735
786
|
id: ++lastId,
|
|
@@ -745,21 +796,23 @@ export default class Inibase {
|
|
|
745
796
|
if (!RETURN)
|
|
746
797
|
throw this.throwError("NO_DATA");
|
|
747
798
|
RETURN = this.formatData(RETURN, schema);
|
|
748
|
-
const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), RETURN);
|
|
799
|
+
const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
|
|
749
800
|
const renameList = [];
|
|
750
801
|
for await (const [path, content] of Object.entries(pathesContents))
|
|
751
802
|
renameList.push(await File.append(path, content));
|
|
752
|
-
await
|
|
753
|
-
|
|
803
|
+
for await (const [tempPath, filePath] of renameList)
|
|
804
|
+
await rename(tempPath, filePath);
|
|
805
|
+
await File.write(join(cashFolderPath, "lastId.inib"), lastId.toString(), true);
|
|
806
|
+
await File.write(join(cashFolderPath, "totalItems.inib"), String(totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)), true);
|
|
754
807
|
if (returnPostedData)
|
|
755
808
|
return this.get(tableName, Utils.isArrayOfObjects(RETURN)
|
|
756
809
|
? RETURN.map((data) => Number(data.id))
|
|
757
|
-
: RETURN.id, options, !Utils.isArrayOfObjects(data) // return only one item if data is not array of objects
|
|
758
|
-
);
|
|
810
|
+
: RETURN.id, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
|
|
811
|
+
undefined, schema);
|
|
759
812
|
}
|
|
760
813
|
async put(tableName, data, where, options = {
|
|
761
814
|
page: 1,
|
|
762
|
-
|
|
815
|
+
perPage: 15,
|
|
763
816
|
}, returnPostedData) {
|
|
764
817
|
const schema = await this.getTableSchema(tableName);
|
|
765
818
|
if (!schema)
|
|
@@ -793,8 +846,14 @@ export default class Inibase {
|
|
|
793
846
|
});
|
|
794
847
|
for await (const [path, content] of Object.entries(pathesContents))
|
|
795
848
|
await File.replace(path, content);
|
|
849
|
+
if (Config.isCacheEnabled) {
|
|
850
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
851
|
+
if (cacheFiles.length)
|
|
852
|
+
for await (const file of cacheFiles)
|
|
853
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
854
|
+
}
|
|
796
855
|
if (returnPostedData)
|
|
797
|
-
return this.get(tableName, where, options);
|
|
856
|
+
return this.get(tableName, where, options, undefined, undefined, schema);
|
|
798
857
|
}
|
|
799
858
|
}
|
|
800
859
|
else if ((Array.isArray(where) &&
|
|
@@ -803,7 +862,7 @@ export default class Inibase {
|
|
|
803
862
|
Utils.isNumber(where)) {
|
|
804
863
|
if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
|
|
805
864
|
Utils.isValidID(where)) {
|
|
806
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
865
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
807
866
|
return this.put(tableName, data, lineNumbers);
|
|
808
867
|
}
|
|
809
868
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
@@ -824,13 +883,20 @@ export default class Inibase {
|
|
|
824
883
|
const renameList = [];
|
|
825
884
|
for await (const [path, content] of Object.entries(pathesContents))
|
|
826
885
|
renameList.push(await File.replace(path, content));
|
|
827
|
-
await
|
|
886
|
+
for await (const [tempPath, filePath] of renameList)
|
|
887
|
+
await rename(tempPath, filePath);
|
|
888
|
+
if (Config.isCacheEnabled) {
|
|
889
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
890
|
+
if (cacheFiles.length)
|
|
891
|
+
for await (const file of cacheFiles)
|
|
892
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
893
|
+
}
|
|
828
894
|
if (returnPostedData)
|
|
829
|
-
return this.get(tableName, where, options, !Array.isArray(where));
|
|
895
|
+
return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
|
|
830
896
|
}
|
|
831
897
|
}
|
|
832
898
|
else if (Utils.isObject(where)) {
|
|
833
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
899
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
834
900
|
if (!lineNumbers || !lineNumbers.length)
|
|
835
901
|
throw this.throwError("NO_ITEMS", tableName);
|
|
836
902
|
return this.put(tableName, data, lineNumbers);
|
|
@@ -848,7 +914,14 @@ export default class Inibase {
|
|
|
848
914
|
if (!where) {
|
|
849
915
|
const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
|
|
850
916
|
if (files.length)
|
|
851
|
-
await
|
|
917
|
+
for await (const file of files)
|
|
918
|
+
await unlink(join(this.folder, this.database, tableName, file));
|
|
919
|
+
if (Config.isCacheEnabled) {
|
|
920
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
921
|
+
if (cacheFiles.length)
|
|
922
|
+
for await (const file of cacheFiles)
|
|
923
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
924
|
+
}
|
|
852
925
|
return "*";
|
|
853
926
|
}
|
|
854
927
|
else if ((Array.isArray(where) &&
|
|
@@ -857,7 +930,7 @@ export default class Inibase {
|
|
|
857
930
|
Utils.isNumber(where)) {
|
|
858
931
|
if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
|
|
859
932
|
Utils.isValidID(where)) {
|
|
860
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
933
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
861
934
|
if (!lineNumbers)
|
|
862
935
|
return null;
|
|
863
936
|
return this.delete(tableName, lineNumbers, where);
|
|
@@ -868,19 +941,29 @@ export default class Inibase {
|
|
|
868
941
|
const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
|
|
869
942
|
if (files.length) {
|
|
870
943
|
if (!_id)
|
|
871
|
-
_id = Object.entries((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt))
|
|
944
|
+
_id = Object.entries((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt)) ?? {}).map(([_key, id]) => UtilsServer.encodeID(Number(id), this.salt));
|
|
872
945
|
if (!_id.length)
|
|
873
946
|
throw this.throwError("NO_ITEMS", tableName);
|
|
874
947
|
const renameList = [];
|
|
875
948
|
for await (const file of files)
|
|
876
949
|
renameList.push(await File.remove(join(this.folder, this.database, tableName, file), where));
|
|
877
|
-
await
|
|
950
|
+
for await (const [tempPath, filePath] of renameList)
|
|
951
|
+
await rename(tempPath, filePath);
|
|
952
|
+
if (Config.isCacheEnabled) {
|
|
953
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
954
|
+
if (cacheFiles.length)
|
|
955
|
+
for await (const file of cacheFiles)
|
|
956
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
957
|
+
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")))
|
|
958
|
+
? Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true))
|
|
959
|
+
: await File.count(join(this.folder, this.database, tableName, "id.inib"))) - (Array.isArray(where) ? where.length : 1)), true);
|
|
960
|
+
}
|
|
878
961
|
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
879
962
|
}
|
|
880
963
|
}
|
|
881
964
|
}
|
|
882
965
|
else if (Utils.isObject(where)) {
|
|
883
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
966
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
884
967
|
if (!lineNumbers || !lineNumbers.length)
|
|
885
968
|
throw this.throwError("NO_ITEMS", tableName);
|
|
886
969
|
return this.delete(tableName, lineNumbers);
|
|
@@ -902,7 +985,7 @@ export default class Inibase {
|
|
|
902
985
|
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
903
986
|
if (await File.isExists(columnPath)) {
|
|
904
987
|
if (where) {
|
|
905
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
988
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
906
989
|
RETURN[column] = lineNumbers
|
|
907
990
|
? await File.sum(columnPath, lineNumbers)
|
|
908
991
|
: 0;
|
|
@@ -926,7 +1009,7 @@ export default class Inibase {
|
|
|
926
1009
|
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
927
1010
|
if (await File.isExists(columnPath)) {
|
|
928
1011
|
if (where) {
|
|
929
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1012
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
930
1013
|
RETURN[column] = lineNumbers
|
|
931
1014
|
? await File.max(columnPath, lineNumbers)
|
|
932
1015
|
: 0;
|
|
@@ -950,7 +1033,7 @@ export default class Inibase {
|
|
|
950
1033
|
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
951
1034
|
if (await File.isExists(columnPath)) {
|
|
952
1035
|
if (where) {
|
|
953
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1036
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
954
1037
|
RETURN[column] = lineNumbers
|
|
955
1038
|
? await File.min(columnPath, lineNumbers)
|
|
956
1039
|
: 0;
|