inibase 1.0.0-rc.26 → 1.0.0-rc.27

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);
@@ -307,10 +303,7 @@ export default class Inibase {
307
303
  const path = join(this.folder, this.database, tableName);
308
304
  let RETURN = {};
309
305
  for await (const field of schema) {
310
- if ((field.type === "array" ||
311
- (Array.isArray(field.type) &&
312
- field.type.includes("array"))) &&
313
- field.children) {
306
+ if (field.type === "array" && field.children) {
314
307
  if (Utils.isArrayOfObjects(field.children)) {
315
308
  if (field.children.filter((children) => children.type === "array" &&
316
309
  Utils.isArrayOfObjects(children.children)).length) {
@@ -392,8 +385,7 @@ export default class Inibase {
392
385
  options.columns = options.columns
393
386
  .filter((column) => column.includes(`${field.key}.`))
394
387
  .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;
388
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
397
389
  if (items)
398
390
  for await (const [index, item] of Object.entries(items)) {
399
391
  if (!RETURN[index])
@@ -404,8 +396,7 @@ export default class Inibase {
404
396
  }
405
397
  }
406
398
  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;
399
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
409
400
  if (items)
410
401
  for (const [index, item] of Object.entries(items)) {
411
402
  if (!RETURN[index])
@@ -436,8 +427,7 @@ export default class Inibase {
436
427
  .filter((column) => column.includes(`${field.key}.`) &&
437
428
  !column.includes(`${field.key}.`))
438
429
  .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;
430
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
441
431
  if (items)
442
432
  for await (const [index, item] of Object.entries(items)) {
443
433
  if (!RETURN[index])
@@ -449,8 +439,7 @@ export default class Inibase {
449
439
  }
450
440
  }
451
441
  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;
442
+ const items = await File.get(join(path, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field?.children, this.salt);
454
443
  if (items)
455
444
  for (const [index, item] of Object.entries(items)) {
456
445
  if (!RETURN[index])
@@ -521,23 +510,26 @@ export default class Inibase {
521
510
  }
522
511
  }
523
512
  async applyCriteria(tableName, schema, options, criteria, allTrue) {
524
- let RETURN = {};
513
+ let RETURN = {}, RETURN_LineNumbers = null;
525
514
  if (!criteria)
526
- return null;
515
+ return [null, null];
527
516
  if (criteria.and && Utils.isObject(criteria.and)) {
528
- const searchResult = await this.applyCriteria(tableName, schema, options, criteria.and, true);
517
+ const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.and, true);
529
518
  if (searchResult) {
530
519
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).length === Object.keys(criteria.and ?? {}).length)));
531
520
  delete criteria.and;
521
+ RETURN_LineNumbers = lineNumbers;
532
522
  }
533
523
  else
534
- return null;
524
+ return [null, null];
535
525
  }
536
526
  if (criteria.or && Utils.isObject(criteria.or)) {
537
- const searchResult = await this.applyCriteria(tableName, schema, options, criteria.or, false);
527
+ const [searchResult, lineNumbers] = await this.applyCriteria(tableName, schema, options, criteria.or, false);
538
528
  delete criteria.or;
539
- if (searchResult)
529
+ if (searchResult) {
540
530
  RETURN = Utils.deepMerge(RETURN, searchResult);
531
+ RETURN_LineNumbers = lineNumbers;
532
+ }
541
533
  }
542
534
  if (Object.keys(criteria).length > 0) {
543
535
  if (allTrue === undefined)
@@ -600,7 +592,7 @@ export default class Inibase {
600
592
  searchOperator = "=";
601
593
  searchComparedAtValue = value;
602
594
  }
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);
595
+ 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
596
  if (searchResult) {
605
597
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
606
598
  id,
@@ -608,7 +600,8 @@ export default class Inibase {
608
600
  [key]: value,
609
601
  },
610
602
  ])));
611
- this.totalItems[tableName + "-" + key] = total_lines;
603
+ this.totalItems[tableName + "-" + key] = totalLines;
604
+ RETURN_LineNumbers = linesNumbers;
612
605
  }
613
606
  if (allTrue && index > 0) {
614
607
  if (!Object.keys(RETURN).length)
@@ -619,31 +612,10 @@ export default class Inibase {
619
612
  }
620
613
  }
621
614
  }
622
- return Object.keys(RETURN).length ? RETURN : null;
615
+ return [Object.keys(RETURN).length ? RETURN : null, RETURN_LineNumbers];
623
616
  }
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
617
+ _filterSchemaByColumns(schema, columns) {
618
+ return schema
647
619
  .map((field) => {
648
620
  if (columns.some((column) => column.startsWith("!")))
649
621
  return columns.includes("!" + field.key) ? null : field;
@@ -653,7 +625,7 @@ export default class Inibase {
653
625
  Utils.isArrayOfObjects(field.children) &&
654
626
  columns.filter((column) => column.startsWith(field.key + ".") ||
655
627
  column.startsWith("!" + field.key + ".")).length) {
656
- field.children = filterSchemaByColumns(field.children, columns
628
+ field.children = this._filterSchemaByColumns(field.children, columns
657
629
  .filter((column) => column.startsWith(field.key + ".") ||
658
630
  column.startsWith("!" + field.key + "."))
659
631
  .map((column) => column.replace(field.key + ".", "")));
@@ -662,13 +634,55 @@ export default class Inibase {
662
634
  return null;
663
635
  })
664
636
  .filter((i) => i);
637
+ }
638
+ async get(tableName, where, options = {
639
+ page: 1,
640
+ perPage: 15,
641
+ }, onlyOne, onlyLinesNumbers, tableSchema) {
642
+ // Ensure options.columns is an array
643
+ if (options.columns) {
644
+ options.columns = Array.isArray(options.columns)
645
+ ? options.columns
646
+ : [options.columns];
647
+ if (options.columns.length && !options.columns.includes("id"))
648
+ options.columns.push("id");
649
+ }
650
+ // Default values for page and perPage
651
+ options.page = options.page || 1;
652
+ options.perPage = options.perPage || 15;
653
+ let RETURN;
654
+ let schema = tableSchema ?? (await this.getTableSchema(tableName));
655
+ if (!schema)
656
+ throw this.throwError("NO_SCHEMA", tableName);
657
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib");
658
+ if (!(await File.isExists(idFilePath)))
659
+ return null;
665
660
  if (options.columns && options.columns.length)
666
- schema = filterSchemaByColumns(schema, options.columns);
661
+ schema = this._filterSchemaByColumns(schema, options.columns);
667
662
  if (!where) {
668
663
  // 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 +
664
+ RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
670
665
  index +
671
666
  1), options));
667
+ if (Config.isCacheEnabled &&
668
+ (await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
669
+ this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
670
+ else {
671
+ this.totalItems[tableName + "-*"] = await File.count(idFilePath);
672
+ if (Config.isCacheEnabled)
673
+ await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
674
+ }
675
+ }
676
+ else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
677
+ (Utils.isNumber(where) && !onlyLinesNumbers)) {
678
+ let lineNumbers = where;
679
+ if (!Array.isArray(lineNumbers))
680
+ lineNumbers = [lineNumbers];
681
+ RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, lineNumbers, options)) ?? {});
682
+ if (!this.totalItems[tableName + "-*"])
683
+ this.totalItems[tableName + "-*"] = lineNumbers.length;
684
+ if (RETURN && RETURN.length && !Array.isArray(where))
685
+ RETURN = RETURN[0];
672
686
  }
