inibase 1.0.0-rc.4 → 1.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts CHANGED
@@ -9,12 +9,9 @@ import {
9
9
  renameSync,
10
10
  } from "fs";
11
11
  import { join, parse } from "path";
12
- import { createDecipheriv, createCipheriv, scryptSync } from "crypto";
13
12
  import Utils from "./utils";
14
13
  import File from "./file";
15
14
 
16
- export { File, Utils };
17
-
18
15
  export type Data = {
19
16
  id?: number | string;
20
17
  [key: string]: any;
@@ -33,31 +30,45 @@ export type FieldType =
33
30
  | "object"
34
31
  | "array"
35
32
  | "password";
36
- type Field = {
33
+ type FieldDefault = {
37
34
  id?: string | number | null | undefined;
38
35
  key: string;
39
36
  required?: boolean;
40
- } & (
41
- | {
42
- type: Exclude<FieldType, "array" | "object">;
43
- required?: boolean;
44
- }
45
- | {
46
- type: "array";
47
- children: FieldType | FieldType[] | Schema;
48
- }
49
- | {
50
- type: "object";
51
- children: Schema;
52
- }
53
- );
37
+ };
38
+ type FieldStringType = {
39
+ type: Exclude<FieldType, "array" | "object">;
40
+ };
41
+ type FieldStringArrayType = {
42
+ type: Exclude<FieldType, "array" | "object">[];
43
+ };
44
+ type FieldArrayType = {
45
+ type: "array";
46
+ children: FieldType | FieldType[] | Schema;
47
+ };
48
+ type FieldArrayArrayType = {
49
+ type: ["array", ...FieldType[]];
50
+ children: FieldType | FieldType[];
51
+ };
52
+ type FieldObjectType = {
53
+ type: "object";
54
+ children: Schema;
55
+ };
56
+ // if "type" is array, make "array" at first place, and "number" & "string" at last place of the array
57
+ type Field = FieldDefault &
58
+ (
59
+ | FieldStringType
60
+ | FieldStringArrayType
61
+ | FieldObjectType
62
+ | FieldArrayType
63
+ | FieldArrayArrayType
64
+ );
54
65
 
55
66
  export type Schema = Field[];
56
67
 
57
68
  export interface Options {
58
69
  page?: number;
59
70
  per_page?: number;
60
- columns?: string[];
71
+ columns?: string[] | string;
61
72
  }
62
73
 
63
74
  export type ComparisonOperator =
@@ -103,7 +114,7 @@ export default class Inibase {
103
114
  public pageInfoArray: Record<string, Record<string, number>>;
104
115
  public pageInfo: pageInfo;
105
116
 
106
- constructor(databaseName: string, mainFolder: string = "/") {
117
+ constructor(databaseName: string, mainFolder: string = ".") {
107
118
  this.database = databaseName;
108
119
  this.databasePath = join(mainFolder, databaseName);
109
120
  this.cache = new Map<string, any>();
@@ -122,14 +133,14 @@ export default class Inibase {
122
133
  ): Error {
123
134
  const errorMessages: Record<string, Record<string, string>> = {
124
135
  en: {
136
+ FIELD_REQUIRED: "REQUIRED: {variable}",
125
137
  NO_SCHEMA: "NO_SCHEMA: {variable}",
126
138
  NO_ITEMS: "NO_ITEMS: {variable}",
139
+ NO_DATA: "NO_DATA: {variable}",
127
140
  INVALID_ID: "INVALID_ID: {variable}",
128
141
  INVALID_TYPE: "INVALID_TYPE: {variable}",
129
- REQUIRED: "REQUIRED: {variable}",
130
- NO_DATA: "NO_DATA: {variable}",
131
142
  INVALID_OPERATOR: "INVALID_OPERATOR: {variable}",
132
- PARAMETERS: "PARAMETERS: {variable}",
143
+ INVALID_PARAMETERS: "PARAMETERS: {variable}",
133
144
  },
134
145
  // Add more languages and error messages as needed
135
146
  };
@@ -157,131 +168,6 @@ export default class Inibase {
157
168
  return new Error(errorMessage);
158
169
  }
159
170
 
160
- public encodeID(id: number, secretKey?: string | number): string {
161
- if (!secretKey) secretKey = this.databasePath;
162
-
163
- const salt = scryptSync(secretKey.toString(), "salt", 32),
164
- cipher = createCipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
165
-
166
- return cipher.update(id.toString(), "utf8", "hex") + cipher.final("hex");
167
- }
168
-
169
- public decodeID(input: string, secretKey?: string | number): number {
170
- if (!secretKey) secretKey = this.databasePath;
171
- const salt = scryptSync(secretKey.toString(), "salt", 32),
172
- decipher = createDecipheriv("aes-256-cbc", salt, salt.subarray(0, 16));
173
- return Number(
174
- decipher.update(input as string, "hex", "utf8") + decipher.final("utf8")
175
- );
176
- }
177
-
178
- public isValidID(input: any): boolean {
179
- return Array.isArray(input)
180
- ? input.every(this.isValidID)
181
- : typeof input === "string" && input.length === 32;
182
- }
183
-
184
- public validateData(
185
- data: Data | Data[],
186
- schema: Schema,
187
- skipRequiredField: boolean = false
188
- ): void {
189
- if (Utils.isArrayOfObjects(data))
190
- for (const single_data of data as Data[])
191
- this.validateData(single_data, schema, skipRequiredField);
192
- else if (!Array.isArray(data)) {
193
- const validateFieldType = (
194
- value: any,
195
- field: Field | FieldType | FieldType[]
196
- ): boolean => {
197
- if (Array.isArray(field))
198
- return field.some((item) => validateFieldType(value, item));
199
- switch (typeof field === "string" ? field : field.type) {
200
- case "string":
201
- return value === null || typeof value === "string";
202
- case "number":
203
- return value === null || typeof value === "number";
204
- case "boolean":
205
- return (
206
- value === null ||
207
- typeof value === "boolean" ||
208
- value === "true" ||
209
- value === "false"
210
- );
211
- case "date":
212
- return value === null || value instanceof Date;
213
- case "object":
214
- return (
215
- value === null ||
216
- (typeof value === "object" &&
217
- !Array.isArray(value) &&
218
- value !== null)
219
- );
220
- case "array":
221
- return (
222
- value === null ||
223
- (Array.isArray(value) &&
224
- value.every((item) => validateFieldType(item, field)))
225
- );
226
- case "email":
227
- return (
228
- value === null ||
229
- (typeof value === "string" &&
230
- /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
231
- );
232
- case "url":
233
- return (
234
- value === null ||
235
- (typeof value === "string" &&
236
- (value[0] === "#" ||
237
- /^((https?|www):\/\/)?[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]+(\/[^\s]*)?$/.test(
238
- value
239
- )))
240
- );
241
- case "table":
242
- // feat: check if id exists
243
- if (Array.isArray(value))
244
- return (
245
- typeof field !== "string" &&
246
- field.type === "table" &&
247
- ((Utils.isArrayOfObjects(value) &&
248
- value.every(
249
- (element) =>
250
- element.hasOwnProperty("id") &&
251
- this.isValidID((element as Data).id)
252
- )) ||
253
- value.every(Utils.isNumber) ||
254
- this.isValidID(value))
255
- );
256
- else if (Utils.isObject(value))
257
- return (
258
- value.hasOwnProperty("id") && this.isValidID((value as Data).id)
259
- );
260
- else return Utils.isNumber(value) || this.isValidID(value);
261
- default:
262
- return true;
263
- }
264
- };
265
- for (const field of schema) {
266
- if (data.hasOwnProperty(field.key)) {
267
- if (!validateFieldType(data[field.key], field))
268
- throw this.throwError("INVALID_TYPE", field.key);
269
- if (
270
- (field.type === "array" || field.type === "object") &&
271
- field.children &&
272
- Utils.isArrayOfObjects(field.children)
273
- )
274
- this.validateData(
275
- data[field.key],
276
- field.children as Schema,
277
- skipRequiredField
278
- );
279
- } else if (field.required && !skipRequiredField)
280
- throw this.throwError("REQUIRED", field.key);
281
- }
282
- }
283
- }
284
-
285
171
  public setTableSchema(tableName: string, schema: Schema): void {
286
172
  const encodeSchema = (schema: Schema) => {
287
173
  let RETURN: any[][] = [],
@@ -289,7 +175,9 @@ export default class Inibase {
289
175
  for (const field of schema) {
290
176
  if (!RETURN[index]) RETURN[index] = [];
291
177
  RETURN[index].push(
292
- field.id ? this.decodeID(field.id as string) : null
178
+ field.id
179
+ ? Utils.decodeID(field.id as string, this.databasePath)
180
+ : null
293
181
  );
294
182
  RETURN[index].push(field.key ?? null);
295
183
  RETURN[index].push(field.required ?? null);
@@ -313,14 +201,22 @@ export default class Inibase {
313
201
  ) {
314
202
  if (!field.id) {
315
203
  oldIndex++;
316
- field = { ...field, id: this.encodeID(oldIndex) };
317
- } else oldIndex = this.decodeID(field.id as string);
204
+ field = {
205
+ ...field,
206
+ id: Utils.encodeID(oldIndex, this.databasePath),
207
+ };
208
+ } else
209
+ oldIndex = Utils.decodeID(field.id as string, this.databasePath);
318
210
  field.children = addIdToSchema(field.children as Schema, oldIndex);
319
211
  oldIndex += field.children.length;
320
- } else if (field.id) oldIndex = this.decodeID(field.id as string);
212
+ } else if (field.id)
213
+ oldIndex = Utils.decodeID(field.id as string, this.databasePath);
321
214
  else {
322
215
  oldIndex++;
323
- field = { ...field, id: this.encodeID(oldIndex) };
216
+ field = {
217
+ ...field,
218
+ id: Utils.encodeID(oldIndex, this.databasePath),
219
+ };
324
220
  }
325
221
  return field;
326
222
  }),
@@ -332,8 +228,10 @@ export default class Inibase {
332
228
  Utils.isArrayOfObjects(lastField.children)
333
229
  )
334
230
  return findLastIdNumber(lastField.children as Schema);
335
- else return this.decodeID(lastField.id as string);
336
- } else return 0;
231
+ else if (lastField.id && Utils.isValidID(lastField.id))
232
+ return Utils.decodeID(lastField.id as string, this.databasePath);
233
+ }
234
+ return 0;
337
235
  };
338
236
 
339
237
  // remove id from schema
@@ -346,7 +244,7 @@ export default class Inibase {
346
244
  // update columns files names based on field id
347
245
  const schemaToIdsPath = (schema: any, prefix = "") => {
348
246
  let RETURN: any = {};
349
- for (const field of schema) {
247
+ for (const field of schema)
350
248
  if (field.children && Utils.isArrayOfObjects(field.children)) {
351
249
  Utils.deepMerge(
352
250
  RETURN,
@@ -357,37 +255,20 @@ export default class Inibase {
357
255
  (field.type === "array" ? ".*." : ".")
358
256
  )
359
257
  );
360
- } else if (this.isValidID(field.id))
361
- RETURN[this.decodeID(field.id)] = File.encodeFileName(
362
- (prefix ?? "") + field.key,
363
- "inib"
364
- );
365
- }
366
- return RETURN;
367
- },
368
- findChangedProperties = (
369
- obj1: Record<string, string>,
370
- obj2: Record<string, string>
371
- ): Record<string, string> | null => {
372
- const result: Record<string, string> = {};
258
+ } else if (Utils.isValidID(field.id))
259
+ RETURN[Utils.decodeID(field.id, this.databasePath)] =
260
+ File.encodeFileName((prefix ?? "") + field.key, "inib");
373
261
 
374
- for (const key1 in obj1) {
375
- if (obj2.hasOwnProperty(key1) && obj1[key1] !== obj2[key1]) {
376
- result[obj1[key1]] = obj2[key1];
377
- }
378
- }
379
-
380
- return Object.keys(result).length ? result : null;
262
+ return RETURN;
381
263
  },
382
- replaceOldPathes = findChangedProperties(
264
+ replaceOldPathes = Utils.findChangedProperties(
383
265
  schemaToIdsPath(this.getTableSchema(tableName)),
384
266
  schemaToIdsPath(schema)
385
267
  );
386
- if (replaceOldPathes) {
268
+ if (replaceOldPathes)
387
269
  for (const [oldPath, newPath] of Object.entries(replaceOldPathes))
388
270
  if (existsSync(join(TablePath, oldPath)))
389
271
  renameSync(join(TablePath, oldPath), join(TablePath, newPath));
390
- }
391
272
  }
392
273
 
393
274
  writeFileSync(
@@ -403,7 +284,7 @@ export default class Inibase {
403
284
  ? decodeSchema(field)
404
285
  : Object.fromEntries(
405
286
  Object.entries({
406
- id: this.encodeID(field[0]),
287
+ id: Utils.encodeID(field[0], this.databasePath),
407
288
  key: field[1],
408
289
  required: field[2],
409
290
  type: field[3],
@@ -428,24 +309,156 @@ export default class Inibase {
428
309
  );
429
310
  }
430
311
  return [
431
- { id: this.encodeID(0), key: "id", type: "number", required: true },
312
+ {
313
+ id: Utils.encodeID(0, this.databasePath),
314
+ key: "id",
315
+ type: "number",
316
+ required: true,
317
+ },
432
318
  ...(this.cache.get(TableSchemaPath) as unknown as Schema),
433
319
  ];
434
320
  }
435
321
 
436
- public getField(keyPath: string, schema: Schema | Field): Field | null {
437
- for (const key of keyPath.split(".")) {
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()) {
438
329
  if (key === "*") continue;
439
330
  const foundItem = (schema as Schema).find((item) => item.key === key);
440
331
  if (!foundItem) return null;
441
- schema =
332
+ if (index === keyPathSplited.length - 1) schema = foundItem;
333
+ if (
442
334
  (foundItem.type === "array" || foundItem.type === "object") &&
443
335
  foundItem.children &&
444
336
  Utils.isArrayOfObjects(foundItem.children)
445
- ? (foundItem.children as Schema)
446
- : foundItem;
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
+ }
447
461
  }
448
- return schema as Field;
449
462
  }
450
463
 
451
464
  public formatData(
@@ -453,89 +466,111 @@ export default class Inibase {
453
466
  schema: Schema,
454
467
  formatOnlyAvailiableKeys?: boolean
455
468
  ): Data | Data[] {
456
- if (Utils.isArrayOfObjects(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))
457
545
  return data.map((single_data: Data) =>
458
- this.formatData(single_data, schema)
546
+ this.formatData(single_data, schema, formatOnlyAvailiableKeys)
459
547
  );
460
- } else if (!Array.isArray(data)) {
548
+ else if (Utils.isObject(data)) {
461
549
  let RETURN: Data = {};
462
550
  for (const field of schema) {
463
551
  if (!data.hasOwnProperty(field.key)) {
552
+ if (formatOnlyAvailiableKeys) continue;
464
553
  RETURN[field.key] = this.getDefaultValue(field);
465
554
  continue;
466
555
  }
467
- if (formatOnlyAvailiableKeys && !data.hasOwnProperty(field.key))
468
- continue;
469
-
470
- if (field.type === "array" || field.type === "object") {
471
- if (field.children)
472
- if (typeof field.children === "string") {
473
- if (field.type === "array" && field.children === "table") {
474
- if (Array.isArray(data[field.key])) {
475
- if (Utils.isArrayOfObjects(data[field.key])) {
476
- if (
477
- data[field.key].every(
478
- (item: any) =>
479
- item.hasOwnProperty("id") &&
480
- (this.isValidID(item.id) || Utils.isNumber(item.id))
481
- )
482
- )
483
- data[field.key].map((item: any) =>
484
- Utils.isNumber(item.id)
485
- ? parseFloat(item.id)
486
- : this.decodeID(item.id)
487
- );
488
- } else if (
489
- this.isValidID(data[field.key]) ||
490
- Utils.isNumber(data[field.key])
491
- )
492
- RETURN[field.key] = data[field.key].map(
493
- (item: number | string) =>
494
- Utils.isNumber(item)
495
- ? parseFloat(item as string)
496
- : this.decodeID(item as string)
497
- );
498
- } else if (this.isValidID(data[field.key]))
499
- RETURN[field.key] = [this.decodeID(data[field.key])];
500
- else if (Utils.isNumber(data[field.key]))
501
- RETURN[field.key] = [parseFloat(data[field.key])];
502
- } else if (data.hasOwnProperty(field.key))
503
- RETURN[field.key] = data[field.key];
504
- } else if (Utils.isArrayOfObjects(field.children))
505
- RETURN[field.key] = this.formatData(
506
- data[field.key],
507
- field.children as Schema,
508
- formatOnlyAvailiableKeys
509
- );
510
- } else if (field.type === "table") {
511
- if (Utils.isObject(data[field.key])) {
512
- if (
513
- data[field.key].hasOwnProperty("id") &&
514
- (this.isValidID(data[field.key].id) ||
515
- Utils.isNumber(data[field.key]))
516
- )
517
- RETURN[field.key] = Utils.isNumber(data[field.key].id)
518
- ? parseFloat(data[field.key].id)
519
- : this.decodeID(data[field.key].id);
520
- } else if (
521
- this.isValidID(data[field.key]) ||
522
- Utils.isNumber(data[field.key])
523
- )
524
- RETURN[field.key] = Utils.isNumber(data[field.key])
525
- ? parseFloat(data[field.key])
526
- : this.decodeID(data[field.key]);
527
- } else if (field.type === "password")
528
- RETURN[field.key] =
529
- data[field.key].length === 161
530
- ? data[field.key]
531
- : Utils.hashPassword(data[field.key]);
532
- else RETURN[field.key] = data[field.key];
556
+ RETURN[field.key] = formatField(data[field.key], field);
533
557
  }
534
558
  return RETURN;
535
559
  } else return [];
536
560
  }
537
561
 
538
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
+
539
574
  switch (field.type) {
540
575
  case "array":
541
576
  return Utils.isArrayOfObjects(field.children)
@@ -562,37 +597,54 @@ export default class Inibase {
562
597
  mainPath: string,
563
598
  data: Data | Data[]
564
599
  ): { [key: string]: string[] } {
565
- const CombineData = (data: Data | Data[], prefix?: string) => {
600
+ const CombineData = (_data: Data | Data[], prefix?: string) => {
566
601
  let RETURN: Record<
567
602
  string,
568
603
  string | boolean | number | null | (string | boolean | number | null)[]
569
604
  > = {};
570
-
571
- if (Utils.isArrayOfObjects(data))
572
- RETURN = Utils.combineObjects(
573
- (data as Data[]).map((single_data) => CombineData(single_data))
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))
574
615
  );
575
616
  else {
576
- for (const [key, value] of Object.entries(data as Data)) {
617
+ for (const [key, value] of Object.entries(_data as Data)) {
577
618
  if (Utils.isObject(value))
578
619
  Object.assign(RETURN, CombineData(value, `${key}.`));
579
620
  else if (Array.isArray(value)) {
580
- if (Utils.isArrayOfObjects(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
+ )
581
633
  Object.assign(
582
634
  RETURN,
583
635
  CombineData(
584
- Utils.combineObjects(value),
636
+ combineObjectsToArray(value.map(combineObjectsToArray)),
585
637
  (prefix ?? "") + key + ".*."
586
638
  )
587
639
  );
588
640
  else
589
- RETURN[(prefix ?? "") + key] = Utils.encode(value) as
641
+ RETURN[(prefix ?? "") + key] = File.encode(value) as
590
642
  | boolean
591
643
  | number
592
644
  | string
593
645
  | null;
594
646
  } else
595
- RETURN[(prefix ?? "") + key] = Utils.encode(value) as
647
+ RETURN[(prefix ?? "") + key] = File.encode(value) as
596
648
  | boolean
597
649
  | number
598
650
  | string
@@ -622,6 +674,8 @@ export default class Inibase {
622
674
  onlyLinesNumbers?: boolean
623
675
  ): Promise<Data | Data[] | number[] | null> {
624
676
  if (!options.columns) options.columns = [];
677
+ else if (!Array.isArray(options.columns))
678
+ options.columns = [options.columns];
625
679
  else if (
626
680
  options.columns.length &&
627
681
  !(options.columns as string[]).includes("id")
@@ -635,23 +689,35 @@ export default class Inibase {
635
689
  const filterSchemaByColumns = (schema: Schema, columns: string[]): Schema =>
636
690
  schema
637
691
  .map((field) => {
638
- if (columns.includes(field.key)) return 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
+
639
697
  if (
640
698
  (field.type === "array" || field.type === "object") &&
641
699
  Utils.isArrayOfObjects(field.children) &&
642
- columns.filter((column) =>
643
- column.startsWith(
644
- field.key + (field.type === "array" ? ".*." : ".")
645
- )
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
+ )
646
708
  ).length
647
709
  ) {
648
710
  field.children = filterSchemaByColumns(
649
711
  field.children as Schema,
650
712
  columns
651
- .filter((column) =>
652
- column.startsWith(
653
- field.key + (field.type === "array" ? ".*." : ".")
654
- )
713
+ .filter(
714
+ (column) =>
715
+ column.startsWith(
716
+ field.key + (field.type === "array" ? ".*." : ".")
717
+ ) ||
718
+ column.startsWith(
719
+ "!" + field.key + (field.type === "array" ? ".*." : ".")
720
+ )
655
721
  )
656
722
  .map((column) =>
657
723
  column.replace(
@@ -677,10 +743,144 @@ export default class Inibase {
677
743
  let RETURN: Record<number, Data> = {};
678
744
  for (const field of schema) {
679
745
  if (
680
- (field.type === "array" || field.type === "object") &&
681
- field.children
746
+ (field.type === "array" ||
747
+ (Array.isArray(field.type) &&
748
+ (field.type as any).includes("array"))) &&
749
+ (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
750
+ .children
682
751
  ) {
683
- if (field.children === "table") {
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
+ ) {
684
884
  if (options.columns)
685
885
  options.columns = (options.columns as string[])
686
886
  .filter((column) => column.includes(`${field.key}.*.`))
@@ -691,8 +891,10 @@ export default class Inibase {
691
891
  path,
692
892
  File.encodeFileName((prefix ?? "") + field.key, "inib")
693
893
  ),
894
+ linesNumber,
694
895
  field.type,
695
- linesNumber
896
+ (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
897
+ .children as FieldType | FieldType[]
696
898
  )) ?? {}
697
899
  )) {
698
900
  if (!RETURN[index]) RETURN[index] = {};
@@ -700,26 +902,44 @@ export default class Inibase {
700
902
  ? await this.get(field.key, value as number, options)
701
903
  : this.getDefaultValue(field);
702
904
  }
703
- } else if (Utils.isArrayOfObjects(field.children)) {
704
- Object.entries(
705
- (await getItemsFromSchema(
905
+ } else if (
906
+ existsSync(
907
+ join(
706
908
  path,
707
- field.children as Schema,
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
+ ),
708
919
  linesNumber,
709
- (prefix ?? "") +
710
- field.key +
711
- (field.type === "array" ? ".*." : ".")
920
+ field.type,
921
+ (field as any)?.children
712
922
  )) ?? {}
713
923
  ).forEach(([index, item]) => {
714
924
  if (!RETURN[index]) RETURN[index] = {};
715
- RETURN[index][field.key] =
716
- field.type === "array" &&
717
- Utils.isObject(item) &&
718
- Object.values(item).every((i) => i === null)
719
- ? []
720
- : item;
925
+ RETURN[index][field.key] = item ?? this.getDefaultValue(field);
721
926
  });
722
- }
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
+ });
723
943
  } else if (field.type === "table") {
724
944
  if (
725
945
  existsSync(join(this.databasePath, field.key)) &&
@@ -744,8 +964,8 @@ export default class Inibase {
744
964
  path,
745
965
  File.encodeFileName((prefix ?? "") + field.key, "inib")
746
966
  ),
747
- "number",
748
- linesNumber
967
+ linesNumber,
968
+ "number"
749
969
  )) ?? {}
750
970
  )) {
751
971
  if (!RETURN[index]) RETURN[index] = {};
@@ -758,21 +978,21 @@ export default class Inibase {
758
978
  existsSync(
759
979
  join(path, File.encodeFileName((prefix ?? "") + field.key, "inib"))
760
980
  )
761
- ) {
981
+ )
762
982
  Object.entries(
763
983
  (await File.get(
764
984
  join(
765
985
  path,
766
986
  File.encodeFileName((prefix ?? "") + field.key, "inib")
767
987
  ),
988
+ linesNumber,
768
989
  field.type,
769
- linesNumber
990
+ (field as any)?.children
770
991
  )) ?? {}
771
992
  ).forEach(([index, item]) => {
772
993
  if (!RETURN[index]) RETURN[index] = {};
773
994
  RETURN[index][field.key] = item ?? this.getDefaultValue(field);
774
995
  });
775
- }
776
996
  }
777
997
  return RETURN;
778
998
  };
@@ -791,18 +1011,19 @@ export default class Inibase {
791
1011
  )
792
1012
  )
793
1013
  );
794
- } else if (this.isValidID(where) || Utils.isNumber(where)) {
1014
+ } else if (Utils.isValidID(where) || Utils.isNumber(where)) {
795
1015
  let Ids = where as string | number | (string | number)[];
796
1016
  if (!Array.isArray(Ids)) Ids = [Ids];
797
1017
  const idFilePath = join(this.databasePath, tableName, "id.inib");
798
1018
  if (!existsSync(idFilePath)) throw this.throwError("NO_ITEMS", tableName);
799
1019
  const [lineNumbers, countItems] = await File.search(
800
1020
  idFilePath,
801
- "number",
802
1021
  "[]",
803
1022
  Utils.isNumber(Ids)
804
- ? Ids.map((id) => parseFloat(id as string))
805
- : Ids.map((id) => this.decodeID(id as string)),
1023
+ ? Ids.map((id) => Number(id as string))
1024
+ : Ids.map((id) => Utils.decodeID(id as string, this.databasePath)),
1025
+ undefined,
1026
+ "number",
806
1027
  undefined,
807
1028
  Ids.length
808
1029
  );
@@ -822,7 +1043,8 @@ export default class Inibase {
822
1043
  } else if (Utils.isObject(where)) {
823
1044
  // Criteria
824
1045
  const FormatObjectCriteriaValue = (
825
- value: string
1046
+ value: string,
1047
+ isParentArray: boolean = false
826
1048
  ): [ComparisonOperator, string | number | boolean | null] => {
827
1049
  switch (value[0]) {
828
1050
  case ">":
@@ -849,10 +1071,19 @@ export default class Inibase {
849
1071
  value.slice(3) as string | number,
850
1072
  ]
851
1073
  : [
852
- value.slice(0, 1) as ComparisonOperator,
1074
+ (value.slice(0, 1) + "=") as ComparisonOperator,
853
1075
  value.slice(1) as string | number,
854
1076
  ];
855
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
+ ];
856
1087
  case "*":
857
1088
  return [
858
1089
  value.slice(0, 1) as ComparisonOperator,
@@ -899,6 +1130,7 @@ export default class Inibase {
899
1130
  allTrue = true;
900
1131
  let index = -1;
901
1132
  for (const [key, value] of Object.entries(criteria)) {
1133
+ const field = this.getField(key, schema as Schema) as Field;
902
1134
  index++;
903
1135
  let searchOperator:
904
1136
  | ComparisonOperator
@@ -1001,10 +1233,11 @@ export default class Inibase {
1001
1233
  tableName,
1002
1234
  File.encodeFileName(key, "inib")
1003
1235
  ),
1004
- this.getField(key, schema as Schema)?.type ?? "string",
1005
1236
  searchOperator,
1006
1237
  searchComparedAtValue,
1007
1238
  searchLogicalOperator,
1239
+ field?.type,
1240
+ (field as any)?.children,
1008
1241
  options.per_page,
1009
1242
  (options.page as number) - 1 * (options.per_page as number) + 1,
1010
1243
  true
@@ -1044,7 +1277,7 @@ export default class Inibase {
1044
1277
  );
1045
1278
  if (greatestColumnTotalItems)
1046
1279
  this.pageInfo = {
1047
- ...(({ columns, ...restOFOptions }) => restOFOptions)(options),
1280
+ ...(({ columns, ...restOfOptions }) => restOfOptions)(options),
1048
1281
  ...this.pageInfoArray[greatestColumnTotalItems],
1049
1282
  total_pages: Math.ceil(
1050
1283
  this.pageInfoArray[greatestColumnTotalItems].total_items /
@@ -1073,12 +1306,12 @@ export default class Inibase {
1073
1306
  return null;
1074
1307
  return Utils.isArrayOfObjects(RETURN)
1075
1308
  ? (RETURN as Data[]).map((data: Data) => {
1076
- data.id = this.encodeID(data.id as number);
1309
+ data.id = Utils.encodeID(data.id as number, this.databasePath);
1077
1310
  return data;
1078
1311
  })
1079
1312
  : {
1080
1313
  ...(RETURN as Data),
1081
- id: this.encodeID((RETURN as Data).id as number),
1314
+ id: Utils.encodeID((RETURN as Data).id as number, this.databasePath),
1082
1315
  };
1083
1316
  }
1084
1317
 
@@ -1095,7 +1328,7 @@ export default class Inibase {
1095
1328
  if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1096
1329
  const idFilePath = join(this.databasePath, tableName, "id.inib");
1097
1330
  let last_id = existsSync(idFilePath)
1098
- ? Number(Object.values(await File.get(idFilePath, "number", -1))[0])
1331
+ ? Number(Object.values(await File.get(idFilePath, -1, "number"))[0])
1099
1332
  : 0;
1100
1333
  if (Utils.isArrayOfObjects(data))
1101
1334
  (data as Data[]).forEach((single_data, index) => {
@@ -1139,8 +1372,12 @@ export default class Inibase {
1139
1372
  public async put(
1140
1373
  tableName: string,
1141
1374
  data: Data | Data[],
1142
- where?: number | string | (number | string)[] | Criteria
1143
- ) {
1375
+ where?: number | string | (number | string)[] | Criteria,
1376
+ options: Options = {
1377
+ page: 1,
1378
+ per_page: 15,
1379
+ }
1380
+ ): Promise<Data | Data[] | null> {
1144
1381
  const schema = this.getTableSchema(tableName);
1145
1382
  if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1146
1383
  this.validateData(data, schema, true);
@@ -1149,54 +1386,59 @@ export default class Inibase {
1149
1386
  if (Utils.isArrayOfObjects(data)) {
1150
1387
  if (
1151
1388
  !(data as Data[]).every(
1152
- (item) => item.hasOwnProperty("id") && this.isValidID(item.id)
1389
+ (item) => item.hasOwnProperty("id") && Utils.isValidID(item.id)
1153
1390
  )
1154
1391
  )
1155
1392
  throw this.throwError("INVALID_ID");
1156
- await this.put(
1393
+ return this.put(
1157
1394
  tableName,
1158
1395
  data,
1159
1396
  (data as Data[]).map((item) => item.id)
1160
1397
  );
1161
1398
  } else if (data.hasOwnProperty("id")) {
1162
- if (!this.isValidID((data as Data).id))
1399
+ if (!Utils.isValidID((data as Data).id))
1163
1400
  throw this.throwError("INVALID_ID", (data as Data).id);
1164
- await this.put(
1401
+ return this.put(
1165
1402
  tableName,
1166
1403
  data,
1167
- this.decodeID((data as Data).id as string)
1404
+ Utils.decodeID((data as Data).id as string, this.databasePath)
1168
1405
  );
1169
1406
  } else {
1170
1407
  const pathesContents = this.joinPathesContents(
1171
1408
  join(this.databasePath, tableName),
1172
1409
  Utils.isArrayOfObjects(data)
1173
1410
  ? (data as Data[]).map((item) => ({
1174
- ...item,
1411
+ ...(({ id, ...restOfData }) => restOfData)(item),
1175
1412
  updated_at: new Date(),
1176
1413
  }))
1177
- : { ...data, updated_at: new Date() }
1414
+ : {
1415
+ ...(({ id, ...restOfData }) => restOfData)(data as Data),
1416
+ updated_at: new Date(),
1417
+ }
1178
1418
  );
1179
1419
  for (const [path, content] of Object.entries(pathesContents))
1180
1420
  await File.replace(path, content);
1421
+ return this.get(tableName, where, options);
1181
1422
  }
1182
- } else if (this.isValidID(where)) {
1423
+ } else if (Utils.isValidID(where)) {
1183
1424
  let Ids = where as string | string[];
1184
1425
  if (!Array.isArray(Ids)) Ids = [Ids];
1185
1426
  const idFilePath = join(this.databasePath, tableName, "id.inib");
1186
1427
  if (!existsSync(idFilePath)) throw this.throwError("NO_ITEMS", tableName);
1187
1428
  const [lineNumbers, countItems] = await File.search(
1188
1429
  idFilePath,
1189
- "number",
1190
1430
  "[]",
1191
- Ids.map((id) => this.decodeID(id)),
1431
+ Ids.map((id) => Utils.decodeID(id, this.databasePath)),
1432
+ undefined,
1433
+ "number",
1192
1434
  undefined,
1193
1435
  Ids.length
1194
1436
  );
1195
1437
  if (!lineNumbers || !Object.keys(lineNumbers).length)
1196
1438
  throw this.throwError("INVALID_ID");
1197
- await this.put(tableName, data, Object.keys(lineNumbers).map(Number));
1439
+ return this.put(tableName, data, Object.keys(lineNumbers).map(Number));
1198
1440
  } else if (Utils.isNumber(where)) {
1199
- // where in this case, is the line(s) number(s) and not id(s)
1441
+ // "where" in this case, is the line(s) number(s) and not id(s)
1200
1442
  const pathesContents = Object.fromEntries(
1201
1443
  Object.entries(
1202
1444
  this.joinPathesContents(
@@ -1221,18 +1463,20 @@ export default class Inibase {
1221
1463
  );
1222
1464
  for (const [path, content] of Object.entries(pathesContents))
1223
1465
  await File.replace(path, content);
1466
+ return this.get(tableName, where, options);
1224
1467
  } else if (typeof where === "object" && !Array.isArray(where)) {
1225
1468
  const lineNumbers = this.get(tableName, where, undefined, true);
1226
1469
  if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
1227
1470
  throw this.throwError("NO_ITEMS", tableName);
1228
- await this.put(tableName, data, lineNumbers);
1229
- } else throw this.throwError("PARAMETERS", tableName);
1471
+ return this.put(tableName, data, lineNumbers);
1472
+ } else throw this.throwError("INVALID_PARAMETERS", tableName);
1230
1473
  }
1231
1474
 
1232
1475
  public async delete(
1233
1476
  tableName: string,
1234
- where?: number | string | (number | string)[] | Criteria
1235
- ) {
1477
+ where?: number | string | (number | string)[] | Criteria,
1478
+ _id?: string | string[]
1479
+ ): Promise<string | string[]> {
1236
1480
  const schema = this.getTableSchema(tableName);
1237
1481
  if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1238
1482
  if (!where) {
@@ -1243,25 +1487,41 @@ export default class Inibase {
1243
1487
  ))
1244
1488
  unlinkSync(join(this.databasePath, tableName, file));
1245
1489
  }
1246
- } else if (this.isValidID(where)) {
1490
+ return "*";
1491
+ } else if (Utils.isValidID(where)) {
1247
1492
  let Ids = where as string | string[];
1248
1493
  if (!Array.isArray(Ids)) Ids = [Ids];
1249
1494
  const idFilePath = join(this.databasePath, tableName, "id.inib");
1250
1495
  if (!existsSync(idFilePath)) throw this.throwError("NO_ITEMS", tableName);
1251
1496
  const [lineNumbers, countItems] = await File.search(
1252
1497
  idFilePath,
1253
- "number",
1254
1498
  "[]",
1255
- Ids.map((id) => this.decodeID(id)),
1499
+ Ids.map((id) => Utils.decodeID(id, this.databasePath)),
1500
+ undefined,
1501
+ "number",
1256
1502
  undefined,
1257
1503
  Ids.length
1258
1504
  );
1259
1505
  if (!lineNumbers || !Object.keys(lineNumbers).length)
1260
1506
  throw this.throwError("INVALID_ID");
1261
- await this.delete(tableName, Object.keys(lineNumbers).map(Number));
1507
+ return this.delete(
1508
+ tableName,
1509
+ Object.keys(lineNumbers).map(Number),
1510
+ where as string | string[]
1511
+ );
1262
1512
  } else if (Utils.isNumber(where)) {
1263
1513
  const files = readdirSync(join(this.databasePath, tableName));
1264
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));
1265
1525
  for (const file in files.filter(
1266
1526
  (fileName: string) => fileName !== "schema.inib"
1267
1527
  ))
@@ -1269,12 +1529,13 @@ export default class Inibase {
1269
1529
  join(this.databasePath, tableName, file),
1270
1530
  where as number | number[]
1271
1531
  );
1532
+ return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
1272
1533
  }
1273
1534
  } else if (typeof where === "object" && !Array.isArray(where)) {
1274
1535
  const lineNumbers = this.get(tableName, where, undefined, true);
1275
1536
  if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
1276
1537
  throw this.throwError("NO_ITEMS", tableName);
1277
- await this.delete(tableName, lineNumbers);
1278
- } else throw this.throwError("PARAMETERS", tableName);
1538
+ return this.delete(tableName, lineNumbers);
1539
+ } else throw this.throwError("INVALID_PARAMETERS", tableName);
1279
1540
  }
1280
1541
  }