inibase 1.0.0-rc.3 → 1.0.0-rc.5
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 +16 -6
- package/file.ts +240 -85
- package/index.test.ts +248 -0
- package/index.ts +726 -436
- package/package.json +3 -2
- package/tsconfig.json +2 -1
- package/utils.ts +99 -44
package/index.ts
CHANGED
|
@@ -9,12 +9,9 @@ import {
|
|
|
9
9
|
renameSync,
|
|
10
10
|
} from "fs";
|
|
11
11
|
import { join, parse } from "path";
|
|
12
|
-
import { createDecipheriv, createCipheriv, scryptSync } from "crypto";
|
|
13
12
|
import Utils from "./utils";
|
|
14
13
|
import File from "./file";
|
|
15
14
|
|
|
16
|
-
export { File, Utils };
|
|
17
|
-
|
|
18
15
|
export type Data = {
|
|
19
16
|
id?: number | string;
|
|
20
17
|
[key: string]: any;
|
|
@@ -33,31 +30,45 @@ export type FieldType =
|
|
|
33
30
|
| "object"
|
|
34
31
|
| "array"
|
|
35
32
|
| "password";
|
|
36
|
-
type
|
|
33
|
+
type FieldDefault = {
|
|
37
34
|
id?: string | number | null | undefined;
|
|
38
35
|
key: string;
|
|
39
36
|
required?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
};
|
|
38
|
+
type FieldStringType = {
|
|
39
|
+
type: Exclude<FieldType, "array" | "object">;
|
|
40
|
+
};
|
|
41
|
+
type FieldStringArrayType = {
|
|
42
|
+
type: Exclude<FieldType, "array" | "object">[];
|
|
43
|
+
};
|
|
44
|
+
type FieldArrayType = {
|
|
45
|
+
type: "array";
|
|
46
|
+
children: FieldType | FieldType[] | Schema;
|
|
47
|
+
};
|
|
48
|
+
type FieldArrayArrayType = {
|
|
49
|
+
type: ["array", ...FieldType[]];
|
|
50
|
+
children: FieldType | FieldType[];
|
|
51
|
+
};
|
|
52
|
+
type FieldObjectType = {
|
|
53
|
+
type: "object";
|
|
54
|
+
children: Schema;
|
|
55
|
+
};
|
|
56
|
+
// if "type" is array, make "array" at first place, and "number" & "string" at last place of the array
|
|
57
|
+
type Field = FieldDefault &
|
|
58
|
+
(
|
|
59
|
+
| FieldStringType
|
|
60
|
+
| FieldStringArrayType
|
|
61
|
+
| FieldObjectType
|
|
62
|
+
| FieldArrayType
|
|
63
|
+
| FieldArrayArrayType
|
|
64
|
+
);
|
|
54
65
|
|
|
55
66
|
export type Schema = Field[];
|
|
56
67
|
|
|
57
68
|
export interface Options {
|
|
58
69
|
page?: number;
|
|
59
70
|
per_page?: number;
|
|
60
|
-
columns?: string[];
|
|
71
|
+
columns?: string[] | string;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
export type ComparisonOperator =
|
|
@@ -79,13 +90,23 @@ type pageInfo = {
|
|
|
79
90
|
|
|
80
91
|
export type Criteria =
|
|
81
92
|
| {
|
|
82
|
-
[logic in "and" | "or"]?: Criteria;
|
|
93
|
+
[logic in "and" | "or"]?: Criteria | (string | number | boolean | null)[];
|
|
83
94
|
}
|
|
84
95
|
| {
|
|
85
96
|
[key: string]: string | number | boolean | Criteria;
|
|
86
97
|
}
|
|
87
98
|
| null;
|
|
88
99
|
|
|
100
|
+
declare global {
|
|
101
|
+
type Entries<T> = {
|
|
102
|
+
[K in keyof T]: [K, T[K]];
|
|
103
|
+
}[keyof T][];
|
|
104
|
+
|
|
105
|
+
interface ObjectConstructor {
|
|
106
|
+
entries<T extends object>(o: T): Entries<T>;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
89
110
|
export default class Inibase {
|
|
90
111
|
public database: string;
|
|
91
112
|
public databasePath: string;
|
|
@@ -93,7 +114,7 @@ export default class Inibase {
|
|
|
93
114
|
public pageInfoArray: Record<string, Record<string, number>>;
|
|
94
115
|
public pageInfo: pageInfo;
|
|
95
116
|
|
|
96
|
-
constructor(databaseName: string, mainFolder: string = "
|
|
117
|
+
constructor(databaseName: string, mainFolder: string = ".") {
|
|
97
118
|
this.database = databaseName;
|
|
98
119
|
this.databasePath = join(mainFolder, databaseName);
|
|
99
120
|
this.cache = new Map<string, any>();
|
|
@@ -112,14 +133,14 @@ export default class Inibase {
|
|
|
112
133
|
): Error {
|
|
113
134
|
const errorMessages: Record<string, Record<string, string>> = {
|
|
114
135
|
en: {
|
|
136
|
+
FIELD_REQUIRED: "REQUIRED: {variable}",
|
|
115
137
|
NO_SCHEMA: "NO_SCHEMA: {variable}",
|
|
116
138
|
NO_ITEMS: "NO_ITEMS: {variable}",
|
|
139
|
+
NO_DATA: "NO_DATA: {variable}",
|
|
117
140
|
INVALID_ID: "INVALID_ID: {variable}",
|
|
118
141
|
INVALID_TYPE: "INVALID_TYPE: {variable}",
|
|
119
|
-
REQUIRED: "REQUIRED: {variable}",
|
|
120
|
-
NO_DATA: "NO_DATA: {variable}",
|
|
121
142
|
INVALID_OPERATOR: "INVALID_OPERATOR: {variable}",
|
|
122
|
-
|
|
143
|
+
INVALID_PARAMETERS: "PARAMETERS: {variable}",
|
|
123
144
|
},
|
|
124
145
|
// Add more languages and error messages as needed
|
|
125
146
|
};
|
|
@@ -147,131 +168,6 @@ export default class Inibase {
|
|
|
147
168
|
return new Error(errorMessage);
|
|
148
169
|
}
|
|
149
170
|
|
|
150
|
-
public encodeID(id: number, secretKey?: string | number): string {
|
|
151
|
-
if (!secretKey) secretKey = this.databasePath;
|
|
152
|
-
|
|
153
|
-
const salt = scryptSync(secretKey.toString(), "salt", 32),
|
|
154
|
-
cipher = createCipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
|
|
155
|
-
|
|
156
|
-
return cipher.update(id.toString(), "utf8", "hex") + cipher.final("hex");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public decodeID(input: string, secretKey?: string | number): number {
|
|
160
|
-
if (!secretKey) secretKey = this.databasePath;
|
|
161
|
-
const salt = scryptSync(secretKey.toString(), "salt", 32),
|
|
162
|
-
decipher = createDecipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
|
|
163
|
-
return Number(
|
|
164
|
-
decipher.update(input as string, "hex", "utf8") + decipher.final("utf8")
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
public isValidID(input: any): boolean {
|
|
169
|
-
return Array.isArray(input)
|
|
170
|
-
? input.every(this.isValidID)
|
|
171
|
-
: typeof input === "string" && input.length === 32;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
public validateData(
|
|
175
|
-
data: Data | Data[],
|
|
176
|
-
schema: Schema,
|
|
177
|
-
skipRequiredField: boolean = false
|
|
178
|
-
): void {
|
|
179
|
-
if (Utils.isArrayOfObjects(data))
|
|
180
|
-
for (const single_data of data as Data[])
|
|
181
|
-
this.validateData(single_data, schema, skipRequiredField);
|
|
182
|
-
else if (!Array.isArray(data)) {
|
|
183
|
-
const validateFieldType = (
|
|
184
|
-
value: any,
|
|
185
|
-
field: Field | FieldType | FieldType[]
|
|
186
|
-
): boolean => {
|
|
187
|
-
if (Array.isArray(field))
|
|
188
|
-
return field.some((item) => validateFieldType(value, item));
|
|
189
|
-
switch (typeof field === "string" ? field : field.type) {
|
|
190
|
-
case "string":
|
|
191
|
-
return value === null || typeof value === "string";
|
|
192
|
-
case "number":
|
|
193
|
-
return value === null || typeof value === "number";
|
|
194
|
-
case "boolean":
|
|
195
|
-
return (
|
|
196
|
-
value === null ||
|
|
197
|
-
typeof value === "boolean" ||
|
|
198
|
-
value === "true" ||
|
|
199
|
-
value === "false"
|
|
200
|
-
);
|
|
201
|
-
case "date":
|
|
202
|
-
return value === null || value instanceof Date;
|
|
203
|
-
case "object":
|
|
204
|
-
return (
|
|
205
|
-
value === null ||
|
|
206
|
-
(typeof value === "object" &&
|
|
207
|
-
!Array.isArray(value) &&
|
|
208
|
-
value !== null)
|
|
209
|
-
);
|
|
210
|
-
case "array":
|
|
211
|
-
return (
|
|
212
|
-
value === null ||
|
|
213
|
-
(Array.isArray(value) &&
|
|
214
|
-
value.every((item) => validateFieldType(item, field)))
|
|
215
|
-
);
|
|
216
|
-
case "email":
|
|
217
|
-
return (
|
|
218
|
-
value === null ||
|
|
219
|
-
(typeof value === "string" &&
|
|
220
|
-
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
|
|
221
|
-
);
|
|
222
|
-
case "url":
|
|
223
|
-
return (
|
|
224
|
-
value === null ||
|
|
225
|
-
(typeof value === "string" &&
|
|
226
|
-
(value[0] === "#" ||
|
|
227
|
-
/^((https?|www):\/\/)?[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]+(\/[^\s]*)?$/.test(
|
|
228
|
-
value
|
|
229
|
-
)))
|
|
230
|
-
);
|
|
231
|
-
case "table":
|
|
232
|
-
// feat: check if id exists
|
|
233
|
-
if (Array.isArray(value))
|
|
234
|
-
return (
|
|
235
|
-
typeof field !== "string" &&
|
|
236
|
-
field.type === "table" &&
|
|
237
|
-
((Utils.isArrayOfObjects(value) &&
|
|
238
|
-
value.every(
|
|
239
|
-
(element) =>
|
|
240
|
-
element.hasOwnProperty("id") &&
|
|
241
|
-
this.isValidID((element as Data).id)
|
|
242
|
-
)) ||
|
|
243
|
-
value.every(Utils.isNumber) ||
|
|
244
|
-
this.isValidID(value))
|
|
245
|
-
);
|
|
246
|
-
else if (Utils.isObject(value))
|
|
247
|
-
return (
|
|
248
|
-
value.hasOwnProperty("id") && this.isValidID((value as Data).id)
|
|
249
|
-
);
|
|
250
|
-
else return Utils.isNumber(value) || this.isValidID(value);
|
|
251
|
-
default:
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
for (const field of schema) {
|
|
256
|
-
if (data.hasOwnProperty(field.key)) {
|
|
257
|
-
if (!validateFieldType(data[field.key], field))
|
|
258
|
-
throw this.throwError("INVALID_TYPE", field.key);
|
|
259
|
-
if (
|
|
260
|
-
(field.type === "array" || field.type === "object") &&
|
|
261
|
-
field.children &&
|
|
262
|
-
Utils.isArrayOfObjects(field.children)
|
|
263
|
-
)
|
|
264
|
-
this.validateData(
|
|
265
|
-
data[field.key],
|
|
266
|
-
field.children as Schema,
|
|
267
|
-
skipRequiredField
|
|
268
|
-
);
|
|
269
|
-
} else if (field.required && !skipRequiredField)
|
|
270
|
-
throw this.throwError("REQUIRED", field.key);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
171
|
public setTableSchema(tableName: string, schema: Schema): void {
|
|
276
172
|
const encodeSchema = (schema: Schema) => {
|
|
277
173
|
let RETURN: any[][] = [],
|
|
@@ -279,7 +175,9 @@ export default class Inibase {
|
|
|
279
175
|
for (const field of schema) {
|
|
280
176
|
if (!RETURN[index]) RETURN[index] = [];
|
|
281
177
|
RETURN[index].push(
|
|
282
|
-
field.id
|
|
178
|
+
field.id
|
|
179
|
+
? Utils.decodeID(field.id as string, this.databasePath)
|
|
180
|
+
: null
|
|
283
181
|
);
|
|
284
182
|
RETURN[index].push(field.key ?? null);
|
|
285
183
|
RETURN[index].push(field.required ?? null);
|
|
@@ -303,14 +201,22 @@ export default class Inibase {
|
|
|
303
201
|
) {
|
|
304
202
|
if (!field.id) {
|
|
305
203
|
oldIndex++;
|
|
306
|
-
field = {
|
|
307
|
-
|
|
204
|
+
field = {
|
|
205
|
+
...field,
|
|
206
|
+
id: Utils.encodeID(oldIndex, this.databasePath),
|
|
207
|
+
};
|
|
208
|
+
} else
|
|
209
|
+
oldIndex = Utils.decodeID(field.id as string, this.databasePath);
|
|
308
210
|
field.children = addIdToSchema(field.children as Schema, oldIndex);
|
|
309
211
|
oldIndex += field.children.length;
|
|
310
|
-
} else if (field.id)
|
|
212
|
+
} else if (field.id)
|
|
213
|
+
oldIndex = Utils.decodeID(field.id as string, this.databasePath);
|
|
311
214
|
else {
|
|
312
215
|
oldIndex++;
|
|
313
|
-
field = {
|
|
216
|
+
field = {
|
|
217
|
+
...field,
|
|
218
|
+
id: Utils.encodeID(oldIndex, this.databasePath),
|
|
219
|
+
};
|
|
314
220
|
}
|
|
315
221
|
return field;
|
|
316
222
|
}),
|
|
@@ -322,8 +228,10 @@ export default class Inibase {
|
|
|
322
228
|
Utils.isArrayOfObjects(lastField.children)
|
|
323
229
|
)
|
|
324
230
|
return findLastIdNumber(lastField.children as Schema);
|
|
325
|
-
else
|
|
326
|
-
|
|
231
|
+
else if (lastField.id && Utils.isValidID(lastField.id))
|
|
232
|
+
return Utils.decodeID(lastField.id as string, this.databasePath);
|
|
233
|
+
}
|
|
234
|
+
return 0;
|
|
327
235
|
};
|
|
328
236
|
|
|
329
237
|
// remove id from schema
|
|
@@ -336,7 +244,7 @@ export default class Inibase {
|
|
|
336
244
|
// update columns files names based on field id
|
|
337
245
|
const schemaToIdsPath = (schema: any, prefix = "") => {
|
|
338
246
|
let RETURN: any = {};
|
|
339
|
-
for (const field of schema)
|
|
247
|
+
for (const field of schema)
|
|
340
248
|
if (field.children && Utils.isArrayOfObjects(field.children)) {
|
|
341
249
|
Utils.deepMerge(
|
|
342
250
|
RETURN,
|
|
@@ -347,37 +255,20 @@ export default class Inibase {
|
|
|
347
255
|
(field.type === "array" ? ".*." : ".")
|
|
348
256
|
)
|
|
349
257
|
);
|
|
350
|
-
} else if (
|
|
351
|
-
RETURN[
|
|
352
|
-
(prefix ?? "") + field.key,
|
|
353
|
-
"inib"
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
return RETURN;
|
|
357
|
-
},
|
|
358
|
-
findChangedProperties = (
|
|
359
|
-
obj1: Record<string, string>,
|
|
360
|
-
obj2: Record<string, string>
|
|
361
|
-
): Record<string, string> | null => {
|
|
362
|
-
const result: Record<string, string> = {};
|
|
258
|
+
} else if (Utils.isValidID(field.id))
|
|
259
|
+
RETURN[Utils.decodeID(field.id, this.databasePath)] =
|
|
260
|
+
File.encodeFileName((prefix ?? "") + field.key, "inib");
|
|
363
261
|
|
|
364
|
-
|
|
365
|
-
if (obj2.hasOwnProperty(key1) && obj1[key1] !== obj2[key1]) {
|
|
366
|
-
result[obj1[key1]] = obj2[key1];
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return Object.keys(result).length ? result : null;
|
|
262
|
+
return RETURN;
|
|
371
263
|
},
|
|
372
|
-
replaceOldPathes = findChangedProperties(
|
|
264
|
+
replaceOldPathes = Utils.findChangedProperties(
|
|
373
265
|
schemaToIdsPath(this.getTableSchema(tableName)),
|
|
374
266
|
schemaToIdsPath(schema)
|
|
375
267
|
);
|
|
376
|
-
if (replaceOldPathes)
|
|
268
|
+
if (replaceOldPathes)
|
|
377
269
|
for (const [oldPath, newPath] of Object.entries(replaceOldPathes))
|
|
378
270
|
if (existsSync(join(TablePath, oldPath)))
|
|
379
271
|
renameSync(join(TablePath, oldPath), join(TablePath, newPath));
|
|
380
|
-
}
|
|
381
272
|
}
|
|
382
273
|
|
|
383
274
|
writeFileSync(
|
|
@@ -393,7 +284,7 @@ export default class Inibase {
|
|
|
393
284
|
? decodeSchema(field)
|
|
394
285
|
: Object.fromEntries(
|
|
395
286
|
Object.entries({
|
|
396
|
-
id:
|
|
287
|
+
id: Utils.encodeID(field[0], this.databasePath),
|
|
397
288
|
key: field[1],
|
|
398
289
|
required: field[2],
|
|
399
290
|
type: field[3],
|
|
@@ -418,24 +309,156 @@ export default class Inibase {
|
|
|
418
309
|
);
|
|
419
310
|
}
|
|
420
311
|
return [
|
|
421
|
-
{
|
|
312
|
+
{
|
|
313
|
+
id: Utils.encodeID(0, this.databasePath),
|
|
314
|
+
key: "id",
|
|
315
|
+
type: "number",
|
|
316
|
+
required: true,
|
|
317
|
+
},
|
|
422
318
|
...(this.cache.get(TableSchemaPath) as unknown as Schema),
|
|
423
319
|
];
|
|
424
320
|
}
|
|
425
321
|
|
|
426
|
-
public getField
|
|
427
|
-
|
|
322
|
+
public getField<Property extends keyof Field | "children">(
|
|
323
|
+
keyPath: string,
|
|
324
|
+
schema: Schema | Field,
|
|
325
|
+
property?: Property
|
|
326
|
+
) {
|
|
327
|
+
const keyPathSplited = keyPath.split(".");
|
|
328
|
+
for (const [index, key] of keyPathSplited.entries()) {
|
|
428
329
|
if (key === "*") continue;
|
|
429
330
|
const foundItem = (schema as Schema).find((item) => item.key === key);
|
|
430
331
|
if (!foundItem) return null;
|
|
431
|
-
schema =
|
|
332
|
+
if (index === keyPathSplited.length - 1) schema = foundItem;
|
|
333
|
+
if (
|
|
432
334
|
(foundItem.type === "array" || foundItem.type === "object") &&
|
|
433
335
|
foundItem.children &&
|
|
434
336
|
Utils.isArrayOfObjects(foundItem.children)
|
|
435
|
-
|
|
436
|
-
|
|
337
|
+
)
|
|
338
|
+
schema = foundItem.children as Schema;
|
|
339
|
+
}
|
|
340
|
+
if (property) {
|
|
341
|
+
switch (property) {
|
|
342
|
+
case "type":
|
|
343
|
+
return (schema as Field).type;
|
|
344
|
+
case "children":
|
|
345
|
+
return (
|
|
346
|
+
schema as
|
|
347
|
+
| (Field & FieldObjectType)
|
|
348
|
+
| FieldArrayType
|
|
349
|
+
| FieldArrayArrayType
|
|
350
|
+
).children;
|
|
351
|
+
|
|
352
|
+
default:
|
|
353
|
+
return (schema as Field)[property as keyof Field];
|
|
354
|
+
}
|
|
355
|
+
} else return schema as Field;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
public validateData(
|
|
359
|
+
data: Data | Data[],
|
|
360
|
+
schema: Schema,
|
|
361
|
+
skipRequiredField: boolean = false
|
|
362
|
+
): void {
|
|
363
|
+
const validateFieldType = (
|
|
364
|
+
value: any,
|
|
365
|
+
fieldType: FieldType | FieldType[],
|
|
366
|
+
fieldChildrenType?: FieldType | FieldType[]
|
|
367
|
+
): boolean => {
|
|
368
|
+
if (value === null) return true;
|
|
369
|
+
if (Array.isArray(fieldType))
|
|
370
|
+
return Utils.detectFieldType(value, fieldType) !== undefined;
|
|
371
|
+
if (fieldType === "array" && fieldChildrenType && Array.isArray(value))
|
|
372
|
+
return value.some(
|
|
373
|
+
(v) =>
|
|
374
|
+
Utils.detectFieldType(
|
|
375
|
+
v,
|
|
376
|
+
Array.isArray(fieldChildrenType)
|
|
377
|
+
? fieldChildrenType
|
|
378
|
+
: [fieldChildrenType]
|
|
379
|
+
) !== undefined
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
switch (fieldType) {
|
|
383
|
+
case "string":
|
|
384
|
+
// TO-DO: and not email, url, password ...
|
|
385
|
+
return !Utils.isNumber(value);
|
|
386
|
+
case "password":
|
|
387
|
+
return !Utils.isNumber(value) && Utils.isPassword(value);
|
|
388
|
+
case "number":
|
|
389
|
+
return Utils.isNumber(value);
|
|
390
|
+
case "boolean":
|
|
391
|
+
return (
|
|
392
|
+
typeof value === "boolean" || value === "true" || value === "false"
|
|
393
|
+
);
|
|
394
|
+
case "date":
|
|
395
|
+
return Utils.isDate(value);
|
|
396
|
+
case "object":
|
|
397
|
+
return Utils.isObject(value);
|
|
398
|
+
case "array":
|
|
399
|
+
return Array.isArray(value);
|
|
400
|
+
case "email":
|
|
401
|
+
return Utils.isEmail(value);
|
|
402
|
+
case "url":
|
|
403
|
+
return Utils.isURL(value);
|
|
404
|
+
case "table":
|
|
405
|
+
// feat: check if id exists
|
|
406
|
+
if (Array.isArray(value))
|
|
407
|
+
return (
|
|
408
|
+
(Utils.isArrayOfObjects(value) &&
|
|
409
|
+
value.every(
|
|
410
|
+
(element: Data) =>
|
|
411
|
+
element.hasOwnProperty("id") &&
|
|
412
|
+
(Utils.isValidID(element.id) || Utils.isNumber(element.id))
|
|
413
|
+
)) ||
|
|
414
|
+
value.every(Utils.isNumber) ||
|
|
415
|
+
Utils.isValidID(value)
|
|
416
|
+
);
|
|
417
|
+
else if (Utils.isObject(value))
|
|
418
|
+
return (
|
|
419
|
+
value.hasOwnProperty("id") &&
|
|
420
|
+
(Utils.isValidID((value as Data).id) ||
|
|
421
|
+
Utils.isNumber((value as Data).id))
|
|
422
|
+
);
|
|
423
|
+
else return Utils.isNumber(value) || Utils.isValidID(value);
|
|
424
|
+
default:
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
if (Utils.isArrayOfObjects(data))
|
|
429
|
+
for (const single_data of data as Data[])
|
|
430
|
+
this.validateData(single_data, schema, skipRequiredField);
|
|
431
|
+
else if (Utils.isObject(data)) {
|
|
432
|
+
for (const field of schema) {
|
|
433
|
+
if (
|
|
434
|
+
!data.hasOwnProperty(field.key) &&
|
|
435
|
+
field.required &&
|
|
436
|
+
!skipRequiredField
|
|
437
|
+
)
|
|
438
|
+
throw this.throwError("FIELD_REQUIRED", field.key);
|
|
439
|
+
if (
|
|
440
|
+
!validateFieldType(
|
|
441
|
+
data[field.key],
|
|
442
|
+
field.type,
|
|
443
|
+
(field as any)?.children &&
|
|
444
|
+
!Utils.isArrayOfObjects((field as any)?.children)
|
|
445
|
+
? (field as any)?.children
|
|
446
|
+
: undefined
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
throw this.throwError("INVALID_TYPE", field.key);
|
|
450
|
+
if (
|
|
451
|
+
(field.type === "array" || field.type === "object") &&
|
|
452
|
+
field.children &&
|
|
453
|
+
Utils.isArrayOfObjects(field.children)
|
|
454
|
+
)
|
|
455
|
+
this.validateData(
|
|
456
|
+
data[field.key],
|
|
457
|
+
field.children as Schema,
|
|
458
|
+
skipRequiredField
|
|
459
|
+
);
|
|
460
|
+
}
|
|
437
461
|
}
|
|
438
|
-
return schema as Field;
|
|
439
462
|
}
|
|
440
463
|
|
|
441
464
|
public formatData(
|
|
@@ -443,89 +466,111 @@ export default class Inibase {
|
|
|
443
466
|
schema: Schema,
|
|
444
467
|
formatOnlyAvailiableKeys?: boolean
|
|
445
468
|
): Data | Data[] {
|
|
446
|
-
|
|
469
|
+
const formatField = (
|
|
470
|
+
value: any,
|
|
471
|
+
field: Field
|
|
472
|
+
): Data | Data[] | number | string => {
|
|
473
|
+
if (Array.isArray(field.type))
|
|
474
|
+
field.type = Utils.detectFieldType(value, field.type);
|
|
475
|
+
|
|
476
|
+
switch (field.type) {
|
|
477
|
+
case "array":
|
|
478
|
+
if (typeof field.children === "string") {
|
|
479
|
+
if (field.type === "array" && field.children === "table") {
|
|
480
|
+
if (Array.isArray(data[field.key])) {
|
|
481
|
+
if (Utils.isArrayOfObjects(data[field.key])) {
|
|
482
|
+
if (
|
|
483
|
+
value.every(
|
|
484
|
+
(item: any) =>
|
|
485
|
+
item.hasOwnProperty("id") &&
|
|
486
|
+
(Utils.isValidID(item.id) || Utils.isNumber(item.id))
|
|
487
|
+
)
|
|
488
|
+
)
|
|
489
|
+
value.map((item: any) =>
|
|
490
|
+
Utils.isNumber(item.id)
|
|
491
|
+
? Number(item.id)
|
|
492
|
+
: Utils.decodeID(item.id, this.databasePath)
|
|
493
|
+
);
|
|
494
|
+
} else if (Utils.isValidID(value) || Utils.isNumber(value))
|
|
495
|
+
return value.map((item: number | string) =>
|
|
496
|
+
Utils.isNumber(item)
|
|
497
|
+
? Number(item as string)
|
|
498
|
+
: Utils.decodeID(item as string, this.databasePath)
|
|
499
|
+
);
|
|
500
|
+
} else if (Utils.isValidID(value))
|
|
501
|
+
return [Utils.decodeID(value, this.databasePath)];
|
|
502
|
+
else if (Utils.isNumber(value)) return [Number(value)];
|
|
503
|
+
} else if (data.hasOwnProperty(field.key)) return value;
|
|
504
|
+
} else if (Utils.isArrayOfObjects(field.children))
|
|
505
|
+
return this.formatData(
|
|
506
|
+
value,
|
|
507
|
+
field.children as Schema,
|
|
508
|
+
formatOnlyAvailiableKeys
|
|
509
|
+
);
|
|
510
|
+
else if (Array.isArray(field.children))
|
|
511
|
+
return Array.isArray(value) ? value : [value];
|
|
512
|
+
break;
|
|
513
|
+
case "object":
|
|
514
|
+
if (Utils.isArrayOfObjects(field.children))
|
|
515
|
+
return this.formatData(
|
|
516
|
+
value,
|
|
517
|
+
field.children,
|
|
518
|
+
formatOnlyAvailiableKeys
|
|
519
|
+
);
|
|
520
|
+
break;
|
|
521
|
+
case "table":
|
|
522
|
+
if (Utils.isObject(value)) {
|
|
523
|
+
if (
|
|
524
|
+
value.hasOwnProperty("id") &&
|
|
525
|
+
(Utils.isValidID(value.id) || Utils.isNumber(value))
|
|
526
|
+
)
|
|
527
|
+
return Utils.isNumber(value.id)
|
|
528
|
+
? Number(value.id)
|
|
529
|
+
: Utils.decodeID(value.id, this.databasePath);
|
|
530
|
+
} else if (Utils.isValidID(value) || Utils.isNumber(value))
|
|
531
|
+
return Utils.isNumber(value)
|
|
532
|
+
? Number(value)
|
|
533
|
+
: Utils.decodeID(value, this.databasePath);
|
|
534
|
+
break;
|
|
535
|
+
case "password":
|
|
536
|
+
return value.length === 161 ? value : Utils.hashPassword(value);
|
|
537
|
+
case "number":
|
|
538
|
+
return Utils.isNumber(value) ? Number(value) : null;
|
|
539
|
+
default:
|
|
540
|
+
return value;
|
|
541
|
+
}
|
|
542
|
+
return null;
|
|
543
|
+
};
|
|
544
|
+
if (Utils.isArrayOfObjects(data))
|
|
447
545
|
return data.map((single_data: Data) =>
|
|
448
|
-
this.formatData(single_data, schema)
|
|
546
|
+
this.formatData(single_data, schema, formatOnlyAvailiableKeys)
|
|
449
547
|
);
|
|
450
|
-
|
|
548
|
+
else if (Utils.isObject(data)) {
|
|
451
549
|
let RETURN: Data = {};
|
|
452
550
|
for (const field of schema) {
|
|
453
551
|
if (!data.hasOwnProperty(field.key)) {
|
|
552
|
+
if (formatOnlyAvailiableKeys) continue;
|
|
454
553
|
RETURN[field.key] = this.getDefaultValue(field);
|
|
455
554
|
continue;
|
|
456
555
|
}
|
|
457
|
-
|
|
458
|
-
continue;
|
|
459
|
-
|
|
460
|
-
if (field.type === "array" || field.type === "object") {
|
|
461
|
-
if (field.children)
|
|
462
|
-
if (typeof field.children === "string") {
|
|
463
|
-
if (field.type === "array" && field.children === "table") {
|
|
464
|
-
if (Array.isArray(data[field.key])) {
|
|
465
|
-
if (Utils.isArrayOfObjects(data[field.key])) {
|
|
466
|
-
if (
|
|
467
|
-
data[field.key].every(
|
|
468
|
-
(item: any) =>
|
|
469
|
-
item.hasOwnProperty("id") &&
|
|
470
|
-
(this.isValidID(item.id) || Utils.isNumber(item.id))
|
|
471
|
-
)
|
|
472
|
-
)
|
|
473
|
-
data[field.key].map((item: any) =>
|
|
474
|
-
Utils.isNumber(item.id)
|
|
475
|
-
? parseFloat(item.id)
|
|
476
|
-
: this.decodeID(item.id)
|
|
477
|
-
);
|
|
478
|
-
} else if (
|
|
479
|
-
this.isValidID(data[field.key]) ||
|
|
480
|
-
Utils.isNumber(data[field.key])
|
|
481
|
-
)
|
|
482
|
-
RETURN[field.key] = data[field.key].map(
|
|
483
|
-
(item: number | string) =>
|
|
484
|
-
Utils.isNumber(item)
|
|
485
|
-
? parseFloat(item as string)
|
|
486
|
-
: this.decodeID(item as string)
|
|
487
|
-
);
|
|
488
|
-
} else if (this.isValidID(data[field.key]))
|
|
489
|
-
RETURN[field.key] = [this.decodeID(data[field.key])];
|
|
490
|
-
else if (Utils.isNumber(data[field.key]))
|
|
491
|
-
RETURN[field.key] = [parseFloat(data[field.key])];
|
|
492
|
-
} else if (data.hasOwnProperty(field.key))
|
|
493
|
-
RETURN[field.key] = data[field.key];
|
|
494
|
-
} else if (Utils.isArrayOfObjects(field.children))
|
|
495
|
-
RETURN[field.key] = this.formatData(
|
|
496
|
-
data[field.key],
|
|
497
|
-
field.children as Schema,
|
|
498
|
-
formatOnlyAvailiableKeys
|
|
499
|
-
);
|
|
500
|
-
} else if (field.type === "table") {
|
|
501
|
-
if (Utils.isObject(data[field.key])) {
|
|
502
|
-
if (
|
|
503
|
-
data[field.key].hasOwnProperty("id") &&
|
|
504
|
-
(this.isValidID(data[field.key].id) ||
|
|
505
|
-
Utils.isNumber(data[field.key]))
|
|
506
|
-
)
|
|
507
|
-
RETURN[field.key] = Utils.isNumber(data[field.key].id)
|
|
508
|
-
? parseFloat(data[field.key].id)
|
|
509
|
-
: this.decodeID(data[field.key].id);
|
|
510
|
-
} else if (
|
|
511
|
-
this.isValidID(data[field.key]) ||
|
|
512
|
-
Utils.isNumber(data[field.key])
|
|
513
|
-
)
|
|
514
|
-
RETURN[field.key] = Utils.isNumber(data[field.key])
|
|
515
|
-
? parseFloat(data[field.key])
|
|
516
|
-
: this.decodeID(data[field.key]);
|
|
517
|
-
} else if (field.type === "password")
|
|
518
|
-
RETURN[field.key] =
|
|
519
|
-
data[field.key].length === 161
|
|
520
|
-
? data[field.key]
|
|
521
|
-
: Utils.hashPassword(data[field.key]);
|
|
522
|
-
else RETURN[field.key] = data[field.key];
|
|
556
|
+
RETURN[field.key] = formatField(data[field.key], field);
|
|
523
557
|
}
|
|
524
558
|
return RETURN;
|
|
525
559
|
} else return [];
|
|
526
560
|
}
|
|
527
561
|
|
|
528
562
|
private getDefaultValue(field: Field): any {
|
|
563
|
+
if (Array.isArray(field.type))
|
|
564
|
+
return this.getDefaultValue({
|
|
565
|
+
...field,
|
|
566
|
+
type: field.type.sort(
|
|
567
|
+
(a: FieldType, b: FieldType) =>
|
|
568
|
+
Number(b === "array") - Number(a === "array") ||
|
|
569
|
+
Number(a === "string") - Number(b === "string") ||
|
|
570
|
+
Number(a === "number") - Number(b === "number")
|
|
571
|
+
)[0],
|
|
572
|
+
} as Field);
|
|
573
|
+
|
|
529
574
|
switch (field.type) {
|
|
530
575
|
case "array":
|
|
531
576
|
return Utils.isArrayOfObjects(field.children)
|
|
@@ -552,37 +597,54 @@ export default class Inibase {
|
|
|
552
597
|
mainPath: string,
|
|
553
598
|
data: Data | Data[]
|
|
554
599
|
): { [key: string]: string[] } {
|
|
555
|
-
const CombineData = (
|
|
600
|
+
const CombineData = (_data: Data | Data[], prefix?: string) => {
|
|
556
601
|
let RETURN: Record<
|
|
557
602
|
string,
|
|
558
603
|
string | boolean | number | null | (string | boolean | number | null)[]
|
|
559
604
|
> = {};
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
605
|
+
const combineObjectsToArray = (input: any[]) =>
|
|
606
|
+
input.reduce(
|
|
607
|
+
(r, c) => (
|
|
608
|
+
Object.keys(c).map((k) => (r[k] = [...(r[k] || []), c[k]])), r
|
|
609
|
+
),
|
|
610
|
+
{}
|
|
611
|
+
);
|
|
612
|
+
if (Utils.isArrayOfObjects(_data))
|
|
613
|
+
RETURN = combineObjectsToArray(
|
|
614
|
+
(_data as Data[]).map((single_data) => CombineData(single_data))
|
|
564
615
|
);
|
|
565
616
|
else {
|
|
566
|
-
for (const [key, value] of Object.entries(
|
|
617
|
+
for (const [key, value] of Object.entries(_data as Data)) {
|
|
567
618
|
if (Utils.isObject(value))
|
|
568
619
|
Object.assign(RETURN, CombineData(value, `${key}.`));
|
|
569
620
|
else if (Array.isArray(value)) {
|
|
570
|
-
if (Utils.isArrayOfObjects(value))
|
|
621
|
+
if (Utils.isArrayOfObjects(value)) {
|
|
571
622
|
Object.assign(
|
|
572
623
|
RETURN,
|
|
573
624
|
CombineData(
|
|
574
|
-
|
|
625
|
+
combineObjectsToArray(value),
|
|
626
|
+
(prefix ?? "") + key + ".*."
|
|
627
|
+
)
|
|
628
|
+
);
|
|
629
|
+
} else if (
|
|
630
|
+
Utils.isArrayOfArrays(value) &&
|
|
631
|
+
value.every(Utils.isArrayOfObjects)
|
|
632
|
+
)
|
|
633
|
+
Object.assign(
|
|
634
|
+
RETURN,
|
|
635
|
+
CombineData(
|
|
636
|
+
combineObjectsToArray(value.map(combineObjectsToArray)),
|
|
575
637
|
(prefix ?? "") + key + ".*."
|
|
576
638
|
)
|
|
577
639
|
);
|
|
578
640
|
else
|
|
579
|
-
RETURN[(prefix ?? "") + key] =
|
|
641
|
+
RETURN[(prefix ?? "") + key] = File.encode(value) as
|
|
580
642
|
| boolean
|
|
581
643
|
| number
|
|
582
644
|
| string
|
|
583
645
|
| null;
|
|
584
646
|
} else
|
|
585
|
-
RETURN[(prefix ?? "") + key] =
|
|
647
|
+
RETURN[(prefix ?? "") + key] = File.encode(value) as
|
|
586
648
|
| boolean
|
|
587
649
|
| number
|
|
588
650
|
| string
|
|
@@ -612,6 +674,8 @@ export default class Inibase {
|
|
|
612
674
|
onlyLinesNumbers?: boolean
|
|
613
675
|
): Promise<Data | Data[] | number[] | null> {
|
|
614
676
|
if (!options.columns) options.columns = [];
|
|
677
|
+
else if (!Array.isArray(options.columns))
|
|
678
|
+
options.columns = [options.columns];
|
|
615
679
|
else if (
|
|
616
680
|
options.columns.length &&
|
|
617
681
|
!(options.columns as string[]).includes("id")
|
|
@@ -625,23 +689,35 @@ export default class Inibase {
|
|
|
625
689
|
const filterSchemaByColumns = (schema: Schema, columns: string[]): Schema =>
|
|
626
690
|
schema
|
|
627
691
|
.map((field) => {
|
|
628
|
-
if (columns.
|
|
692
|
+
if (columns.some((column) => column.startsWith("!")))
|
|
693
|
+
return columns.includes("!" + field.key) ? null : field;
|
|
694
|
+
if (columns.includes(field.key) || columns.includes("*"))
|
|
695
|
+
return field;
|
|
696
|
+
|
|
629
697
|
if (
|
|
630
698
|
(field.type === "array" || field.type === "object") &&
|
|
631
699
|
Utils.isArrayOfObjects(field.children) &&
|
|
632
|
-
columns.filter(
|
|
633
|
-
column
|
|
634
|
-
|
|
635
|
-
|
|
700
|
+
columns.filter(
|
|
701
|
+
(column) =>
|
|
702
|
+
column.startsWith(
|
|
703
|
+
field.key + (field.type === "array" ? ".*." : ".")
|
|
704
|
+
) ||
|
|
705
|
+
column.startsWith(
|
|
706
|
+
"!" + field.key + (field.type === "array" ? ".*." : ".")
|
|
707
|
+
)
|
|
636
708
|
).length
|
|
637
709
|
) {
|
|
638
710
|
field.children = filterSchemaByColumns(
|
|
639
711
|
field.children as Schema,
|
|
640
712
|
columns
|
|
641
|
-
.filter(
|
|
642
|
-
column
|
|
643
|
-
|
|
644
|
-
|
|
713
|
+
.filter(
|
|
714
|
+
(column) =>
|
|
715
|
+
column.startsWith(
|
|
716
|
+
field.key + (field.type === "array" ? ".*." : ".")
|
|
717
|
+
) ||
|
|
718
|
+
column.startsWith(
|
|
719
|
+
"!" + field.key + (field.type === "array" ? ".*." : ".")
|
|
720
|
+
)
|
|
645
721
|
)
|
|
646
722
|
.map((column) =>
|
|
647
723
|
column.replace(
|
|
@@ -667,10 +743,144 @@ export default class Inibase {
|
|
|
667
743
|
let RETURN: Record<number, Data> = {};
|
|
668
744
|
for (const field of schema) {
|
|
669
745
|
if (
|
|
670
|
-
(field.type === "array" ||
|
|
671
|
-
|
|
746
|
+
(field.type === "array" ||
|
|
747
|
+
(Array.isArray(field.type) &&
|
|
748
|
+
(field.type as any).includes("array"))) &&
|
|
749
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
750
|
+
.children
|
|
672
751
|
) {
|
|
673
|
-
if (
|
|
752
|
+
if (
|
|
753
|
+
Utils.isArrayOfObjects(
|
|
754
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
755
|
+
.children
|
|
756
|
+
)
|
|
757
|
+
) {
|
|
758
|
+
if (
|
|
759
|
+
(
|
|
760
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
761
|
+
.children as Schema
|
|
762
|
+
).filter(
|
|
763
|
+
(children) =>
|
|
764
|
+
children.type === "array" &&
|
|
765
|
+
Utils.isArrayOfObjects(children.children)
|
|
766
|
+
).length
|
|
767
|
+
) {
|
|
768
|
+
// one of children has array field type and has children array of object = Schema
|
|
769
|
+
Object.entries(
|
|
770
|
+
(await getItemsFromSchema(
|
|
771
|
+
path,
|
|
772
|
+
(
|
|
773
|
+
(
|
|
774
|
+
field as FieldDefault &
|
|
775
|
+
(FieldArrayType | FieldArrayArrayType)
|
|
776
|
+
).children as Schema
|
|
777
|
+
).filter(
|
|
778
|
+
(children) =>
|
|
779
|
+
children.type === "array" &&
|
|
780
|
+
Utils.isArrayOfObjects(children.children)
|
|
781
|
+
),
|
|
782
|
+
linesNumber,
|
|
783
|
+
(prefix ?? "") + field.key + ".*."
|
|
784
|
+
)) ?? {}
|
|
785
|
+
).forEach(([index, item]) => {
|
|
786
|
+
if (Utils.isObject(item)) {
|
|
787
|
+
if (!RETURN[index]) RETURN[index] = {};
|
|
788
|
+
if (!RETURN[index][field.key]) RETURN[index][field.key] = [];
|
|
789
|
+
for (const child_field of (
|
|
790
|
+
(
|
|
791
|
+
field as FieldDefault &
|
|
792
|
+
(FieldArrayType | FieldArrayArrayType)
|
|
793
|
+
).children as Schema
|
|
794
|
+
).filter(
|
|
795
|
+
(children) =>
|
|
796
|
+
children.type === "array" &&
|
|
797
|
+
Utils.isArrayOfObjects(children.children)
|
|
798
|
+
)) {
|
|
799
|
+
if (Utils.isObject(item[child_field.key])) {
|
|
800
|
+
Object.entries(item[child_field.key]).forEach(
|
|
801
|
+
([key, value]) => {
|
|
802
|
+
for (let _i = 0; _i < value.length; _i++) {
|
|
803
|
+
if (!RETURN[index][field.key][_i])
|
|
804
|
+
RETURN[index][field.key][_i] = {};
|
|
805
|
+
if (!RETURN[index][field.key][_i][child_field.key])
|
|
806
|
+
RETURN[index][field.key][_i][child_field.key] =
|
|
807
|
+
[];
|
|
808
|
+
value[_i].forEach((_element, _index) => {
|
|
809
|
+
if (
|
|
810
|
+
!RETURN[index][field.key][_i][child_field.key][
|
|
811
|
+
_index
|
|
812
|
+
]
|
|
813
|
+
)
|
|
814
|
+
RETURN[index][field.key][_i][child_field.key][
|
|
815
|
+
_index
|
|
816
|
+
] = {};
|
|
817
|
+
RETURN[index][field.key][_i][child_field.key][
|
|
818
|
+
_index
|
|
819
|
+
][key] = _element;
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
(
|
|
829
|
+
field as FieldDefault & (FieldArrayType | FieldArrayArrayType)
|
|
830
|
+
).children = (
|
|
831
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
832
|
+
.children as Schema
|
|
833
|
+
).filter(
|
|
834
|
+
(children) =>
|
|
835
|
+
children.type !== "array" ||
|
|
836
|
+
!Utils.isArrayOfObjects(children.children)
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
Object.entries(
|
|
840
|
+
(await getItemsFromSchema(
|
|
841
|
+
path,
|
|
842
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
843
|
+
.children as Schema,
|
|
844
|
+
linesNumber,
|
|
845
|
+
(prefix ?? "") + field.key + ".*."
|
|
846
|
+
)) ?? {}
|
|
847
|
+
).forEach(([index, item]) => {
|
|
848
|
+
if (!RETURN[index]) RETURN[index] = {};
|
|
849
|
+
if (Utils.isObject(item)) {
|
|
850
|
+
if (!Object.values(item).every((i) => i === null)) {
|
|
851
|
+
if (RETURN[index][field.key])
|
|
852
|
+
Object.entries(item).forEach(([key, value], _index) => {
|
|
853
|
+
RETURN[index][field.key] = RETURN[index][field.key].map(
|
|
854
|
+
(_obj, _i) => ({ ..._obj, [key]: value[_index] })
|
|
855
|
+
);
|
|
856
|
+
});
|
|
857
|
+
else if (Object.values(item).every(Utils.isArrayOfArrays))
|
|
858
|
+
RETURN[index][field.key] = item;
|
|
859
|
+
else {
|
|
860
|
+
RETURN[index][field.key] = [];
|
|
861
|
+
Object.entries(item).forEach(([key, value]) => {
|
|
862
|
+
for (let _i = 0; _i < value.length; _i++) {
|
|
863
|
+
if (!RETURN[index][field.key][_i])
|
|
864
|
+
RETURN[index][field.key][_i] = {};
|
|
865
|
+
RETURN[index][field.key][_i][key] = value[_i];
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
} else RETURN[index][field.key] = null;
|
|
870
|
+
} else RETURN[index][field.key] = item;
|
|
871
|
+
});
|
|
872
|
+
} else if (
|
|
873
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
874
|
+
.children === "table" ||
|
|
875
|
+
(Array.isArray(
|
|
876
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
877
|
+
.children
|
|
878
|
+
) &&
|
|
879
|
+
(
|
|
880
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
881
|
+
.children as FieldType[]
|
|
882
|
+
).includes("table"))
|
|
883
|
+
) {
|
|
674
884
|
if (options.columns)
|
|
675
885
|
options.columns = (options.columns as string[])
|
|
676
886
|
.filter((column) => column.includes(`${field.key}.*.`))
|
|
@@ -681,8 +891,10 @@ export default class Inibase {
|
|
|
681
891
|
path,
|
|
682
892
|
File.encodeFileName((prefix ?? "") + field.key, "inib")
|
|
683
893
|
),
|
|
894
|
+
linesNumber,
|
|
684
895
|
field.type,
|
|
685
|
-
|
|
896
|
+
(field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
|
|
897
|
+
.children as FieldType | FieldType[]
|
|
686
898
|
)) ?? {}
|
|
687
899
|
)) {
|
|
688
900
|
if (!RETURN[index]) RETURN[index] = {};
|
|
@@ -690,26 +902,44 @@ export default class Inibase {
|
|
|
690
902
|
? await this.get(field.key, value as number, options)
|
|
691
903
|
: this.getDefaultValue(field);
|
|
692
904
|
}
|
|
693
|
-
} else if (
|
|
694
|
-
|
|
695
|
-
(
|
|
905
|
+
} else if (
|
|
906
|
+
existsSync(
|
|
907
|
+
join(
|
|
696
908
|
path,
|
|
697
|
-
|
|
909
|
+
File.encodeFileName((prefix ?? "") + field.key, "inib")
|
|
910
|
+
)
|
|
911
|
+
)
|
|
912
|
+
)
|
|
913
|
+
Object.entries(
|
|
914
|
+
(await File.get(
|
|
915
|
+
join(
|
|
916
|
+
path,
|
|
917
|
+
File.encodeFileName((prefix ?? "") + field.key, "inib")
|
|
918
|
+
),
|
|
698
919
|
linesNumber,
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
(field.type === "array" ? ".*." : ".")
|
|
920
|
+
field.type,
|
|
921
|
+
(field as any)?.children
|
|
702
922
|
)) ?? {}
|
|
703
923
|
).forEach(([index, item]) => {
|
|
704
924
|
if (!RETURN[index]) RETURN[index] = {};
|
|
705
|
-
RETURN[index][field.key] =
|
|
706
|
-
field.type === "array" &&
|
|
707
|
-
Utils.isObject(item) &&
|
|
708
|
-
Object.values(item).every((i) => i === null)
|
|
709
|
-
? []
|
|
710
|
-
: item;
|
|
925
|
+
RETURN[index][field.key] = item ?? this.getDefaultValue(field);
|
|
711
926
|
});
|
|
712
|
-
|
|
927
|
+
} else if (field.type === "object") {
|
|
928
|
+
Object.entries(
|
|
929
|
+
(await getItemsFromSchema(
|
|
930
|
+
path,
|
|
931
|
+
field.children as Schema,
|
|
932
|
+
linesNumber,
|
|
933
|
+
(prefix ?? "") + field.key + "."
|
|
934
|
+
)) ?? {}
|
|
935
|
+
).forEach(([index, item]) => {
|
|
936
|
+
if (!RETURN[index]) RETURN[index] = {};
|
|
937
|
+
if (Utils.isObject(item)) {
|
|
938
|
+
if (!Object.values(item).every((i) => i === null))
|
|
939
|
+
RETURN[index][field.key] = item;
|
|
940
|
+
else RETURN[index][field.key] = null;
|
|
941
|
+
} else RETURN[index][field.key] = null;
|
|
942
|
+
});
|
|
713
943
|
} else if (field.type === "table") {
|
|
714
944
|
if (
|
|
715
945
|
existsSync(join(this.databasePath, field.key)) &&
|
|
@@ -734,8 +964,8 @@ export default class Inibase {
|
|
|
734
964
|
path,
|
|
735
965
|
File.encodeFileName((prefix ?? "") + field.key, "inib")
|
|
736
966
|
),
|
|
737
|
-
|
|
738
|
-
|
|
967
|
+
linesNumber,
|
|
968
|
+
"number"
|
|
739
969
|
)) ?? {}
|
|
740
970
|
)) {
|
|
741
971
|
if (!RETURN[index]) RETURN[index] = {};
|
|
@@ -748,21 +978,21 @@ export default class Inibase {
|
|
|
748
978
|
existsSync(
|
|
749
979
|
join(path, File.encodeFileName((prefix ?? "") + field.key, "inib"))
|
|
750
980
|
)
|
|
751
|
-
)
|
|
981
|
+
)
|
|
752
982
|
Object.entries(
|
|
753
983
|
(await File.get(
|
|
754
984
|
join(
|
|
755
985
|
path,
|
|
756
986
|
File.encodeFileName((prefix ?? "") + field.key, "inib")
|
|
757
987
|
),
|
|
988
|
+
linesNumber,
|
|
758
989
|
field.type,
|
|
759
|
-
|
|
990
|
+
(field as any)?.children
|
|
760
991
|
)) ?? {}
|
|
761
992
|
).forEach(([index, item]) => {
|
|
762
993
|
if (!RETURN[index]) RETURN[index] = {};
|
|
763
994
|
RETURN[index][field.key] = item ?? this.getDefaultValue(field);
|
|
764
995
|
});
|
|
765
|
-
}
|
|
766
996
|
}
|
|
767
997
|
return RETURN;
|
|
768
998
|
};
|
|
@@ -781,18 +1011,19 @@ export default class Inibase {
|
|
|
781
1011
|
)
|
|
782
1012
|
)
|
|
783
1013
|
);
|
|
784
|
-
} else if (
|
|
1014
|
+
} else if (Utils.isValidID(where) || Utils.isNumber(where)) {
|
|
785
1015
|
let Ids = where as string | number | (string | number)[];
|
|
786
1016
|
if (!Array.isArray(Ids)) Ids = [Ids];
|
|
787
1017
|
const idFilePath = join(this.databasePath, tableName, "id.inib");
|
|
788
1018
|
if (!existsSync(idFilePath)) throw this.throwError("NO_ITEMS", tableName);
|
|
789
1019
|
const [lineNumbers, countItems] = await File.search(
|
|
790
1020
|
idFilePath,
|
|
791
|
-
"number",
|
|
792
1021
|
"[]",
|
|
793
1022
|
Utils.isNumber(Ids)
|
|
794
|
-
? Ids.map((id) =>
|
|
795
|
-
: Ids.map((id) =>
|
|
1023
|
+
? Ids.map((id) => Number(id as string))
|
|
1024
|
+
: Ids.map((id) => Utils.decodeID(id as string, this.databasePath)),
|
|
1025
|
+
undefined,
|
|
1026
|
+
"number",
|
|
796
1027
|
undefined,
|
|
797
1028
|
Ids.length
|
|
798
1029
|
);
|
|
@@ -809,10 +1040,11 @@ export default class Inibase {
|
|
|
809
1040
|
)) ?? {}
|
|
810
1041
|
);
|
|
811
1042
|
if (RETURN.length && !Array.isArray(where)) RETURN = RETURN[0];
|
|
812
|
-
} else if (
|
|
1043
|
+
} else if (Utils.isObject(where)) {
|
|
813
1044
|
// Criteria
|
|
814
1045
|
const FormatObjectCriteriaValue = (
|
|
815
|
-
value: string
|
|
1046
|
+
value: string,
|
|
1047
|
+
isParentArray: boolean = false
|
|
816
1048
|
): [ComparisonOperator, string | number | boolean | null] => {
|
|
817
1049
|
switch (value[0]) {
|
|
818
1050
|
case ">":
|
|
@@ -839,10 +1071,19 @@ export default class Inibase {
|
|
|
839
1071
|
value.slice(3) as string | number,
|
|
840
1072
|
]
|
|
841
1073
|
: [
|
|
842
|
-
value.slice(0, 1) as ComparisonOperator,
|
|
1074
|
+
(value.slice(0, 1) + "=") as ComparisonOperator,
|
|
843
1075
|
value.slice(1) as string | number,
|
|
844
1076
|
];
|
|
845
1077
|
case "=":
|
|
1078
|
+
return isParentArray
|
|
1079
|
+
? [
|
|
1080
|
+
value.slice(0, 1) as ComparisonOperator,
|
|
1081
|
+
value.slice(1) as string | number,
|
|
1082
|
+
]
|
|
1083
|
+
: [
|
|
1084
|
+
value.slice(0, 1) as ComparisonOperator,
|
|
1085
|
+
(value.slice(1) + ",") as string,
|
|
1086
|
+
];
|
|
846
1087
|
case "*":
|
|
847
1088
|
return [
|
|
848
1089
|
value.slice(0, 1) as ComparisonOperator,
|
|
@@ -856,11 +1097,14 @@ export default class Inibase {
|
|
|
856
1097
|
const applyCriteria = async (
|
|
857
1098
|
criteria?: Criteria,
|
|
858
1099
|
allTrue?: boolean
|
|
859
|
-
): Promise<Data | null> => {
|
|
860
|
-
let RETURN: Data = {};
|
|
1100
|
+
): Promise<Record<number, Data> | null> => {
|
|
1101
|
+
let RETURN: Record<number, Data> = {};
|
|
861
1102
|
if (!criteria) return null;
|
|
862
|
-
if (criteria.and &&
|
|
863
|
-
const searchResult = await applyCriteria(
|
|
1103
|
+
if (criteria.and && Utils.isObject(criteria.and)) {
|
|
1104
|
+
const searchResult = await applyCriteria(
|
|
1105
|
+
criteria.and as Criteria,
|
|
1106
|
+
true
|
|
1107
|
+
);
|
|
864
1108
|
if (searchResult) {
|
|
865
1109
|
RETURN = Utils.deepMerge(
|
|
866
1110
|
RETURN,
|
|
@@ -876,118 +1120,124 @@ export default class Inibase {
|
|
|
876
1120
|
} else return null;
|
|
877
1121
|
}
|
|
878
1122
|
|
|
879
|
-
if (criteria.or &&
|
|
880
|
-
const searchResult = await applyCriteria(criteria.or);
|
|
1123
|
+
if (criteria.or && Utils.isObject(criteria.or)) {
|
|
1124
|
+
const searchResult = await applyCriteria(criteria.or as Criteria);
|
|
881
1125
|
delete criteria.or;
|
|
882
1126
|
if (searchResult) RETURN = Utils.deepMerge(RETURN, searchResult);
|
|
883
1127
|
}
|
|
884
1128
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
index
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
index
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
if (value?.or && Array.isArray(value.or)) {
|
|
911
|
-
const searchCriteria = value.or
|
|
912
|
-
.map(
|
|
913
|
-
(
|
|
914
|
-
single_or
|
|
915
|
-
): [ComparisonOperator, string | number | boolean | null] =>
|
|
916
|
-
typeof single_or === "string"
|
|
917
|
-
? FormatObjectCriteriaValue(single_or)
|
|
918
|
-
: ["=", single_or]
|
|
1129
|
+
if (Object.keys(criteria).length > 0) {
|
|
1130
|
+
allTrue = true;
|
|
1131
|
+
let index = -1;
|
|
1132
|
+
for (const [key, value] of Object.entries(criteria)) {
|
|
1133
|
+
const field = this.getField(key, schema as Schema) as Field;
|
|
1134
|
+
index++;
|
|
1135
|
+
let searchOperator:
|
|
1136
|
+
| ComparisonOperator
|
|
1137
|
+
| ComparisonOperator[]
|
|
1138
|
+
| undefined = undefined,
|
|
1139
|
+
searchComparedAtValue:
|
|
1140
|
+
| string
|
|
1141
|
+
| number
|
|
1142
|
+
| boolean
|
|
1143
|
+
| null
|
|
1144
|
+
| (string | number | boolean | null)[]
|
|
1145
|
+
| undefined = undefined,
|
|
1146
|
+
searchLogicalOperator: "and" | "or" | undefined = undefined;
|
|
1147
|
+
if (Utils.isObject(value)) {
|
|
1148
|
+
if (
|
|
1149
|
+
(value as Criteria)?.or &&
|
|
1150
|
+
Array.isArray((value as Criteria).or)
|
|
1151
|
+
) {
|
|
1152
|
+
const searchCriteria = (
|
|
1153
|
+
(value as Criteria).or as (string | number | boolean)[]
|
|
919
1154
|
)
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1155
|
+
.map(
|
|
1156
|
+
(
|
|
1157
|
+
single_or
|
|
1158
|
+
): [ComparisonOperator, string | number | boolean | null] =>
|
|
1159
|
+
typeof single_or === "string"
|
|
1160
|
+
? FormatObjectCriteriaValue(single_or)
|
|
1161
|
+
: ["=", single_or]
|
|
1162
|
+
)
|
|
1163
|
+
.filter((a) => a) as [ComparisonOperator, string | number][];
|
|
1164
|
+
if (searchCriteria.length > 0) {
|
|
1165
|
+
searchOperator = searchCriteria.map(
|
|
1166
|
+
(single_or) => single_or[0]
|
|
1167
|
+
);
|
|
1168
|
+
searchComparedAtValue = searchCriteria.map(
|
|
1169
|
+
(single_or) => single_or[1]
|
|
1170
|
+
);
|
|
1171
|
+
searchLogicalOperator = "or";
|
|
1172
|
+
}
|
|
1173
|
+
delete (value as Criteria).or;
|
|
929
1174
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1175
|
+
if (
|
|
1176
|
+
(value as Criteria)?.and &&
|
|
1177
|
+
Array.isArray((value as Criteria).and)
|
|
1178
|
+
) {
|
|
1179
|
+
const searchCriteria = (
|
|
1180
|
+
(value as Criteria).and as (string | number | boolean)[]
|
|
1181
|
+
)
|
|
1182
|
+
.map(
|
|
1183
|
+
(
|
|
1184
|
+
single_and
|
|
1185
|
+
): [ComparisonOperator, string | number | boolean | null] =>
|
|
1186
|
+
typeof single_and === "string"
|
|
1187
|
+
? FormatObjectCriteriaValue(single_and)
|
|
1188
|
+
: ["=", single_and]
|
|
1189
|
+
)
|
|
1190
|
+
.filter((a) => a) as [ComparisonOperator, string | number][];
|
|
1191
|
+
if (searchCriteria.length > 0) {
|
|
1192
|
+
searchOperator = searchCriteria.map(
|
|
1193
|
+
(single_and) => single_and[0]
|
|
1194
|
+
);
|
|
1195
|
+
searchComparedAtValue = searchCriteria.map(
|
|
1196
|
+
(single_and) => single_and[1]
|
|
1197
|
+
);
|
|
1198
|
+
searchLogicalOperator = "and";
|
|
1199
|
+
}
|
|
1200
|
+
delete (value as Criteria).and;
|
|
1201
|
+
}
|
|
1202
|
+
} else if (Array.isArray(value)) {
|
|
1203
|
+
const searchCriteria = value
|
|
934
1204
|
.map(
|
|
935
1205
|
(
|
|
936
|
-
|
|
1206
|
+
single
|
|
937
1207
|
): [ComparisonOperator, string | number | boolean | null] =>
|
|
938
|
-
typeof
|
|
939
|
-
? FormatObjectCriteriaValue(
|
|
940
|
-
: ["=",
|
|
1208
|
+
typeof single === "string"
|
|
1209
|
+
? FormatObjectCriteriaValue(single)
|
|
1210
|
+
: ["=", single]
|
|
941
1211
|
)
|
|
942
1212
|
.filter((a) => a) as [ComparisonOperator, string | number][];
|
|
943
1213
|
if (searchCriteria.length > 0) {
|
|
944
|
-
searchOperator = searchCriteria.map(
|
|
945
|
-
(single_and) => single_and[0]
|
|
946
|
-
);
|
|
1214
|
+
searchOperator = searchCriteria.map((single) => single[0]);
|
|
947
1215
|
searchComparedAtValue = searchCriteria.map(
|
|
948
|
-
(
|
|
1216
|
+
(single) => single[1]
|
|
949
1217
|
);
|
|
950
1218
|
searchLogicalOperator = "and";
|
|
951
1219
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
? FormatObjectCriteriaValue(single)
|
|
962
|
-
: ["=", single]
|
|
963
|
-
)
|
|
964
|
-
.filter((a) => a) as [ComparisonOperator, string | number][];
|
|
965
|
-
if (searchCriteria.length > 0) {
|
|
966
|
-
searchOperator = searchCriteria.map((single) => single[0]);
|
|
967
|
-
searchComparedAtValue = searchCriteria.map((single) => single[1]);
|
|
968
|
-
searchLogicalOperator = "and";
|
|
969
|
-
}
|
|
970
|
-
} else if (typeof value === "string") {
|
|
971
|
-
const ComparisonOperatorValue = FormatObjectCriteriaValue(value);
|
|
972
|
-
if (ComparisonOperatorValue) {
|
|
973
|
-
searchOperator = ComparisonOperatorValue[0];
|
|
974
|
-
searchComparedAtValue = ComparisonOperatorValue[1];
|
|
1220
|
+
} else if (typeof value === "string") {
|
|
1221
|
+
const ComparisonOperatorValue = FormatObjectCriteriaValue(value);
|
|
1222
|
+
if (ComparisonOperatorValue) {
|
|
1223
|
+
searchOperator = ComparisonOperatorValue[0];
|
|
1224
|
+
searchComparedAtValue = ComparisonOperatorValue[1];
|
|
1225
|
+
}
|
|
1226
|
+
} else {
|
|
1227
|
+
searchOperator = "=";
|
|
1228
|
+
searchComparedAtValue = value as number | boolean;
|
|
975
1229
|
}
|
|
976
|
-
} else {
|
|
977
|
-
searchOperator = "=";
|
|
978
|
-
searchComparedAtValue = value;
|
|
979
|
-
}
|
|
980
|
-
if (searchOperator && searchComparedAtValue) {
|
|
981
1230
|
const [searchResult, totlaItems] = await File.search(
|
|
982
1231
|
join(
|
|
983
1232
|
this.databasePath,
|
|
984
1233
|
tableName,
|
|
985
1234
|
File.encodeFileName(key, "inib")
|
|
986
1235
|
),
|
|
987
|
-
this.getField(key, schema as Schema)?.type ?? "string",
|
|
988
1236
|
searchOperator,
|
|
989
1237
|
searchComparedAtValue,
|
|
990
1238
|
searchLogicalOperator,
|
|
1239
|
+
field?.type,
|
|
1240
|
+
(field as any)?.children,
|
|
991
1241
|
options.per_page,
|
|
992
1242
|
(options.page as number) - 1 * (options.per_page as number) + 1,
|
|
993
1243
|
true
|
|
@@ -997,12 +1247,20 @@ export default class Inibase {
|
|
|
997
1247
|
if (!this.pageInfoArray[key]) this.pageInfoArray[key] = {};
|
|
998
1248
|
this.pageInfoArray[key].total_items = totlaItems;
|
|
999
1249
|
}
|
|
1250
|
+
if (allTrue && index > 0) {
|
|
1251
|
+
if (!Object.keys(RETURN).length) RETURN = {};
|
|
1252
|
+
RETURN = Object.fromEntries(
|
|
1253
|
+
Object.entries(RETURN).filter(
|
|
1254
|
+
([_index, item]) => Object.keys(item).length > index
|
|
1255
|
+
)
|
|
1256
|
+
);
|
|
1257
|
+
if (!Object.keys(RETURN).length) RETURN = {};
|
|
1258
|
+
}
|
|
1000
1259
|
}
|
|
1001
1260
|
}
|
|
1002
|
-
return Object.keys(RETURN).length
|
|
1261
|
+
return Object.keys(RETURN).length ? RETURN : null;
|
|
1003
1262
|
};
|
|
1004
|
-
|
|
1005
|
-
RETURN = await applyCriteria(where);
|
|
1263
|
+
RETURN = await applyCriteria(where as Criteria);
|
|
1006
1264
|
if (RETURN) {
|
|
1007
1265
|
if (onlyLinesNumbers) return Object.keys(RETURN).map(Number);
|
|
1008
1266
|
const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]).map(
|
|
@@ -1019,7 +1277,7 @@ export default class Inibase {
|
|
|
1019
1277
|
);
|
|
1020
1278
|
if (greatestColumnTotalItems)
|
|
1021
1279
|
this.pageInfo = {
|
|
1022
|
-
...(({ columns, ...
|
|
1280
|
+
...(({ columns, ...restOfOptions }) => restOfOptions)(options),
|
|
1023
1281
|
...this.pageInfoArray[greatestColumnTotalItems],
|
|
1024
1282
|
total_pages: Math.ceil(
|
|
1025
1283
|
this.pageInfoArray[greatestColumnTotalItems].total_items /
|
|
@@ -1040,17 +1298,21 @@ export default class Inibase {
|
|
|
1040
1298
|
);
|
|
1041
1299
|
}
|
|
1042
1300
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1301
|
+
if (
|
|
1302
|
+
!RETURN ||
|
|
1303
|
+
(Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
|
|
1304
|
+
(Array.isArray(RETURN) && !RETURN.length)
|
|
1305
|
+
)
|
|
1306
|
+
return null;
|
|
1307
|
+
return Utils.isArrayOfObjects(RETURN)
|
|
1308
|
+
? (RETURN as Data[]).map((data: Data) => {
|
|
1309
|
+
data.id = Utils.encodeID(data.id as number, this.databasePath);
|
|
1310
|
+
return data;
|
|
1311
|
+
})
|
|
1312
|
+
: {
|
|
1313
|
+
...(RETURN as Data),
|
|
1314
|
+
id: Utils.encodeID((RETURN as Data).id as number, this.databasePath),
|
|
1315
|
+
};
|
|
1054
1316
|
}
|
|
1055
1317
|
|
|
1056
1318
|
public async post(
|
|
@@ -1066,7 +1328,7 @@ export default class Inibase {
|
|
|
1066
1328
|
if (!schema) throw this.throwError("NO_SCHEMA", tableName);
|
|
1067
1329
|
const idFilePath = join(this.databasePath, tableName, "id.inib");
|
|
1068
1330
|
let last_id = existsSync(idFilePath)
|
|
1069
|
-
? Number(Object.values(await File.get(idFilePath, "number"
|
|
1331
|
+
? Number(Object.values(await File.get(idFilePath, -1, "number"))[0])
|
|
1070
1332
|
: 0;
|
|
1071
1333
|
if (Utils.isArrayOfObjects(data))
|
|
1072
1334
|
(data as Data[]).forEach((single_data, index) => {
|
|
@@ -1110,8 +1372,12 @@ export default class Inibase {
|
|
|
1110
1372
|
public async put(
|
|
1111
1373
|
tableName: string,
|
|
1112
1374
|
data: Data | Data[],
|
|
1113
|
-
where?: number | string | (number | string)[] | Criteria
|
|
1114
|
-
|
|
1375
|
+
where?: number | string | (number | string)[] | Criteria,
|
|
1376
|
+
options: Options = {
|
|
1377
|
+
page: 1,
|
|
1378
|
+
per_page: 15,
|
|
1379
|
+
}
|
|
1380
|
+
): Promise<Data | Data[] | null> {
|
|
1115
1381
|
const schema = this.getTableSchema(tableName);
|
|
1116
1382
|
if (!schema) throw this.throwError("NO_SCHEMA", tableName);
|
|
1117
1383
|
this.validateData(data, schema, true);
|
|
@@ -1120,54 +1386,59 @@ export default class Inibase {
|
|
|
1120
1386
|
if (Utils.isArrayOfObjects(data)) {
|
|
1121
1387
|
if (
|
|
1122
1388
|
!(data as Data[]).every(
|
|
1123
|
-
(item) => item.hasOwnProperty("id") &&
|
|
1389
|
+
(item) => item.hasOwnProperty("id") && Utils.isValidID(item.id)
|
|
1124
1390
|
)
|
|
1125
1391
|
)
|
|
1126
1392
|
throw this.throwError("INVALID_ID");
|
|
1127
|
-
|
|
1393
|
+
return this.put(
|
|
1128
1394
|
tableName,
|
|
1129
1395
|
data,
|
|
1130
1396
|
(data as Data[]).map((item) => item.id)
|
|
1131
1397
|
);
|
|
1132
1398
|
} else if (data.hasOwnProperty("id")) {
|
|
1133
|
-
if (!
|
|
1399
|
+
if (!Utils.isValidID((data as Data).id))
|
|
1134
1400
|
throw this.throwError("INVALID_ID", (data as Data).id);
|
|
1135
|
-
|
|
1401
|
+
return this.put(
|
|
1136
1402
|
tableName,
|
|
1137
1403
|
data,
|
|
1138
|
-
|
|
1404
|
+
Utils.decodeID((data as Data).id as string, this.databasePath)
|
|
1139
1405
|
);
|
|
1140
1406
|
} else {
|
|
1141
1407
|
const pathesContents = this.joinPathesContents(
|
|
1142
1408
|
join(this.databasePath, tableName),
|
|
1143
1409
|
Utils.isArrayOfObjects(data)
|
|
1144
1410
|
? (data as Data[]).map((item) => ({
|
|
1145
|
-
...item,
|
|
1411
|
+
...(({ id, ...restOfData }) => restOfData)(item),
|
|
1146
1412
|
updated_at: new Date(),
|
|
1147
1413
|
}))
|
|
1148
|
-
: {
|
|
1414
|
+
: {
|
|
1415
|
+
...(({ id, ...restOfData }) => restOfData)(data as Data),
|
|
1416
|
+
updated_at: new Date(),
|
|
1417
|
+
}
|
|
1149
1418
|
);
|
|
1150
1419
|
for (const [path, content] of Object.entries(pathesContents))
|
|
1151
1420
|
await File.replace(path, content);
|
|
1421
|
+
return this.get(tableName, where, options);
|
|
1152
1422
|
}
|
|
1153
|
-
} else if (
|
|
1423
|
+
} else if (Utils.isValidID(where)) {
|
|
1154
1424
|
let Ids = where as string | string[];
|
|
1155
1425
|
if (!Array.isArray(Ids)) Ids = [Ids];
|
|
1156
1426
|
const idFilePath = join(this.databasePath, tableName, "id.inib");
|
|
1157
1427
|
if (!existsSync(idFilePath)) throw this.throwError("NO_ITEMS", tableName);
|
|
1158
1428
|
const [lineNumbers, countItems] = await File.search(
|
|
1159
1429
|
idFilePath,
|
|
1160
|
-
"number",
|
|
1161
1430
|
"[]",
|
|
1162
|
-
Ids.map((id) =>
|
|
1431
|
+
Ids.map((id) => Utils.decodeID(id, this.databasePath)),
|
|
1432
|
+
undefined,
|
|
1433
|
+
"number",
|
|
1163
1434
|
undefined,
|
|
1164
1435
|
Ids.length
|
|
1165
1436
|
);
|
|
1166
1437
|
if (!lineNumbers || !Object.keys(lineNumbers).length)
|
|
1167
1438
|
throw this.throwError("INVALID_ID");
|
|
1168
|
-
|
|
1439
|
+
return this.put(tableName, data, Object.keys(lineNumbers).map(Number));
|
|
1169
1440
|
} else if (Utils.isNumber(where)) {
|
|
1170
|
-
// where in this case, is the line(s) number(s) and not id(s)
|
|
1441
|
+
// "where" in this case, is the line(s) number(s) and not id(s)
|
|
1171
1442
|
const pathesContents = Object.fromEntries(
|
|
1172
1443
|
Object.entries(
|
|
1173
1444
|
this.joinPathesContents(
|
|
@@ -1192,18 +1463,20 @@ export default class Inibase {
|
|
|
1192
1463
|
);
|
|
1193
1464
|
for (const [path, content] of Object.entries(pathesContents))
|
|
1194
1465
|
await File.replace(path, content);
|
|
1466
|
+
return this.get(tableName, where, options);
|
|
1195
1467
|
} else if (typeof where === "object" && !Array.isArray(where)) {
|
|
1196
1468
|
const lineNumbers = this.get(tableName, where, undefined, true);
|
|
1197
1469
|
if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
|
|
1198
1470
|
throw this.throwError("NO_ITEMS", tableName);
|
|
1199
|
-
|
|
1200
|
-
} else throw this.throwError("
|
|
1471
|
+
return this.put(tableName, data, lineNumbers);
|
|
1472
|
+
} else throw this.throwError("INVALID_PARAMETERS", tableName);
|
|
1201
1473
|
}
|
|
1202
1474
|
|
|
1203
1475
|
public async delete(
|
|
1204
1476
|
tableName: string,
|
|
1205
|
-
where?: number | string | (number | string)[] | Criteria
|
|
1206
|
-
|
|
1477
|
+
where?: number | string | (number | string)[] | Criteria,
|
|
1478
|
+
_id?: string | string[]
|
|
1479
|
+
): Promise<string | string[]> {
|
|
1207
1480
|
const schema = this.getTableSchema(tableName);
|
|
1208
1481
|
if (!schema) throw this.throwError("NO_SCHEMA", tableName);
|
|
1209
1482
|
if (!where) {
|
|
@@ -1214,25 +1487,41 @@ export default class Inibase {
|
|
|
1214
1487
|
))
|
|
1215
1488
|
unlinkSync(join(this.databasePath, tableName, file));
|
|
1216
1489
|
}
|
|
1217
|
-
|
|
1490
|
+
return "*";
|
|
1491
|
+
} else if (Utils.isValidID(where)) {
|
|
1218
1492
|
let Ids = where as string | string[];
|
|
1219
1493
|
if (!Array.isArray(Ids)) Ids = [Ids];
|
|
1220
1494
|
const idFilePath = join(this.databasePath, tableName, "id.inib");
|
|
1221
1495
|
if (!existsSync(idFilePath)) throw this.throwError("NO_ITEMS", tableName);
|
|
1222
1496
|
const [lineNumbers, countItems] = await File.search(
|
|
1223
1497
|
idFilePath,
|
|
1224
|
-
"number",
|
|
1225
1498
|
"[]",
|
|
1226
|
-
Ids.map((id) =>
|
|
1499
|
+
Ids.map((id) => Utils.decodeID(id, this.databasePath)),
|
|
1500
|
+
undefined,
|
|
1501
|
+
"number",
|
|
1227
1502
|
undefined,
|
|
1228
1503
|
Ids.length
|
|
1229
1504
|
);
|
|
1230
1505
|
if (!lineNumbers || !Object.keys(lineNumbers).length)
|
|
1231
1506
|
throw this.throwError("INVALID_ID");
|
|
1232
|
-
|
|
1507
|
+
return this.delete(
|
|
1508
|
+
tableName,
|
|
1509
|
+
Object.keys(lineNumbers).map(Number),
|
|
1510
|
+
where as string | string[]
|
|
1511
|
+
);
|
|
1233
1512
|
} else if (Utils.isNumber(where)) {
|
|
1234
1513
|
const files = readdirSync(join(this.databasePath, tableName));
|
|
1235
1514
|
if (files.length) {
|
|
1515
|
+
if (!_id)
|
|
1516
|
+
_id = Object.values(
|
|
1517
|
+
await File.get(
|
|
1518
|
+
join(this.databasePath, tableName, "id.inib"),
|
|
1519
|
+
where as number | number[],
|
|
1520
|
+
"number"
|
|
1521
|
+
)
|
|
1522
|
+
)
|
|
1523
|
+
.map(Number)
|
|
1524
|
+
.map((id) => Utils.encodeID(id, this.databasePath));
|
|
1236
1525
|
for (const file in files.filter(
|
|
1237
1526
|
(fileName: string) => fileName !== "schema.inib"
|
|
1238
1527
|
))
|
|
@@ -1240,12 +1529,13 @@ export default class Inibase {
|
|
|
1240
1529
|
join(this.databasePath, tableName, file),
|
|
1241
1530
|
where as number | number[]
|
|
1242
1531
|
);
|
|
1532
|
+
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
1243
1533
|
}
|
|
1244
1534
|
} else if (typeof where === "object" && !Array.isArray(where)) {
|
|
1245
1535
|
const lineNumbers = this.get(tableName, where, undefined, true);
|
|
1246
1536
|
if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
|
|
1247
1537
|
throw this.throwError("NO_ITEMS", tableName);
|
|
1248
|
-
|
|
1249
|
-
} else throw this.throwError("
|
|
1538
|
+
return this.delete(tableName, lineNumbers);
|
|
1539
|
+
} else throw this.throwError("INVALID_PARAMETERS", tableName);
|
|
1250
1540
|
}
|
|
1251
1541
|
}
|