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