673
687
  else if ((Array.isArray(where) &&
674
688
  (where.every(Utils.isValidID) || where.every(Utils.isNumber))) ||
@@ -685,29 +699,56 @@ export default class Inibase {
685
699
  ? Object.keys(lineNumbers).map(Number)
686
700
  : null;
687
701
  RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
702
+ if (!this.totalItems[tableName + "-*"]) {
703
+ if (Config.isCacheEnabled &&
704
+ (await File.isExists(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"))))
705
+ this.totalItems[tableName + "-*"] = Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true));
706
+ else {
707
+ this.totalItems[tableName + "-*"] = await File.count(idFilePath);
708
+ if (Config.isCacheEnabled)
709
+ await File.write(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), String(this.totalItems[tableName + "-*"]), true);
710
+ }
711
+ }
688
712
  if (RETURN && RETURN.length && !Array.isArray(where))
689
713
  RETURN = RETURN[0];
690
714
  }
691
715
  else if (Utils.isObject(where)) {
716
+ let cachedFilePath = "";
692
717
  // 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)));
718
+ if (Config.isCacheEnabled)
719
+ cachedFilePath = join(this.folder, this.database, tableName, ".tmp", `${UtilsServer.hashObject(where)}.inib`);
720
+ if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
721
+ const cachedItems = (await File.read(cachedFilePath, true)).split(",");
722
+ this.totalItems[tableName + "-*"] = cachedItems.length;
723
+ return this.get(tableName, cachedItems
724
+ .slice((options.page - 1) * options.perPage, options.page * options.perPage)
725
+ .map(Number), options, undefined, undefined, schema);
726
+ }
727
+ else {
728
+ let linesNumbers;
729
+ [RETURN, linesNumbers] = await this.applyCriteria(tableName, schema, options, where);
730
+ if (RETURN && linesNumbers) {
731
+ if (onlyLinesNumbers)
732
+ return Object.keys(RETURN).map(Number);
733
+ const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
734
+ RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter((field) => !alreadyExistsColumns.includes(field.key)), Object.keys(RETURN).map(Number), options)));
735
+ if (Config.isCacheEnabled)
736
+ await File.write(cachedFilePath, Array.from(linesNumbers).join(","), true);
737
+ }
699
738
  }
