inibase 1.0.0-rc.11 → 1.0.0-rc.110

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/index.ts DELETED
@@ -1,1510 +0,0 @@
1
- import {
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";
12
- import File from "./file";
13
- import Utils from "./utils";
14
- import UtilsServer from "./utils.server";
15
-
16
- export type Data = {
17
- id?: number | string;
18
- [key: string]: any;
19
- created_at?: Date;
20
- updated_at?: Date;
21
- };
22
-
23
- export type FieldType =
24
- | "string"
25
- | "number"
26
- | "boolean"
27
- | "date"
28
- | "email"
29
- | "url"
30
- | "table"
31
- | "object"
32
- | "array"
33
- | "password"
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
- );
70
-
71
- export type Schema = Field[];
72
-
73
- export interface Options {
74
- page?: number;
75
- per_page?: number;
76
- columns?: string[] | string;
77
- }
78
-
79
- export type ComparisonOperator =
80
- | "="
81
- | "!="
82
- | ">"
83
- | "<"
84
- | ">="
85
- | "<="
86
- | "*"
87
- | "!*"
88
- | "[]"
89
- | "![]";
90
-
91
- type pageInfo = {
92
- total?: number;
93
- total_pages?: number;
94
- } & Options;
95
-
96
- export type Criteria =
97
- | {
98
- [logic in "and" | "or"]?: Criteria | (string | number | boolean | null)[];
99
- }
100
- | {
101
- [key: string]: string | number | boolean | Criteria;
102
- }
103
- | null;
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
-
115
- export default class Inibase {
116
- public folder: string;
117
- public database: string;
118
- public table: string;
119
- public pageInfo: pageInfo;
120
- private cache: Map<string, string>;
121
- private totalItems: Record<string, number>;
122
- private salt: Buffer;
123
-
124
- constructor(database: string, mainFolder: string = ".") {
125
- this.database = database;
126
- this.folder = mainFolder;
127
- this.table = null;
128
- this.cache = new Map<string, any>();
129
- this.totalItems = {};
130
- this.pageInfo = { page: 1, per_page: 15 };
131
- this.salt = scryptSync(database, "salt", 32);
132
- }
133
-
134
- private throwError(
135
- code: string,
136
- variable?:
137
- | string
138
- | number
139
- | (string | number)[]
140
- | Record<string, string | number>,
141
- language: string = "en"
142
- ): Error {
143
- const errorMessages: Record<string, Record<string, string>> = {
144
- en: {
145
- FIELD_REQUIRED: "REQUIRED: {variable}",
146
- NO_SCHEMA: "NO_SCHEMA: {variable}",
147
- NO_ITEMS: "NO_ITEMS: {variable}",
148
- NO_DATA: "NO_DATA: {variable}",
149
- INVALID_ID: "INVALID_ID: {variable}",
150
- INVALID_TYPE: "INVALID_TYPE: {variable}",
151
- INVALID_OPERATOR: "INVALID_OPERATOR: {variable}",
152
- INVALID_PARAMETERS: "PARAMETERS: {variable}",
153
- },
154
- // Add more languages and error messages as needed
155
- };
156
-
157
- let errorMessage = errorMessages[language][code] || code;
158
- if (variable) {
159
- if (
160
- typeof variable === "string" ||
161
- typeof variable === "number" ||
162
- Array.isArray(variable)
163
- )
164
- errorMessage = errorMessage.replaceAll(
165
- `{variable}`,
166
- Array.isArray(variable) ? variable.join(", ") : (variable as string)
167
- );
168
- else
169
- Object.keys(variable).forEach(
170
- (variableKey) =>
171
- (errorMessage = errorMessage.replaceAll(
172
- `{${variableKey}}`,
173
- variable[variableKey].toString()
174
- ))
175
- );
176
- }
177
- return new Error(errorMessage);
178
- }
179
-
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);
190
- }
191
- return 0;
192
- }
193
-
194
- public async setTableSchema(
195
- tableName: string,
196
- schema: Schema
197
- ): Promise<void> {
198
- const encodeSchema = (schema: Schema) => {
199
- let RETURN: any[][] = [],
200
- index = 0;
201
- for (const field of schema) {
202
- if (!RETURN[index]) RETURN[index] = [];
203
- RETURN[index].push(
204
- field.id
205
- ? UtilsServer.decodeID(field.id as string, this.salt)
206
- : null
207
- );
208
- RETURN[index].push(field.key ?? null);
209
- RETURN[index].push(field.required ?? null);
210
- RETURN[index].push(field.type ?? null);
211
- RETURN[index].push(
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
216
- : null
217
- );
218
- index++;
219
- }
220
- return RETURN;
221
- },
222
- addIdToSchema = (schema: Schema, oldIndex: number = 0) =>
223
- schema.map((field) => {
224
- if (
225
- (field.type === "array" || field.type === "object") &&
226
- Utils.isArrayOfObjects(field.children)
227
- ) {
228
- if (!field.id) {
229
- oldIndex++;
230
- field = {
231
- ...field,
232
- id: UtilsServer.encodeID(oldIndex, this.salt),
233
- };
234
- } else
235
- oldIndex = UtilsServer.decodeID(field.id as string, this.salt);
236
- field.children = addIdToSchema(field.children as Schema, oldIndex);
237
- oldIndex += field.children.length;
238
- } else if (field.id)
239
- oldIndex = UtilsServer.decodeID(field.id as string, this.salt);
240
- else {
241
- oldIndex++;
242
- field = {
243
- ...field,
244
- id: UtilsServer.encodeID(oldIndex, this.salt),
245
- };
246
- }
247
- return field;
248
- });
249
-
250
- // remove id from schema
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)) {
260
- // update columns files names based on field id
261
- const schemaToIdsPath = (schema: any, prefix = "") => {
262
- let RETURN: any = {};
263
- for (const field of schema)
264
- if (field.children && Utils.isArrayOfObjects(field.children)) {
265
- Utils.deepMerge(
266
- RETURN,
267
- schemaToIdsPath(
268
- field.children,
269
- (prefix ?? "") + field.key + "."
270
- )
271
- );
272
- } else if (Utils.isValidID(field.id))
273
- RETURN[UtilsServer.decodeID(field.id, this.salt)] =
274
- (prefix ?? "") + field.key + ".inib";
275
-
276
- return RETURN;
277
- },
278
- replaceOldPathes = Utils.findChangedProperties(
279
- schemaToIdsPath(await this.getTableSchema(tableName)),
280
- schemaToIdsPath(schema)
281
- );
282
- if (replaceOldPathes)
283
- for (const [oldPath, newPath] of Object.entries(replaceOldPathes))
284
- if (await File.isExists(join(TablePath, oldPath)))
285
- await rename(join(TablePath, oldPath), join(TablePath, newPath));
286
- }
287
-
288
- await writeFile(
289
- join(TablePath, "schema"),
290
- JSON.stringify(encodeSchema(schema))
291
- );
292
- }
293
-
294
- public async getTableSchema(tableName: string): Promise<Schema | undefined> {
295
- const decodeSchema = (encodedSchema: any) => {
296
- return encodedSchema.map((field: any) =>
297
- Array.isArray(field[0])
298
- ? decodeSchema(field)
299
- : Object.fromEntries(
300
- Object.entries({
301
- id: UtilsServer.encodeID(field[0], this.salt),
302
- key: field[1],
303
- required: field[2],
304
- type: field[3],
305
- children: field[4]
306
- ? Array.isArray(field[4])
307
- ? decodeSchema(field[4])
308
- : field[4]
309
- : null,
310
- }).filter(([_, v]) => v != null)
311
- )
312
- );
313
- },
314
- TableSchemaPath = join(this.folder, this.database, tableName, "schema");
315
- if (!(await File.isExists(TableSchemaPath))) return undefined;
316
- if (!this.cache.has(TableSchemaPath)) {
317
- const TableSchemaPathContent = await readFile(TableSchemaPath, {
318
- encoding: "utf8",
319
- });
320
- this.cache.set(
321
- TableSchemaPath,
322
- TableSchemaPathContent
323
- ? decodeSchema(JSON.parse(TableSchemaPathContent.toString()))
324
- : ""
325
- );
326
- }
327
- const schema = this.cache.get(TableSchemaPath) as unknown as Schema,
328
- lastIdNumber = this.findLastIdNumber(schema);
329
- return [
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
- },
349
- ];
350
- }
351
-
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()) {
359
- if (key === "*") continue;
360
- const foundItem = (schema as Schema).find((item) => item.key === key);
361
- if (!foundItem) return null;
362
- if (index === keyPathSplited.length - 1) schema = foundItem;
363
- if (
364
- (foundItem.type === "array" || foundItem.type === "object") &&
365
- foundItem.children &&
366
- Utils.isArrayOfObjects(foundItem.children)
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
- }
427
- }
428
- }
429
-
430
- public formatData(
431
- data: Data | Data[],
432
- schema: Schema,
433
- formatOnlyAvailiableKeys?: boolean
434
- ): Data | Data[] {
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))
517
- return data.map((single_data: Data) =>
518
- this.formatData(single_data, schema, formatOnlyAvailiableKeys)
519
- );
520
- else if (Utils.isObject(data)) {
521
- let RETURN: Data = {};
522
- for (const field of schema) {
523
- if (!data.hasOwnProperty(field.key)) {
524
- if (formatOnlyAvailiableKeys || !field.required) continue;
525
- RETURN[field.key] = this.getDefaultValue(field);
526
- continue;
527
- }
528
- RETURN[field.key] = formatField(data[field.key], field);
529
- }
530
- return RETURN;
531
- } else return [];
532
- }
533
-
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
-
546
- switch (field.type) {
547
- case "array":
548
- return Utils.isArrayOfObjects(field.children)
549
- ? [
550
- this.getDefaultValue({
551
- ...field,
552
- type: "object",
553
- children: field.children as Schema,
554
- }),
555
- ]
556
- : [];
557
- case "object":
558
- return Utils.combineObjects(
559
- field.children.map((f) => ({ [f.key]: this.getDefaultValue(f) }))
560
- );
561
- case "boolean":
562
- return false;
563
- default:
564
- return null;
565
- }
566
- }
567
-
568
- public joinPathesContents(
569
- mainPath: string,
570
- data: Data | Data[]
571
- ): { [key: string]: string[] } {
572
- const CombineData = (_data: Data | Data[], prefix?: string) => {
573
- let RETURN: Record<
574
- string,
575
- string | boolean | number | null | (string | boolean | number | null)[]
576
- > = {};
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))
587
- );
588
- else {
589
- for (const [key, value] of Object.entries(_data as Data)) {
590
- if (Utils.isObject(value))
591
- Object.assign(RETURN, CombineData(value, `${key}.`));
592
- else if (Array.isArray(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
- )
605
- Object.assign(
606
- RETURN,
607
- CombineData(
608
- combineObjectsToArray(value.map(combineObjectsToArray)),
609
- (prefix ?? "") + key + "."
610
- )
611
- );
612
- else {
613
- RETURN[(prefix ?? "") + key] = File.encode(value) as
614
- | boolean
615
- | number
616
- | string
617
- | null;
618
- }
619
- } else
620
- RETURN[(prefix ?? "") + key] = File.encode(value) as
621
- | boolean
622
- | number
623
- | string
624
- | null;
625
- }
626
- }
627
- return RETURN;
628
- };
629
- const addPathToKeys = (obj: Record<string, any>, path: string) => {
630
- const newObject: Record<string, any> = {};
631
-
632
- for (const key in obj) newObject[join(path, key + ".inib")] = obj[key];
633
-
634
- return newObject;
635
- };
636
- return addPathToKeys(CombineData(data), mainPath);
637
- }
638
-
639
- public async getOne(
640
- tableName: string,
641
- where?: string | number | (string | number)[] | Criteria,
642
- options: Options = {
643
- page: 1,
644
- per_page: 15,
645
- }
646
- ): Promise<Data | null> {
647
- const _get = await this.get(tableName, where, options);
648
- if (!_get) return null;
649
- else if (Array.isArray(_get)) return (_get as Data)[0];
650
- else return _get;
651
- }
652
-
653
- public async get(
654
- tableName: string,
655
- where?: string | number | (string | number)[] | Criteria,
656
- options: Options = {
657
- page: 1,
658
- per_page: 15,
659
- },
660
- onlyLinesNumbers?: boolean
661
- ): Promise<Data | Data[] | number[] | null> {
662
- if (!options.columns) options.columns = [];
663
- else if (!Array.isArray(options.columns))
664
- options.columns = [options.columns];
665
- if (options.columns.length && !(options.columns as string[]).includes("id"))
666
- options.columns.push("id");
667
- if (!options.page) options.page = 1;
668
- if (!options.per_page) options.per_page = 15;
669
- let RETURN!: Data | Data[] | null;
670
- let schema = await this.getTableSchema(tableName);
671
- if (!schema) throw this.throwError("NO_SCHEMA", tableName);
672
- const idFilePath = join(this.folder, this.database, tableName, "id.inib");
673
- if (!(await File.isExists(idFilePath))) return null;
674
- const filterSchemaByColumns = (schema: Schema, columns: string[]): Schema =>
675
- schema
676
- .map((field) => {
677
- if (columns.some((column) => column.startsWith("!")))
678
- return columns.includes("!" + field.key) ? null : field;
679
- if (columns.includes(field.key) || columns.includes("*"))
680
- return field;
681
-
682
- if (
683
- (field.type === "array" || field.type === "object") &&
684
- Utils.isArrayOfObjects(field.children) &&
685
- columns.filter(
686
- (column) =>
687
- column.startsWith(field.key + ".") ||
688
- column.startsWith("!" + field.key + ".")
689
- ).length
690
- ) {
691
- field.children = filterSchemaByColumns(
692
- field.children as Schema,
693
- columns
694
- .filter(
695
- (column) =>
696
- column.startsWith(field.key + ".") ||
697
- column.startsWith("!" + field.key + ".")
698
- )
699
- .map((column) => column.replace(field.key + ".", ""))
700
- );
701
- return field;
702
- }
703
- return null;
704
- })
705
- .filter((i) => i) as Schema;
706
- if (options.columns.length)
707
- schema = filterSchemaByColumns(schema, options.columns);
708
-
709
- const getItemsFromSchema = async (
710
- path: string,
711
- schema: Schema,
712
- linesNumber: number[],
713
- prefix?: string
714
- ) => {
715
- let RETURN: Record<number, Data> = {};
716
- for (const field of schema) {
717
- if (
718
- (field.type === "array" ||
719
- (Array.isArray(field.type) &&
720
- (field.type as any).includes("array"))) &&
721
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
722
- .children
723
- ) {
724
- if (
725
- Utils.isArrayOfObjects(
726
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
727
- .children
728
- )
729
- ) {
730
- if (
731
- (
732
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
733
- .children as Schema
734
- ).filter(
735
- (children) =>
736
- children.type === "array" &&
737
- Utils.isArrayOfObjects(children.children)
738
- ).length
739
- ) {
740
- // one of children has array field type and has children array of object = Schema
741
- Object.entries(
742
- (await getItemsFromSchema(
743
- path,
744
- (
745
- (
746
- field as FieldDefault &
747
- (FieldArrayType | FieldArrayArrayType)
748
- ).children as Schema
749
- ).filter(
750
- (children) =>
751
- children.type === "array" &&
752
- Utils.isArrayOfObjects(children.children)
753
- ),
754
- linesNumber,
755
- (prefix ?? "") + field.key + "."
756
- )) ?? {}
757
- ).forEach(([index, item]) => {
758
- if (Utils.isObject(item)) {
759
- if (!RETURN[index]) RETURN[index] = {};
760
- if (!RETURN[index][field.key]) RETURN[index][field.key] = [];
761
- for (const child_field of (
762
- (
763
- field as FieldDefault &
764
- (FieldArrayType | FieldArrayArrayType)
765
- ).children as Schema
766
- ).filter(
767
- (children) =>
768
- children.type === "array" &&
769
- Utils.isArrayOfObjects(children.children)
770
- )) {
771
- if (Utils.isObject(item[child_field.key])) {
772
- Object.entries(item[child_field.key]).forEach(
773
- ([key, value]) => {
774
- for (let _i = 0; _i < value.length; _i++) {
775
- if (!RETURN[index][field.key][_i])
776
- RETURN[index][field.key][_i] = {};
777
- if (!RETURN[index][field.key][_i][child_field.key])
778
- RETURN[index][field.key][_i][child_field.key] =
779
- [];
780
- value[_i].forEach((_element, _index) => {
781
- if (
782
- !RETURN[index][field.key][_i][child_field.key][
783
- _index
784
- ]
785
- )
786
- RETURN[index][field.key][_i][child_field.key][
787
- _index
788
- ] = {};
789
- RETURN[index][field.key][_i][child_field.key][
790
- _index
791
- ][key] = _element;
792
- });
793
- }
794
- }
795
- );
796
- }
797
- }
798
- }
799
- });
800
- (
801
- field as FieldDefault & (FieldArrayType | FieldArrayArrayType)
802
- ).children = (
803
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
804
- .children as Schema
805
- ).filter(
806
- (children) =>
807
- children.type !== "array" ||
808
- !Utils.isArrayOfObjects(children.children)
809
- );
810
- }
811
- Object.entries(
812
- (await getItemsFromSchema(
813
- path,
814
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
815
- .children as Schema,
816
- linesNumber,
817
- (prefix ?? "") + field.key + "."
818
- )) ?? {}
819
- ).forEach(([index, item]) => {
820
- if (!RETURN[index]) RETURN[index] = {};
821
- if (Utils.isObject(item)) {
822
- if (!Object.values(item).every((i) => i === null)) {
823
- if (RETURN[index][field.key])
824
- Object.entries(item).forEach(([key, value], _index) => {
825
- RETURN[index][field.key] = RETURN[index][field.key].map(
826
- (_obj, _i) => ({ ..._obj, [key]: value[_i] })
827
- );
828
- });
829
- else if (Object.values(item).every(Utils.isArrayOfArrays))
830
- RETURN[index][field.key] = item;
831
- else {
832
- RETURN[index][field.key] = [];
833
- Object.entries(item).forEach(([key, value]) => {
834
- for (let _i = 0; _i < value.length; _i++) {
835
- if (!RETURN[index][field.key][_i])
836
- RETURN[index][field.key][_i] = {};
837
- RETURN[index][field.key][_i][key] = value[_i];
838
- }
839
- });
840
- }
841
- } else RETURN[index][field.key] = null;
842
- } else RETURN[index][field.key] = item;
843
- });
844
- } else if (
845
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
846
- .children === "table" ||
847
- (Array.isArray(
848
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
849
- .children
850
- ) &&
851
- (
852
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
853
- .children as FieldType[]
854
- ).includes("table"))
855
- ) {
856
- if (options.columns)
857
- options.columns = (options.columns as string[])
858
- .filter((column) => column.includes(`${field.key}.`))
859
- .map((column) => column.replace(`${field.key}.`, ""));
860
- const [items, total_lines] = await File.get(
861
- join(path, (prefix ?? "") + field.key + ".inib"),
862
- linesNumber,
863
- field.type,
864
- (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
865
- .children as FieldType | FieldType[],
866
- this.salt
867
- );
868
-
869
- this.totalItems[tableName + "-" + field.key] = total_lines;
870
- for (const [index, item] of Object.entries(items)) {
871
- if (!RETURN[index]) RETURN[index] = {};
872
- RETURN[index][field.key] = item
873
- ? await this.get(field.key, item as number, options)
874
- : this.getDefaultValue(field);
875
- }
876
- } else if (
877
- await File.isExists(
878
- join(path, (prefix ?? "") + field.key + ".inib")
879
- )
880
- ) {
881
- const [items, total_lines] = await File.get(
882
- join(path, (prefix ?? "") + field.key + ".inib"),
883
- linesNumber,
884
- field.type,
885
- (field as any)?.children,
886
- this.salt
887
- );
888
-
889
- this.totalItems[tableName + "-" + field.key] = total_lines;
890
- for (const [index, item] of Object.entries(items)) {
891
- if (!RETURN[index]) RETURN[index] = {};
892
- RETURN[index][field.key] = item ?? this.getDefaultValue(field);
893
- }
894
- }
895
- } else if (field.type === "object") {
896
- for (const [index, item] of Object.entries(
897
- (await getItemsFromSchema(
898
- path,
899
- field.children as Schema,
900
- linesNumber,
901
- (prefix ?? "") + field.key + "."
902
- )) ?? {}
903
- )) {
904
- if (!RETURN[index]) RETURN[index] = {};
905
- if (Utils.isObject(item)) {
906
- if (!Object.values(item).every((i) => i === null))
907
- RETURN[index][field.key] = item;
908
- else RETURN[index][field.key] = null;
909
- } else RETURN[index][field.key] = null;
910
- }
911
- } else if (field.type === "table") {
912
- if (
913
- (await File.isExists(
914
- join(this.folder, this.database, field.key)
915
- )) &&
916
- (await File.isExists(
917
- join(path, (prefix ?? "") + field.key + ".inib")
918
- ))
919
- ) {
920
- if (options.columns)
921
- options.columns = (options.columns as string[])
922
- .filter(
923
- (column) =>
924
- column.includes(`${field.key}.`) &&
925
- !column.includes(`${field.key}.`)
926
- )
927
- .map((column) => column.replace(`${field.key}.`, ""));
928
- const [items, total_lines] = await File.get(
929
- join(path, (prefix ?? "") + field.key + ".inib"),
930
- linesNumber,
931
- "number",
932
- undefined,
933
- this.salt
934
- );
935
- this.totalItems[tableName + "-" + field.key] = total_lines;
936
- for (const [index, item] of Object.entries(items)) {
937
- if (!RETURN[index]) RETURN[index] = {};
938
- RETURN[index][field.key] = item
939
- ? await this.get(field.key, item as number, options)
940
- : this.getDefaultValue(field);
941
- }
942
- }
943
- } else if (
944
- await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))
945
- ) {
946
- const [items, total_lines] = await File.get(
947
- join(path, (prefix ?? "") + field.key + ".inib"),
948
- linesNumber,
949
- field.type,
950
- (field as any)?.children,
951
- this.salt
952
- );
953
-
954
- this.totalItems[tableName + "-" + field.key] = total_lines;
955
- for (const [index, item] of Object.entries(items)) {
956
- if (!RETURN[index]) RETURN[index] = {};
957
- RETURN[index][field.key] = item ?? this.getDefaultValue(field);
958
- }
959
- }
960
- }
961
- return RETURN;
962
- };
963
- if (!where) {
964
- // Display all data
965
- RETURN = Object.values(
966
- await getItemsFromSchema(
967
- join(this.folder, this.database, tableName),
968
- schema,
969
- Array.from(
970
- { length: options.per_page },
971
- (_, index) =>
972
- ((options.page as number) - 1) * (options.per_page as number) +
973
- index +
974
- 1
975
- )
976
- )
977
- );
978
- } else if (Utils.isValidID(where) || Utils.isNumber(where)) {
979
- let Ids = where as string | number | (string | number)[];
980
- if (!Array.isArray(Ids)) Ids = [Ids];
981
- const [lineNumbers, countItems] = await File.search(
982
- idFilePath,
983
- "[]",
984
- Utils.isNumber(Ids)
985
- ? Ids.map((id) => Number(id as string))
986
- : Ids.map((id) => UtilsServer.decodeID(id as string, this.salt)),
987
- undefined,
988
- "number",
989
- undefined,
990
- Ids.length,
991
- 0,
992
- false,
993
- this.salt
994
- );
995
- if (!lineNumbers || !Object.keys(lineNumbers).length)
996
- throw this.throwError(
997
- "INVALID_ID",
998
- where as number | string | (number | string)[]
999
- );
1000
- RETURN = Object.values(
1001
- (await getItemsFromSchema(
1002
- join(this.folder, this.database, tableName),
1003
- schema,
1004
- Object.keys(lineNumbers).map(Number)
1005
- )) ?? {}
1006
- );
1007
- if (RETURN.length && !Array.isArray(where)) RETURN = RETURN[0];
1008
- } else if (Utils.isObject(where)) {
1009
- // Criteria
1010
- const FormatObjectCriteriaValue = (
1011
- value: string,
1012
- isParentArray: boolean = false
1013
- ): [ComparisonOperator, string | number | boolean | null] => {
1014
- switch (value[0]) {
1015
- case ">":
1016
- case "<":
1017
- case "[":
1018
- return ["=", "]", "*"].includes(value[1])
1019
- ? [
1020
- value.slice(0, 2) as ComparisonOperator,
1021
- value.slice(2) as string | number,
1022
- ]
1023
- : [
1024
- value.slice(0, 1) as ComparisonOperator,
1025
- value.slice(1) as string | number,
1026
- ];
1027
- case "!":
1028
- return ["=", "*"].includes(value[1])
1029
- ? [
1030
- value.slice(0, 2) as ComparisonOperator,
1031
- value.slice(2) as string | number,
1032
- ]
1033
- : value[1] === "["
1034
- ? [
1035
- value.slice(0, 3) as ComparisonOperator,
1036
- value.slice(3) as string | number,
1037
- ]
1038
- : [
1039
- (value.slice(0, 1) + "=") as ComparisonOperator,
1040
- value.slice(1) as string | number,
1041
- ];
1042
- case "=":
1043
- return isParentArray
1044
- ? [
1045
- value.slice(0, 1) as ComparisonOperator,
1046
- value.slice(1) as string | number,
1047
- ]
1048
- : [
1049
- value.slice(0, 1) as ComparisonOperator,
1050
- (value.slice(1) + ",") as string,
1051
- ];
1052
- case "*":
1053
- return [
1054
- value.slice(0, 1) as ComparisonOperator,
1055
- value.slice(1) as string | number,
1056
- ];
1057
- default:
1058
- return ["=", value];
1059
- }
1060
- };
1061
-
1062
- const applyCriteria = async (
1063
- criteria?: Criteria,
1064
- allTrue?: boolean
1065
- ): Promise<Record<number, Data> | null> => {
1066
- let RETURN: Record<number, Data> = {};
1067
- if (!criteria) return null;
1068
- if (criteria.and && Utils.isObject(criteria.and)) {
1069
- const searchResult = await applyCriteria(
1070
- criteria.and as Criteria,
1071
- true
1072
- );
1073
- if (searchResult) {
1074
- RETURN = Utils.deepMerge(
1075
- RETURN,
1076
- Object.fromEntries(
1077
- Object.entries(searchResult).filter(
1078
- ([_k, v], _i) =>
1079
- Object.keys(v).length ===
1080
- Object.keys(criteria.and ?? {}).length
1081
- )
1082
- )
1083
- );
1084
- delete criteria.and;
1085
- } else return null;
1086
- }
1087
-
1088
- if (criteria.or && Utils.isObject(criteria.or)) {
1089
- const searchResult = await applyCriteria(
1090
- criteria.or as Criteria,
1091
- false
1092
- );
1093
- delete criteria.or;
1094
- if (searchResult) RETURN = Utils.deepMerge(RETURN, searchResult);
1095
- }
1096
-
1097
- if (Object.keys(criteria).length > 0) {
1098
- if (allTrue === undefined) allTrue = true;
1099
-
1100
- let index = -1;
1101
- for (const [key, value] of Object.entries(criteria)) {
1102
- const field = this.getField(key, schema as Schema) as Field;
1103
- index++;
1104
- let searchOperator:
1105
- | ComparisonOperator
1106
- | ComparisonOperator[]
1107
- | undefined = undefined,
1108
- searchComparedAtValue:
1109
- | string
1110
- | number
1111
- | boolean
1112
- | null
1113
- | (string | number | boolean | null)[]
1114
- | undefined = undefined,
1115
- searchLogicalOperator: "and" | "or" | undefined = undefined;
1116
- if (Utils.isObject(value)) {
1117
- if (
1118
- (value as Criteria)?.or &&
1119
- Array.isArray((value as Criteria).or)
1120
- ) {
1121
- const searchCriteria = (
1122
- (value as Criteria).or as (string | number | boolean)[]
1123
- )
1124
- .map(
1125
- (
1126
- single_or
1127
- ): [ComparisonOperator, string | number | boolean | null] =>
1128
- typeof single_or === "string"
1129
- ? FormatObjectCriteriaValue(single_or)
1130
- : ["=", single_or]
1131
- )
1132
- .filter((a) => a) as [ComparisonOperator, string | number][];
1133
- if (searchCriteria.length > 0) {
1134
- searchOperator = searchCriteria.map(
1135
- (single_or) => single_or[0]
1136
- );
1137
- searchComparedAtValue = searchCriteria.map(
1138
- (single_or) => single_or[1]
1139
- );
1140
- searchLogicalOperator = "or";
1141
- }
1142
- delete (value as Criteria).or;
1143
- }
1144
- if (
1145
- (value as Criteria)?.and &&
1146
- Array.isArray((value as Criteria).and)
1147
- ) {
1148
- const searchCriteria = (
1149
- (value as Criteria).and as (string | number | boolean)[]
1150
- )
1151
- .map(
1152
- (
1153
- single_and
1154
- ): [ComparisonOperator, string | number | boolean | null] =>
1155
- typeof single_and === "string"
1156
- ? FormatObjectCriteriaValue(single_and)
1157
- : ["=", single_and]
1158
- )
1159
- .filter((a) => a) as [ComparisonOperator, string | number][];
1160
- if (searchCriteria.length > 0) {
1161
- searchOperator = searchCriteria.map(
1162
- (single_and) => single_and[0]
1163
- );
1164
- searchComparedAtValue = searchCriteria.map(
1165
- (single_and) => single_and[1]
1166
- );
1167
- searchLogicalOperator = "and";
1168
- }
1169
- delete (value as Criteria).and;
1170
- }
1171
- } else if (Array.isArray(value)) {
1172
- const searchCriteria = value
1173
- .map(
1174
- (
1175
- single
1176
- ): [ComparisonOperator, string | number | boolean | null] =>
1177
- typeof single === "string"
1178
- ? FormatObjectCriteriaValue(single)
1179
- : ["=", single]
1180
- )
1181
- .filter((a) => a) as [ComparisonOperator, string | number][];
1182
- if (searchCriteria.length > 0) {
1183
- searchOperator = searchCriteria.map((single) => single[0]);
1184
- searchComparedAtValue = searchCriteria.map(
1185
- (single) => single[1]
1186
- );
1187
- searchLogicalOperator = "and";
1188
- }
1189
- } else if (typeof value === "string") {
1190
- const ComparisonOperatorValue = FormatObjectCriteriaValue(value);
1191
- if (ComparisonOperatorValue) {
1192
- searchOperator = ComparisonOperatorValue[0];
1193
- searchComparedAtValue = ComparisonOperatorValue[1];
1194
- }
1195
- } else {
1196
- searchOperator = "=";
1197
- searchComparedAtValue = value as number | boolean;
1198
- }
1199
- const [searchResult, total_lines] = await File.search(
1200
- join(this.folder, this.database, tableName, key + ".inib"),
1201
- searchOperator,
1202
- searchComparedAtValue,
1203
- searchLogicalOperator,
1204
- field?.type,
1205
- (field as any)?.children,
1206
- options.per_page,
1207
- (options.page as number) - 1 * (options.per_page as number) + 1,
1208
- true,
1209
- this.salt
1210
- );
1211
- if (searchResult) {
1212
- RETURN = Utils.deepMerge(RETURN, searchResult);
1213
- this.totalItems[tableName + "-" + key] = total_lines;
1214
- }
1215
-
1216
- if (allTrue && index > 0) {
1217
- if (!Object.keys(RETURN).length) RETURN = {};
1218
- RETURN = Object.fromEntries(
1219
- Object.entries(RETURN).filter(
1220
- ([_index, item]) => Object.keys(item).length > index
1221
- )
1222
- );
1223
- if (!Object.keys(RETURN).length) RETURN = {};
1224
- }
1225
- }
1226
- }
1227
-
1228
- return Object.keys(RETURN).length ? RETURN : null;
1229
- };
1230
-
1231
- RETURN = await applyCriteria(where as Criteria);
1232
- if (RETURN) {
1233
- if (onlyLinesNumbers) return Object.keys(RETURN).map(Number);
1234
- const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]).map(
1235
- (key) => parse(key).name
1236
- );
1237
- RETURN = Object.values(
1238
- Utils.deepMerge(
1239
- await getItemsFromSchema(
1240
- join(this.folder, this.database, tableName),
1241
- schema.filter(
1242
- (field) => !alreadyExistsColumns.includes(field.key)
1243
- ),
1244
- Object.keys(RETURN).map(Number)
1245
- ),
1246
- RETURN
1247
- )
1248
- );
1249
- }
1250
- }
1251
- if (
1252
- !RETURN ||
1253
- (Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
1254
- (Array.isArray(RETURN) && !RETURN.length)
1255
- )
1256
- return null;
1257
-
1258
- const greatestTotalItems = Math.max(
1259
- ...Object.entries(this.totalItems)
1260
- .filter(([k]) => k.startsWith(tableName + "-"))
1261
- .map(([, v]) => v)
1262
- );
1263
- this.pageInfo = {
1264
- ...(({ columns, ...restOfOptions }) => restOfOptions)(options),
1265
- total_pages: Math.ceil(greatestTotalItems / options.per_page),
1266
- total: greatestTotalItems,
1267
- };
1268
- return RETURN;
1269
- }
1270
-
1271
- public async post(
1272
- tableName: string,
1273
- data: Data | Data[],
1274
- options: Options = {
1275
- page: 1,
1276
- per_page: 15,
1277
- },
1278
- returnPostedData: boolean = true
1279
- ): Promise<Data | Data[] | null | void> {
1280
- const schema = await this.getTableSchema(tableName);
1281
- let RETURN: Data | Data[] | null | undefined;
1282
- if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1283
- const idFilePath = join(this.folder, this.database, tableName, "id.inib");
1284
- let last_id = (await File.isExists(idFilePath))
1285
- ? Number(
1286
- Object.values(
1287
- (await File.get(idFilePath, -1, "number", undefined, this.salt))[0]
1288
- )[0]
1289
- )
1290
- : 0;
1291
- if (Utils.isArrayOfObjects(data))
1292
- (data as Data[]).forEach((single_data, index) => {
1293
- if (!RETURN) RETURN = [];
1294
- RETURN[index] = (({ id, updated_at, created_at, ...rest }) => ({
1295
- id: ++last_id,
1296
- ...rest,
1297
- created_at: new Date(),
1298
- }))(single_data);
1299
- });
1300
- else
1301
- RETURN = (({ id, updated_at, created_at, ...rest }) => ({
1302
- id: ++last_id,
1303
- ...rest,
1304
- created_at: new Date(),
1305
- }))(data as Data);
1306
- if (!RETURN) throw this.throwError("NO_DATA");
1307
-
1308
- RETURN = this.formatData(RETURN, schema);
1309
- const pathesContents = this.joinPathesContents(
1310
- join(this.folder, this.database, tableName),
1311
- RETURN
1312
- );
1313
- for await (const [path, content] of Object.entries(pathesContents))
1314
- await appendFile(
1315
- path,
1316
- (Array.isArray(content) ? content.join("\n") : content ?? "") + "\n"
1317
- );
1318
-
1319
- if (returnPostedData)
1320
- return this.get(
1321
- tableName,
1322
- Utils.isArrayOfObjects(RETURN)
1323
- ? RETURN.map((data: Data) => data.id)
1324
- : ((RETURN as Data).id as number),
1325
- options
1326
- );
1327
- }
1328
-
1329
- public async put(
1330
- tableName: string,
1331
- data: Data | Data[],
1332
- where?: number | string | (number | string)[] | Criteria,
1333
- options: Options = {
1334
- page: 1,
1335
- per_page: 15,
1336
- },
1337
- returnPostedData: boolean = true
1338
- ): Promise<Data | Data[] | null | void> {
1339
- const schema = await this.getTableSchema(tableName);
1340
- if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1341
- const idFilePath = join(this.folder, this.database, tableName, "id.inib");
1342
- if (!(await File.isExists(idFilePath)))
1343
- throw this.throwError("NO_ITEMS", tableName);
1344
- data = this.formatData(data, schema, true);
1345
- if (!where) {
1346
- if (Utils.isArrayOfObjects(data)) {
1347
- if (
1348
- !(data as Data[]).every(
1349
- (item) => item.hasOwnProperty("id") && Utils.isValidID(item.id)
1350
- )
1351
- )
1352
- throw this.throwError("INVALID_ID");
1353
- return this.put(
1354
- tableName,
1355
- data,
1356
- (data as Data[]).map((item) => item.id)
1357
- );
1358
- } else if (data.hasOwnProperty("id")) {
1359
- if (!Utils.isValidID((data as Data).id))
1360
- throw this.throwError("INVALID_ID", (data as Data).id);
1361
- return this.put(
1362
- tableName,
1363
- data,
1364
- UtilsServer.decodeID((data as Data).id as string, this.salt)
1365
- );
1366
- } else {
1367
- const pathesContents = this.joinPathesContents(
1368
- join(this.folder, this.database, tableName),
1369
- Utils.isArrayOfObjects(data)
1370
- ? (data as Data[]).map((item) => ({
1371
- ...(({ id, ...restOfData }) => restOfData)(item),
1372
- updated_at: new Date(),
1373
- }))
1374
- : {
1375
- ...(({ id, ...restOfData }) => restOfData)(data as Data),
1376
- updated_at: new Date(),
1377
- }
1378
- );
1379
- for (const [path, content] of Object.entries(pathesContents))
1380
- await File.replace(path, content);
1381
- if (returnPostedData) return this.get(tableName, where, options);
1382
- }
1383
- } else if (Utils.isValidID(where)) {
1384
- let Ids = where as string | string[];
1385
- if (!Array.isArray(Ids)) Ids = [Ids];
1386
- const [lineNumbers, countItems] = await File.search(
1387
- idFilePath,
1388
- "[]",
1389
- Ids.map((id) => UtilsServer.decodeID(id, this.salt)),
1390
- undefined,
1391
- "number",
1392
- undefined,
1393
- Ids.length,
1394
- 0,
1395
- false,
1396
- this.salt
1397
- );
1398
- if (!lineNumbers || !Object.keys(lineNumbers).length)
1399
- throw this.throwError("INVALID_ID");
1400
- return this.put(tableName, data, Object.keys(lineNumbers).map(Number));
1401
- } else if (Utils.isNumber(where)) {
1402
- // "where" in this case, is the line(s) number(s) and not id(s)
1403
- const pathesContents = Object.fromEntries(
1404
- Object.entries(
1405
- this.joinPathesContents(
1406
- join(this.folder, this.database, tableName),
1407
- Utils.isArrayOfObjects(data)
1408
- ? (data as Data[]).map((item) => ({
1409
- ...item,
1410
- updated_at: new Date(),
1411
- }))
1412
- : { ...data, updated_at: new Date() }
1413
- )
1414
- ).map(([key, value]) => [
1415
- key,
1416
- ([...(Array.isArray(where) ? where : [where])] as number[]).reduce(
1417
- (obj, key, index) => ({
1418
- ...obj,
1419
- [key]: Array.isArray(value) ? value[index] : value,
1420
- }),
1421
- {}
1422
- ),
1423
- ])
1424
- );
1425
- for (const [path, content] of Object.entries(pathesContents))
1426
- await File.replace(path, content);
1427
- if (returnPostedData) return this.get(tableName, where, options);
1428
- } else if (typeof where === "object" && !Array.isArray(where)) {
1429
- const lineNumbers = this.get(tableName, where, undefined, true);
1430
- if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
1431
- throw this.throwError("NO_ITEMS", tableName);
1432
- return this.put(tableName, data, lineNumbers);
1433
- } else throw this.throwError("INVALID_PARAMETERS", tableName);
1434
- }
1435
-
1436
- public async delete(
1437
- tableName: string,
1438
- where?: number | string | (number | string)[] | Criteria,
1439
- _id?: string | string[]
1440
- ): Promise<string | string[] | null> {
1441
- const schema = await this.getTableSchema(tableName);
1442
- if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1443
- const idFilePath = join(this.folder, this.database, tableName, "id.inib");
1444
- if (!(await File.isExists(idFilePath)))
1445
- throw this.throwError("NO_ITEMS", tableName);
1446
- if (!where) {
1447
- const files = await readdir(join(this.folder, this.database, tableName));
1448
- if (files.length) {
1449
- for (const file in files.filter(
1450
- (fileName: string) => fileName !== "schema"
1451
- ))
1452
- await unlink(join(this.folder, this.database, tableName, file));
1453
- }
1454
- return "*";
1455
- } else if (Utils.isValidID(where)) {
1456
- let Ids = where as string | string[];
1457
- if (!Array.isArray(Ids)) Ids = [Ids];
1458
- const [lineNumbers, countItems] = await File.search(
1459
- idFilePath,
1460
- "[]",
1461
- Ids.map((id) => UtilsServer.decodeID(id, this.salt)),
1462
- undefined,
1463
- "number",
1464
- undefined,
1465
- Ids.length,
1466
- 0,
1467
- false,
1468
- this.salt
1469
- );
1470
- if (!lineNumbers || !Object.keys(lineNumbers).length)
1471
- throw this.throwError("INVALID_ID");
1472
- return this.delete(
1473
- tableName,
1474
- Object.keys(lineNumbers).map(Number),
1475
- where as string | string[]
1476
- );
1477
- } else if (Utils.isNumber(where)) {
1478
- const files = await readdir(join(this.folder, this.database, tableName));
1479
- if (files.length) {
1480
- if (!_id)
1481
- _id = Object.values(
1482
- (
1483
- await File.get(
1484
- join(this.folder, this.database, tableName, "id.inib"),
1485
- where as number | number[],
1486
- "number",
1487
- undefined,
1488
- this.salt
1489
- )
1490
- )[0]
1491
- ).map((id) => UtilsServer.encodeID(Number(id), this.salt));
1492
- for (const file of files.filter(
1493
- (fileName: string) =>
1494
- fileName.endsWith(".inib") && fileName !== "schema"
1495
- ))
1496
- await File.remove(
1497
- join(this.folder, this.database, tableName, file),
1498
- where as number | number[]
1499
- );
1500
- return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
1501
- }
1502
- } else if (typeof where === "object" && !Array.isArray(where)) {
1503
- const lineNumbers = this.get(tableName, where, undefined, true);
1504
- if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
1505
- throw this.throwError("NO_ITEMS", tableName);
1506
- return this.delete(tableName, lineNumbers);
1507
- } else throw this.throwError("INVALID_PARAMETERS", tableName);
1508
- return null;
1509
- }
1510
- }