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