700
739
  }
701
740
  if (!RETURN ||
702
741
  (Utils.isObject(RETURN) && !Object.keys(RETURN).length) ||
703
742
  (Array.isArray(RETURN) && !RETURN.length))
704
743
  return null;
705
- const greatestTotalItems = Math.max(...Object.entries(this.totalItems)
706
- .filter(([k]) => k.startsWith(tableName + "-"))
707
- .map(([, v]) => v));
708
- this.pageInfo = {
744
+ const greatestTotalItems = this.totalItems[tableName + "-*"] ??
745
+ Math.max(...Object.entries(this.totalItems)
746
+ .filter(([k]) => k.startsWith(tableName + "-"))
747
+ .map(([, v]) => v));
748
+ this.pageInfo[tableName] = {
709
749
  ...(({ columns, ...restOfOptions }) => restOfOptions)(options),
710
- total_pages: Math.ceil(greatestTotalItems / options.per_page),
750
+ perPage: Array.isArray(RETURN) ? RETURN.length : 1,
751
+ totalPages: Math.ceil(greatestTotalItems / options.perPage),
711
752
  total: greatestTotalItems,
712
753
  };
713
754
  return onlyOne && Array.isArray(RETURN) ? RETURN[0] : RETURN;
@@ -716,7 +757,7 @@ export default class Inibase {
716
757
  if (!options)
717
758
  options = {
718
759
  page: 1,
719
- per_page: 15,
760
+ perPage: 15,
720
761
  };
721
762
  if (!returnPostedData)
722
763
  returnPostedData = false;
@@ -724,12 +765,22 @@ export default class Inibase {
724
765
  let RETURN;
725
766
  if (!schema)
726
767
  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;
768
+ const idFilePath = join(this.folder, this.database, tableName, "id.inib"), cashFolderPath = join(this.folder, this.database, tableName, ".tmp");
769
+ let lastId = 0, totalItems = 0, lastIdObj;
770
+ if (await File.isExists(idFilePath)) {
771
+ if (await File.isExists(join(cashFolderPath, "lastId.inib"))) {
772
+ lastId = Number(await File.read(join(cashFolderPath, "lastId.inib"), true));
773
+ if (await File.isExists(join(cashFolderPath, "totalItems.inib")))
774
+ totalItems = Number(await File.read(join(cashFolderPath, "totalItems.inib"), true));
775
+ else
776
+ totalItems = await File.count(join(this.folder, this.database, tableName, "id.inib"));
777
+ }
778
+ else {
779
+ [lastIdObj, totalItems] = await File.get(idFilePath, -1, "number", undefined, this.salt, true);
780
+ if (lastIdObj)
781
+ lastId = Number(Object.entries(lastIdObj)[0][1]) ?? 0;
782
+ }
783
+ }
733
784
  if (Utils.isArrayOfObjects(data))
734
785
  RETURN = data.map(({ id, updatedAt, createdAt, ...rest }) => ({
735
786
  id: ++lastId,
@@ -745,21 +796,23 @@ export default class Inibase {
745
796
  if (!RETURN)
746
797
  throw this.throwError("NO_DATA");
747
798
  RETURN = this.formatData(RETURN, schema);
748
- const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), RETURN);
799
+ const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
749
800
  const renameList = [];
750
801
  for await (const [path, content] of Object.entries(pathesContents))
751
802
  renameList.push(await File.append(path, content));
752
- await Promise.all(renameList.map(([tempPath, filePath]) => rename(tempPath, filePath)));
753
- await writeFile(idCacheFilePath, lastId.toString());
803
+ for await (const [tempPath, filePath] of renameList)
804
+ await rename(tempPath, filePath);
805
+ await File.write(join(cashFolderPath, "lastId.inib"), lastId.toString(), true);
806
+ await File.write(join(cashFolderPath, "totalItems.inib"), String(totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)), true);
754
807
  if (returnPostedData)
755
808
  return this.get(tableName, Utils.isArrayOfObjects(RETURN)
756
809
  ? 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
- );
810
+ : RETURN.id, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
811
+ undefined, schema);
759
812
  }
760
813
  async put(tableName, data, where, options = {
761
814
  page: 1,
762
- per_page: 15,
815
+ perPage: 15,
763
816
  }, returnPostedData) {
764
817
  const schema = await this.getTableSchema(tableName);
765
818
  if (!schema)
@@ -793,8 +846,14 @@ export default class Inibase {
793
846
  });
794
847
  for await (const [path, content] of Object.entries(pathesContents))
795
848
  await File.replace(path, content);
849
+ if (Config.isCacheEnabled) {
850
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
851
+ if (cacheFiles.length)
852
+ for await (const file of cacheFiles)
853
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
854
+ }
796
855
  if (returnPostedData)
797
- return this.get(tableName, where, options);
856
+ return this.get(tableName, where, options, undefined, undefined, schema);
798
857
  }
799
858
  }
