inibase 1.0.0-rc.60 → 1.0.0-rc.61

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,5 +1,5 @@
1
1
  import "dotenv/config";
2
- import { unlink, rename, mkdir, readdir } from "node:fs/promises";
2
+ import { unlink, rename, mkdir, readdir, open, writeFile, readFile, } from "node:fs/promises";
3
3
  import { existsSync, appendFileSync, readFileSync } from "node:fs";
4
4
  import { join, parse } from "node:path";
5
5
  import { scryptSync, randomBytes } from "node:crypto";
@@ -8,22 +8,19 @@ import Inison from "inison";
8
8
  import * as File from "./file.js";
9
9
  import * as Utils from "./utils.js";
10
10
  import * as UtilsServer from "./utils.server.js";
11
- import * as Config from "./config.js";
12
11
  export default class Inibase {
13
- folder;
14
- database;
15
- table;
16
12
  pageInfo;
13
+ salt;
14
+ databasePath;
15
+ tables;
17
16
  fileExtension = ".txt";
18
17
  checkIFunique;
19
18
  totalItems;
20
- salt;
21
- constructor(database, mainFolder = ".", _table = null, _totalItems = {}, _pageInfo = {}, _isThreadEnabled = false) {
22
- this.database = database;
23
- this.folder = mainFolder;
24
- this.table = _table;
25
- this.totalItems = _totalItems;
26
- this.pageInfo = _pageInfo;
19
+ constructor(database, mainFolder = ".") {
20
+ this.databasePath = join(mainFolder, database);
21
+ this.tables = {};
22
+ this.totalItems = {};
23
+ this.pageInfo = {};
27
24
  this.checkIFunique = {};
28
25
  if (!process.env.INIBASE_SECRET) {
29
26
  if (existsSync(".env") &&
@@ -34,25 +31,14 @@ export default class Inibase {
34
31
  }
35
32
  else
36
33
  this.salt = Buffer.from(process.env.INIBASE_SECRET, "hex");
37
- // try {
38
- // if (Config.isCompressionEnabled)
39
- // execSync(
40
- // `gzip -r ${join(this.folder, this.database)}/*/*.txt 2>/dev/null`,
41
- // );
42
- // else
43
- // execSync(
44
- // `gunzip -r ${join(
45
- // this.folder,
46
- // this.database,
47
- // )}/*/*.txt.gz 2>/dev/null`,
48
- // );
49
- // } catch {}
50
34
  }
51
35
  throwError(code, variable, language = "en") {
52
36
  const errorMessages = {
53
37
  en: {
54
38
  FIELD_UNIQUE: "Field {variable} should be unique, got {variable} instead",
55
39
  FIELD_REQUIRED: "Field {variable} is required",
40
+ TABLE_EXISTS: "Table {variable} already exists",
41
+ TABLE_NOT_EXISTS: "Table {variable} doesn't exist",
56
42
  NO_SCHEMA: "Table {variable} does't have a schema",
57
43
  NO_ITEMS: "Table {variable} is empty",
58
44
  NO_RESULTS: "No results found for table {variable}",
@@ -74,61 +60,123 @@ export default class Inibase {
74
60
  : errorMessage.replaceAll("{variable}", `'${variable.toString()}'`)
75
61
  : errorMessage.replaceAll("{variable}", ""));
76
62
  }
77
- getFileExtension = () => {
63
+ getFileExtension = (tableName) => {
78
64
  let mainExtension = this.fileExtension;
79
65
  // TODO: ADD ENCRYPTION
80
- // if(Config.isEncryptionEnabled)
66
+ // if(this.tables[tableName].config.encryption)
81
67
  // mainExtension += ".enc"
82
- if (Config.isCompressionEnabled)
68
+ if (this.tables[tableName].config.compression)
83
69
  mainExtension += ".gz";
84
70
  return mainExtension;
85
71
  };
86
- _schemaToIdsPath = (schema, prefix = "") => {
72
+ _schemaToIdsPath = (tableName, schema, prefix = "") => {
87
73
  const RETURN = {};
88
74
  for (const field of schema)
89
75
  if ((field.type === "array" || field.type === "object") &&
90
76
  field.children &&
91
77
  Utils.isArrayOfObjects(field.children)) {
92
- Utils.deepMerge(RETURN, this._schemaToIdsPath(field.children, `${(prefix ?? "") + field.key}.`));
78
+ Utils.deepMerge(RETURN, this._schemaToIdsPath(tableName, field.children, `${(prefix ?? "") + field.key}.`));
93
79
  }
94
80
  else if (field.id)
95
- RETURN[field.id] = `${(prefix ?? "") + field.key}${this.getFileExtension()}`;
81
+ RETURN[field.id] = `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`;
96
82
  return RETURN;
97
83
  };
98
- async setTableSchema(tableName, schema) {
99
- const tablePath = join(this.folder, this.database, tableName), tableSchemaPath = join(tablePath, "schema.json"), isTablePathExists = await File.isExists(tablePath);
100
- // remove id from schema
101
- schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
102
- if (!isTablePathExists)
103
- await mkdir(tablePath, { recursive: true });
104
- if (!(await File.isExists(join(tablePath, ".tmp"))))
105
- await mkdir(join(tablePath, ".tmp"));
106
- if (!(await File.isExists(join(tablePath, ".cache"))))
107
- await mkdir(join(tablePath, ".cache"));
108
- if (await File.isExists(tableSchemaPath)) {
109
- // update columns files names based on field id
110
- const currentSchema = await this.getTableSchema(tableName, false);
111
- schema = UtilsServer.addIdToSchema(schema, currentSchema?.length
112
- ? UtilsServer.findLastIdNumber(currentSchema, this.salt)
113
- : 0, this.salt, false);
114
- if (currentSchema?.length) {
115
- const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath(currentSchema), this._schemaToIdsPath(schema));
116
- if (replaceOldPathes)
117
- await Promise.all(Object.entries(replaceOldPathes).map(async ([oldPath, newPath]) => {
118
- if (await File.isExists(join(tablePath, oldPath)))
119
- await rename(join(tablePath, oldPath), join(tablePath, newPath));
120
- }));
84
+ async createTable(tableName, schema, config) {
85
+ const tablePath = join(this.databasePath, tableName);
86
+ if (await File.isExists(tablePath))
87
+ this.throwError("TABLE_EXISTS", tableName);
88
+ await Promise.all([
89
+ mkdir(tablePath, { recursive: true }),
90
+ mkdir(join(tablePath, ".tmp")),
91
+ mkdir(join(tablePath, ".cache")),
92
+ ]);
93
+ if (config) {
94
+ if (config.compression)
95
+ await open(join(tablePath, ".compression.config"), "w");
96
+ if (config.cache)
97
+ await open(join(tablePath, ".cache.config"), "w");
98
+ if (config.prepend)
99
+ await open(join(tablePath, ".prepend.config"), "w");
100
+ }
101
+ if (schema)
102
+ await writeFile(join(tablePath, "schema.json"), JSON.stringify(UtilsServer.addIdToSchema(schema, 0, this.salt, false), null, 2));
103
+ }
104
+ async updateTable(tableName, schema, config) {
105
+ const table = await this.getTable(tableName), tablePath = join(this.databasePath, tableName);
106
+ if (config) {
107
+ if (config.compression !== undefined) {
108
+ if (!config.compression && table.config.compression) {
109
+ try {
110
+ await UtilsServer.exec(`gunzip -r ${tablePath}/*.txt.gz 2>/dev/null`);
111
+ await unlink(join(tablePath, ".compression.config"));
112
+ }
113
+ catch { }
114
+ }
115
+ else if (config.compression && !table.config.compression) {
116
+ try {
117
+ await UtilsServer.exec(`gzip -r ${tablePath}/*.txt 2>/dev/null`);
118
+ await open(join(tablePath, ".compression.config"), "w");
119
+ }
120
+ catch { }
121
+ }
122
+ }
123
+ if (config.cache !== undefined) {
124
+ if (config.cache && !table.config.cache)
125
+ await open(join(tablePath, ".cache.config"), "w");
126
+ else if (!config.cache && table.config.cache)
127
+ await unlink(join(tablePath, ".cache.config"));
128
+ }
129
+ if (config.prepend !== undefined) {
130
+ if (config.prepend && !table.config.prepend)
131
+ await open(join(tablePath, ".prepend.config"), "w");
132
+ else if (!config.prepend && table.config.prepend)
133
+ await unlink(join(tablePath, ".prepend.config"));
121
134
  }
122
135
  }
123
- else
124
- schema = UtilsServer.addIdToSchema(schema, 0, this.salt, false);
125
- await File.write(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2), true);
136
+ if (schema) {
137
+ // remove id from schema
138
+ schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
139
+ if (await File.isExists(join(tablePath, "schema.json"))) {
140
+ // update columns files names based on field id
141
+ const currentSchema = await this.getTableSchema(tableName, false);
142
+ schema = UtilsServer.addIdToSchema(schema, currentSchema?.length
143
+ ? UtilsServer.findLastIdNumber(currentSchema, this.salt)
144
+ : 0, this.salt, false);
145
+ if (currentSchema?.length) {
146
+ const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath(tableName, currentSchema), this._schemaToIdsPath(tableName, schema));
147
+ if (replaceOldPathes)
148
+ await Promise.all(Object.entries(replaceOldPathes).map(async ([oldPath, newPath]) => {
149
+ if (await File.isExists(join(tablePath, oldPath)))
150
+ await rename(join(tablePath, oldPath), join(tablePath, newPath));
151
+ }));
152
+ }
153
+ }
154
+ else
155
+ schema = UtilsServer.addIdToSchema(schema, 0, this.salt, false);
156
+ await writeFile(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2));
157
+ delete this.tables[tableName];
158
+ }
159
+ }
160
+ async getTable(tableName) {
161
+ const tablePath = join(this.databasePath, tableName);
162
+ if (!(await File.isExists(tablePath)))
163
+ this.throwError("TABLE_NOT_EXISTS", tableName);
164
+ if (!this.tables[tableName])
165
+ this.tables[tableName] = {
166
+ schema: await this.getTableSchema(tableName),
167
+ config: {
168
+ compression: await File.isExists(join(tablePath, ".compression.config")),
169
+ cache: await File.isExists(join(tablePath, ".cache.config")),
170
+ prepend: await File.isExists(join(tablePath, ".prepend.config")),
171
+ },
172
+ };
173
+ return this.tables[tableName];
126
174
  }
127
175
  async getTableSchema(tableName, encodeIDs = true) {
128
- const tableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
176
+ const tableSchemaPath = join(this.databasePath, tableName, "schema.json");
129
177
  if (!(await File.isExists(tableSchemaPath)))
130
178
  return undefined;
131
- const schemaFile = await File.read(tableSchemaPath, true);
179
+ const schemaFile = await readFile(tableSchemaPath, "utf8");
132
180
  if (!schemaFile)
133
181
  return undefined;
134
182
  const schema = JSON.parse(schemaFile), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
@@ -156,15 +204,13 @@ export default class Inibase {
156
204
  },
157
205
  ];
158
206
  }
159
- async getSchemaWhenTableNotEmpty(tableName, schema) {
160
- const tablePath = join(this.folder, this.database, tableName);
161
- if (!schema)
162
- schema = await this.getTableSchema(tableName);
163
- if (!schema)
207
+ async throwErrorIfTableEmpty(tableName) {
208
+ const table = await this.getTable(tableName);
209
+ if (!table.schema)
164
210
  throw this.throwError("NO_SCHEMA", tableName);
165
- if (!(await File.isExists(join(tablePath, `id${this.getFileExtension()}`))))
211
+ if (!(await File.isExists(join(this.databasePath, tableName, `id${this.getFileExtension(tableName)}`))))
166
212
  throw this.throwError("NO_ITEMS", tableName);
167
- return schema;
213
+ return table;
168
214
  }
169
215
  validateData(data, schema, skipRequiredField = false) {
170
216
  if (Utils.isArrayOfObjects(data))
@@ -285,12 +331,12 @@ export default class Inibase {
285
331
  return null;
286
332
  }
287
333
  async checkUnique(tableName, schema) {
288
- const tablePath = join(this.folder, this.database, tableName);
334
+ const tablePath = join(this.databasePath, tableName);
289
335
  for await (const [key, values] of Object.entries(this.checkIFunique)) {
290
336
  const field = Utils.getField(key, schema);
291
337
  if (!field)
292
338
  continue;
293
- const [searchResult, totalLines] = await File.search(join(tablePath, `${key}${this.getFileExtension()}`), Array.isArray(values) ? "=" : "[]", values, undefined, field.type, field.children, 1, undefined, false, this.salt);
339
+ const [searchResult, totalLines] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), Array.isArray(values) ? "=" : "[]", values, undefined, field.type, field.children, 1, undefined, false, this.salt);
294
340
  if (searchResult && totalLines > 0)
295
341
  throw this.throwError("FIELD_UNIQUE", [
296
342
  field.key,
@@ -376,14 +422,12 @@ export default class Inibase {
376
422
  }
377
423
  return RETURN;
378
424
  };
379
- _addPathToKeys = (obj, path) => {
380
- const newObject = {};
381
- for (const key in obj)
382
- newObject[join(path, `${key}${this.getFileExtension()}`)] = obj[key];
383
- return newObject;
384
- };
385
- joinPathesContents(mainPath, data) {
386
- return this._addPathToKeys(this._CombineData(data), mainPath);
425
+ joinPathesContents(tableName, data) {
426
+ const tablePath = join(this.databasePath, tableName), combinedData = this._CombineData(data);
427
+ const newCombinedData = {};
428
+ for (const [key, value] of Object.entries(combinedData))
429
+ newCombinedData[join(tablePath, `${key}${this.getFileExtension(tableName)}`)] = value;
430
+ return newCombinedData;
387
431
  }
388
432
  _getItemsFromSchemaHelper(RETURN, item, index, field) {
389
433
  if (Utils.isObject(item)) {
@@ -427,7 +471,7 @@ export default class Inibase {
427
471
  }
428
472
  }
429
473
  async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
430
- const tablePath = join(this.folder, this.database, tableName);
474
+ const tablePath = join(this.databasePath, tableName);
431
475
  let RETURN = {};
432
476
  for await (const field of schema) {
433
477
  if ((field.type === "array" ||
@@ -498,13 +542,13 @@ export default class Inibase {
498
542
  (Array.isArray(field.type) && field.type.includes("table")) ||
499
543
  (Array.isArray(field.children) && field.children.includes("table"))) {
500
544
  if (field.table &&
501
- (await File.isExists(join(this.folder, this.database, field.table))) &&
502
- (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`)))) {
545
+ (await File.isExists(join(this.databasePath, field.table))) &&
546
+ (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
503
547
  if (options.columns)
504
548
  options.columns = options.columns
505
549
  .filter((column) => column.includes(`${field.key}.`))
506
550
  .map((column) => column.replace(`${field.key}.`, ""));
507
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
551
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
508
552
  if (items)
509
553
  for await (const [index, item] of Object.entries(items)) {
510
554
  if (!RETURN[index])
@@ -515,8 +559,8 @@ export default class Inibase {
515
559
  }
516
560
  }
517
561
  }
518
- else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`))) {
519
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
562
+ else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
563
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
520
564
  if (items)
521
565
  for (const [index, item] of Object.entries(items)) {
522
566
  if (!RETURN[index])
@@ -543,13 +587,13 @@ export default class Inibase {
543
587
  }
544
588
  else if (field.type === "table") {
545
589
  if (field.table &&
546
- (await File.isExists(join(this.folder, this.database, field.table))) &&
547
- (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`)))) {
590
+ (await File.isExists(join(this.databasePath, field.table))) &&
591
+ (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
548
592
  if (options.columns)
549
593
  options.columns = options.columns
550
594
  .filter((column) => column.includes(`${field.key}.`))
551
595
  .map((column) => column.replace(`${field.key}.`, ""));
552
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, "number", undefined, this.salt);
596
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, "number", undefined, this.salt);
553
597
  if (items)
554
598
  for await (const [index, item] of Object.entries(items)) {
555
599
  if (!RETURN[index])
@@ -560,8 +604,8 @@ export default class Inibase {
560
604
  }
561
605
  }
562
606
  }
563
- else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`))) {
564
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
607
+ else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
608
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
565
609
  if (items)
566
610
  for (const [index, item] of Object.entries(items)) {
567
611
  if (!RETURN[index])
@@ -578,7 +622,7 @@ export default class Inibase {
578
622
  return RETURN;
579
623
  }
580
624
  async applyCriteria(tableName, schema, options, criteria, allTrue) {
581
- const tablePath = join(this.folder, this.database, tableName);
625
+ const tablePath = join(this.databasePath, tableName);
582
626
  let RETURN = {}, RETURN_LineNumbers = null;
583
627
  if (!criteria)
584
628
  return [null, null];
@@ -662,7 +706,7 @@ export default class Inibase {
662
706
  searchOperator = "=";
663
707
  searchComparedAtValue = value;
664
708
  }
665
- const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension()}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
709
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
666
710
  if (searchResult) {
667
711
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
668
712
  id,
@@ -713,14 +757,14 @@ export default class Inibase {
713
757
  async get(tableName, where, options = {
714
758
  page: 1,
715
759
  perPage: 15,
716
- }, onlyOne, onlyLinesNumbers, tableSchema, skipIdColumn) {
717
- const tablePath = join(this.folder, this.database, tableName);
760
+ }, onlyOne, onlyLinesNumbers, _skipIdColumn) {
761
+ const tablePath = join(this.databasePath, tableName);
718
762
  // Ensure options.columns is an array
719
763
  if (options.columns) {
720
764
  options.columns = Array.isArray(options.columns)
721
765
  ? options.columns
722
766
  : [options.columns];
723
- if (!skipIdColumn &&
767
+ if (!_skipIdColumn &&
724
768
  options.columns.length &&
725
769
  !options.columns.includes("id"))
726
770
  options.columns.push("id");
@@ -729,7 +773,8 @@ export default class Inibase {
729
773
  options.page = options.page || 1;
730
774
  options.perPage = options.perPage || 15;
731
775
  let RETURN;
732
- let schema = await this.getSchemaWhenTableNotEmpty(tableName, tableSchema);
776
+ let schema = (await this.throwErrorIfTableEmpty(tableName))
777
+ .schema;
733
778
  if (options.columns?.length)
734
779
  schema = this._filterSchemaByColumns(schema, options.columns);
735
780
  if (where &&
@@ -741,14 +786,14 @@ export default class Inibase {
741
786
  RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
742
787
  index +
743
788
  1), options));
744
- if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
745
- this.totalItems[`${tableName}-*`] = Number((await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true)).split(",")[1]);
789
+ if (await File.isExists(join(tablePath, ".pagination")))
790
+ this.totalItems[`${tableName}-*`] = Number((await readFile(join(tablePath, ".pagination"), "utf8")).split(",")[1]);
746
791
  else {
747
- let [lastId, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
792
+ let [lastId, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true);
748
793
  if (lastId)
749
794
  lastId = Number(Object.keys(lastId)?.[0] ?? 0);
750
795
  this.totalItems[`${tableName}-*`] = totalItems;
751
- await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
796
+ await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems}`);
752
797
  }
753
798
  }
754
799
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -771,7 +816,7 @@ export default class Inibase {
771
816
  let Ids = where;
772
817
  if (!Array.isArray(Ids))
773
818
  Ids = [Ids];
774
- const [lineNumbers, countItems] = await File.search(join(tablePath, `id${this.getFileExtension()}`), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[`${tableName}-*`], this.salt);
819
+ const [lineNumbers, countItems] = await File.search(join(tablePath, `id${this.getFileExtension(tableName)}`), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[`${tableName}-*`], this.salt);
775
820
  if (!lineNumbers)
776
821
  throw this.throwError("NO_RESULTS", tableName);
777
822
  if (onlyLinesNumbers)
@@ -787,16 +832,17 @@ export default class Inibase {
787
832
  else if (Utils.isObject(where)) {
788
833
  let cachedFilePath = "";
789
834
  // Criteria
790
- if (Config.isCacheEnabled)
835
+ if (this.tables[tableName].config.cache)
791
836
  cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}${this.fileExtension}`);
792
- if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
793
- const cachedItems = (await File.read(cachedFilePath, true)).split(",");
837
+ if (this.tables[tableName].config.cache &&
838
+ (await File.isExists(cachedFilePath))) {
839
+ const cachedItems = (await readFile(cachedFilePath, "utf8")).split(",");
794
840
  this.totalItems[`${tableName}-*`] = cachedItems.length;
795
841
  if (onlyLinesNumbers)
796
842
  return cachedItems.map(Number);
797
843
  return this.get(tableName, cachedItems
798
844
  .slice((options.page - 1) * options.perPage, options.page * options.perPage)
799
- .map(Number), options, undefined, undefined, schema);
845
+ .map(Number), options);
800
846
  }
801
847
  let linesNumbers = null;
802
848
  [RETURN, linesNumbers] = await this.applyCriteria(tableName, schema, options, where);
@@ -805,8 +851,8 @@ export default class Inibase {
805
851
  return Object.keys(RETURN).map(Number);
806
852
  const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
807
853
  RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter(({ key }) => !alreadyExistsColumns.includes(key)), Object.keys(RETURN).map(Number), options)));
808
- if (Config.isCacheEnabled)
809
- await File.write(cachedFilePath, Array.from(linesNumbers).join(","), true);
854
+ if (this.tables[tableName].config.cache)
855
+ await writeFile(cachedFilePath, Array.from(linesNumbers).join(","));
810
856
  }
811
857
  }
812
858
  if (!RETURN ||
@@ -831,7 +877,7 @@ export default class Inibase {
831
877
  page: 1,
832
878
  perPage: 15,
833
879
  };
834
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getTableSchema(tableName);
880
+ const tablePath = join(this.databasePath, tableName), schema = (await this.getTable(tableName)).schema;
835
881
  if (!schema)
836
882
  throw this.throwError("NO_SCHEMA", tableName);
837
883
  if (!returnPostedData)
@@ -841,14 +887,14 @@ export default class Inibase {
841
887
  let lastId = 0, totalItems = 0, renameList = [];
842
888
  try {
843
889
  await File.lock(join(tablePath, ".tmp"), keys);
844
- if (await File.isExists(join(tablePath, `id${this.getFileExtension()}`))) {
845
- if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
846
- [lastId, totalItems] = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
890
+ if (await File.isExists(join(tablePath, `id${this.getFileExtension(tableName)}`))) {
891
+ if (await File.isExists(join(tablePath, ".pagination")))
892
+ [lastId, totalItems] = (await readFile(join(tablePath, ".pagination"), "utf8"))
847
893
  .split(",")
848
894
  .map(Number);
849
895
  else {
850
896
  let lastIdObj = null;
851
- [lastIdObj, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
897
+ [lastIdObj, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true);
852
898
  if (lastIdObj)
853
899
  lastId = Number(Object.keys(lastIdObj)?.[0] ?? 0);
854
900
  }
@@ -868,27 +914,26 @@ export default class Inibase {
868
914
  this.validateData(RETURN, schema);
869
915
  await this.checkUnique(tableName, schema);
870
916
  RETURN = this.formatData(RETURN, schema);
871
- const pathesContents = this.joinPathesContents(tablePath, Config.isReverseEnabled
917
+ const pathesContents = this.joinPathesContents(tableName, this.tables[tableName].config.prepend
872
918
  ? Array.isArray(RETURN)
873
919
  ? RETURN.toReversed()
874
920
  : RETURN
875
921
  : RETURN);
876
- await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content))));
922
+ await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content, this.tables[tableName].config.prepend))));
877
923
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
878
924
  renameList = [];
879
925
  totalItems += Array.isArray(RETURN) ? RETURN.length : 1;
880
- if (Config.isCacheEnabled)
926
+ if (this.tables[tableName].config.cache)
881
927
  await this.clearCache(tablePath);
882
- await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
928
+ await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems}`);
883
929
  if (returnPostedData)
884
- return this.get(tableName, Config.isReverseEnabled
930
+ return this.get(tableName, this.tables[tableName].config.prepend
885
931
  ? Array.isArray(RETURN)
886
932
  ? RETURN.map((_, index) => index + 1)
887
933
  : 1
888
934
  : Array.isArray(RETURN)
889
935
  ? RETURN.map((_, index) => totalItems - index)
890
- : totalItems, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
891
- undefined, schema);
936
+ : totalItems, options, !Utils.isArrayOfObjects(data));
892
937
  }
893
938
  finally {
894
939
  if (renameList.length)
@@ -901,7 +946,7 @@ export default class Inibase {
901
946
  perPage: 15,
902
947
  }, returnUpdatedData) {
903
948
  let renameList = [];
904
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
949
+ const tablePath = join(this.databasePath, tableName), schema = (await this.throwErrorIfTableEmpty(tableName)).schema;
905
950
  this.validateData(data, schema, true);
906
951
  await this.checkUnique(tableName, schema);
907
952
  data = this.formatData(data, schema, true);
@@ -919,13 +964,13 @@ export default class Inibase {
919
964
  return this.put(tableName, data, data.id);
920
965
  }
921
966
  let totalItems;
922
- if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
923
- totalItems = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
967
+ if (await File.isExists(join(tablePath, ".pagination")))
968
+ totalItems = (await readFile(join(tablePath, ".pagination"), "utf8"))
924
969
  .split(",")
925
970
  .map(Number)[1];
926
971
  else
927
- totalItems = await File.count(join(tablePath, `id${this.getFileExtension()}`));
928
- const pathesContents = this.joinPathesContents(tablePath, {
972
+ totalItems = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
973
+ const pathesContents = this.joinPathesContents(tableName, {
929
974
  ...(({ id, ...restOfData }) => restOfData)(data),
930
975
  updatedAt: Date.now(),
931
976
  });
@@ -938,10 +983,10 @@ export default class Inibase {
938
983
  renameList.push(await File.replace(path, replacementObject));
939
984
  }));
940
985
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
941
- if (Config.isCacheEnabled)
986
+ if (this.tables[tableName].config.cache)
942
987
  await this.clearCache(join(tablePath, ".cache"));
943
988
  if (returnUpdatedData)
944
- return await this.get(tableName, where, options, undefined, undefined, schema);
989
+ return await this.get(tableName, undefined, options);
945
990
  }
946
991
  finally {
947
992
  if (renameList.length)
@@ -951,13 +996,13 @@ export default class Inibase {
951
996
  }
952
997
  else if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
953
998
  Utils.isValidID(where)) {
954
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
999
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
955
1000
  return this.put(tableName, data, lineNumbers);
956
1001
  }
957
1002
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
958
1003
  Utils.isNumber(where)) {
959
1004
  // "where" in this case, is the line(s) number(s) and not id(s)
960
- const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(tablePath, Utils.isArrayOfObjects(data)
1005
+ const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(tableName, Utils.isArrayOfObjects(data)
961
1006
  ? data.map((item) => ({
962
1007
  ...item,
963
1008
  updatedAt: Date.now(),
@@ -976,10 +1021,10 @@ export default class Inibase {
976
1021
  await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
977
1022
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
978
1023
  renameList = [];
979
- if (Config.isCacheEnabled)
1024
+ if (this.tables[tableName].config.cache)
980
1025
  await this.clearCache(tablePath);
981
1026
  if (returnUpdatedData)
982
- return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
1027
+ return this.get(tableName, where, options, !Array.isArray(where));
983
1028
  }
984
1029
  finally {
985
1030
  if (renameList.length)
@@ -988,7 +1033,7 @@ export default class Inibase {
988
1033
  }
989
1034
  }
990
1035
  else if (Utils.isObject(where)) {
991
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1036
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
992
1037
  if (returnUpdatedData)
993
1038
  return this.put(tableName, data, lineNumbers, options, returnUpdatedData);
994
1039
  await this.put(tableName, data, lineNumbers, options, returnUpdatedData);
@@ -998,14 +1043,15 @@ export default class Inibase {
998
1043
  }
999
1044
  async delete(tableName, where, _id) {
1000
1045
  const renameList = [];
1001
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1046
+ const tablePath = join(this.databasePath, tableName);
1047
+ await this.throwErrorIfTableEmpty(tableName);
1002
1048
  if (!where) {
1003
1049
  try {
1004
1050
  await File.lock(join(tablePath, ".tmp"));
1005
1051
  await Promise.all((await readdir(tablePath))
1006
1052
  ?.filter((fileName) => fileName.endsWith(".inib"))
1007
1053
  .map(async (file) => unlink(join(tablePath, file))));
1008
- if (Config.isCacheEnabled)
1054
+ if (this.tables[tableName].config.cache)
1009
1055
  await this.clearCache(tablePath);
1010
1056
  }
1011
1057
  finally {
@@ -1015,25 +1061,25 @@ export default class Inibase {
1015
1061
  }
1016
1062
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
1017
1063
  Utils.isValidID(where)) {
1018
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1064
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1019
1065
  return this.delete(tableName, lineNumbers, where);
1020
1066
  }
1021
1067
  if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1022
1068
  Utils.isNumber(where)) {
1023
1069
  // "where" in this case, is the line(s) number(s) and not id(s)
1024
- const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension()));
1070
+ const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)));
1025
1071
  if (files.length) {
1026
1072
  try {
1027
1073
  await File.lock(join(tablePath, ".tmp"));
1028
1074
  await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
1029
1075
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1030
- if (Config.isCacheEnabled)
1076
+ if (this.tables[tableName].config.cache)
1031
1077
  await this.clearCache(tablePath);
1032
- if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`))) {
1033
- const [lastId, totalItems] = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
1078
+ if (await File.isExists(join(tablePath, ".pagination"))) {
1079
+ const [lastId, totalItems] = (await readFile(join(tablePath, ".pagination"), "utf8"))
1034
1080
  .split(",")
1035
1081
  .map(Number);
1036
- await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
1082
+ await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`);
1037
1083
  }
1038
1084
  if (_id)
1039
1085
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
@@ -1047,7 +1093,7 @@ export default class Inibase {
1047
1093
  }
1048
1094
  }
1049
1095
  else if (Utils.isObject(where)) {
1050
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1096
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1051
1097
  return this.delete(tableName, lineNumbers);
1052
1098
  }
1053
1099
  else
@@ -1056,14 +1102,15 @@ export default class Inibase {
1056
1102
  }
1057
1103
  async sum(tableName, columns, where) {
1058
1104
  const RETURN = {};
1059
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1105
+ const tablePath = join(this.databasePath, tableName);
1106
+ await this.throwErrorIfTableEmpty(tableName);
1060
1107
  if (!Array.isArray(columns))
1061
1108
  columns = [columns];
1062
1109
  for await (const column of columns) {
1063
- const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1110
+ const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
1064
1111
  if (await File.isExists(columnPath)) {
1065
1112
  if (where) {
1066
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1113
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1067
1114
  RETURN[column] = lineNumbers
1068
1115
  ? await File.sum(columnPath, lineNumbers)
1069
1116
  : 0;
@@ -1076,14 +1123,15 @@ export default class Inibase {
1076
1123
  }
1077
1124
  async max(tableName, columns, where) {
1078
1125
  const RETURN = {};
1079
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1126
+ const tablePath = join(this.databasePath, tableName);
1127
+ await this.throwErrorIfTableEmpty(tableName);
1080
1128
  if (!Array.isArray(columns))
1081
1129
  columns = [columns];
1082
1130
  for await (const column of columns) {
1083
- const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1131
+ const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
1084
1132
  if (await File.isExists(columnPath)) {
1085
1133
  if (where) {
1086
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1134
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1087
1135
  RETURN[column] = lineNumbers
1088
1136
  ? await File.max(columnPath, lineNumbers)
1089
1137
  : 0;
@@ -1096,14 +1144,15 @@ export default class Inibase {
1096
1144
  }
1097
1145
  async min(tableName, columns, where) {
1098
1146
  const RETURN = {};
1099
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1147
+ const tablePath = join(this.databasePath, tableName);
1148
+ await this.throwErrorIfTableEmpty(tableName);
1100
1149
  if (!Array.isArray(columns))
1101
1150
  columns = [columns];
1102
1151
  for await (const column of columns) {
1103
- const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1152
+ const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
1104
1153
  if (await File.isExists(columnPath)) {
1105
1154
  if (where) {
1106
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1155
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1107
1156
  RETURN[column] = lineNumbers
1108
1157
  ? await File.min(columnPath, lineNumbers)
1109
1158
  : 0;
@@ -1118,7 +1167,7 @@ export default class Inibase {
1118
1167
  page: 1,
1119
1168
  perPage: 15,
1120
1169
  }) {
1121
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1170
+ const tablePath = join(this.databasePath, tableName), schema = (await this.throwErrorIfTableEmpty(tableName)).schema;
1122
1171
  // Default values for page and perPage
1123
1172
  options.page = options.page || 1;
1124
1173
  options.perPage = options.perPage || 15;
@@ -1137,11 +1186,11 @@ export default class Inibase {
1137
1186
  }
1138
1187
  let cacheKey = "";
1139
1188
  // Criteria
1140
- if (Config.isCacheEnabled)
1189
+ if (this.tables[tableName].config.cache)
1141
1190
  cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
1142
1191
  if (where) {
1143
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1144
- keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension()}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
1192
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1193
+ keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
1145
1194
  isLineNumbers = false;
1146
1195
  if (!keepItems.length)
1147
1196
  throw this.throwError("NO_RESULTS", tableName);
@@ -1151,7 +1200,7 @@ export default class Inibase {
1151
1200
  keepItems = Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
1152
1201
  index +
1153
1202
  1);
1154
- const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension()}`));
1203
+ const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension(tableName)}`));
1155
1204
  // Construct the paste command to merge files and filter lines by IDs
1156
1205
  const pasteCommand = `paste ${filesPathes.join(" ")}`;
1157
1206
  // Construct the sort command dynamically based on the number of files for sorting
@@ -1176,19 +1225,20 @@ export default class Inibase {
1176
1225
  await File.lock(join(tablePath, ".tmp"), cacheKey);
1177
1226
  // Combine the commands
1178
1227
  // Execute the command synchronously
1179
- const { stdout } = await UtilsServer.exec(Config.isCacheEnabled
1228
+ const lines = (await UtilsServer.exec(this.tables[tableName].config.cache
1180
1229
  ? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
1181
1230
  ? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
1182
1231
  : `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
1183
1232
  : `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
1184
1233
  encoding: "utf-8",
1185
- });
1234
+ })).stdout
1235
+ .trim()
1236
+ .split("\n");
1186
1237
  // Parse the result and extract the specified lines
1187
- const lines = stdout.trim().split("\n");
1188
1238
  const outputArray = lines.map((line) => {
1189
1239
  const splitedFileColumns = line.split("\t"); // Assuming tab-separated columns
1190
1240
  const outputObject = {};
1191
- // Extract values for each file, including `id${this.getFileExtension()}`
1241
+ // Extract values for each file, including `id${this.getFileExtension(tableName)}`
1192
1242
  filesPathes.forEach((fileName, index) => {
1193
1243
  const field = Utils.getField(parse(fileName).name, schema);
1194
1244
  if (field)
@@ -1196,7 +1246,7 @@ export default class Inibase {
1196
1246
  });
1197
1247
  return outputObject;
1198
1248
  });
1199
- const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), options, undefined, undefined, schema, true);
1249
+ const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), options, undefined, undefined, true);
1200
1250
  return restOfColumns
1201
1251
  ? outputArray.map((item, index) => ({
1202
1252
  ...item,