inibase 1.0.0-rc.5 → 1.0.0-rc.51

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