inibase 1.0.0-rc.26 → 1.0.0-rc.28

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/dist/index.js CHANGED
@@ -1,33 +1,32 @@
1
- import { unlink, rename, readFile, writeFile, mkdir, readdir, } from "node:fs/promises";
1
+ import { unlink, rename, mkdir, readdir } from "node:fs/promises";
2
2
  import { existsSync, appendFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- // import { cpus } from "node:os";
4
+ import { cpus } from "node:os";
5
5
  import { scryptSync, randomBytes } from "node:crypto";
6
6
  import File from "./file.js";
7
7
  import Utils from "./utils.js";
8
8
  import UtilsServer from "./utils.server.js";
9
+ import Config from "./config.js";
10
+ process.env.UV_THREADPOOL_SIZE = cpus().length.toString();
9
11
  export default class Inibase {
10
12
  folder;
11
13
  database;
12
14
  table;
13
15
  pageInfo;
14
- cache;
15
16
  totalItems;
16
17
  salt;
17
18
  constructor(database, mainFolder = ".") {
18
19
  this.database = database;
19
20
  this.folder = mainFolder;
20
21
  this.table = null;
21
- this.cache = new Map();
22
22
  this.totalItems = {};
23
- this.pageInfo = { page: 1, per_page: 15 };
23
+ this.pageInfo = {};
24
24
  if (!existsSync(".env") || !process.env.INIBASE_SECRET) {
25
25
  this.salt = scryptSync(randomBytes(16), randomBytes(16), 32);
26
26
  appendFileSync(".env", `INIBASE_SECRET=${this.salt.toString("hex")}\n`);
27
27
  }
28
- else {
28
+ else
29
29
  this.salt = Buffer.from(process.env.INIBASE_SECRET, "hex");
30
- }
31
30
  }
32
31
  throwError(code, variable, language = "en") {
33
32
  const errorMessages = {
@@ -56,7 +55,9 @@ export default class Inibase {
56
55
  }
57
56
  async setTableSchema(tableName, schema) {
58
57
  const decodeIdFromSchema = (schema) => schema.map((field) => {
59
- if (field.children && Utils.isArrayOfObjects(field.children))
58
+ if ((field.type === "array" || field.type === "object") &&
59
+ field.children &&
60
+ Utils.isArrayOfObjects(field.children))
60
61
  field.children = decodeIdFromSchema(field.children);
61
62
  if (field.id && !Utils.isNumber(field.id))
62
63
  field.id = UtilsServer.decodeID(field.id, this.salt);
@@ -75,7 +76,9 @@ export default class Inibase {
75
76
  const schemaToIdsPath = (schema, prefix = "") => {
76
77
  let RETURN = {};
77
78
  for (const field of schema)
78
- if (field.children && Utils.isArrayOfObjects(field.children)) {
79
+ if ((field.type === "array" || field.type === "object") &&
80
+ field.children &&
81
+ Utils.isArrayOfObjects(field.children)) {
79
82
  Utils.deepMerge(RETURN, schemaToIdsPath(field.children, (prefix ?? "") + field.key + "."));
80
83
  }
81
84
  else if (Utils.isValidID(field.id))
@@ -88,17 +91,16 @@ export default class Inibase {
88
91
  if (await File.isExists(join(TablePath, oldPath)))
89
92
  await rename(join(TablePath, oldPath), join(TablePath, newPath));
90
93
  }
91
- await writeFile(join(TablePath, "schema.json"), JSON.stringify(decodeIdFromSchema(schema)));
94
+ await File.write(join(TablePath, "schema.json"), JSON.stringify(decodeIdFromSchema(schema), null, 2), true);
92
95
  }
93
96
  async getTableSchema(tableName) {
94
97
  const TableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
95
98
  if (!(await File.isExists(TableSchemaPath)))
96
99
  return undefined;
97
- if (!this.cache.has(TableSchemaPath))
98
- this.cache.set(TableSchemaPath, await readFile(TableSchemaPath, "utf8"));
99
- if (!this.cache.get(TableSchemaPath))
100
+ const schemaFile = await File.read(TableSchemaPath, true);
101
+ if (!schemaFile)
100
102
  return undefined;
101
- const schema = JSON.parse(this.cache.get(TableSchemaPath) ?? ""), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
103
+ const schema = JSON.parse(schemaFile), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
102
104
  return [
103
105
  {
104
106
  id: UtilsServer.encodeID(0, this.salt),
@@ -121,49 +123,45 @@ export default class Inibase {
121
123
  },
122
124
  ];
123
125
  }
124
- getField(keyPath, schema, property) {
126
+ getField(keyPath, schema) {
127
+ let RETURN = null;
125
128
  const keyPathSplited = keyPath.split(".");
126
129
  for (const [index, key] of keyPathSplited.entries()) {
127
- if (key === "*")
128
- continue;
129
130
  const foundItem = schema.find((item) => item.key === key);
130
131
  if (!foundItem)
131
132
  return null;
132
133
  if (index === keyPathSplited.length - 1)
133
- schema = foundItem;
134
+ RETURN = foundItem;
134
135
  if ((foundItem.type === "array" || foundItem.type === "object") &&
135
136
  foundItem.children &&
136
137
  Utils.isArrayOfObjects(foundItem.children))
137
- schema = foundItem.children;
138
- }
139
- if (property) {
140
- switch (property) {
141
- case "type":
142
- return schema.type;
143
- case "children":
144
- return schema.children;
145
- default:
146
- return schema[property];
147
- }
138
+ RETURN = foundItem.children;
148
139
  }
149
- else
150
- return schema;
140
+ if (!RETURN)
141
+ return null;
142
+ return Utils.isArrayOfObjects(RETURN) ? RETURN[0] : RETURN;
151
143
  }
152
144
  validateData(data, schema, skipRequiredField = false) {
153
145
  if (Utils.isArrayOfObjects(data))
154
146
  for (const single_data of data)
155
147
  this.validateData(single_data, schema, skipRequiredField);
156
148
  else if (Utils.isObject(data)) {
157
- for (const { key, type, required, children } of schema) {
158
- if (!data.hasOwnProperty(key) && required && !skipRequiredField)
159
- throw this.throwError("FIELD_REQUIRED", key);
160
- if (data.hasOwnProperty(key) &&
161
- !Utils.validateFieldType(data[key], type, children && !Utils.isArrayOfObjects(children) ? children : undefined))
162
- throw this.throwError("INVALID_TYPE", key + " " + type + " " + data[key]);
163
- if ((type === "array" || type === "object") &&
164
- children &&
165
- Utils.isArrayOfObjects(children))
166
- this.validateData(data[key], children, skipRequiredField);
149
+ for (const field of schema) {
150
+ if (!data.hasOwnProperty(field.key) &&
151
+ field.required &&
152
+ !skipRequiredField)
153
+ throw this.throwError("FIELD_REQUIRED", field.key);
154
+ if (data.hasOwnProperty(field.key) &&
155
+ !Utils.validateFieldType(data[field.key], field.type, (field.type === "array" || field.type === "object") &&
156
+ field.children &&
157
+ !Utils.isArrayOfObjects(field.children)
158
+ ? field.children
159
+ : undefined))
160
+ throw this.throwError("INVALID_TYPE", field.key + " " + field.type + " " + data[field.key]);
161
+ if ((field.type === "array" || field.type === "object") &&
162
+ field.children &&
163
+ Utils.isArrayOfObjects(field.children))
164
+ this.validateData(data[field.key], field.children, skipRequiredField);
167
165
  }
168
166
  }
169
167
  }
@@ -172,10 +170,8 @@ export default class Inibase {
172
170
  field.type = Utils.detectFieldType(value, field.type) ?? field.type[0];
173
171
  switch (field.type) {
174
172
  case "array":
175
- if (!Array.isArray(value))
176
- value = [value];
177
173
  if (typeof field.children === "string") {
178
- if (field.type === "array" && field.children === "table") {
174
+ if (field.children === "table") {
179
175
  if (Array.isArray(value)) {
180
176
  if (Utils.isArrayOfObjects(value)) {
181
177
  if (value.every((item) => item.hasOwnProperty("id") &&
@@ -197,8 +193,8 @@ export default class Inibase {
197
193
  else if (Utils.isNumber(value))
198
194
  return [Number(value)];
199
195
  }
200
- else if (value.hasOwnProperty(field.key))
201
- return value;
196
+ else
197
+ return Array.isArray(value) ? value : [value];
202
198
  }
203
199
  else if (Utils.isArrayOfObjects(field.children))
204
200
  return this.formatData(value, field.children, formatOnlyAvailiableKeys);
@@ -308,8 +304,7 @@ export default class Inibase {
308
304
  let RETURN = {};
309
305
  for await (const field of schema) {
310
306
  if ((field.type === "array" ||
311
- (Array.isArray(field.type) &&
312
- field.type.includes("array"))) &&
307
+ (Array.isArray(field.type) && field.type.includes("array"))) &&
313
308
  field.children) {
314
309
  if (Utils.isArrayOfObjects(field.children)) {
315
310
  if (field.children.filter((children) => children.type === "array" &&
@@ -387,13 +382,13 @@ export default class Inibase {
387
382
  });
388
383
  }
389
384
  else if (field.children === "table" ||
385
+ (Array.isArray(field.type) && field.type.includes("table")) ||
390
386
  (Array.isArray(field.children) && field.children.includes("table"))) {
391
387
  if (options.columns)
392
388
  options.columns = options.columns
393
389
  .filter((column) => column.includes(`${field.key}.`))
394
390
  .map((column) => column.replace(`${field.key}.`, ""));
395
- const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
396
- this.totalItems[tableName + "-" + field.key] = total_lines;
391
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
397
392
  if (items)
398
393
  for await (const [index, item] of Object.entries(items)) {
399
394
  if (!RETURN[index])
@@ -404,8 +399,7 @@ export default class Inibase {
404
399
  }
405
400
  }
406
401
  else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
407
- const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
408
- this.totalItems[tableName + "-" + field.key] = total_lines;
402
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
409
403
  if (items)
410
404
  for (const [index, item] of Object.entries(items)) {
411
405
  if (!RETURN[index])
@@ -436,8 +430,7 @@ export default class Inibase {
436
430
  .filter((column) => column.includes(`${field.key}.`) &&
437
431
  !column.includes(`${field.key}.`))
438
432
  .map((column) => column.replace(`${field.key}.`, ""));
439
- const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
440
- this.totalItems[tableName + "-" + field.key] = total_lines;
433
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
441
434
  if (items)
442
435
  for await (const [index, item] of Object.entries(items)) {
443
436
  if (!RETURN[index])
@@ -449,8 +442,7 @@ export default class Inibase {
449
442
  }
450
443
  }
451
444
  else if (await File.isExists(join(path, (prefix ?? "") + field.key + ".inib"))) {
452
- const [items, total_lines] = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
453
- this.totalItems[tableName + "-" + field.key] = total_lines;
445
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
454
446
  if (items)
455
447
  for (const [index, item] of Object.entries(items)) {
456
448
  if (!RETURN[index])
@@ -521,23 +513,26 @@ export default class Inibase {
521
513
  }
522
514
  }
523
515
  async applyCriteria(tableName, schema, options, criteria, allTrue) {
524
- let RETURN = {};
516
+ let RETURN = {}, RETURN_LineNumbers = null;
525
517
  if (!criteria)
526
- return null;
518
+ return [null, null];
527
519
  if (criteria.and && Utils.isObject(criteria.and)) {
528
- const searchResult = await this.applyCriteria(tableName, schema, options, criteria.and, true);
520
+ const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.and, true);
529
521
  if (searchResult) {
530
522
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).length === Object.keys(criteria.and ?? {}).length)));
531
523
  delete criteria.and;
524
+ RETURN_LineNumbers = lineNumbers;
532
525
  }
533
526
  else
534
- return null;
527
+ return [null, null];
535
528
  }
536
529
  if (criteria.or && Utils.isObject(criteria.or)) {
537
- const searchResult = await this.applyCriteria(tableName, schema, options, criteria.or, false);
530
+ const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.or, false);
538
531
  delete criteria.or;
539
- if (searchResult)
532
+ if (searchResult) {
540
533
  RETURN = Utils.deepMerge(RETURN, searchResult);
534
+ RETURN_LineNumbers = lineNumbers;
535
+ }
541
536
  }
542
537
  if (Object.keys(criteria).length > 0) {
543
538
  if (allTrue === undefined)
@@ -600,7 +595,7 @@ export default class Inibase {
600
595
  searchOperator = "=";
601
596
  searchComparedAtValue = value;
602
597
  }
603
- const [searchResult, total_lines] = await File.search(join(this.folder, this.database, tableName, key + ".inib"), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.per_page, options.page - 1 * options.per_page + 1, true, this.salt);
598
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(this.folder, this.database, tableName, key + ".inib"), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
604
599
  if (searchResult) {
605
600
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
606
601
  id,
@@ -608,7 +603,8 @@ export default class Inibase {
608
603
  [key]: value,
609
604
  },
610
605
  ])));
611
- this.totalItems[tableName + "-" + key] = total_lines;
606
+ this.totalItems[tableName + "-" + key] = totalLines;
607
+ RETURN_LineNumbers = linesNumbers;
612
608
  }
613
609
  if (allTrue && index > 0) {
614
610
  if (!Object.keys(RETURN).length)
@@ -619,31 +615,10 @@ export default class Inibase {
619
615
  }
620
616
  }
621
617
  }
622
- return Object.keys(RETURN).length ? RETURN : null;
618
+ return [Object.keys(RETURN).length ? RETURN : null, RETURN_LineNumbers];
623
619
  }
624
- async get(tableName, where, options = {
625
- page: 1,
626
- per_page: 15,
627
- }, onlyOne, onlyLinesNumbers) {
628
- if (options.columns) {
629
- if (!Array.isArray(options.columns))
630
- options.columns = [options.columns];
631
- if (options.columns.length &&
632
- !options.columns.includes("id"))
633
- options.columns.push("id");
634
- }
635
- if (!options.page)
636
- options.page = 1;
637
- if (!options.per_page)
638
- options.per_page = 15;
639
- let RETURN;
640
- let schema = await this.getTableSchema(tableName);
641
- if (!schema)
642
- throw this.throwError("NO_SCHEMA", tableName);
643
- const idFilePath = join(this.folder, this.database, tableName, "id.inib");
644
- if (!(await File.isExists(idFilePath)))
645
- return null;
646
- const filterSchemaByColumns = (schema, columns) => schema
620
+ _filterSchemaByColumns(schema, columns) {
621
+ return schema
647
622
  .map((field) => {
648
623
  if (columns.some((column) => column.startsWith("!")))
649
624
  return columns.includes("!" + field.key) ? null : field;
@@ -653,7 +628,7 @@ export default class Inibase {
653
628
  Utils.isArrayOfObjects(field.children) &&
654
629
  columns.filter((column) => column.startsWith(field.key + ".") ||
655
630
  column.startsWith("!" + field.key + ".")).length) {
656
- field.children = filterSchemaByColumns(field.children, columns
631
+ field.children = this._filterSchemaByColumns(field.children, columns
657
632
  .filter((column) => column.startsWith(field.key + ".") ||
658
633
  column.startsWith("!" + field.key + "."))
659
634
  .map((column) => column.replace(field.key + ".", "")));
@@ -662,13 +637,55 @@ export default class Inibase {
662
637
  return null;
663
638
  })
664
639
  .filter((i) => i);
640
+ }
641
+ async get(tableName, where, options = {
642
+ page: 1,
643
+ perPage: 15,
644
+ }, onlyOne, onlyLinesNumbers, tableSchema) {
645
+ // Ensure options.columns is an array
646
+ if (options.columns) {
647
+ options.columns = Array.isArray(options.columns)
648
+ ? options.columns
649
+ : [options.columns];
650
+ if (options.columns.length && !options.columns.includes("id"))
651
+ options.columns.push("id");
652
+ }
653
+ // Default values for page and perPage
654
+ options.page = options.page || 1;
655
+ options.perPage = options.perPage || 15;
656
+ let RETURN;
657
+ let schema = tableSchema ?? (await this.getTableSchema(tableName));
658
+ if (!schema)
659
+ throw this.throwError("NO_SCHEMA", tableName);
660
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib");
661
+ if (!(await File.isExists(idFilePath)))
662
+ return null;
665
663
  if (options.columns && options.columns.length)
666
- schema = filterSchemaByColumns(schema, options.columns);
664
+ schema = this._filterSchemaByColumns(schema, options.columns);
667
665
  if (!where) {
668
666
  // Display all data
669
- RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.per_page }, (_, index) => (options.page - 1) * options.per_page +
667
+ RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
670
668
  index +
671
669
  1), options));
670
+ if (Config.isCacheEnabled &&
671
+ (await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
672
+ this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
673
+ else {
674
+ this.totalItems[tableName + "-*"] = await File.count(idFilePath);
675
+ if (Config.isCacheEnabled)
676
+ await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
677
+ }
678
+ }
679
+ else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
680
+ (Utils.isNumber(where) && !onlyLinesNumbers)) {
681
+ let lineNumbers = where;
682
+ if (!Array.isArray(lineNumbers))
683
+ lineNumbers = [lineNumbers];
684
+ RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, lineNumbers, options)) ?? {});
685
+ if (!this.totalItems[tableName + "-*"])
686
+ this.totalItems[tableName + "-*"] = lineNumbers.length;
687
+ if (RETURN && RETURN.length && !Array.isArray(where))
688
+ RETURN = RETURN[0];
672
689
  }
673
690
  else if ((Array.isArray(where) &&
674
691
  (where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
@@ -685,29 +702,56 @@ export default class Inibase {
685
702
  ? Object.keys(lineNumbers).map(Number)
686
703
  : null;
687
704
  RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
705
+ if (!this.totalItems[tableName + "-*"]) {
706
+ if (Config.isCacheEnabled &&
707
+ (await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
708
+ this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
709
+ else {
710
+ this.totalItems[tableName + "-*"] = await File.count(idFilePath);
711
+ if (Config.isCacheEnabled)
712
+ await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
713
+ }
714
+ }
688
715
  if (RETURN && RETURN.length && !Array.isArray(where))
689
716
  RETURN = RETURN[0];
690
717
  }
691
718
  else if (Utils.isObject(where)) {
719
+ let cachedFilePath = "";
692
720
  // Criteria
693
- RETURN = await this.applyCriteria(tableName, schema, options, where);
694
- if (RETURN) {
695
- if (onlyLinesNumbers)
696
- return Object.keys(RETURN).map(Number);
697
- const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
698
- RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter((field) => !alreadyExistsColumns.includes(field.key)), Object.keys(RETURN).map(Number), options)));
721
+ if (Config.isCacheEnabled)
722
+ cachedFilePath = join(this.folder, this.database, tableName, ".tmp", `${UtilsServer.hashObject(where)}.inib`);
723
+ if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
724
+ const cachedItems = (await File.read(cachedFilePath, true)).split(",");
725
+ this.totalItems[tableName + "-*"] = cachedItems.length;
726
+ return this.get(tableName, cachedItems
727
+ .slice((options.page - 1) * options.perPage, options.page * options.perPage)
728
+ .map(Number), options, undefined, undefined, schema);
729
+ }
730
+ else {
731
+ let linesNumbers;
732
+ [RETURN, linesNumbers] = await this.applyCriteria(tableName, schema, options, where);
733
+ if (RETURN && linesNumbers) {
734
+ if (onlyLinesNumbers)
735
+ return Object.keys(RETURN).map(Number);
736
+ const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
737
+ RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter(({ key }) => !alreadyExistsColumns.includes(key)), Object.keys(RETURN).map(Number), options)));
738
+ if (Config.isCacheEnabled)
739
+ await File.write(cachedFilePath, Array.from(linesNumbers).join(","), true);
740
+ }
699
741
  }
700
742
  }
701
743
  if (!RETURN ||
702
744
  (Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
703
745
  (Array.isArray(RETURN) && !RETURN.length))
704
746
  return null;
705
- const greatestTotalItems = Math.max(...Object.entries(this.totalItems)
706
- .filter(([k]) => k.startsWith(tableName + "-"))
707
- .map(([, v]) => v));
708
- this.pageInfo = {
747
+ const greatestTotalItems = this.totalItems[tableName + "-*"] ??
748
+ Math.max(...Object.entries(this.totalItems)
749
+ .filter(([k]) => k.startsWith(tableName + "-"))
750
+ .map(([, v]) => v));
751
+ this.pageInfo[tableName] = {
709
752
  ...(({ columns, ...restOfOptions }) => restOfOptions)(options),
710
- total_pages: Math.ceil(greatestTotalItems / options.per_page),
753
+ perPage: Array.isArray(RETURN) ? RETURN.length : 1,
754
+ totalPages: Math.ceil(greatestTotalItems / options.perPage),
711
755
  total: greatestTotalItems,
712
756
  };
713
757
  return onlyOne && Array.isArray(RETURN) ? RETURN[0] : RETURN;
@@ -716,7 +760,7 @@ export default class Inibase {
716
760
  if (!options)
717
761
  options = {
718
762
  page: 1,
719
- per_page: 15,
763
+ perPage: 15,
720
764
  };
721
765
  if (!returnPostedData)
722
766
  returnPostedData = false;
@@ -724,12 +768,22 @@ export default class Inibase {
724
768
  let RETURN;
725
769
  if (!schema)
726
770
  throw this.throwError("NO_SCHEMA", tableName);
727
- const idFilePath = join(this.folder, this.database, tableName, "id.inib"), idCacheFilePath = join(this.folder, this.database, tableName, ".tmp", "lastId.inib");
728
- let lastId = (await File.isExists(idFilePath))
729
- ? Number((await File.isExists(idCacheFilePath))
730
- ? (await readFile(idCacheFilePath)).toString()
731
- : Object.entries((await File.get(idFilePath, -1, "number", undefined, this.salt))[0] ?? {})[0][1] ?? 0)
732
- : 0;
771
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib"), cashFolderPath = join(this.folder, this.database, tableName, ".tmp");
772
+ let lastId = 0, totalItems = 0, lastIdObj;
773
+ if (await File.isExists(idFilePath)) {
774
+ if (await File.isExists(join(cashFolderPath, "lastId.inib"))) {
775
+ lastId = Number(await File.read(join(cashFolderPath, "lastId.inib"), true));
776
+ if (await File.isExists(join(cashFolderPath, "totalItems.inib")))
777
+ totalItems = Number(await File.read(join(cashFolderPath, "totalItems.inib"), true));
778
+ else
779
+ totalItems = await File.count(join(this.folder, this.database, tableName, "id.inib"));
780
+ }
781
+ else {
782
+ [lastIdObj, totalItems] = await File.get(idFilePath, -1, "number", undefined, this.salt, true);
783
+ if (lastIdObj)
784
+ lastId = Number(Object.entries(lastIdObj)[0][1]) ?? 0;
785
+ }
786
+ }
733
787
  if (Utils.isArrayOfObjects(data))
734
788
  RETURN = data.map(({ id, updatedAt, createdAt, ...rest }) => ({
735
789
  id: ++lastId,
@@ -745,21 +799,23 @@ export default class Inibase {
745
799
  if (!RETURN)
746
800
  throw this.throwError("NO_DATA");
747
801
  RETURN = this.formatData(RETURN, schema);
748
- const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), RETURN);
802
+ const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
749
803
  const renameList = [];
750
804
  for await (const [path, content] of Object.entries(pathesContents))
751
805
  renameList.push(await File.append(path, content));
752
- await Promise.all(renameList.map(([tempPath, filePath]) => rename(tempPath, filePath)));
753
- await writeFile(idCacheFilePath, lastId.toString());
806
+ for await (const [tempPath, filePath] of renameList)
807
+ await rename(tempPath, filePath);
808
+ await File.write(join(cashFolderPath, "lastId.inib"), lastId.toString(), true);
809
+ await File.write(join(cashFolderPath, "totalItems.inib"), String(totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)), true);
754
810
  if (returnPostedData)
755
811
  return this.get(tableName, Utils.isArrayOfObjects(RETURN)
756
812
  ? RETURN.map((data) => Number(data.id))
757
- : RETURN.id, options, !Utils.isArrayOfObjects(data) // return only one item if data is not array of objects
758
- );
813
+ : RETURN.id, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
814
+ undefined, schema);
759
815
  }
760
816
  async put(tableName, data, where, options = {
761
817
  page: 1,
762
- per_page: 15,
818
+ perPage: 15,
763
819
  }, returnPostedData) {
764
820
  const schema = await this.getTableSchema(tableName);
765
821
  if (!schema)
@@ -793,8 +849,14 @@ export default class Inibase {
793
849
  });
794
850
  for await (const [path, content] of Object.entries(pathesContents))
795
851
  await File.replace(path, content);
852
+ if (Config.isCacheEnabled) {
853
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
854
+ if (cacheFiles.length)
855
+ for await (const file of cacheFiles)
856
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
857
+ }
796
858
  if (returnPostedData)
797
- return this.get(tableName, where, options);
859
+ return this.get(tableName, where, options, undefined, undefined, schema);
798
860
  }
799
861
  }
800
862
  else if ((Array.isArray(where) &&
@@ -803,7 +865,7 @@ export default class Inibase {
803
865
  Utils.isNumber(where)) {
804
866
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
805
867
  Utils.isValidID(where)) {
806
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
868
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
807
869
  return this.put(tableName, data, lineNumbers);
808
870
  }
809
871
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -824,13 +886,20 @@ export default class Inibase {
824
886
  const renameList = [];
825
887
  for await (const [path, content] of Object.entries(pathesContents))
826
888
  renameList.push(await File.replace(path, content));
827
- await Promise.all(renameList.map(([tempPath, filePath]) => rename(tempPath, filePath)));
889
+ for await (const [tempPath, filePath] of renameList)
890
+ await rename(tempPath, filePath);
891
+ if (Config.isCacheEnabled) {
892
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
893
+ if (cacheFiles.length)
894
+ for await (const file of cacheFiles)
895
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
896
+ }
828
897
  if (returnPostedData)
829
- return this.get(tableName, where, options, !Array.isArray(where));
898
+ return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
830
899
  }
831
900
  }
832
901
  else if (Utils.isObject(where)) {
833
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
902
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
834
903
  if (!lineNumbers || !lineNumbers.length)
835
904
  throw this.throwError("NO_ITEMS", tableName);
836
905
  return this.put(tableName, data, lineNumbers);
@@ -848,7 +917,14 @@ export default class Inibase {
848
917
  if (!where) {
849
918
  const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
850
919
  if (files.length)
851
- await Promise.all(files.map((file) => unlink(join(this.folder, this.database, tableName, file))));
920
+ for await (const file of files)
921
+ await unlink(join(this.folder, this.database, tableName, file));
922
+ if (Config.isCacheEnabled) {
923
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
924
+ if (cacheFiles.length)
925
+ for await (const file of cacheFiles)
926
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
927
+ }
852
928
  return "*";
853
929
  }
854
930
  else if ((Array.isArray(where) &&
@@ -857,7 +933,7 @@ export default class Inibase {
857
933
  Utils.isNumber(where)) {
858
934
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
859
935
  Utils.isValidID(where)) {
860
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
936
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
861
937
  if (!lineNumbers)
862
938
  return null;
863
939
  return this.delete(tableName, lineNumbers, where);
@@ -868,19 +944,29 @@ export default class Inibase {
868
944
  const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
869
945
  if (files.length) {
870
946
  if (!_id)
871
- _id = Object.entries((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt))[0] ?? {}).map(([_key, id]) => UtilsServer.encodeID(Number(id), this.salt));
947
+ _id = Object.entries((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt)) ?? {}).map(([_key, id]) => UtilsServer.encodeID(Number(id), this.salt));
872
948
  if (!_id.length)
873
949
  throw this.throwError("NO_ITEMS", tableName);
874
950
  const renameList = [];
875
951
  for await (const file of files)
876
952
  renameList.push(await File.remove(join(this.folder, this.database, tableName, file), where));
877
- await Promise.all(renameList.map(([tempPath, filePath]) => rename(tempPath, filePath)));
953
+ for await (const [tempPath, filePath] of renameList)
954
+ await rename(tempPath, filePath);
955
+ if (Config.isCacheEnabled) {
956
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
957
+ if (cacheFiles.length)
958
+ for await (const file of cacheFiles)
959
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
960
+ await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(((await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib")))
961
+ ? Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true))
962
+ : await File.count(join(this.folder, this.database, tableName, "id.inib"))) - (Array.isArray(where) ? where.length : 1)), true);
963
+ }
878
964
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
879
965
  }
880
966
  }
881
967
  }
882
968
  else if (Utils.isObject(where)) {
883
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
969
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
884
970
  if (!lineNumbers || !lineNumbers.length)
885
971
  throw this.throwError("NO_ITEMS", tableName);
886
972
  return this.delete(tableName, lineNumbers);
@@ -902,7 +988,7 @@ export default class Inibase {
902
988
  const columnPath = join(this.folder, this.database, tableName, column + ".inib");
903
989
  if (await File.isExists(columnPath)) {
904
990
  if (where) {
905
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
991
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
906
992
  RETURN[column] = lineNumbers
907
993
  ? await File.sum(columnPath, lineNumbers)
908
994
  : 0;
@@ -926,7 +1012,7 @@ export default class Inibase {
926
1012
  const columnPath = join(this.folder, this.database, tableName, column + ".inib");
927
1013
  if (await File.isExists(columnPath)) {
928
1014
  if (where) {
929
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1015
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
930
1016
  RETURN[column] = lineNumbers
931
1017
  ? await File.max(columnPath, lineNumbers)
932
1018
  : 0;
@@ -950,7 +1036,7 @@ export default class Inibase {
950
1036
  const columnPath = join(this.folder, this.database, tableName, column + ".inib");
951
1037
  if (await File.isExists(columnPath)) {
952
1038
  if (where) {
953
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1039
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
954
1040
  RETURN[column] = lineNumbers
955
1041
  ? await File.min(columnPath, lineNumbers)
956
1042
  : 0;