800
859
  else if ((Array.isArray(where) &&
@@ -803,7 +862,7 @@ export default class Inibase {
803
862
  Utils.isNumber(where)) {
804
863
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
805
864
  Utils.isValidID(where)) {
806
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
865
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
807
866
  return this.put(tableName, data, lineNumbers);
808
867
  }
809
868
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -824,13 +883,20 @@ export default class Inibase {
824
883
  const renameList = [];
825
884
  for await (const [path, content] of Object.entries(pathesContents))
826
885
  renameList.push(await File.replace(path, content));
827
- await Promise.all(renameList.map(([tempPath, filePath]) => rename(tempPath, filePath)));
886
+ for await (const [tempPath, filePath] of renameList)
887
+ await rename(tempPath, filePath);
888
+ if (Config.isCacheEnabled) {
889
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
890
+ if (cacheFiles.length)
891
+ for await (const file of cacheFiles)
892
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
893
+ }
828
894
  if (returnPostedData)
829
- return this.get(tableName, where, options, !Array.isArray(where));
895
+ return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
830
896
  }
831
897
  }
832
898
  else if (Utils.isObject(where)) {
833
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
899
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
834
900
  if (!lineNumbers || !lineNumbers.length)
835
901
  throw this.throwError("NO_ITEMS", tableName);
836
902
  return this.put(tableName, data, lineNumbers);
@@ -848,7 +914,14 @@ export default class Inibase {
848
914
  if (!where) {
849
915
  const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
850
916
  if (files.length)
851
- await Promise.all(files.map((file) => unlink(join(this.folder, this.database, tableName, file))));
917
+ for await (const file of files)
918
+ await unlink(join(this.folder, this.database, tableName, file));
919
+ if (Config.isCacheEnabled) {
920
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
921
+ if (cacheFiles.length)
922
+ for await (const file of cacheFiles)
923
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
924
+ }
852
925
  return "*";
853
926
  }
854
927
  else if ((Array.isArray(where) &&
@@ -857,7 +930,7 @@ export default class Inibase {
857
930
  Utils.isNumber(where)) {
858
931
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
859
932
  Utils.isValidID(where)) {
860
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
933
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
861
934
  if (!lineNumbers)
862
935
  return null;
863
936
  return this.delete(tableName, lineNumbers, where);
@@ -868,19 +941,29 @@ export default class Inibase {
868
941
  const files = (await readdir(join(this.folder, this.database, tableName)))?.filter((fileName) => fileName.endsWith(".inib"));
869
942
  if (files.length) {
870
943
  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));
944
+ _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
945
  if (!_id.length)
873
946
  throw this.throwError("NO_ITEMS", tableName);
874
947
  const renameList = [];
875
948
  for await (const file of files)
876
949
  renameList.push(await File.remove(join(this.folder, this.database, tableName, file), where));
877
- await Promise.all(renameList.map(([tempPath, filePath]) => rename(tempPath, filePath)));
950
+ for await (const [tempPath, filePath] of renameList)
951
+ await rename(tempPath, filePath);
952
+ if (Config.isCacheEnabled) {
953
+ const cacheFiles = (await readdir(join(this.folder, this.database, tableName, ".tmp")))?.filter((fileName) => !["lastId.inib", "totalItems.inib"].includes(fileName));
954
+ if (cacheFiles.length)
955
+ for await (const file of cacheFiles)
956
+ await unlink(join(this.folder, this.database, tableName, ".tmp", file));
957
+ 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")))
958
+ ? Number(await File.read(join(this.folder, this.database, tableName, ".tmp", "totalItems.inib"), true))
959
+ : await File.count(join(this.folder, this.database, tableName, "id.inib"))) - (Array.isArray(where) ? where.length : 1)), true);
960
+ }
878
961
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
879
962
  }
880
963
  }
881
964
  }
