inibase 1.0.0-rc.5 → 1.0.0-rc.7

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
@@ -1,16 +1,17 @@
1
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";
2
+ unlink,
3
+ rename,
4
+ readFile,
5
+ writeFile,
6
+ appendFile,
7
+ mkdir,
8
+ readdir,
9
+ } from "node:fs/promises";
10
+ import { join, parse } from "node:path";
11
+ import { scryptSync } from "node:crypto";
13
12
  import File from "./file";
13
+ import Utils from "./utils";
14
+ import UtilsServer from "./utils.server";
14
15
 
15
16
  export type Data = {
16
17
  id?: number | string;
@@ -29,11 +30,15 @@ export type FieldType =
29
30
  | "table"
30
31
  | "object"
31
32
  | "array"
32
- | "password";
33
+ | "password"
34
+ | "html"
35
+ | "ip"
36
+ | "id";
33
37
  type FieldDefault = {
34
38
  id?: string | number | null | undefined;
35
39
  key: string;
36
40
  required?: boolean;
41
+ children?: any;
37
42
  };
38
43
  type FieldStringType = {
39
44
  type: Exclude<FieldType, "array" | "object">;
@@ -54,7 +59,7 @@ type FieldObjectType = {
54
59
  children: Schema;
55
60
  };
56
61
  // if "type" is array, make "array" at first place, and "number" & "string" at last place of the array
57
- type Field = FieldDefault &
62
+ export type Field = FieldDefault &
58
63
  (
59
64
  | FieldStringType
60
65
  | FieldStringArrayType
@@ -84,7 +89,7 @@ export type ComparisonOperator =
84
89
  | "![]";
85
90
 
86
91
  type pageInfo = {
87
- total_items?: number;
92
+ total?: number;
88
93
  total_pages?: number;
89
94
  } & Options;
90
95
 
@@ -108,18 +113,22 @@ declare global {
108
113
  }
109
114
 
110
115
  export default class Inibase {
116
+ public folder: string;
111
117
  public database: string;
112
- public databasePath: string;
113
- public cache: Map<string, string>;
114
- public pageInfoArray: Record<string, Record<string, number>>;
118
+ public table: string;
115
119
  public pageInfo: pageInfo;
120
+ private cache: Map<string, string>;
121
+ private totalItems: Record<string, number>;
122
+ private salt: Buffer;
116
123
 
117
- constructor(databaseName: string, mainFolder: string = ".") {
118
- this.database = databaseName;
119
- this.databasePath = join(mainFolder, databaseName);
124
+ constructor(database: string, mainFolder: string = ".") {
125
+ this.database = database;
126
+ this.folder = mainFolder;
127
+ this.table = null;
120
128
  this.cache = new Map<string, any>();
121
- this.pageInfoArray = {};
129
+ this.totalItems = {};
122
130
  this.pageInfo = { page: 1, per_page: 15 };
131
+ this.salt = scryptSync(database, "salt", 32);
123
132
  }
124
133
 
125
134
  private throwError(
@@ -168,7 +177,24 @@ export default class Inibase {
168
177
  return new Error(errorMessage);
169
178
  }
170
179
 
171
- public setTableSchema(tableName: string, schema: Schema): void {
180
+ private findLastIdNumber(schema: Schema): number {
181
+ const lastField = schema[schema.length - 1];
182
+ if (lastField) {
183
+ if (
184
+ (lastField.type === "array" || lastField.type === "object") &&
185
+ Utils.isArrayOfObjects(lastField.children)
186
+ )
187
+ return this.findLastIdNumber(lastField.children as Schema);
188
+ else if (lastField.id && Utils.isValidID(lastField.id))
189
+ return UtilsServer.decodeID(lastField.id as string, this.salt);
190
+ }
191
+ return 0;
192
+ }
193
+
194
+ public async setTableSchema(
195
+ tableName: string,
196
+ schema: Schema
197
+ ): Promise<void> {
172
198
  const encodeSchema = (schema: Schema) => {
173
199
  let RETURN: any[][] = [],
174
200
  index = 0;
@@ -176,17 +202,17 @@ export default class Inibase {
176
202
  if (!RETURN[index]) RETURN[index] = [];
177
203
  RETURN[index].push(
178
204
  field.id
179
- ? Utils.decodeID(field.id as string, this.databasePath)
205
+ ? UtilsServer.decodeID(field.id as string, this.salt)
180
206
  : null
181
207
  );
182
208
  RETURN[index].push(field.key ?? null);
183
209
  RETURN[index].push(field.required ?? null);
184
210
  RETURN[index].push(field.type ?? null);
185
211
  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
212
+ (field as any).children
213
+ ? Utils.isArrayOfObjects((field as any).children)
214
+ ? encodeSchema((field as any).children as Schema) ?? null
215
+ : (field as any).children
190
216
  : null
191
217
  );
192
218
  index++;
@@ -203,44 +229,34 @@ export default class Inibase {
203
229
  oldIndex++;
204
230
  field = {
205
231
  ...field,
206
- id: Utils.encodeID(oldIndex, this.databasePath),
232
+ id: UtilsServer.encodeID(oldIndex, this.salt),
207
233
  };
208
234
  } else
209
- oldIndex = Utils.decodeID(field.id as string, this.databasePath);
235
+ oldIndex = UtilsServer.decodeID(field.id as string, this.salt);
210
236
  field.children = addIdToSchema(field.children as Schema, oldIndex);
211
237
  oldIndex += field.children.length;
212
238
  } else if (field.id)
213
- oldIndex = Utils.decodeID(field.id as string, this.databasePath);
239
+ oldIndex = UtilsServer.decodeID(field.id as string, this.salt);
214
240
  else {
215
241
  oldIndex++;
216
242
  field = {
217
243
  ...field,
218
- id: Utils.encodeID(oldIndex, this.databasePath),
244
+ id: UtilsServer.encodeID(oldIndex, this.salt),
219
245
  };
220
246
  }
221
247
  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
- };
248
+ });
236
249
 
237
250
  // 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)) {
251
+ schema = schema.filter(
252
+ (field) => !["id", "created_at", "updated_at"].includes(field.key)
253
+ );
254
+ schema = addIdToSchema(schema, this.findLastIdNumber(schema));
255
+ const TablePath = join(this.folder, this.database, tableName),
256
+ TableSchemaPath = join(TablePath, "schema");
257
+ if (!(await File.isExists(TablePath)))
258
+ await mkdir(TablePath, { recursive: true });
259
+ if (await File.isExists(TableSchemaPath)) {
244
260
  // update columns files names based on field id
245
261
  const schemaToIdsPath = (schema: any, prefix = "") => {
246
262
  let RETURN: any = {};
@@ -256,35 +272,35 @@ export default class Inibase {
256
272
  )
257
273
  );
258
274
  } else if (Utils.isValidID(field.id))
259
- RETURN[Utils.decodeID(field.id, this.databasePath)] =
275
+ RETURN[UtilsServer.decodeID(field.id, this.salt)] =
260
276
  File.encodeFileName((prefix ?? "") + field.key, "inib");
261
277
 
262
278
  return RETURN;
263
279
  },
264
280
  replaceOldPathes = Utils.findChangedProperties(
265
- schemaToIdsPath(this.getTableSchema(tableName)),
281
+ schemaToIdsPath(await this.getTableSchema(tableName)),
266
282
  schemaToIdsPath(schema)
267
283
  );
268
284
  if (replaceOldPathes)
269
285
  for (const [oldPath, newPath] of Object.entries(replaceOldPathes))
270
- if (existsSync(join(TablePath, oldPath)))
271
- renameSync(join(TablePath, oldPath), join(TablePath, newPath));
286
+ if (await File.isExists(join(TablePath, oldPath)))
287
+ await rename(join(TablePath, oldPath), join(TablePath, newPath));
272
288
  }
273
289
 
274
- writeFileSync(
275
- join(TablePath, "schema.inib"),
290
+ await writeFile(
291
+ join(TablePath, "schema"),
276
292
  JSON.stringify(encodeSchema(schema))
277
293
  );
278
294
  }
279
295
 
280
- public getTableSchema(tableName: string): Schema | undefined {
296
+ public async getTableSchema(tableName: string): Promise<Schema | undefined> {
281
297
  const decodeSchema = (encodedSchema: any) => {
282
298
  return encodedSchema.map((field: any) =>
283
299
  Array.isArray(field[0])
284
300
  ? decodeSchema(field)
285
301
  : Object.fromEntries(
286
302
  Object.entries({
287
- id: Utils.encodeID(field[0], this.databasePath),
303
+ id: UtilsServer.encodeID(field[0], this.salt),
288
304
  key: field[1],
289
305
  required: field[2],
290
306
  type: field[3],
@@ -297,10 +313,12 @@ export default class Inibase {
297
313
  )
298
314
  );
299
315
  },
300
- TableSchemaPath = join(this.databasePath, tableName, "schema.inib");
301
- if (!existsSync(TableSchemaPath)) return undefined;
316
+ TableSchemaPath = join(this.folder, this.database, tableName, "schema");
317
+ if (!(await File.isExists(TableSchemaPath))) return undefined;
302
318
  if (!this.cache.has(TableSchemaPath)) {
303
- const TableSchemaPathContent = readFileSync(TableSchemaPath);
319
+ const TableSchemaPathContent = await readFile(TableSchemaPath, {
320
+ encoding: "utf8",
321
+ });
304
322
  this.cache.set(
305
323
  TableSchemaPath,
306
324
  TableSchemaPathContent
@@ -308,14 +326,28 @@ export default class Inibase {
308
326
  : ""
309
327
  );
310
328
  }
329
+ const schema = this.cache.get(TableSchemaPath) as unknown as Schema,
330
+ lastIdNumber = this.findLastIdNumber(schema);
311
331
  return [
312
332
  {
313
- id: Utils.encodeID(0, this.databasePath),
333
+ id: UtilsServer.encodeID(0, this.salt),
314
334
  key: "id",
315
- type: "number",
335
+ type: "id",
316
336
  required: true,
317
337
  },
318
- ...(this.cache.get(TableSchemaPath) as unknown as Schema),
338
+ ...schema,
339
+ {
340
+ id: UtilsServer.encodeID(lastIdNumber + 1, this.salt),
341
+ key: "created_at",
342
+ type: "date",
343
+ required: true,
344
+ },
345
+ {
346
+ id: UtilsServer.encodeID(lastIdNumber + 2, this.salt),
347
+ key: "updated_at",
348
+ type: "date",
349
+ required: false,
350
+ },
319
351
  ];
320
352
  }
321
353
 
@@ -360,71 +392,6 @@ export default class Inibase {
360
392
  schema: Schema,
361
393
  skipRequiredField: boolean = false
362
394
  ): 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
395
  if (Utils.isArrayOfObjects(data))
429
396
  for (const single_data of data as Data[])
430
397
  this.validateData(single_data, schema, skipRequiredField);
@@ -437,7 +404,8 @@ export default class Inibase {
437
404
  )
438
405
  throw this.throwError("FIELD_REQUIRED", field.key);
439
406
  if (
440
- !validateFieldType(
407
+ data.hasOwnProperty(field.key) &&
408
+ !Utils.validateFieldType(
441
409
  data[field.key],
442
410
  field.type,
443
411
  (field as any)?.children &&
@@ -472,7 +440,6 @@ export default class Inibase {
472
440
  ): Data | Data[] | number | string => {
473
441
  if (Array.isArray(field.type))
474
442
  field.type = Utils.detectFieldType(value, field.type);
475
-
476
443
  switch (field.type) {
477
444
  case "array":
478
445
  if (typeof field.children === "string") {
@@ -489,16 +456,16 @@ export default class Inibase {
489
456
  value.map((item: any) =>
490
457
  Utils.isNumber(item.id)
491
458
  ? Number(item.id)
492
- : Utils.decodeID(item.id, this.databasePath)
459
+ : UtilsServer.decodeID(item.id, this.salt)
493
460
  );
494
461
  } else if (Utils.isValidID(value) || Utils.isNumber(value))
495
462
  return value.map((item: number | string) =>
496
463
  Utils.isNumber(item)
497
464
  ? Number(item as string)
498
- : Utils.decodeID(item as string, this.databasePath)
465
+ : UtilsServer.decodeID(item as string, this.salt)
499
466
  );
500
467
  } else if (Utils.isValidID(value))
501
- return [Utils.decodeID(value, this.databasePath)];
468
+ return [UtilsServer.decodeID(value, this.salt)];
502
469
  else if (Utils.isNumber(value)) return [Number(value)];
503
470
  } else if (data.hasOwnProperty(field.key)) return value;
504
471
  } else if (Utils.isArrayOfObjects(field.children))
@@ -526,21 +493,28 @@ export default class Inibase {
526
493
  )
527
494
  return Utils.isNumber(value.id)
528
495
  ? Number(value.id)
529
- : Utils.decodeID(value.id, this.databasePath);
496
+ : UtilsServer.decodeID(value.id, this.salt);
530
497
  } else if (Utils.isValidID(value) || Utils.isNumber(value))
531
498
  return Utils.isNumber(value)
532
499
  ? Number(value)
533
- : Utils.decodeID(value, this.databasePath);
500
+ : UtilsServer.decodeID(value, this.salt);
534
501
  break;
535
502
  case "password":
536
- return value.length === 161 ? value : Utils.hashPassword(value);
503
+ return value.length === 161 ? value : UtilsServer.hashPassword(value);
537
504
  case "number":
538
505
  return Utils.isNumber(value) ? Number(value) : null;
506
+ case "id":
507
+ return Utils.isNumber(value)
508
+ ? value
509
+ : UtilsServer.decodeID(value, this.salt);
539
510
  default:
540
511
  return value;
541
512
  }
542
513
  return null;
543
514
  };
515
+
516
+ this.validateData(data, schema, formatOnlyAvailiableKeys);
517
+
544
518
  if (Utils.isArrayOfObjects(data))
545
519
  return data.map((single_data: Data) =>
546
520
  this.formatData(single_data, schema, formatOnlyAvailiableKeys)
@@ -549,7 +523,7 @@ export default class Inibase {
549
523
  let RETURN: Data = {};
550
524
  for (const field of schema) {
551
525
  if (!data.hasOwnProperty(field.key)) {
552
- if (formatOnlyAvailiableKeys) continue;
526
+ if (formatOnlyAvailiableKeys || !field.required) continue;
553
527
  RETURN[field.key] = this.getDefaultValue(field);
554
528
  continue;
555
529
  }
@@ -676,16 +650,15 @@ export default class Inibase {
676
650
  if (!options.columns) options.columns = [];
677
651
  else if (!Array.isArray(options.columns))
678
652
  options.columns = [options.columns];
679
- else if (
680
- options.columns.length &&
681
- !(options.columns as string[]).includes("id")
682
- )
653
+ if (options.columns.length && !(options.columns as string[]).includes("id"))
683
654
  options.columns.push("id");
684
655
  if (!options.page) options.page = 1;
685
656
  if (!options.per_page) options.per_page = 15;
686
657
  let RETURN!: Data | Data[] | null;
687
- let schema = this.getTableSchema(tableName);
658
+ let schema = await this.getTableSchema(tableName);
688
659
  if (!schema) throw this.throwError("NO_SCHEMA", tableName);
660
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib");
661
+ if (!(await File.isExists(idFilePath))) return null;
689
662
  const filterSchemaByColumns = (schema: Schema, columns: string[]): Schema =>
690
663
  schema
691
664
  .map((field) => {
@@ -699,12 +672,8 @@ export default class Inibase {
699
672
  Utils.isArrayOfObjects(field.children) &&
700
673
  columns.filter(
701
674
  (column) =>
702
- column.startsWith(
703
- field.key + (field.type === "array" ? ".*." : ".")
704
- ) ||
705
- column.startsWith(
706
- "!" + field.key + (field.type === "array" ? ".*." : ".")
707
- )
675
+ column.startsWith(field.key + ".") ||
676
+ column.startsWith("!" + field.key + ".")
708
677
  ).length
709
678
  ) {
710
679
  field.children = filterSchemaByColumns(
@@ -712,19 +681,10 @@ export default class Inibase {
712
681
  columns
713
682
  .filter(
714
683
  (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
- )
684
+ column.startsWith(field.key + ".") ||
685
+ column.startsWith("!" + field.key + ".")
727
686
  )
687
+ .map((column) => column.replace(field.key + ".", ""))
728
688
  );
729
689
  return field;
730
690
  }
@@ -851,7 +811,7 @@ export default class Inibase {
851
811
  if (RETURN[index][field.key])
852
812
  Object.entries(item).forEach(([key, value], _index) => {
853
813
  RETURN[index][field.key] = RETURN[index][field.key].map(
854
- (_obj, _i) => ({ ..._obj, [key]: value[_index] })
814
+ (_obj, _i) => ({ ..._obj, [key]: value[_i] })
855
815
  );
856
816
  });
857
817
  else if (Object.values(item).every(Utils.isArrayOfArrays))
@@ -885,70 +845,77 @@ export default class Inibase {
885
845
  options.columns = (options.columns as string[])
886
846
  .filter((column) => column.includes(`${field.key}.*.`))
887
847
  .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
- )) {
848
+ const [items, total_lines] = await File.get(
849
+ join(
850
+ path,
851
+ File.encodeFileName((prefix ?? "") + field.key, "inib")
852
+ ),
853
+ linesNumber,
854
+ field.type,
855
+ (field as FieldDefault & (FieldArrayType | FieldArrayArrayType))
856
+ .children as FieldType | FieldType[],
857
+ this.salt
858
+ );
859
+
860
+ this.totalItems[tableName + "-" + field.key] = total_lines;
861
+ for (const [index, item] of Object.entries(items)) {
900
862
  if (!RETURN[index]) RETURN[index] = {};
901
- RETURN[index][field.key] = value
902
- ? await this.get(field.key, value as number, options)
863
+ RETURN[index][field.key] = item
864
+ ? await this.get(field.key, item as number, options)
903
865
  : this.getDefaultValue(field);
904
866
  }
905
867
  } else if (
906
- existsSync(
868
+ await File.isExists(
907
869
  join(
908
870
  path,
909
871
  File.encodeFileName((prefix ?? "") + field.key, "inib")
910
872
  )
911
873
  )
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]) => {
874
+ ) {
875
+ const [items, total_lines] = await File.get(
876
+ join(
877
+ path,
878
+ File.encodeFileName((prefix ?? "") + field.key, "inib")
879
+ ),
880
+ linesNumber,
881
+ field.type,
882
+ (field as any)?.children,
883
+ this.salt
884
+ );
885
+
886
+ this.totalItems[tableName + "-" + field.key] = total_lines;
887
+ for (const [index, item] of Object.entries(items)) {
924
888
  if (!RETURN[index]) RETURN[index] = {};
925
889
  RETURN[index][field.key] = item ?? this.getDefaultValue(field);
926
- });
890
+ }
891
+ }
927
892
  } else if (field.type === "object") {
928
- Object.entries(
893
+ for (const [index, item] of Object.entries(
929
894
  (await getItemsFromSchema(
930
895
  path,
931
896
  field.children as Schema,
932
897
  linesNumber,
933
898
  (prefix ?? "") + field.key + "."
934
899
  )) ?? {}
935
- ).forEach(([index, item]) => {
900
+ )) {
936
901
  if (!RETURN[index]) RETURN[index] = {};
937
902
  if (Utils.isObject(item)) {
938
903
  if (!Object.values(item).every((i) => i === null))
939
904
  RETURN[index][field.key] = item;
940
905
  else RETURN[index][field.key] = null;
941
906
  } else RETURN[index][field.key] = null;
942
- });
907
+ }
943
908
  } else if (field.type === "table") {
944
909
  if (
945
- existsSync(join(this.databasePath, field.key)) &&
946
- existsSync(
910
+ (await File.isExists(
911
+ join(this.folder, this.database, field.key)
912
+ )) &&
913
+ (await File.isExists(
947
914
  join(
948
915
  path,
949
916
  File.encodeFileName((prefix ?? "") + field.key, "inib")
950
917
  )
951
- )
918
+ ))
952
919
  ) {
953
920
  if (options.columns)
954
921
  options.columns = (options.columns as string[])
@@ -958,41 +925,43 @@ export default class Inibase {
958
925
  !column.includes(`${field.key}.*.`)
959
926
  )
960
927
  .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
- )) {
928
+ const [items, total_lines] = await File.get(
929
+ join(
930
+ path,
931
+ File.encodeFileName((prefix ?? "") + field.key, "inib")
932
+ ),
933
+ linesNumber,
934
+ "number",
935
+ undefined,
936
+ this.salt
937
+ );
938
+ this.totalItems[tableName + "-" + field.key] = total_lines;
939
+ for (const [index, item] of Object.entries(items)) {
971
940
  if (!RETURN[index]) RETURN[index] = {};
972
- RETURN[index][field.key] = value
973
- ? await this.get(field.key, value as number, options)
941
+ RETURN[index][field.key] = item
942
+ ? await this.get(field.key, item as number, options)
974
943
  : this.getDefaultValue(field);
975
944
  }
976
945
  }
977
946
  } else if (
978
- existsSync(
947
+ await File.isExists(
979
948
  join(path, File.encodeFileName((prefix ?? "") + field.key, "inib"))
980
949
  )
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]) => {
950
+ ) {
951
+ const [items, total_lines] = await File.get(
952
+ join(path, File.encodeFileName((prefix ?? "") + field.key, "inib")),
953
+ linesNumber,
954
+ field.type,
955
+ (field as any)?.children,
956
+ this.salt
957
+ );
958
+
959
+ this.totalItems[tableName + "-" + field.key] = total_lines;
960
+ for (const [index, item] of Object.entries(items)) {
993
961
  if (!RETURN[index]) RETURN[index] = {};
994
962
  RETURN[index][field.key] = item ?? this.getDefaultValue(field);
995
- });
963
+ }
964
+ }
996
965
  }
997
966
  return RETURN;
998
967
  };
@@ -1000,7 +969,7 @@ export default class Inibase {
1000
969
  // Display all data
1001
970
  RETURN = Object.values(
1002
971
  await getItemsFromSchema(
1003
- join(this.databasePath, tableName),
972
+ join(this.folder, this.database, tableName),
1004
973
  schema,
1005
974
  Array.from(
1006
975
  { length: options.per_page },
@@ -1014,18 +983,19 @@ export default class Inibase {
1014
983
  } else if (Utils.isValidID(where) || Utils.isNumber(where)) {
1015
984
  let Ids = where as string | number | (string | number)[];
1016
985
  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
986
  const [lineNumbers, countItems] = await File.search(
1020
987
  idFilePath,
1021
988
  "[]",
1022
989
  Utils.isNumber(Ids)
1023
990
  ? Ids.map((id) => Number(id as string))
1024
- : Ids.map((id) => Utils.decodeID(id as string, this.databasePath)),
991
+ : Ids.map((id) => UtilsServer.decodeID(id as string, this.salt)),
1025
992
  undefined,
1026
993
  "number",
1027
994
  undefined,
1028
- Ids.length
995
+ Ids.length,
996
+ 0,
997
+ false,
998
+ this.salt
1029
999
  );
1030
1000
  if (!lineNumbers || !Object.keys(lineNumbers).length)
1031
1001
  throw this.throwError(
@@ -1034,7 +1004,7 @@ export default class Inibase {
1034
1004
  );
1035
1005
  RETURN = Object.values(
1036
1006
  (await getItemsFromSchema(
1037
- join(this.databasePath, tableName),
1007
+ join(this.folder, this.database, tableName),
1038
1008
  schema,
1039
1009
  Object.keys(lineNumbers).map(Number)
1040
1010
  )) ?? {}
@@ -1227,9 +1197,10 @@ export default class Inibase {
1227
1197
  searchOperator = "=";
1228
1198
  searchComparedAtValue = value as number | boolean;
1229
1199
  }
1230
- const [searchResult, totlaItems] = await File.search(
1200
+ const [searchResult, total_lines] = await File.search(
1231
1201
  join(
1232
- this.databasePath,
1202
+ this.folder,
1203
+ this.database,
1233
1204
  tableName,
1234
1205
  File.encodeFileName(key, "inib")
1235
1206
  ),
@@ -1240,12 +1211,12 @@ export default class Inibase {
1240
1211
  (field as any)?.children,
1241
1212
  options.per_page,
1242
1213
  (options.page as number) - 1 * (options.per_page as number) + 1,
1243
- true
1214
+ true,
1215
+ this.salt
1244
1216
  );
1245
1217
  if (searchResult) {
1246
1218
  RETURN = Utils.deepMerge(RETURN, searchResult);
1247
- if (!this.pageInfoArray[key]) this.pageInfoArray[key] = {};
1248
- this.pageInfoArray[key].total_items = totlaItems;
1219
+ this.totalItems[tableName + "-" + key] = total_lines;
1249
1220
  }
1250
1221
  if (allTrue && index > 0) {
1251
1222
  if (!Object.keys(RETURN).length) RETURN = {};
@@ -1264,30 +1235,12 @@ export default class Inibase {
1264
1235
  if (RETURN) {
1265
1236
  if (onlyLinesNumbers) return Object.keys(RETURN).map(Number);
1266
1237
  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
- };
1238
+ (key) => File.decodeFileName(parse(key).name)
1239
+ );
1287
1240
  RETURN = Object.values(
1288
1241
  Utils.deepMerge(
1289
1242
  await getItemsFromSchema(
1290
- join(this.databasePath, tableName),
1243
+ join(this.folder, this.database, tableName),
1291
1244
  schema.filter(
1292
1245
  (field) => !alreadyExistsColumns.includes(field.key)
1293
1246
  ),
@@ -1304,15 +1257,18 @@ export default class Inibase {
1304
1257
  (Array.isArray(RETURN) && !RETURN.length)
1305
1258
  )
1306
1259
  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
- };
1260
+
1261
+ const greatestTotalItems = Math.max(
1262
+ ...Object.entries(this.totalItems)
1263
+ .filter(([k]) => k.startsWith(tableName + "-"))
1264
+ .map(([, v]) => v)
1265
+ );
1266
+ this.pageInfo = {
1267
+ ...(({ columns, ...restOfOptions }) => restOfOptions)(options),
1268
+ total_pages: Math.ceil(greatestTotalItems / options.per_page),
1269
+ total: greatestTotalItems,
1270
+ };
1271
+ return RETURN;
1316
1272
  }
1317
1273
 
1318
1274
  public async post(
@@ -1321,52 +1277,55 @@ export default class Inibase {
1321
1277
  options: Options = {
1322
1278
  page: 1,
1323
1279
  per_page: 15,
1324
- }
1325
- ): Promise<Data | Data[] | null> {
1326
- const schema = this.getTableSchema(tableName);
1280
+ },
1281
+ returnPostedData: boolean = true
1282
+ ): Promise<Data | Data[] | null | void> {
1283
+ const schema = await this.getTableSchema(tableName);
1327
1284
  let RETURN: Data | Data[] | null | undefined;
1328
1285
  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])
1286
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib");
1287
+ let last_id = (await File.isExists(idFilePath))
1288
+ ? Number(
1289
+ Object.values(
1290
+ await File.get(idFilePath, -1, "number", undefined, this.salt)
1291
+ )[0]
1292
+ )
1332
1293
  : 0;
1333
1294
  if (Utils.isArrayOfObjects(data))
1334
1295
  (data as Data[]).forEach((single_data, index) => {
1335
1296
  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();
1297
+ RETURN[index] = (({ id, updated_at, created_at, ...rest }) => ({
1298
+ id: ++last_id,
1299
+ ...rest,
1300
+ created_at: new Date(),
1301
+ }))(single_data);
1341
1302
  });
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
- }
1303
+ else
1304
+ RETURN = (({ id, updated_at, created_at, ...rest }) => ({
1305
+ id: ++last_id,
1306
+ ...rest,
1307
+ created_at: new Date(),
1308
+ }))(data as Data);
1349
1309
  if (!RETURN) throw this.throwError("NO_DATA");
1350
- this.validateData(RETURN, schema);
1351
1310
  RETURN = this.formatData(RETURN, schema);
1352
1311
  const pathesContents = this.joinPathesContents(
1353
- join(this.databasePath, tableName),
1312
+ join(this.folder, this.database, tableName),
1354
1313
  RETURN
1355
1314
  );
1356
- for (const [path, content] of Object.entries(pathesContents))
1357
- appendFileSync(
1315
+ for await (const [path, content] of Object.entries(pathesContents))
1316
+ await appendFile(
1358
1317
  path,
1359
- (Array.isArray(content) ? content.join("\n") : content ?? "") + "\n",
1360
- "utf8"
1318
+ (Array.isArray(content) ? content.join("\n") : content ?? "") + "\n"
1361
1319
  );
1362
1320
 
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
- );
1321
+ if (returnPostedData)
1322
+ return this.get(
1323
+ tableName,
1324
+ Utils.isArrayOfObjects(RETURN)
1325
+ ? RETURN.map((data: Data) => data.id)
1326
+ : ((RETURN as Data).id as number),
1327
+ options
1328
+ );
1370
1329
  }
1371
1330
 
1372
1331
  public async put(
@@ -1376,11 +1335,14 @@ export default class Inibase {
1376
1335
  options: Options = {
1377
1336
  page: 1,
1378
1337
  per_page: 15,
1379
- }
1380
- ): Promise<Data | Data[] | null> {
1381
- const schema = this.getTableSchema(tableName);
1338
+ },
1339
+ returnPostedData: boolean = true
1340
+ ): Promise<Data | Data[] | null | void> {
1341
+ const schema = await this.getTableSchema(tableName);
1382
1342
  if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1383
- this.validateData(data, schema, true);
1343
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib");
1344
+ if (!(await File.isExists(idFilePath)))
1345
+ throw this.throwError("NO_ITEMS", tableName);
1384
1346
  data = this.formatData(data, schema, true);
1385
1347
  if (!where) {
1386
1348
  if (Utils.isArrayOfObjects(data)) {
@@ -1401,11 +1363,11 @@ export default class Inibase {
1401
1363
  return this.put(
1402
1364
  tableName,
1403
1365
  data,
1404
- Utils.decodeID((data as Data).id as string, this.databasePath)
1366
+ UtilsServer.decodeID((data as Data).id as string, this.salt)
1405
1367
  );
1406
1368
  } else {
1407
1369
  const pathesContents = this.joinPathesContents(
1408
- join(this.databasePath, tableName),
1370
+ join(this.folder, this.database, tableName),
1409
1371
  Utils.isArrayOfObjects(data)
1410
1372
  ? (data as Data[]).map((item) => ({
1411
1373
  ...(({ id, ...restOfData }) => restOfData)(item),
@@ -1418,21 +1380,22 @@ export default class Inibase {
1418
1380
  );
1419
1381
  for (const [path, content] of Object.entries(pathesContents))
1420
1382
  await File.replace(path, content);
1421
- return this.get(tableName, where, options);
1383
+ if (returnPostedData) return this.get(tableName, where, options);
1422
1384
  }
1423
1385
  } else if (Utils.isValidID(where)) {
1424
1386
  let Ids = where as string | string[];
1425
1387
  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
1388
  const [lineNumbers, countItems] = await File.search(
1429
1389
  idFilePath,
1430
1390
  "[]",
1431
- Ids.map((id) => Utils.decodeID(id, this.databasePath)),
1391
+ Ids.map((id) => UtilsServer.decodeID(id, this.salt)),
1432
1392
  undefined,
1433
1393
  "number",
1434
1394
  undefined,
1435
- Ids.length
1395
+ Ids.length,
1396
+ 0,
1397
+ false,
1398
+ this.salt
1436
1399
  );
1437
1400
  if (!lineNumbers || !Object.keys(lineNumbers).length)
1438
1401
  throw this.throwError("INVALID_ID");
@@ -1442,7 +1405,7 @@ export default class Inibase {
1442
1405
  const pathesContents = Object.fromEntries(
1443
1406
  Object.entries(
1444
1407
  this.joinPathesContents(
1445
- join(this.databasePath, tableName),
1408
+ join(this.folder, this.database, tableName),
1446
1409
  Utils.isArrayOfObjects(data)
1447
1410
  ? (data as Data[]).map((item) => ({
1448
1411
  ...item,
@@ -1463,7 +1426,7 @@ export default class Inibase {
1463
1426
  );
1464
1427
  for (const [path, content] of Object.entries(pathesContents))
1465
1428
  await File.replace(path, content);
1466
- return this.get(tableName, where, options);
1429
+ if (returnPostedData) return this.get(tableName, where, options);
1467
1430
  } else if (typeof where === "object" && !Array.isArray(where)) {
1468
1431
  const lineNumbers = this.get(tableName, where, undefined, true);
1469
1432
  if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
@@ -1476,31 +1439,35 @@ export default class Inibase {
1476
1439
  tableName: string,
1477
1440
  where?: number | string | (number | string)[] | Criteria,
1478
1441
  _id?: string | string[]
1479
- ): Promise<string | string[]> {
1480
- const schema = this.getTableSchema(tableName);
1442
+ ): Promise<string | string[] | null> {
1443
+ const schema = await this.getTableSchema(tableName);
1481
1444
  if (!schema) throw this.throwError("NO_SCHEMA", tableName);
1445
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib");
1446
+ if (!(await File.isExists(idFilePath)))
1447
+ throw this.throwError("NO_ITEMS", tableName);
1482
1448
  if (!where) {
1483
- const files = readdirSync(join(this.databasePath, tableName));
1449
+ const files = await readdir(join(this.folder, this.database, tableName));
1484
1450
  if (files.length) {
1485
1451
  for (const file in files.filter(
1486
- (fileName: string) => fileName !== "schema.inib"
1452
+ (fileName: string) => fileName !== "schema"
1487
1453
  ))
1488
- unlinkSync(join(this.databasePath, tableName, file));
1454
+ await unlink(join(this.folder, this.database, tableName, file));
1489
1455
  }
1490
1456
  return "*";
1491
1457
  } else if (Utils.isValidID(where)) {
1492
1458
  let Ids = where as string | string[];
1493
1459
  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
1460
  const [lineNumbers, countItems] = await File.search(
1497
1461
  idFilePath,
1498
1462
  "[]",
1499
- Ids.map((id) => Utils.decodeID(id, this.databasePath)),
1463
+ Ids.map((id) => UtilsServer.decodeID(id, this.salt)),
1500
1464
  undefined,
1501
1465
  "number",
1502
1466
  undefined,
1503
- Ids.length
1467
+ Ids.length,
1468
+ 0,
1469
+ false,
1470
+ this.salt
1504
1471
  );
1505
1472
  if (!lineNumbers || !Object.keys(lineNumbers).length)
1506
1473
  throw this.throwError("INVALID_ID");
@@ -1510,23 +1477,26 @@ export default class Inibase {
1510
1477
  where as string | string[]
1511
1478
  );
1512
1479
  } else if (Utils.isNumber(where)) {
1513
- const files = readdirSync(join(this.databasePath, tableName));
1480
+ const files = await readdir(join(this.folder, this.database, tableName));
1514
1481
  if (files.length) {
1515
1482
  if (!_id)
1516
1483
  _id = Object.values(
1517
1484
  await File.get(
1518
- join(this.databasePath, tableName, "id.inib"),
1485
+ join(this.folder, this.database, tableName, "id.inib"),
1519
1486
  where as number | number[],
1520
- "number"
1487
+ "number",
1488
+ undefined,
1489
+ this.salt
1521
1490
  )
1522
1491
  )
1523
1492
  .map(Number)
1524
- .map((id) => Utils.encodeID(id, this.databasePath));
1525
- for (const file in files.filter(
1526
- (fileName: string) => fileName !== "schema.inib"
1493
+ .map((id) => UtilsServer.encodeID(id, this.salt));
1494
+ for (const file of files.filter(
1495
+ (fileName: string) =>
1496
+ fileName.endsWith(".inib") && fileName !== "schema"
1527
1497
  ))
1528
1498
  await File.remove(
1529
- join(this.databasePath, tableName, file),
1499
+ join(this.folder, this.database, tableName, file),
1530
1500
  where as number | number[]
1531
1501
  );
1532
1502
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
@@ -1537,5 +1507,6 @@ export default class Inibase {
1537
1507
  throw this.throwError("NO_ITEMS", tableName);
1538
1508
  return this.delete(tableName, lineNumbers);
1539
1509
  } else throw this.throwError("INVALID_PARAMETERS", tableName);
1510
+ return null;
1540
1511
  }
1541
1512
  }