inibase 1.0.0-rc.12 → 1.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/file.d.ts +33 -0
- package/dist/file.js +501 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +940 -0
- package/dist/utils.d.ts +54 -0
- package/dist/utils.js +301 -0
- package/package.json +21 -9
- package/.env.example +0 -1
- package/file.ts +0 -674
- package/index.test.ts +0 -248
- package/index.ts +0 -1587
- package/tsconfig.json +0 -7
- package/utils.ts +0 -366
package/dist/index.js
ADDED
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
import { unlink, rename, readFile, writeFile, mkdir, readdir, } from "node:fs/promises";
|
|
2
|
+
import { join, parse } from "node:path";
|
|
3
|
+
import { scryptSync } from "node:crypto";
|
|
4
|
+
import File from "./file";
|
|
5
|
+
import Utils from "./utils";
|
|
6
|
+
export default class Inibase {
|
|
7
|
+
folder;
|
|
8
|
+
database;
|
|
9
|
+
table;
|
|
10
|
+
pageInfo;
|
|
11
|
+
cache;
|
|
12
|
+
totalItems;
|
|
13
|
+
salt;
|
|
14
|
+
constructor(database, mainFolder = ".") {
|
|
15
|
+
this.database = database;
|
|
16
|
+
this.folder = mainFolder;
|
|
17
|
+
this.table = null;
|
|
18
|
+
this.cache = new Map();
|
|
19
|
+
this.totalItems = {};
|
|
20
|
+
this.pageInfo = { page: 1, per_page: 15 };
|
|
21
|
+
this.salt = scryptSync(process.env.INIBASE_SECRET ?? "inibase", (process.env.INIBASE_SECRET ?? "inibase") + "_salt", 32);
|
|
22
|
+
}
|
|
23
|
+
throwError(code, variable, language = "en") {
|
|
24
|
+
const errorMessages = {
|
|
25
|
+
en: {
|
|
26
|
+
FIELD_REQUIRED: "REQUIRED: {variable}",
|
|
27
|
+
NO_SCHEMA: "NO_SCHEMA: {variable}",
|
|
28
|
+
NO_ITEMS: "NO_ITEMS: {variable}",
|
|
29
|
+
NO_DATA: "NO_DATA: {variable}",
|
|
30
|
+
INVALID_ID: "INVALID_ID: {variable}",
|
|
31
|
+
INVALID_TYPE: "INVALID_TYPE: {variable}",
|
|
32
|
+
INVALID_OPERATOR: "INVALID_OPERATOR: {variable}",
|
|
33
|
+
INVALID_PARAMETERS: "PARAMETERS: {variable}",
|
|
34
|
+
},
|
|
35
|
+
// Add more languages and error messages as needed
|
|
36
|
+
};
|
|
37
|
+
let errorMessage = errorMessages[language][code] || code;
|
|
38
|
+
if (variable) {
|
|
39
|
+
if (typeof variable === "string" ||
|
|
40
|
+
typeof variable === "number" ||
|
|
41
|
+
Array.isArray(variable))
|
|
42
|
+
errorMessage = errorMessage.replaceAll(`{variable}`, Array.isArray(variable) ? variable.join(", ") : variable);
|
|
43
|
+
else
|
|
44
|
+
Object.keys(variable).forEach((variableKey) => (errorMessage = errorMessage.replaceAll(`{${variableKey}}`, variable[variableKey].toString())));
|
|
45
|
+
}
|
|
46
|
+
return new Error(errorMessage);
|
|
47
|
+
}
|
|
48
|
+
async setTableSchema(tableName, schema) {
|
|
49
|
+
const decodeIdFromSchema = (schema) => schema.map((field) => {
|
|
50
|
+
if (field.children && Utils.isArrayOfObjects(field.children))
|
|
51
|
+
field.children = decodeIdFromSchema(field.children);
|
|
52
|
+
if (!Utils.isNumber(field.id))
|
|
53
|
+
field.id = Utils.decodeID(field.id, this.salt);
|
|
54
|
+
return field;
|
|
55
|
+
});
|
|
56
|
+
// remove id from schema
|
|
57
|
+
schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
|
|
58
|
+
schema = Utils.addIdToSchema(schema, Utils.findLastIdNumber(schema, this.salt), this.salt);
|
|
59
|
+
const TablePath = join(this.folder, this.database, tableName), TableSchemaPath = join(TablePath, "schema.json");
|
|
60
|
+
if (!(await File.isExists(TablePath)))
|
|
61
|
+
await mkdir(TablePath, { recursive: true });
|
|
62
|
+
if (await File.isExists(TableSchemaPath)) {
|
|
63
|
+
// update columns files names based on field id
|
|
64
|
+
const schemaToIdsPath = (schema, prefix = "") => {
|
|
65
|
+
let RETURN = {};
|
|
66
|
+
for (const field of schema)
|
|
67
|
+
if (field.children && Utils.isArrayOfObjects(field.children)) {
|
|
68
|
+
Utils.deepMerge(RETURN, schemaToIdsPath(field.children, (prefix ?? "") + field.key + "."));
|
|
69
|
+
}
|
|
70
|
+
else if (Utils.isValidID(field.id))
|
|
71
|
+
RETURN[Utils.decodeID(field.id, this.salt)] =
|
|
72
|
+
(prefix ?? "") + field.key + ".inib";
|
|
73
|
+
return RETURN;
|
|
74
|
+
}, replaceOldPathes = Utils.findChangedProperties(schemaToIdsPath(await this.getTableSchema(tableName)), schemaToIdsPath(schema));
|
|
75
|
+
if (replaceOldPathes)
|
|
76
|
+
for (const [oldPath, newPath] of Object.entries(replaceOldPathes))
|
|
77
|
+
if (await File.isExists(join(TablePath, oldPath)))
|
|
78
|
+
await rename(join(TablePath, oldPath), join(TablePath, newPath));
|
|
79
|
+
}
|
|
80
|
+
await writeFile(join(TablePath, "schema.json"), JSON.stringify(decodeIdFromSchema(schema)));
|
|
81
|
+
}
|
|
82
|
+
async getTableSchema(tableName) {
|
|
83
|
+
const TableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
|
|
84
|
+
if (!(await File.isExists(TableSchemaPath)))
|
|
85
|
+
return undefined;
|
|
86
|
+
if (!this.cache.has(TableSchemaPath))
|
|
87
|
+
this.cache.set(TableSchemaPath, await readFile(TableSchemaPath, "utf8"));
|
|
88
|
+
if (!this.cache.get(TableSchemaPath))
|
|
89
|
+
return undefined;
|
|
90
|
+
const schema = JSON.parse(this.cache.get(TableSchemaPath)), lastIdNumber = Utils.findLastIdNumber(schema, this.salt);
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
id: Utils.encodeID(0, this.salt),
|
|
94
|
+
key: "id",
|
|
95
|
+
type: "id",
|
|
96
|
+
required: true,
|
|
97
|
+
},
|
|
98
|
+
...Utils.addIdToSchema(schema, lastIdNumber, this.salt),
|
|
99
|
+
{
|
|
100
|
+
id: Utils.encodeID(lastIdNumber + 1, this.salt),
|
|
101
|
+
key: "createdAt",
|
|
102
|
+
type: "date",
|
|
103
|
+
required: true,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: Utils.encodeID(lastIdNumber + 2, this.salt),
|
|
107
|
+
key: "updatedAt",
|
|
108
|
+
type: "date",
|
|
109
|
+
required: false,
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
getField(keyPath, schema, property) {
|
|
114
|
+
const keyPathSplited = keyPath.split(".");
|
|
115
|
+
for (const [index, key] of keyPathSplited.entries()) {
|
|
116
|
+
if (key === "*")
|
|
117
|
+
continue;
|
|
118
|
+
const foundItem = schema.find((item) => item.key === key);
|
|
119
|
+
if (!foundItem)
|
|
120
|
+
return null;
|
|
121
|
+
if (index === keyPathSplited.length - 1)
|
|
122
|
+
schema = foundItem;
|
|
123
|
+
if ((foundItem.type === "array" || foundItem.type === "object") &&
|
|
124
|
+
foundItem.children &&
|
|
125
|
+
Utils.isArrayOfObjects(foundItem.children))
|
|
126
|
+
schema = foundItem.children;
|
|
127
|
+
}
|
|
128
|
+
if (property) {
|
|
129
|
+
switch (property) {
|
|
130
|
+
case "type":
|
|
131
|
+
return schema.type;
|
|
132
|
+
case "children":
|
|
133
|
+
return schema.children;
|
|
134
|
+
default:
|
|
135
|
+
return schema[property];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
return schema;
|
|
140
|
+
}
|
|
141
|
+
validateData(data, schema, skipRequiredField = false) {
|
|
142
|
+
if (Utils.isArrayOfObjects(data))
|
|
143
|
+
for (const single_data of data)
|
|
144
|
+
this.validateData(single_data, schema, skipRequiredField);
|
|
145
|
+
else if (Utils.isObject(data)) {
|
|
146
|
+
for (const { key, type, required, children } of schema) {
|
|
147
|
+
if (!data.hasOwnProperty(key) && required && !skipRequiredField)
|
|
148
|
+
throw this.throwError("FIELD_REQUIRED", key);
|
|
149
|
+
if (data.hasOwnProperty(key) &&
|
|
150
|
+
!Utils.validateFieldType(data[key], type, children && !Utils.isArrayOfObjects(children) ? children : undefined))
|
|
151
|
+
throw this.throwError("INVALID_TYPE", key);
|
|
152
|
+
if ((type === "array" || type === "object") &&
|
|
153
|
+
children &&
|
|
154
|
+
Utils.isArrayOfObjects(children))
|
|
155
|
+
this.validateData(data[key], children, skipRequiredField);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
formatData(data, schema, formatOnlyAvailiableKeys) {
|
|
160
|
+
this.validateData(data, schema, formatOnlyAvailiableKeys);
|
|
161
|
+
const formatField = (value, field) => {
|
|
162
|
+
if (Array.isArray(field.type))
|
|
163
|
+
field.type = Utils.detectFieldType(value, field.type);
|
|
164
|
+
switch (field.type) {
|
|
165
|
+
case "array":
|
|
166
|
+
if (!Array.isArray(value))
|
|
167
|
+
value = [value];
|
|
168
|
+
if (typeof field.children === "string") {
|
|
169
|
+
if (field.type === "array" && field.children === "table") {
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
if (Utils.isArrayOfObjects(value)) {
|
|
172
|
+
if (value.every((item) => item.hasOwnProperty("id") &&
|
|
173
|
+
(Utils.isValidID(item.id) || Utils.isNumber(item.id))))
|
|
174
|
+
value.map((item) => Utils.isNumber(item.id)
|
|
175
|
+
? Number(item.id)
|
|
176
|
+
: Utils.decodeID(item.id, this.salt));
|
|
177
|
+
}
|
|
178
|
+
else if (value.every(Utils.isValidID) ||
|
|
179
|
+
value.every(Utils.isNumber))
|
|
180
|
+
return value.map((item) => Utils.isNumber(item)
|
|
181
|
+
? Number(item)
|
|
182
|
+
: Utils.decodeID(item, this.salt));
|
|
183
|
+
}
|
|
184
|
+
else if (Utils.isValidID(value))
|
|
185
|
+
return [Utils.decodeID(value, this.salt)];
|
|
186
|
+
else if (Utils.isNumber(value))
|
|
187
|
+
return [Number(value)];
|
|
188
|
+
}
|
|
189
|
+
else if (data.hasOwnProperty(field.key))
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
else if (Utils.isArrayOfObjects(field.children))
|
|
193
|
+
return this.formatData(value, field.children, formatOnlyAvailiableKeys);
|
|
194
|
+
else if (Array.isArray(field.children))
|
|
195
|
+
return Array.isArray(value) ? value : [value];
|
|
196
|
+
break;
|
|
197
|
+
case "object":
|
|
198
|
+
if (Utils.isArrayOfObjects(field.children))
|
|
199
|
+
return this.formatData(value, field.children, formatOnlyAvailiableKeys);
|
|
200
|
+
break;
|
|
201
|
+
case "table":
|
|
202
|
+
if (Array.isArray(value))
|
|
203
|
+
value = value[0];
|
|
204
|
+
if (Utils.isObject(value)) {
|
|
205
|
+
if (value.hasOwnProperty("id") &&
|
|
206
|
+
(Utils.isValidID(value.id) ||
|
|
207
|
+
Utils.isNumber(value.id)))
|
|
208
|
+
return Utils.isNumber(value.id)
|
|
209
|
+
? Number(value.id)
|
|
210
|
+
: Utils.decodeID(value.id, this.salt);
|
|
211
|
+
}
|
|
212
|
+
else if (Utils.isValidID(value) || Utils.isNumber(value))
|
|
213
|
+
return Utils.isNumber(value)
|
|
214
|
+
? Number(value)
|
|
215
|
+
: Utils.decodeID(value, this.salt);
|
|
216
|
+
break;
|
|
217
|
+
case "password":
|
|
218
|
+
if (Array.isArray(value))
|
|
219
|
+
value = value[0];
|
|
220
|
+
return typeof value === "string" && value.length === 161
|
|
221
|
+
? value
|
|
222
|
+
: Utils.hashPassword(String(value));
|
|
223
|
+
case "number":
|
|
224
|
+
if (Array.isArray(value))
|
|
225
|
+
value = value[0];
|
|
226
|
+
return Utils.isNumber(value) ? Number(value) : null;
|
|
227
|
+
case "id":
|
|
228
|
+
if (Array.isArray(value))
|
|
229
|
+
value = value[0];
|
|
230
|
+
return Utils.isNumber(value)
|
|
231
|
+
? value
|
|
232
|
+
: Utils.decodeID(value, this.salt);
|
|
233
|
+
default:
|
|
234
|
+
return value;
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
};
|
|
238
|
+
if (Utils.isArrayOfObjects(data))
|
|
239
|
+
return data.map((single_data) => this.formatData(single_data, schema, formatOnlyAvailiableKeys));
|
|
240
|
+
else if (Utils.isObject(data)) {
|
|
241
|
+
let RETURN = {};
|
|
242
|
+
for (const field of schema) {
|
|
243
|
+
if (!data.hasOwnProperty(field.key)) {
|
|
244
|
+
if (formatOnlyAvailiableKeys || !field.required)
|
|
245
|
+
continue;
|
|
246
|
+
RETURN[field.key] = this.getDefaultValue(field);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
RETURN[field.key] = formatField(data[field.key], field);
|
|
250
|
+
}
|
|
251
|
+
return RETURN;
|
|
252
|
+
}
|
|
253
|
+
else
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
getDefaultValue(field) {
|
|
257
|
+
if (Array.isArray(field.type))
|
|
258
|
+
return this.getDefaultValue({
|
|
259
|
+
...field,
|
|
260
|
+
type: field.type.sort((a, b) => Number(b === "array") - Number(a === "array") ||
|
|
261
|
+
Number(a === "string") - Number(b === "string") ||
|
|
262
|
+
Number(a === "number") - Number(b === "number"))[0],
|
|
263
|
+
});
|
|
264
|
+
switch (field.type) {
|
|
265
|
+
case "array":
|
|
266
|
+
return Utils.isArrayOfObjects(field.children)
|
|
267
|
+
? [
|
|
268
|
+
this.getDefaultValue({
|
|
269
|
+
...field,
|
|
270
|
+
type: "object",
|
|
271
|
+
children: field.children,
|
|
272
|
+
}),
|
|
273
|
+
]
|
|
274
|
+
: [];
|
|
275
|
+
case "object":
|
|
276
|
+
return Utils.combineObjects(field.children.map((f) => ({
|
|
277
|
+
[f.key]: this.getDefaultValue(f),
|
|
278
|
+
})));
|
|
279
|
+
case "boolean":
|
|
280
|
+
return false;
|
|
281
|
+
default:
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
joinPathesContentsReplacement(mainPath, data, startingtAt = 1) {
|
|
286
|
+
if (Utils.isArrayOfObjects(data)) {
|
|
287
|
+
return Utils.combineObjects(data.map((single_data) => this.joinPathesContentsReplacement(mainPath, single_data, startingtAt++)));
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
return Object.fromEntries(Object.entries(Utils.objectToDotNotation(data)).map(([key, value]) => [
|
|
291
|
+
join(mainPath, key + ".inib"),
|
|
292
|
+
{ [startingtAt]: value },
|
|
293
|
+
]));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
joinPathesContents(mainPath, data, startWith = 1) {
|
|
297
|
+
const CombineData = (_data, prefix) => {
|
|
298
|
+
let RETURN = {};
|
|
299
|
+
const combineObjectsToArray = (input) => input.reduce((r, c) => (Object.keys(c).map((k) => (r[k] = [...(r[k] || []), c[k]])), r), {});
|
|
300
|
+
if (Utils.isArrayOfObjects(_data))
|
|
301
|
+
RETURN = combineObjectsToArray(_data.map((single_data) => CombineData(single_data)));
|
|
302
|
+
else {
|
|
303
|
+
for (const [key, value] of Object.entries(_data)) {
|
|
304
|
+
if (Utils.isObject(value))
|
|
305
|
+
Object.assign(RETURN, CombineData(value, `${key}.`));
|
|
306
|
+
else if (Utils.isArrayOfObjects(value)) {
|
|
307
|
+
Object.assign(RETURN, CombineData(combineObjectsToArray(value), (prefix ?? "") + key + "."));
|
|
308
|
+
}
|
|
309
|
+
else if (Utils.isArrayOfArrays(value) &&
|
|
310
|
+
value.every(Utils.isArrayOfObjects))
|
|
311
|
+
Object.assign(RETURN, CombineData(combineObjectsToArray(value.map(combineObjectsToArray)), (prefix ?? "") + key + "."));
|
|
312
|
+
else
|
|
313
|
+
RETURN[(prefix ?? "") + key] = File.encode(value);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return RETURN;
|
|
317
|
+
};
|
|
318
|
+
const addPathToKeys = (obj, path) => {
|
|
319
|
+
const newObject = {};
|
|
320
|
+
for (const key in obj)
|
|
321
|
+
newObject[join(path, key + ".inib")] = obj[key];
|
|
322
|
+
return newObject;
|
|
323
|
+
};
|
|
324
|
+
return addPathToKeys(CombineData(data), mainPath);
|
|
325
|
+
}
|
|
326
|
+
async get(tableName, where, options = {
|
|
327
|
+
page: 1,
|
|
328
|
+
per_page: 15,
|
|
329
|
+
}, onlyOne, onlyLinesNumbers) {
|
|
330
|
+
if (!options.columns)
|
|
331
|
+
options.columns = [];
|
|
332
|
+
else if (!Array.isArray(options.columns))
|
|
333
|
+
options.columns = [options.columns];
|
|
334
|
+
if (options.columns.length && !options.columns.includes("id"))
|
|
335
|
+
options.columns.push("id");
|
|
336
|
+
if (!options.page)
|
|
337
|
+
options.page = 1;
|
|
338
|
+
if (!options.per_page)
|
|
339
|
+
options.per_page = 15;
|
|
340
|
+
let RETURN;
|
|
341
|
+
let schema = await this.getTableSchema(tableName);
|
|
342
|
+
if (!schema)
|
|
343
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
344
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
345
|
+
if (!(await File.isExists(idFilePath)))
|
|
346
|
+
return null;
|
|
347
|
+
const filterSchemaByColumns = (schema, columns) => schema
|
|
348
|
+
.map((field) => {
|
|
349
|
+
if (columns.some((column) => column.startsWith("!")))
|
|
350
|
+
return columns.includes("!" + field.key) ? null : field;
|
|
351
|
+
if (columns.includes(field.key) || columns.includes("*"))
|
|
352
|
+
return field;
|
|
353
|
+
if ((field.type === "array" || field.type === "object") &&
|
|
354
|
+
Utils.isArrayOfObjects(field.children) &&
|
|
355
|
+
columns.filter((column) => column.startsWith(field.key + ".") ||
|
|
356
|
+
column.startsWith("!" + field.key + ".")).length) {
|
|
357
|
+
field.children = filterSchemaByColumns(field.children, columns
|
|
358
|
+
.filter((column) => column.startsWith(field.key + ".") ||
|
|
359
|
+
column.startsWith("!" + field.key + "."))
|
|
360
|
+
.map((column) => column.replace(field.key + ".", "")));
|
|
361
|
+
return field;
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
})
|
|
365
|
+
.filter((i) => i);
|
|
366
|
+
if (options.columns.length)
|
|
367
|
+
schema = filterSchemaByColumns(schema, options.columns);
|
|
368
|
+
const getItemsFromSchema = async (path, schema, linesNumber, prefix) => {
|
|
369
|
+
let RETURN = {};
|
|
370
|
+
for (const field of schema) {
|
|
371
|
+
if ((field.type === "array" ||
|
|
372
|
+
(Array.isArray(field.type) &&
|
|
373
|
+
field.type.includes("array"))) &&
|
|
374
|
+
field
|
|
375
|
+
.children) {
|
|
376
|
+
if (Utils.isArrayOfObjects(field
|
|
377
|
+
.children)) {
|
|
378
|
+
if (field
|
|
379
|
+
.children.filter((children) => children.type === "array" &&
|
|
380
|
+
Utils.isArrayOfObjects(children.children)).length) {
|
|
381
|
+
// one of children has array field type and has children array of object = Schema
|
|
382
|
+
Object.entries((await getItemsFromSchema(path, field.children.filter((children) => children.type === "array" &&
|
|
383
|
+
Utils.isArrayOfObjects(children.children)), linesNumber, (prefix ?? "") + field.key + ".")) ?? {}).forEach(([index, item]) => {
|
|
384
|
+
if (Utils.isObject(item)) {
|
|
385
|
+
if (!RETURN[index])
|
|
386
|
+
RETURN[index] = {};
|
|
387
|
+
if (!RETURN[index][field.key])
|
|
388
|
+
RETURN[index][field.key] = [];
|
|
389
|
+
for (const child_field of field.children.filter((children) => children.type === "array" &&
|
|
390
|
+
Utils.isArrayOfObjects(children.children))) {
|
|
391
|
+
if (Utils.isObject(item[child_field.key])) {
|
|
392
|
+
Object.entries(item[child_field.key]).forEach(([key, value]) => {
|
|
393
|
+
for (let _i = 0; _i < value.length; _i++) {
|
|
394
|
+
if (!RETURN[index][field.key][_i])
|
|
395
|
+
RETURN[index][field.key][_i] = {};
|
|
396
|
+
if (!RETURN[index][field.key][_i][child_field.key])
|
|
397
|
+
RETURN[index][field.key][_i][child_field.key] =
|
|
398
|
+
[];
|
|
399
|
+
value[_i].forEach((_element, _index) => {
|
|
400
|
+
if (!RETURN[index][field.key][_i][child_field.key][_index])
|
|
401
|
+
RETURN[index][field.key][_i][child_field.key][_index] = {};
|
|
402
|
+
RETURN[index][field.key][_i][child_field.key][_index][key] = _element;
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
field.children = field
|
|
411
|
+
.children.filter((children) => children.type !== "array" ||
|
|
412
|
+
!Utils.isArrayOfObjects(children.children));
|
|
413
|
+
}
|
|
414
|
+
Object.entries((await getItemsFromSchema(path, field
|
|
415
|
+
.children, linesNumber, (prefix ?? "") + field.key + ".")) ?? {}).forEach(([index, item]) => {
|
|
416
|
+
if (!RETURN[index])
|
|
417
|
+
RETURN[index] = {};
|
|
418
|
+
if (Utils.isObject(item)) {
|
|
419
|
+
if (!Object.values(item).every((i) => i === null)) {
|
|
420
|
+
if (RETURN[index][field.key])
|
|
421
|
+
Object.entries(item).forEach(([key, value], _index) => {
|
|
422
|
+
RETURN[index][field.key] = RETURN[index][field.key].map((_obj, _i) => ({
|
|
423
|
+
..._obj,
|
|
424
|
+
[key]: value[_i],
|
|
425
|
+
}));
|
|
426
|
+
});
|
|
427
|
+
else if (Object.values(item).every(Utils.isArrayOfArrays))
|
|
428
|
+
RETURN[index][field.key] = item;
|
|
429
|
+
else {
|
|
430
|
+
RETURN[index][field.key] = [];
|
|
431
|
+
Object.entries(item).forEach(([key, value]) => {
|
|
432
|
+
for (let _i = 0; _i < value.length; _i++) {
|
|
433
|
+
if (!RETURN[index][field.key][_i])
|
|
434
|
+
RETURN[index][field.key][_i] = {};
|
|
435
|
+
RETURN[index][field.key][_i][key] = value[_i];
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else
|
|
441
|
+
RETURN[index][field.key] = null;
|
|
442
|
+
}
|
|
443
|
+
else
|
|
444
|
+
RETURN[index][field.key] = item;
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
else if (field
|
|
448
|
+
.children === "table" ||
|
|
449
|
+
(Array.isArray(field
|
|
450
|
+
.children) &&
|
|
451
|
+
field
|
|
452
|
+
.children.includes("table"))) {
|
|
453
|
+
if (options.columns)
|
|
454
|
+
options.columns = options.columns
|
|
455
|
+
.filter((column) => column.includes(`${field.key}.`))
|
|
456
|
+
.map((column) => column.replace(`${field.key}.`, ""));
|
|
457
|
+
const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field
|
|
458
|
+
.children, this.salt);
|
|
459
|
+
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
460
|
+
for (const [index, item] of Object.entries(items)) {
|
|
461
|
+
if (!RETURN[index])
|
|
462
|
+
RETURN[index] = {};
|
|
463
|
+
RETURN[index][field.key] = item
|
|
464
|
+
? await this.get(field.key, item, options)
|
|
465
|
+
: this.getDefaultValue(field);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
|
|
469
|
+
const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
470
|
+
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
471
|
+
for (const [index, item] of Object.entries(items)) {
|
|
472
|
+
if (!RETURN[index])
|
|
473
|
+
RETURN[index] = {};
|
|
474
|
+
RETURN[index][field.key] = item ?? this.getDefaultValue(field);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
else if (field.type === "object") {
|
|
479
|
+
for (const [index, item] of Object.entries((await getItemsFromSchema(path, field.children, linesNumber, (prefix ?? "") + field.key + ".")) ?? {})) {
|
|
480
|
+
if (!RETURN[index])
|
|
481
|
+
RETURN[index] = {};
|
|
482
|
+
if (Utils.isObject(item)) {
|
|
483
|
+
if (!Object.values(item).every((i) => i === null))
|
|
484
|
+
RETURN[index][field.key] = item;
|
|
485
|
+
else
|
|
486
|
+
RETURN[index][field.key] = null;
|
|
487
|
+
}
|
|
488
|
+
else
|
|
489
|
+
RETURN[index][field.key] = null;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
else if (field.type === "table") {
|
|
493
|
+
if ((await File.isExists(join(this.folder, this.database, field.key))) &&
|
|
494
|
+
(await File.isExists(join(path, (prefix ?? "") + field.key + ".inib")))) {
|
|
495
|
+
if (options.columns)
|
|
496
|
+
options.columns = options.columns
|
|
497
|
+
.filter((column) => column.includes(`${field.key}.`) &&
|
|
498
|
+
!column.includes(`${field.key}.`))
|
|
499
|
+
.map((column) => column.replace(`${field.key}.`, ""));
|
|
500
|
+
const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
|
|
501
|
+
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
502
|
+
for (const [index, item] of Object.entries(items)) {
|
|
503
|
+
if (!RETURN[index])
|
|
504
|
+
RETURN[index] = {};
|
|
505
|
+
RETURN[index][field.key] = item
|
|
506
|
+
? await this.get(field.key, item, options)
|
|
507
|
+
: this.getDefaultValue(field);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
|
|
512
|
+
const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
|
|
513
|
+
this.totalItems[tableName + "-" + field.key] = total_lines;
|
|
514
|
+
for (const [index, item] of Object.entries(items)) {
|
|
515
|
+
if (!RETURN[index])
|
|
516
|
+
RETURN[index] = {};
|
|
517
|
+
RETURN[index][field.key] = item ?? this.getDefaultValue(field);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return RETURN;
|
|
522
|
+
};
|
|
523
|
+
if (!where) {
|
|
524
|
+
// Display all data
|
|
525
|
+
RETURN = Object.values(await getItemsFromSchema(join(this.folder, this.database, tableName), schema, Array.from({ length: options.per_page }, (_, index) => (options.page - 1) * options.per_page +
|
|
526
|
+
index +
|
|
527
|
+
1)));
|
|
528
|
+
}
|
|
529
|
+
else if ((Array.isArray(where) &&
|
|
530
|
+
(where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
|
|
531
|
+
Utils.isValidID(where) ||
|
|
532
|
+
Utils.isNumber(where)) {
|
|
533
|
+
let Ids = where;
|
|
534
|
+
if (!Array.isArray(Ids))
|
|
535
|
+
Ids = [Ids];
|
|
536
|
+
const [lineNumbers, countItems] = await File.search(idFilePath, "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : Utils.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, false, this.salt);
|
|
537
|
+
if (!lineNumbers || !Object.keys(lineNumbers).length)
|
|
538
|
+
throw this.throwError("INVALID_ID", where);
|
|
539
|
+
if (onlyLinesNumbers)
|
|
540
|
+
return Object.keys(lineNumbers).map(Number);
|
|
541
|
+
RETURN = Object.values((await getItemsFromSchema(join(this.folder, this.database, tableName), schema, Object.keys(lineNumbers).map(Number))) ?? {});
|
|
542
|
+
if (RETURN.length && !Array.isArray(where))
|
|
543
|
+
RETURN = RETURN[0];
|
|
544
|
+
}
|
|
545
|
+
else if (Utils.isObject(where)) {
|
|
546
|
+
// Criteria
|
|
547
|
+
const FormatObjectCriteriaValue = (value, isParentArray = false) => {
|
|
548
|
+
switch (value[0]) {
|
|
549
|
+
case ">":
|
|
550
|
+
case "<":
|
|
551
|
+
case "[":
|
|
552
|
+
return ["=", "]", "*"].includes(value[1])
|
|
553
|
+
? [
|
|
554
|
+
value.slice(0, 2),
|
|
555
|
+
value.slice(2),
|
|
556
|
+
]
|
|
557
|
+
: [
|
|
558
|
+
value.slice(0, 1),
|
|
559
|
+
value.slice(1),
|
|
560
|
+
];
|
|
561
|
+
case "!":
|
|
562
|
+
return ["=", "*"].includes(value[1])
|
|
563
|
+
? [
|
|
564
|
+
value.slice(0, 2),
|
|
565
|
+
value.slice(2),
|
|
566
|
+
]
|
|
567
|
+
: value[1] === "["
|
|
568
|
+
? [
|
|
569
|
+
value.slice(0, 3),
|
|
570
|
+
value.slice(3),
|
|
571
|
+
]
|
|
572
|
+
: [
|
|
573
|
+
(value.slice(0, 1) + "="),
|
|
574
|
+
value.slice(1),
|
|
575
|
+
];
|
|
576
|
+
case "=":
|
|
577
|
+
return isParentArray
|
|
578
|
+
? [
|
|
579
|
+
value.slice(0, 1),
|
|
580
|
+
value.slice(1),
|
|
581
|
+
]
|
|
582
|
+
: [
|
|
583
|
+
value.slice(0, 1),
|
|
584
|
+
(value.slice(1) + ","),
|
|
585
|
+
];
|
|
586
|
+
case "*":
|
|
587
|
+
return [
|
|
588
|
+
value.slice(0, 1),
|
|
589
|
+
value.slice(1),
|
|
590
|
+
];
|
|
591
|
+
default:
|
|
592
|
+
return ["=", value];
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
const applyCriteria = async (criteria, allTrue) => {
|
|
596
|
+
let RETURN = {};
|
|
597
|
+
if (!criteria)
|
|
598
|
+
return null;
|
|
599
|
+
if (criteria.and && Utils.isObject(criteria.and)) {
|
|
600
|
+
const searchResult = await applyCriteria(criteria.and, true);
|
|
601
|
+
if (searchResult) {
|
|
602
|
+
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).length ===
|
|
603
|
+
Object.keys(criteria.and ?? {}).length)));
|
|
604
|
+
delete criteria.and;
|
|
605
|
+
}
|
|
606
|
+
else
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
if (criteria.or && Utils.isObject(criteria.or)) {
|
|
610
|
+
const searchResult = await applyCriteria(criteria.or, false);
|
|
611
|
+
delete criteria.or;
|
|
612
|
+
if (searchResult)
|
|
613
|
+
RETURN = Utils.deepMerge(RETURN, searchResult);
|
|
614
|
+
}
|
|
615
|
+
if (Object.keys(criteria).length > 0) {
|
|
616
|
+
if (allTrue === undefined)
|
|
617
|
+
allTrue = true;
|
|
618
|
+
let index = -1;
|
|
619
|
+
for (const [key, value] of Object.entries(criteria)) {
|
|
620
|
+
const field = this.getField(key, schema);
|
|
621
|
+
index++;
|
|
622
|
+
let searchOperator = undefined, searchComparedAtValue = undefined, searchLogicalOperator = undefined;
|
|
623
|
+
if (Utils.isObject(value)) {
|
|
624
|
+
if (value?.or &&
|
|
625
|
+
Array.isArray(value.or)) {
|
|
626
|
+
const searchCriteria = value.or
|
|
627
|
+
.map((single_or) => typeof single_or === "string"
|
|
628
|
+
? FormatObjectCriteriaValue(single_or)
|
|
629
|
+
: ["=", single_or])
|
|
630
|
+
.filter((a) => a);
|
|
631
|
+
if (searchCriteria.length > 0) {
|
|
632
|
+
searchOperator = searchCriteria.map((single_or) => single_or[0]);
|
|
633
|
+
searchComparedAtValue = searchCriteria.map((single_or) => single_or[1]);
|
|
634
|
+
searchLogicalOperator = "or";
|
|
635
|
+
}
|
|
636
|
+
delete value.or;
|
|
637
|
+
}
|
|
638
|
+
if (value?.and &&
|
|
639
|
+
Array.isArray(value.and)) {
|
|
640
|
+
const searchCriteria = value.and
|
|
641
|
+
.map((single_and) => typeof single_and === "string"
|
|
642
|
+
? FormatObjectCriteriaValue(single_and)
|
|
643
|
+
: ["=", single_and])
|
|
644
|
+
.filter((a) => a);
|
|
645
|
+
if (searchCriteria.length > 0) {
|
|
646
|
+
searchOperator = searchCriteria.map((single_and) => single_and[0]);
|
|
647
|
+
searchComparedAtValue = searchCriteria.map((single_and) => single_and[1]);
|
|
648
|
+
searchLogicalOperator = "and";
|
|
649
|
+
}
|
|
650
|
+
delete value.and;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else if (Array.isArray(value)) {
|
|
654
|
+
const searchCriteria = value
|
|
655
|
+
.map((single) => typeof single === "string"
|
|
656
|
+
? FormatObjectCriteriaValue(single)
|
|
657
|
+
: ["=", single])
|
|
658
|
+
.filter((a) => a);
|
|
659
|
+
if (searchCriteria.length > 0) {
|
|
660
|
+
searchOperator = searchCriteria.map((single) => single[0]);
|
|
661
|
+
searchComparedAtValue = searchCriteria.map((single) => single[1]);
|
|
662
|
+
searchLogicalOperator = "and";
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else if (typeof value === "string") {
|
|
666
|
+
const ComparisonOperatorValue = FormatObjectCriteriaValue(value);
|
|
667
|
+
if (ComparisonOperatorValue) {
|
|
668
|
+
searchOperator = ComparisonOperatorValue[0];
|
|
669
|
+
searchComparedAtValue = ComparisonOperatorValue[1];
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
searchOperator = "=";
|
|
674
|
+
searchComparedAtValue = value;
|
|
675
|
+
}
|
|
676
|
+
const [searchResult, total_lines] = await File.search(join(this.folder, this.database, tableName, key + ".inib"), searchOperator, searchComparedAtValue, searchLogicalOperator, field?.type, field?.children, options.per_page, options.page - 1 * options.per_page + 1, true, this.salt);
|
|
677
|
+
if (searchResult) {
|
|
678
|
+
RETURN = Utils.deepMerge(RETURN, searchResult);
|
|
679
|
+
this.totalItems[tableName + "-" + key] = total_lines;
|
|
680
|
+
}
|
|
681
|
+
if (allTrue && index > 0) {
|
|
682
|
+
if (!Object.keys(RETURN).length)
|
|
683
|
+
RETURN = {};
|
|
684
|
+
RETURN = Object.fromEntries(Object.entries(RETURN).filter(([_index, item]) => Object.keys(item).length > index));
|
|
685
|
+
if (!Object.keys(RETURN).length)
|
|
686
|
+
RETURN = {};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return Object.keys(RETURN).length ? RETURN : null;
|
|
691
|
+
};
|
|
692
|
+
RETURN = await applyCriteria(where);
|
|
693
|
+
if (RETURN) {
|
|
694
|
+
if (onlyLinesNumbers)
|
|
695
|
+
return Object.keys(RETURN).map(Number);
|
|
696
|
+
const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]).map((key) => parse(key).name);
|
|
697
|
+
RETURN = Object.values(Utils.deepMerge(RETURN, await getItemsFromSchema(join(this.folder, this.database, tableName), schema.filter((field) => !alreadyExistsColumns.includes(field.key)), Object.keys(RETURN).map(Number))));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (!RETURN ||
|
|
701
|
+
(Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
|
|
702
|
+
(Array.isArray(RETURN) && !RETURN.length))
|
|
703
|
+
return null;
|
|
704
|
+
const greatestTotalItems = Math.max(...Object.entries(this.totalItems)
|
|
705
|
+
.filter(([k]) => k.startsWith(tableName + "-"))
|
|
706
|
+
.map(([, v]) => v));
|
|
707
|
+
this.pageInfo = {
|
|
708
|
+
...(({ columns, ...restOfOptions }) => restOfOptions)(options),
|
|
709
|
+
total_pages: Math.ceil(greatestTotalItems / options.per_page),
|
|
710
|
+
total: greatestTotalItems,
|
|
711
|
+
};
|
|
712
|
+
return onlyOne ? RETURN[0] : RETURN;
|
|
713
|
+
}
|
|
714
|
+
async post(tableName, data, options = {
|
|
715
|
+
page: 1,
|
|
716
|
+
per_page: 15,
|
|
717
|
+
}, returnPostedData = true) {
|
|
718
|
+
const schema = await this.getTableSchema(tableName);
|
|
719
|
+
let RETURN;
|
|
720
|
+
if (!schema)
|
|
721
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
722
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
723
|
+
let [last_line_number, last_id] = (await File.isExists(idFilePath))
|
|
724
|
+
? Object.entries((await File.get(idFilePath, -1, "number", undefined, this.salt))[0])[0] ?? [0, 0]
|
|
725
|
+
: [0, 0];
|
|
726
|
+
if (Utils.isArrayOfObjects(data))
|
|
727
|
+
RETURN = data.map(({ id, updatedAt, createdAt, ...rest }) => ({
|
|
728
|
+
id: ++last_id,
|
|
729
|
+
...rest,
|
|
730
|
+
createdAt: new Date(),
|
|
731
|
+
}));
|
|
732
|
+
else
|
|
733
|
+
RETURN = (({ id, updatedAt, createdAt, ...rest }) => ({
|
|
734
|
+
id: ++last_id,
|
|
735
|
+
...rest,
|
|
736
|
+
createdAt: new Date(),
|
|
737
|
+
}))(data);
|
|
738
|
+
if (!RETURN)
|
|
739
|
+
throw this.throwError("NO_DATA");
|
|
740
|
+
RETURN = this.formatData(RETURN, schema);
|
|
741
|
+
const pathesContents = this.joinPathesContentsReplacement(join(this.folder, this.database, tableName), RETURN, ++last_line_number);
|
|
742
|
+
for await (const [path, content] of Object.entries(pathesContents))
|
|
743
|
+
await File.replace(path, content, this.salt);
|
|
744
|
+
if (returnPostedData)
|
|
745
|
+
return this.get(tableName, Utils.isArrayOfObjects(RETURN)
|
|
746
|
+
? RETURN.map((data) => data.id)
|
|
747
|
+
: RETURN.id, options, !Utils.isArrayOfObjects(data) // return only one item if data is not array of objects
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
async put(tableName, data, where, options = {
|
|
751
|
+
page: 1,
|
|
752
|
+
per_page: 15,
|
|
753
|
+
}, returnPostedData) {
|
|
754
|
+
const schema = await this.getTableSchema(tableName);
|
|
755
|
+
if (!schema)
|
|
756
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
757
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
758
|
+
if (!(await File.isExists(idFilePath)))
|
|
759
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
760
|
+
data = this.formatData(data, schema, true);
|
|
761
|
+
if (!where) {
|
|
762
|
+
if (Utils.isArrayOfObjects(data)) {
|
|
763
|
+
if (!data.every((item) => item.hasOwnProperty("id") && Utils.isValidID(item.id)))
|
|
764
|
+
throw this.throwError("INVALID_ID");
|
|
765
|
+
return this.put(tableName, data, data.map((item) => item.id));
|
|
766
|
+
}
|
|
767
|
+
else if (data.hasOwnProperty("id")) {
|
|
768
|
+
if (!Utils.isValidID(data.id))
|
|
769
|
+
throw this.throwError("INVALID_ID", data.id);
|
|
770
|
+
return this.put(tableName, data, Utils.decodeID(data.id, this.salt));
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), Utils.isArrayOfObjects(data)
|
|
774
|
+
? data.map((item) => ({
|
|
775
|
+
...(({ id, ...restOfData }) => restOfData)(item),
|
|
776
|
+
updatedAt: new Date(),
|
|
777
|
+
}))
|
|
778
|
+
: {
|
|
779
|
+
...(({ id, ...restOfData }) => restOfData)(data),
|
|
780
|
+
updatedAt: new Date(),
|
|
781
|
+
});
|
|
782
|
+
for await (const [path, content] of Object.entries(pathesContents))
|
|
783
|
+
await File.replace(path, content, this.salt);
|
|
784
|
+
if (returnPostedData)
|
|
785
|
+
return this.get(tableName, where, options);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
else if ((Array.isArray(where) &&
|
|
789
|
+
(where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
|
|
790
|
+
Utils.isValidID(where) ||
|
|
791
|
+
Utils.isNumber(where)) {
|
|
792
|
+
if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
|
|
793
|
+
Utils.isValidID(where)) {
|
|
794
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
795
|
+
return this.put(tableName, data, lineNumbers);
|
|
796
|
+
}
|
|
797
|
+
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
798
|
+
Utils.isNumber(where)) {
|
|
799
|
+
// "where" in this case, is the line(s) number(s) and not id(s)
|
|
800
|
+
const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(join(this.folder, this.database, tableName), Utils.isArrayOfObjects(data)
|
|
801
|
+
? data.map((item) => ({
|
|
802
|
+
...item,
|
|
803
|
+
updatedAt: new Date(),
|
|
804
|
+
}))
|
|
805
|
+
: { ...data, updatedAt: new Date() })).map(([key, value]) => [
|
|
806
|
+
key,
|
|
807
|
+
[...(Array.isArray(where) ? where : [where])].reduce((obj, key, index) => ({
|
|
808
|
+
...obj,
|
|
809
|
+
[key]: Array.isArray(value) ? value[index] : value,
|
|
810
|
+
}), {}),
|
|
811
|
+
]));
|
|
812
|
+
for await (const [path, content] of Object.entries(pathesContents))
|
|
813
|
+
await File.replace(path, content, this.salt);
|
|
814
|
+
if (returnPostedData)
|
|
815
|
+
return this.get(tableName, where, options, !Array.isArray(where));
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
else if (typeof where === "object") {
|
|
819
|
+
const lineNumbers = this.get(tableName, where, undefined, undefined, true);
|
|
820
|
+
if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
|
|
821
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
822
|
+
return this.put(tableName, data, lineNumbers);
|
|
823
|
+
}
|
|
824
|
+
else
|
|
825
|
+
throw this.throwError("INVALID_PARAMETERS", tableName);
|
|
826
|
+
}
|
|
827
|
+
async delete(tableName, where, _id) {
|
|
828
|
+
const schema = await this.getTableSchema(tableName);
|
|
829
|
+
if (!schema)
|
|
830
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
831
|
+
const idFilePath = join(this.folder, this.database, tableName, "id.inib");
|
|
832
|
+
if (!(await File.isExists(idFilePath)))
|
|
833
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
834
|
+
if (!where) {
|
|
835
|
+
const files = await readdir(join(this.folder, this.database, tableName));
|
|
836
|
+
if (files.length) {
|
|
837
|
+
for (const file in files.filter((fileName) => fileName !== "schema.json"))
|
|
838
|
+
await unlink(join(this.folder, this.database, tableName, file));
|
|
839
|
+
}
|
|
840
|
+
return "*";
|
|
841
|
+
}
|
|
842
|
+
else if ((Array.isArray(where) &&
|
|
843
|
+
(where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
|
|
844
|
+
Utils.isValidID(where) ||
|
|
845
|
+
Utils.isNumber(where)) {
|
|
846
|
+
if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
|
|
847
|
+
Utils.isValidID(where)) {
|
|
848
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
849
|
+
return this.delete(tableName, lineNumbers, where);
|
|
850
|
+
}
|
|
851
|
+
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
852
|
+
Utils.isNumber(where)) {
|
|
853
|
+
// "where" in this case, is the line(s) number(s) and not id(s)
|
|
854
|
+
const files = await readdir(join(this.folder, this.database, tableName));
|
|
855
|
+
if (files.length) {
|
|
856
|
+
if (!_id)
|
|
857
|
+
_id = Object.values((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt))[0]).map((id) => Utils.encodeID(Number(id), this.salt));
|
|
858
|
+
for (const file of files.filter((fileName) => fileName.endsWith(".inib")))
|
|
859
|
+
await File.remove(join(this.folder, this.database, tableName, file), where);
|
|
860
|
+
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else if (typeof where === "object" && !Array.isArray(where)) {
|
|
865
|
+
const lineNumbers = this.get(tableName, where, undefined, undefined, true);
|
|
866
|
+
if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
|
|
867
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
868
|
+
return this.delete(tableName, lineNumbers);
|
|
869
|
+
}
|
|
870
|
+
else
|
|
871
|
+
throw this.throwError("INVALID_PARAMETERS", tableName);
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
async sum(tableName, columns, where) {
|
|
875
|
+
let RETURN;
|
|
876
|
+
const schema = await this.getTableSchema(tableName);
|
|
877
|
+
if (!schema)
|
|
878
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
879
|
+
if (!(await File.isExists(join(this.folder, this.database, tableName, "id.inib"))))
|
|
880
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
881
|
+
if (!Array.isArray(columns))
|
|
882
|
+
columns = [columns];
|
|
883
|
+
for await (const column of columns) {
|
|
884
|
+
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
885
|
+
if (await File.isExists(columnPath)) {
|
|
886
|
+
if (where) {
|
|
887
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
888
|
+
RETURN[column] = await File.sum(columnPath, lineNumbers);
|
|
889
|
+
}
|
|
890
|
+
else
|
|
891
|
+
RETURN[column] = await File.sum(columnPath);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return Array.isArray(columns) ? RETURN : Object.values(RETURN)[0];
|
|
895
|
+
}
|
|
896
|
+
async max(tableName, columns, where) {
|
|
897
|
+
let RETURN;
|
|
898
|
+
const schema = await this.getTableSchema(tableName);
|
|
899
|
+
if (!schema)
|
|
900
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
901
|
+
if (!(await File.isExists(join(this.folder, this.database, tableName, "id.inib"))))
|
|
902
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
903
|
+
if (!Array.isArray(columns))
|
|
904
|
+
columns = [columns];
|
|
905
|
+
for await (const column of columns) {
|
|
906
|
+
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
907
|
+
if (await File.isExists(columnPath)) {
|
|
908
|
+
if (where) {
|
|
909
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
910
|
+
RETURN[column] = await File.max(columnPath, lineNumbers);
|
|
911
|
+
}
|
|
912
|
+
else
|
|
913
|
+
RETURN[column] = await File.max(columnPath);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return RETURN;
|
|
917
|
+
}
|
|
918
|
+
async min(tableName, columns, where) {
|
|
919
|
+
let RETURN;
|
|
920
|
+
const schema = await this.getTableSchema(tableName);
|
|
921
|
+
if (!schema)
|
|
922
|
+
throw this.throwError("NO_SCHEMA", tableName);
|
|
923
|
+
if (!(await File.isExists(join(this.folder, this.database, tableName, "id.inib"))))
|
|
924
|
+
throw this.throwError("NO_ITEMS", tableName);
|
|
925
|
+
if (!Array.isArray(columns))
|
|
926
|
+
columns = [columns];
|
|
927
|
+
for await (const column of columns) {
|
|
928
|
+
const columnPath = join(this.folder, this.database, tableName, column + ".inib");
|
|
929
|
+
if (await File.isExists(columnPath)) {
|
|
930
|
+
if (where) {
|
|
931
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
932
|
+
RETURN[column] = await File.min(columnPath, lineNumbers);
|
|
933
|
+
}
|
|
934
|
+
else
|
|
935
|
+
RETURN[column] = await File.min(columnPath);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return RETURN;
|
|
939
|
+
}
|
|
940
|
+
}
|