inibase 1.0.0-rc.26 → 1.0.0-rc.28
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 +376 -242
- package/dist/index.d.ts +15 -14
- package/dist/index.js +214 -128
- package/dist/utils.server.d.ts +46 -2
- 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);
|
|
@@ -308,8 +304,7 @@ export default class Inibase {
|
|
|
308
304
|
let RETURN = {};
|
|
309
305
|
for await (const field of schema) {
|
|
310
306
|
if ((field.type === "array" ||
|
|
311
|
-
(Array.isArray(field.type) &&
|
|
312
|
-
field.type.includes("array"))) &&
|
|
307
|
+
(Array.isArray(field.type) && field.type.includes("array"))) &&
|
|
313
308
|
field.children) {
|
|
314
309
|
if (Utils.isArrayOfObjects(field.children)) {
|
|
315
310
|
if (field.children.filter((children) => children.type === "array" &&
|
|
@@ -387,13 +382,13 @@ export default class Inibase {
|
|
|
387
382
|
});
|
|
388
383
|
}
|
|
389
384
|
else if (field.children === "table" ||
|
|
385
|
+
(Array.isArray(field.type) && field.type.includes("table")) ||
|
|
390
386
|
(Array.isArray(field.children) && field.children.includes("table"))) {
|
|
391
387
|
if (options.columns)
|
|
392
388
|
options.columns = options.columns
|
|
393
389
|
.filter((column) => column.includes(`${field.key}.`))
|
|
394
390
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
395
|
-
const
|
|
396
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
391
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
|
|
397
392
|
if (items)
|
|
398
393
|
for await (const [index, item] of Object.entries(items)) {
|
|
399
394
|
if (!RETURN[index])
|
|
@@ -404,8 +399,7 @@ export default class Inibase {
|
|
|
404
399
|
}
|
|
405
400
|
}
|
|
406
401
|
else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
|
|
407
|
-
const
|
|
408
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
402
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
409
403
|
if (items)
|
|
410
404
|
for (const [index, item] of Object.entries(items)) {
|
|
411
405
|
if (!RETURN[index])
|
|
@@ -436,8 +430,7 @@ export default class Inibase {
|
|
|
436
430
|
.filter((column) => column.includes(`${field.key}.`) &&
|
|
437
431
|
!column.includes(`${field.key}.`))
|
|
438
432
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
439
|
-
const
|
|
440
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
433
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
|
|
441
434
|
if (items)
|
|
442
435
|
for await (const [index, item] of Object.entries(items)) {
|
|
443
436
|
if (!RETURN[index])
|
|
@@ -449,8 +442,7 @@ export default class Inibase {
|
|
|
449
442
|
}
|
|
450
443
|
}
|
|
451
444
|
else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
|
|
452
|
-
const
|
|
453
|
-
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
445
|
+
const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
454
446
|
if (items)
|
|
455
447
|
for (const [index, item] of Object.entries(items)) {
|
|
456
448
|
if (!RETURN[index])
|
|
@@ -521,23 +513,26 @@ export default class Inibase {
|
|
|
521
513
|
}
|
|
522
514
|
}
|
|
523
515
|
async applyCriteria(tableName, schema, options, criteria, allTrue) {
|
|
524
|
-
let RETURN = {};
|
|
516
|
+
let RETURN = {}, RETURN_LineNumbers = null;
|
|
525
517
|
if (!criteria)
|
|
526
|
-
return null;
|
|
518
|
+
return [null, null];
|
|
527
519
|
if (criteria.and && Utils.isObject(criteria.and)) {
|
|
528
|
-
const searchResult = await this.applyCriteria(tableName, schema, options, criteria.and, true);
|
|
520
|
+
const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.and, true);
|
|
529
521
|
if (searchResult) {
|
|
530
522
|
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).length === Object.keys(criteria.and ?? {}).length)));
|
|
531
523
|
delete criteria.and;
|
|
524
|
+
RETURN_LineNumbers = lineNumbers;
|
|
532
525
|
}
|
|
533
526
|
else
|
|
534
|
-
return null;
|
|
527
|
+
return [null, null];
|
|
535
528
|
}
|
|
536
529
|
if (criteria.or && Utils.isObject(criteria.or)) {
|
|
537
|
-
const searchResult = await this.applyCriteria(tableName, schema, options, criteria.or, false);
|
|
530
|
+
const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.or, false);
|
|
538
531
|
delete criteria.or;
|
|
539
|
-
if (searchResult)
|
|
532
|
+
if (searchResult) {
|
|
540
533
|
RETURN = Utils.deepMerge(RETURN, searchResult);
|
|
534
|
+
RETURN_LineNumbers = lineNumbers;
|
|
535
|
+
}
|
|
541
536
|
}
|
|
542
537
|
if (Object.keys(criteria).length > 0) {
|
|
543
538
|
if (allTrue === undefined)
|
|
@@ -600,7 +595,7 @@ export default class Inibase {
|
|
|
600
595
|
searchOperator = "=";
|
|
601
596
|
searchComparedAtValue = value;
|
|
602
597
|
}
|
|
603
|
-
const [searchResult,
|
|
598
|
+
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
599
|
if (searchResult) {
|
|
605
600
|
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
|
|
606
601
|
id,
|
|
@@ -608,7 +603,8 @@ export default class Inibase {
|
|
|
608
603
|
[key]: value,
|
|
609
604
|
},
|
|
610
605
|
])));
|
|
611
|
-
this.totalItems[tableName + "-" + key] =
|
|
606
|
+
this.totalItems[tableName + "-" + key] = totalLines;
|
|
607
|
+
RETURN_LineNumbers = linesNumbers;
|
|
612
608
|
}
|
|
613
609
|
if (allTrue && index > 0) {
|
|
614
610
|
if (!Object.keys(RETURN).length)
|
|
@@ -619,31 +615,10 @@ export default class Inibase {
|
|
|
619
615
|
}
|
|
620
616
|
}
|
|
621
617
|
}
|
|
622
|
-
return Object.keys(RETURN).length ? RETURN : null;
|
|
618
|
+
return [Object.keys(RETURN).length ? RETURN : null, RETURN_LineNumbers];
|
|
623
619
|
}
|
|
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
|
|
620
|
+
_filterSchemaByColumns(schema, columns) {
|
|
621
|
+
return schema
|
|
647
622
|
.map((field) => {
|
|
648
623
|
if (columns.some((column) => column.startsWith("!")))
|
|
649
624
|
return columns.includes("!" + field.key) ? null : field;
|
|
@@ -653,7 +628,7 @@ export default class Inibase {
|
|
|
653
628
|
Utils.isArrayOfObjects(field.children) &&
|
|
654
629
|
columns.filter((column) => column.startsWith(field.key + ".") ||
|
|
655
630
|
column.startsWith("!" + field.key + ".")).length) {
|
|
656
|
-
field.children =
|
|
631
|
+
field.children = this._filterSchemaByColumns(field.children, columns
|
|
657
632
|
.filter((column) => column.startsWith(field.key + ".") ||
|
|
658
633
|
column.startsWith("!" + field.key + "."))
|
|
659
634
|
.map((column) => column.replace(field.key + ".", "")));
|
|
@@ -662,13 +637,55 @@ export default class Inibase {
|
|
|
662
637
|
return null;
|
|
663
638
|
})
|
|
664
639
|
.filter((i) => i);
|
|
640
|
+
}
|
|
641
|
+
async get(tableName, where, options = {
|
|
642
|
+
page: 1,
|
|
643
|
+
perPage: 15,
|
|
644
|
+
}, onlyOne, onlyLinesNumbers, tableSchema) {
|
|
645
|
+
// Ensure options.columns is an array
|
|
646
|
+
if (options.columns) {
|
|
647
|
+
options.columns = Array.isArray(options.columns)
|
|
648
|
+
? options.columns
|
|
649
|
+
: [options.columns];
|
|
650
|
+
if (options.columns.length && !options.columns.includes("id"))
|
|
651
|
+
options.columns.push("id");
|
|
652
|
+
}
|
|
653
|
+
// Default values for page and perPage
|
|
654
|
+
options.page = options.page || 1;
|
|
655
|
+
options.perPage = options.perPage || 15;
|
|
656
|
+
let RETURN;
|
|
657
|
+
let schema = tableSchema ?? (await this.getTableSchema(tableName));
|
|
658
|
+
if (!schema)
|
|
659
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
660
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
661
|
+
if (!(await File.isExists(idFilePath)))
|
|
662
|
+
return null;
|
|
665
663
|
if (options.columns && options.columns.length)
|
|
666
|
-
schema =
|
|
664
|
+
schema = this._filterSchemaByColumns(schema, options.columns);
|
|
667
665
|
if (!where) {
|
|
668
666
|
// Display all data
|
|
669
|
-
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.
|
|
667
|
+
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
670
668
|
index +
|
|
671
669
|
1), options));
|
|
670
|
+
if (Config.isCacheEnabled &&
|
|
671
|
+
(await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
|
|
672
|
+
this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
|
|
673
|
+
else {
|
|
674
|
+
this.totalItems[tableName + "-*"] = await File.count(idFilePath);
|
|
675
|
+
if (Config.isCacheEnabled)
|
|
676
|
+
await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
680
|
+
(Utils.isNumber(where) && !onlyLinesNumbers)) {
|
|
681
|
+
let lineNumbers = where;
|
|
682
|
+
if (!Array.isArray(lineNumbers))
|
|
683
|
+
lineNumbers = [lineNumbers];
|
|
684
|
+
RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, lineNumbers, options)) ?? {});
|
|
685
|
+
if (!this.totalItems[tableName + "-*"])
|
|
686
|
+
this.totalItems[tableName + "-*"] = lineNumbers.length;
|
|
687
|
+
if (RETURN && RETURN.length && !Array.isArray(where))
|
|
688
|
+
RETURN = RETURN[0];
|
|
672
689
|
}
|
|
673
690
|
else if ((Array.isArray(where) &&
|
|
674
691
|
(where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
|
|
@@ -685,29 +702,56 @@ export default class Inibase {
|
|
|
685
702
|
? Object.keys(lineNumbers).map(Number)
|
|
686
703
|
: null;
|
|
687
704
|
RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
|
|
705
|
+
if (!this.totalItems[tableName + "-*"]) {
|
|
706
|
+
if (Config.isCacheEnabled &&
|
|
707
|
+
(await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
|
|
708
|
+
this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
|
|
709
|
+
else {
|
|
710
|
+
this.totalItems[tableName + "-*"] = await File.count(idFilePath);
|
|
711
|
+
if (Config.isCacheEnabled)
|
|
712
|
+
await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
688
715
|
if (RETURN && RETURN.length && !Array.isArray(where))
|
|
689
716
|
RETURN = RETURN[0];
|
|
690
717
|
}
|
|
691
718
|
else if (Utils.isObject(where)) {
|
|
719
|
+
let cachedFilePath = "";
|
|
692
720
|
// Criteria
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
721
|
+
if (Config.isCacheEnabled)
|
|
722
|
+
cachedFilePath = join(this.folder, this.database, tableName, ".tmp", `${UtilsServer.hashObject(where)}.inib`);
|
|
723
|
+
if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
|
|
724
|
+
const cachedItems = (await File.read(cachedFilePath, true)).split(",");
|
|
725
|
+
this.totalItems[tableName + "-*"] = cachedItems.length;
|
|
726
|
+
return this.get(tableName, cachedItems
|
|
727
|
+
.slice((options.page - 1) * options.perPage, options.page * options.perPage)
|
|
728
|
+
.map(Number), options, undefined, undefined, schema);
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
let linesNumbers;
|
|
732
|
+
[RETURN, linesNumbers] = await this.applyCriteria(tableName, schema, options, where);
|
|
733
|
+
if (RETURN && linesNumbers) {
|
|
734
|
+
if (onlyLinesNumbers)
|
|
735
|
+
return Object.keys(RETURN).map(Number);
|
|
736
|
+
const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
|
|
737
|
+
RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter(({ key }) => !alreadyExistsColumns.includes(key)), Object.keys(RETURN).map(Number), options)));
|
|
738
|
+
if (Config.isCacheEnabled)
|
|
739
|
+
await File.write(cachedFilePath, Array.from(linesNumbers).join(","), true);
|
|
740
|
+
}
|
|
699
741
|
}
|
|
700
742
|
}
|
|
701
743
|
if (!RETURN ||
|
|
702
744
|
(Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
|
|
703
745
|
(Array.isArray(RETURN) && !RETURN.length))
|
|
704
746
|
return null;
|
|
705
|
-
const greatestTotalItems =
|
|
706
|
-
.
|
|
707
|
-
|
|
708
|
-
|
|
747
|
+
const greatestTotalItems = this.totalItems[tableName + "-*"] ??
|
|
748
|
+
Math.max(...Object.entries(this.totalItems)
|
|
749
|
+
.filter(([k]) => k.startsWith(tableName + "-"))
|
|
750
|
+
.map(([, v]) => v));
|
|
751
|
+
this.pageInfo[tableName] = {
|
|
709
752
|
...(({ columns, ...restOfOptions }) => restOfOptions)(options),
|
|
710
|
-
|
|
753
|
+
perPage: Array.isArray(RETURN) ? RETURN.length : 1,
|
|
754
|
+
totalPages: Math.ceil(greatestTotalItems / options.perPage),
|
|
711
755
|
total: greatestTotalItems,
|
|
712
756
|
};
|
|
713
757
|
return onlyOne && Array.isArray(RETURN) ? RETURN[0] : RETURN;
|
|
@@ -716,7 +760,7 @@ export default class Inibase {
|
|
|
716
760
|
if (!options)
|
|
717
761
|
options = {
|
|
718
762
|
page: 1,
|
|
719
|
-
|
|
763
|
+
perPage: 15,
|
|
720
764
|
};
|
|
721
765
|
if (!returnPostedData)
|
|
722
766
|
returnPostedData = false;
|
|
@@ -724,12 +768,22 @@ export default class Inibase {
|
|
|
724
768
|
let RETURN;
|
|
725
769
|
if (!schema)
|
|
726
770
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
727
|
-
const idFilePath = join(this.folder, this.database, tableName, "id.inib"),
|
|
728
|
-
let lastId =
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
771
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib"), cashFolderPath = join(this.folder, this.database, tableName, ".tmp");
|
|
772
|
+
let lastId = 0, totalItems = 0, lastIdObj;
|
|
773
|
+
if (await File.isExists(idFilePath)) {
|
|
774
|
+
if (await File.isExists(join(cashFolderPath, "lastId.inib"))) {
|
|
775
|
+
lastId = Number(await File.read(join(cashFolderPath, "lastId.inib"), true));
|
|
776
|
+
if (await File.isExists(join(cashFolderPath, "totalItems.inib")))
|
|
777
|
+
totalItems = Number(await File.read(join(cashFolderPath, "totalItems.inib"), true));
|
|
778
|
+
else
|
|
779
|
+
totalItems = await File.count(join(this.folder, this.database, tableName, "id.inib"));
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
[lastIdObj, totalItems] = await File.get(idFilePath, -1, "number", undefined, this.salt, true);
|
|
783
|
+
if (lastIdObj)
|
|
784
|
+
lastId = Number(Object.entries(lastIdObj)[0][1]) ?? 0;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
733
787
|
if (Utils.isArrayOfObjects(data))
|
|
734
788
|
RETURN = data.map(({ id, updatedAt, createdAt, ...rest }) => ({
|
|
735
789
|
id: ++lastId,
|
|
@@ -745,21 +799,23 @@ export default class Inibase {
|
|
|
745
799
|
if (!RETURN)
|
|
746
800
|
throw this.throwError("NO_DATA");
|
|
747
801
|
RETURN = this.formatData(RETURN, schema);
|
|
748
|
-
const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), RETURN);
|
|
802
|
+
const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
|
|
749
803
|
const renameList = [];
|
|
750
804
|
for await (const [path, content] of Object.entries(pathesContents))
|
|
751
805
|
renameList.push(await File.append(path, content));
|
|
752
|
-
await
|
|
753
|
-
|
|
806
|
+
for await (const [tempPath, filePath] of renameList)
|
|
807
|
+
await rename(tempPath, filePath);
|
|
808
|
+
await File.write(join(cashFolderPath, "lastId.inib"), lastId.toString(), true);
|
|
809
|
+
await File.write(join(cashFolderPath, "totalItems.inib"), String(totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)), true);
|
|
754
810
|
if (returnPostedData)
|
|
755
811
|
return this.get(tableName, Utils.isArrayOfObjects(RETURN)
|
|
756
812
|
? 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
|
-
);
|
|
813
|
+
: RETURN.id, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
|
|
814
|
+
undefined, schema);
|
|
759
815
|
}
|
|
760
816
|
async put(tableName, data, where, options = {
|
|
761
817
|
page: 1,
|
|
762
|
-
|
|
818
|
+
perPage: 15,
|
|
763
819
|
}, returnPostedData) {
|
|
764
820
|
const schema = await this.getTableSchema(tableName);
|
|
765
821
|
if (!schema)
|
|
@@ -793,8 +849,14 @@ export default class Inibase {
|
|
|
793
849
|
});
|
|
794
850
|
for await (const [path, content] of Object.entries(pathesContents))
|
|
795
851
|
await File.replace(path, content);
|
|
852
|
+
if (Config.isCacheEnabled) {
|
|
853
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
854
|
+
if (cacheFiles.length)
|
|
855
|
+
for await (const file of cacheFiles)
|
|
856
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
857
|
+
}
|
|
796
858
|
if (returnPostedData)
|
|
797
|
-
return this.get(tableName, where, options);
|
|
859
|
+
return this.get(tableName, where, options, undefined, undefined, schema);
|
|
798
860
|
}
|
|
799
861
|
}
|
|
800
862
|
else if ((Array.isArray(where) &&
|
|
@@ -803,7 +865,7 @@ export default class Inibase {
|
|
|
803
865
|
Utils.isNumber(where)) {
|
|
804
866
|
if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
|
|
805
867
|
Utils.isValidID(where)) {
|
|
806
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
868
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
807
869
|
return this.put(tableName, data, lineNumbers);
|
|
808
870
|
}
|
|
809
871
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
@@ -824,13 +886,20 @@ export default class Inibase {
|
|
|
824
886
|
const renameList = [];
|
|
825
887
|
for await (const [path, content] of Object.entries(pathesContents))
|
|
826
888
|
renameList.push(await File.replace(path, content));
|
|
827
|
-
await
|
|
889
|
+
for await (const [tempPath, filePath] of renameList)
|
|
890
|
+
await rename(tempPath, filePath);
|
|
891
|
+
if (Config.isCacheEnabled) {
|
|
892
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
893
|
+
if (cacheFiles.length)
|
|
894
|
+
for await (const file of cacheFiles)
|
|
895
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
896
|
+
}
|
|
828
897
|
if (returnPostedData)
|
|
829
|
-
return this.get(tableName, where, options, !Array.isArray(where));
|
|
898
|
+
return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
|
|
830
899
|
}
|
|
831
900
|
}
|
|
832
901
|
else if (Utils.isObject(where)) {
|
|
833
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
902
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
834
903
|
if (!lineNumbers || !lineNumbers.length)
|
|
835
904
|
throw this.throwError("NO_ITEMS", tableName);
|
|
836
905
|
return this.put(tableName, data, lineNumbers);
|
|
@@ -848,7 +917,14 @@ export default class Inibase {
|
|
|
848
917
|
if (!where) {
|
|
849
918
|
const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
|
|
850
919
|
if (files.length)
|
|
851
|
-
await
|
|
920
|
+
for await (const file of files)
|
|
921
|
+
await unlink(join(this.folder, this.database, tableName, file));
|
|
922
|
+
if (Config.isCacheEnabled) {
|
|
923
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
924
|
+
if (cacheFiles.length)
|
|
925
|
+
for await (const file of cacheFiles)
|
|
926
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
927
|
+
}
|
|
852
928
|
return "*";
|
|
853
929
|
}
|
|
854
930
|
else if ((Array.isArray(where) &&
|
|
@@ -857,7 +933,7 @@ export default class Inibase {
|
|
|
857
933
|
Utils.isNumber(where)) {
|
|
858
934
|
if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
|
|
859
935
|
Utils.isValidID(where)) {
|
|
860
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
936
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
861
937
|
if (!lineNumbers)
|
|
862
938
|
return null;
|
|
863
939
|
return this.delete(tableName, lineNumbers, where);
|
|
@@ -868,19 +944,29 @@ export default class Inibase {
|
|
|
868
944
|
const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
|
|
869
945
|
if (files.length) {
|
|
870
946
|
if (!_id)
|
|
871
|
-
_id = Object.entries((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt))
|
|
947
|
+
_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
948
|
if (!_id.length)
|
|
873
949
|
throw this.throwError("NO_ITEMS", tableName);
|
|
874
950
|
const renameList = [];
|
|
875
951
|
for await (const file of files)
|
|
876
952
|
renameList.push(await File.remove(join(this.folder, this.database, tableName, file), where));
|
|
877
|
-
await
|
|
953
|
+
for await (const [tempPath, filePath] of renameList)
|
|
954
|
+
await rename(tempPath, filePath);
|
|
955
|
+
if (Config.isCacheEnabled) {
|
|
956
|
+
const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
|
|
957
|
+
if (cacheFiles.length)
|
|
958
|
+
for await (const file of cacheFiles)
|
|
959
|
+
await unlink(join(this.folder, this.database, tableName, ".tmp", file));
|
|
960
|
+
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")))
|
|
961
|
+
? Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true))
|
|
962
|
+
: await File.count(join(this.folder, this.database, tableName, "id.inib"))) - (Array.isArray(where) ? where.length : 1)), true);
|
|
963
|
+
}
|
|
878
964
|
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
879
965
|
}
|
|
880
966
|
}
|
|
881
967
|
}
|
|
882
968
|
else if (Utils.isObject(where)) {
|
|
883
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
969
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
884
970
|
if (!lineNumbers || !lineNumbers.length)
|
|
885
971
|
throw this.throwError("NO_ITEMS", tableName);
|
|
886
972
|
return this.delete(tableName, lineNumbers);
|
|
@@ -902,7 +988,7 @@ export default class Inibase {
|
|
|
902
988
|
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
903
989
|
if (await File.isExists(columnPath)) {
|
|
904
990
|
if (where) {
|
|
905
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
991
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
906
992
|
RETURN[column] = lineNumbers
|
|
907
993
|
? await File.sum(columnPath, lineNumbers)
|
|
908
994
|
: 0;
|
|
@@ -926,7 +1012,7 @@ export default class Inibase {
|
|
|
926
1012
|
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
927
1013
|
if (await File.isExists(columnPath)) {
|
|
928
1014
|
if (where) {
|
|
929
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1015
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
930
1016
|
RETURN[column] = lineNumbers
|
|
931
1017
|
? await File.max(columnPath, lineNumbers)
|
|
932
1018
|
: 0;
|
|
@@ -950,7 +1036,7 @@ export default class Inibase {
|
|
|
950
1036
|
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
951
1037
|
if (await File.isExists(columnPath)) {
|
|
952
1038
|
if (where) {
|
|
953
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1039
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
954
1040
|
RETURN[column] = lineNumbers
|
|
955
1041
|
? await File.min(columnPath, lineNumbers)
|
|
956
1042
|
: 0;
|