882
965
  else if (Utils.isObject(where)) {
883
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
966
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
884
967
  if (!lineNumbers || !lineNumbers.length)
885
968
  throw this.throwError("NO_ITEMS", tableName);
886
969
  return this.delete(tableName, lineNumbers);
@@ -902,7 +985,7 @@ export default class Inibase {
902
985
  const columnPath = join(this.folder, this.database, tableName, column + ".inib");
903
986
  if (await File.isExists(columnPath)) {
904
987
  if (where) {
905
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
988
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
906
989
  RETURN[column] = lineNumbers
907
990
  ? await File.sum(columnPath, lineNumbers)
908
991
  : 0;
@@ -926,7 +1009,7 @@ export default class Inibase {
926
1009
  const columnPath = join(this.folder, this.database, tableName, column + ".inib");
927
1010
  if (await File.isExists(columnPath)) {
928
1011
  if (where) {
929
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1012
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
930
1013
  RETURN[column] = lineNumbers
931
1014
  ? await File.max(columnPath, lineNumbers)
932
1015
  : 0;
@@ -950,7 +1033,7 @@ export default class Inibase {
950
1033
  const columnPath = join(this.folder, this.database, tableName, column + ".inib");
951
1034
  if (await File.isExists(columnPath)) {
952
1035
  if (where) {
953
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1036
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
954
1037
  RETURN[column] = lineNumbers
955
1038
  ? await File.min(columnPath, lineNumbers)
956
1039
  : 0;