inibase 1.0.0-rc.60 → 1.0.0-rc.62

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,120 @@ 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 mkdir(join(tablePath, ".tmp"), { recursive: true });
89
+ await mkdir(join(tablePath, ".cache"));
90
+ if (config) {
91
+ if (config.compression)
92
+ await open(join(tablePath, ".compression.config"), "w");
93
+ if (config.cache)
94
+ await open(join(tablePath, ".cache.config"), "w");
95
+ if (config.prepend)
96
+ await open(join(tablePath, ".prepend.config"), "w");
97
+ }
98
+ if (schema)
99
+ await writeFile(join(tablePath, "schema.json"), JSON.stringify(UtilsServer.addIdToSchema(schema, 0, this.salt, false), null, 2));
100
+ }
101
+ async updateTable(tableName, schema, config) {
102
+ const table = await this.getTable(tableName), tablePath = join(this.databasePath, tableName);
103
+ if (config) {
104
+ if (config.compression !== undefined) {
105
+ if (!config.compression && table.config.compression) {
106
+ try {
107
+ await UtilsServer.exec(`gunzip -r ${tablePath}/*.txt.gz 2>/dev/null`);
108
+ await unlink(join(tablePath, ".compression.config"));
109
+ }
110
+ catch { }
111
+ }
112
+ else if (config.compression && !table.config.compression) {
113
+ try {
114
+ await UtilsServer.exec(`gzip -r ${tablePath}/*.txt 2>/dev/null`);
115
+ await open(join(tablePath, ".compression.config"), "w");
116
+ }
117
+ catch { }
118
+ }
119
+ }
120
+ if (config.cache !== undefined) {
121
+ if (config.cache && !table.config.cache)
122
+ await open(join(tablePath, ".cache.config"), "w");
123
+ else if (!config.cache && table.config.cache)
124
+ await unlink(join(tablePath, ".cache.config"));
125
+ }
126
+ if (config.prepend !== undefined) {
127
+ if (config.prepend && !table.config.prepend)
128
+ await open(join(tablePath, ".prepend.config"), "w");
129
+ else if (!config.prepend && table.config.prepend)
130
+ await unlink(join(tablePath, ".prepend.config"));
121
131
  }
122
132
  }
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);
133
+ if (schema) {
134
+ // remove id from schema
135
+ schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
136
+ if (await File.isExists(join(tablePath, "schema.json"))) {
137
+ // update columns files names based on field id
138
+ const currentSchema = await this.getTableSchema(tableName, false);
139
+ schema = UtilsServer.addIdToSchema(schema, currentSchema?.length
140
+ ? UtilsServer.findLastIdNumber(currentSchema, this.salt)
141
+ : 0, this.salt, false);
142
+ if (currentSchema?.length) {
143
+ const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath(tableName, currentSchema), this._schemaToIdsPath(tableName, schema));
144
+ if (replaceOldPathes)
145
+ await Promise.all(Object.entries(replaceOldPathes).map(async ([oldPath, newPath]) => {
146
+ if (await File.isExists(join(tablePath, oldPath)))
147
+ await rename(join(tablePath, oldPath), join(tablePath, newPath));
148
+ }));
149
+ }
150
+ }
151
+ else
152
+ schema = UtilsServer.addIdToSchema(schema, 0, this.salt, false);
153
+ await writeFile(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2));
154
+ delete this.tables[tableName];
155
+ }
156
+ }
157
+ async getTable(tableName) {
158
+ const tablePath = join(this.databasePath, tableName);
159
+ if (!(await File.isExists(tablePath)))
160
+ this.throwError("TABLE_NOT_EXISTS", tableName);
161
+ if (!this.tables[tableName])
162
+ this.tables[tableName] = {
163
+ schema: await this.getTableSchema(tableName),
164
+ config: {
165
+ compression: await File.isExists(join(tablePath, ".compression.config")),
166
+ cache: await File.isExists(join(tablePath, ".cache.config")),
167
+ prepend: await File.isExists(join(tablePath, ".prepend.config")),
168
+ },
169
+ };
170
+ return this.tables[tableName];
126
171
  }
127
172
  async getTableSchema(tableName, encodeIDs = true) {
128
- const tableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
173
+ const tableSchemaPath = join(this.databasePath, tableName, "schema.json");
129
174
  if (!(await File.isExists(tableSchemaPath)))
130
175
  return undefined;
131
- const schemaFile = await File.read(tableSchemaPath, true);
176
+ const schemaFile = await readFile(tableSchemaPath, "utf8");
132
177
  if (!schemaFile)
133
178
  return undefined;
134
179
  const schema = JSON.parse(schemaFile), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
@@ -156,15 +201,13 @@ export default class Inibase {
156
201
  },
157
202
  ];
158
203
  }
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)
204
+ async throwErrorIfTableEmpty(tableName) {
205
+ const table = await this.getTable(tableName);
206
+ if (!table.schema)
164
207
  throw this.throwError("NO_SCHEMA", tableName);
165
- if (!(await File.isExists(join(tablePath, `id${this.getFileExtension()}`))))
208
+ if (!(await File.isExists(join(this.databasePath, tableName, `id${this.getFileExtension(tableName)}`))))
166
209
  throw this.throwError("NO_ITEMS", tableName);
167
- return schema;
210
+ return table;
168
211
  }
169
212
  validateData(data, schema, skipRequiredField = false) {
170
213
  if (Utils.isArrayOfObjects(data))
@@ -285,12 +328,12 @@ export default class Inibase {
285
328
  return null;
286
329
  }
287
330
  async checkUnique(tableName, schema) {
288
- const tablePath = join(this.folder, this.database, tableName);
331
+ const tablePath = join(this.databasePath, tableName);
289
332
  for await (const [key, values] of Object.entries(this.checkIFunique)) {
290
333
  const field = Utils.getField(key, schema);
291
334
  if (!field)
292
335
  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);
336
+ 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
337
  if (searchResult && totalLines > 0)
295
338
  throw this.throwError("FIELD_UNIQUE", [
296
339
  field.key,
@@ -376,14 +419,12 @@ export default class Inibase {
376
419
  }
377
420
  return RETURN;
378
421
  };
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);
422
+ joinPathesContents(tableName, data) {
423
+ const tablePath = join(this.databasePath, tableName), combinedData = this._CombineData(data);
424
+ const newCombinedData = {};
425
+ for (const [key, value] of Object.entries(combinedData))
426
+ newCombinedData[join(tablePath, `${key}${this.getFileExtension(tableName)}`)] = value;
427
+ return newCombinedData;
387
428
  }
388
429
  _getItemsFromSchemaHelper(RETURN, item, index, field) {
389
430
  if (Utils.isObject(item)) {
@@ -427,7 +468,7 @@ export default class Inibase {
427
468
  }
428
469
  }
429
470
  async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
430
- const tablePath = join(this.folder, this.database, tableName);
471
+ const tablePath = join(this.databasePath, tableName);
431
472
  let RETURN = {};
432
473
  for await (const field of schema) {
433
474
  if ((field.type === "array" ||
@@ -498,13 +539,13 @@ export default class Inibase {
498
539
  (Array.isArray(field.type) && field.type.includes("table")) ||
499
540
  (Array.isArray(field.children) && field.children.includes("table"))) {
500
541
  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()}`)))) {
542
+ (await File.isExists(join(this.databasePath, field.table))) &&
543
+ (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
503
544
  if (options.columns)
504
545
  options.columns = options.columns
505
546
  .filter((column) => column.includes(`${field.key}.`))
506
547
  .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);
548
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
508
549
  if (items)
509
550
  for await (const [index, item] of Object.entries(items)) {
510
551
  if (!RETURN[index])
@@ -515,8 +556,8 @@ export default class Inibase {
515
556
  }
516
557
  }
517
558
  }
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);
559
+ else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
560
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
520
561
  if (items)
521
562
  for (const [index, item] of Object.entries(items)) {
522
563
  if (!RETURN[index])
@@ -543,13 +584,13 @@ export default class Inibase {
543
584
  }
544
585
  else if (field.type === "table") {
545
586
  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()}`)))) {
587
+ (await File.isExists(join(this.databasePath, field.table))) &&
588
+ (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
548
589
  if (options.columns)
549
590
  options.columns = options.columns
550
591
  .filter((column) => column.includes(`${field.key}.`))
551
592
  .map((column) => column.replace(`${field.key}.`, ""));
552
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, "number", undefined, this.salt);
593
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, "number", undefined, this.salt);
553
594
  if (items)
554
595
  for await (const [index, item] of Object.entries(items)) {
555
596
  if (!RETURN[index])
@@ -560,8 +601,8 @@ export default class Inibase {
560
601
  }
561
602
  }
562
603
  }
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);
604
+ else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
605
+ const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
565
606
  if (items)
566
607
  for (const [index, item] of Object.entries(items)) {
567
608
  if (!RETURN[index])
@@ -578,7 +619,7 @@ export default class Inibase {
578
619
  return RETURN;
579
620
  }
580
621
  async applyCriteria(tableName, schema, options, criteria, allTrue) {
581
- const tablePath = join(this.folder, this.database, tableName);
622
+ const tablePath = join(this.databasePath, tableName);
582
623
  let RETURN = {}, RETURN_LineNumbers = null;
583
624
  if (!criteria)
584
625
  return [null, null];
@@ -662,7 +703,7 @@ export default class Inibase {
662
703
  searchOperator = "=";
663
704
  searchComparedAtValue = value;
664
705
  }
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);
706
+ 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
707
  if (searchResult) {
667
708
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
668
709
  id,
@@ -713,14 +754,14 @@ export default class Inibase {
713
754
  async get(tableName, where, options = {
714
755
  page: 1,
715
756
  perPage: 15,
716
- }, onlyOne, onlyLinesNumbers, tableSchema, skipIdColumn) {
717
- const tablePath = join(this.folder, this.database, tableName);
757
+ }, onlyOne, onlyLinesNumbers, _skipIdColumn) {
758
+ const tablePath = join(this.databasePath, tableName);
718
759
  // Ensure options.columns is an array
719
760
  if (options.columns) {
720
761
  options.columns = Array.isArray(options.columns)
721
762
  ? options.columns
722
763
  : [options.columns];
723
- if (!skipIdColumn &&
764
+ if (!_skipIdColumn &&
724
765
  options.columns.length &&
725
766
  !options.columns.includes("id"))
726
767
  options.columns.push("id");
@@ -729,7 +770,8 @@ export default class Inibase {
729
770
  options.page = options.page || 1;
730
771
  options.perPage = options.perPage || 15;
731
772
  let RETURN;
732
- let schema = await this.getSchemaWhenTableNotEmpty(tableName, tableSchema);
773
+ let schema = (await this.throwErrorIfTableEmpty(tableName))
774
+ .schema;
733
775
  if (options.columns?.length)
734
776
  schema = this._filterSchemaByColumns(schema, options.columns);
735
777
  if (where &&
@@ -741,14 +783,14 @@ export default class Inibase {
741
783
  RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
742
784
  index +
743
785
  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]);
786
+ if (await File.isExists(join(tablePath, ".pagination")))
787
+ this.totalItems[`${tableName}-*`] = Number((await readFile(join(tablePath, ".pagination"), "utf8")).split(",")[1]);
746
788
  else {
747
- let [lastId, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
789
+ let [lastId, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true);
748
790
  if (lastId)
749
791
  lastId = Number(Object.keys(lastId)?.[0] ?? 0);
750
792
  this.totalItems[`${tableName}-*`] = totalItems;
751
- await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
793
+ await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems}`);
752
794
  }
753
795
  }
754
796
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -771,7 +813,7 @@ export default class Inibase {
771
813
  let Ids = where;
772
814
  if (!Array.isArray(Ids))
773
815
  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);
816
+ 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
817
  if (!lineNumbers)
776
818
  throw this.throwError("NO_RESULTS", tableName);
777
819
  if (onlyLinesNumbers)
@@ -787,16 +829,17 @@ export default class Inibase {
787
829
  else if (Utils.isObject(where)) {
788
830
  let cachedFilePath = "";
789
831
  // Criteria
790
- if (Config.isCacheEnabled)
832
+ if (this.tables[tableName].config.cache)
791
833
  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(",");
834
+ if (this.tables[tableName].config.cache &&
835
+ (await File.isExists(cachedFilePath))) {
836
+ const cachedItems = (await readFile(cachedFilePath, "utf8")).split(",");
794
837
  this.totalItems[`${tableName}-*`] = cachedItems.length;
795
838
  if (onlyLinesNumbers)
796
839
  return cachedItems.map(Number);
797
840
  return this.get(tableName, cachedItems
798
841
  .slice((options.page - 1) * options.perPage, options.page * options.perPage)
799
- .map(Number), options, undefined, undefined, schema);
842
+ .map(Number), options);
800
843
  }
801
844
  let linesNumbers = null;
802
845
  [RETURN, linesNumbers] = await this.applyCriteria(tableName, schema, options, where);
@@ -805,8 +848,8 @@ export default class Inibase {
805
848
  return Object.keys(RETURN).map(Number);
806
849
  const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
807
850
  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);
851
+ if (this.tables[tableName].config.cache)
852
+ await writeFile(cachedFilePath, Array.from(linesNumbers).join(","));
810
853
  }
811
854
  }
812
855
  if (!RETURN ||
@@ -831,7 +874,7 @@ export default class Inibase {
831
874
  page: 1,
832
875
  perPage: 15,
833
876
  };
834
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getTableSchema(tableName);
877
+ const tablePath = join(this.databasePath, tableName), schema = (await this.getTable(tableName)).schema;
835
878
  if (!schema)
836
879
  throw this.throwError("NO_SCHEMA", tableName);
837
880
  if (!returnPostedData)
@@ -841,14 +884,14 @@ export default class Inibase {
841
884
  let lastId = 0, totalItems = 0, renameList = [];
842
885
  try {
843
886
  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))
887
+ if (await File.isExists(join(tablePath, `id${this.getFileExtension(tableName)}`))) {
888
+ if (await File.isExists(join(tablePath, ".pagination")))
889
+ [lastId, totalItems] = (await readFile(join(tablePath, ".pagination"), "utf8"))
847
890
  .split(",")
848
891
  .map(Number);
849
892
  else {
850
893
  let lastIdObj = null;
851
- [lastIdObj, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
894
+ [lastIdObj, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true);
852
895
  if (lastIdObj)
853
896
  lastId = Number(Object.keys(lastIdObj)?.[0] ?? 0);
854
897
  }
@@ -868,27 +911,26 @@ export default class Inibase {
868
911
  this.validateData(RETURN, schema);
869
912
  await this.checkUnique(tableName, schema);
870
913
  RETURN = this.formatData(RETURN, schema);
871
- const pathesContents = this.joinPathesContents(tablePath, Config.isReverseEnabled
914
+ const pathesContents = this.joinPathesContents(tableName, this.tables[tableName].config.prepend
872
915
  ? Array.isArray(RETURN)
873
916
  ? RETURN.toReversed()
874
917
  : RETURN
875
918
  : RETURN);
876
- await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content))));
919
+ await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.append(path, content, this.tables[tableName].config.prepend))));
877
920
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
878
921
  renameList = [];
879
922
  totalItems += Array.isArray(RETURN) ? RETURN.length : 1;
880
- if (Config.isCacheEnabled)
923
+ if (this.tables[tableName].config.cache)
881
924
  await this.clearCache(tablePath);
882
- await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
925
+ await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems}`);
883
926
  if (returnPostedData)
884
- return this.get(tableName, Config.isReverseEnabled
927
+ return this.get(tableName, this.tables[tableName].config.prepend
885
928
  ? Array.isArray(RETURN)
886
929
  ? RETURN.map((_, index) => index + 1)
887
930
  : 1
888
931
  : Array.isArray(RETURN)
889
932
  ? 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);
933
+ : totalItems, options, !Utils.isArrayOfObjects(data));
892
934
  }
893
935
  finally {
894
936
  if (renameList.length)
@@ -901,7 +943,7 @@ export default class Inibase {
901
943
  perPage: 15,
902
944
  }, returnUpdatedData) {
903
945
  let renameList = [];
904
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
946
+ const tablePath = join(this.databasePath, tableName), schema = (await this.throwErrorIfTableEmpty(tableName)).schema;
905
947
  this.validateData(data, schema, true);
906
948
  await this.checkUnique(tableName, schema);
907
949
  data = this.formatData(data, schema, true);
@@ -919,13 +961,13 @@ export default class Inibase {
919
961
  return this.put(tableName, data, data.id);
920
962
  }
921
963
  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))
964
+ if (await File.isExists(join(tablePath, ".pagination")))
965
+ totalItems = (await readFile(join(tablePath, ".pagination"), "utf8"))
924
966
  .split(",")
925
967
  .map(Number)[1];
926
968
  else
927
- totalItems = await File.count(join(tablePath, `id${this.getFileExtension()}`));
928
- const pathesContents = this.joinPathesContents(tablePath, {
969
+ totalItems = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
970
+ const pathesContents = this.joinPathesContents(tableName, {
929
971
  ...(({ id, ...restOfData }) => restOfData)(data),
930
972
  updatedAt: Date.now(),
931
973
  });
@@ -938,10 +980,10 @@ export default class Inibase {
938
980
  renameList.push(await File.replace(path, replacementObject));
939
981
  }));
940
982
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
941
- if (Config.isCacheEnabled)
983
+ if (this.tables[tableName].config.cache)
942
984
  await this.clearCache(join(tablePath, ".cache"));
943
985
  if (returnUpdatedData)
944
- return await this.get(tableName, where, options, undefined, undefined, schema);
986
+ return await this.get(tableName, undefined, options);
945
987
  }
946
988
  finally {
947
989
  if (renameList.length)
@@ -951,13 +993,13 @@ export default class Inibase {
951
993
  }
952
994
  else if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
953
995
  Utils.isValidID(where)) {
954
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
996
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
955
997
  return this.put(tableName, data, lineNumbers);
956
998
  }
957
999
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
958
1000
  Utils.isNumber(where)) {
959
1001
  // "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)
1002
+ const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(tableName, Utils.isArrayOfObjects(data)
961
1003
  ? data.map((item) => ({
962
1004
  ...item,
963
1005
  updatedAt: Date.now(),
@@ -976,10 +1018,10 @@ export default class Inibase {
976
1018
  await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
977
1019
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
978
1020
  renameList = [];
979
- if (Config.isCacheEnabled)
1021
+ if (this.tables[tableName].config.cache)
980
1022
  await this.clearCache(tablePath);
981
1023
  if (returnUpdatedData)
982
- return this.get(tableName, where, options, !Array.isArray(where), undefined, schema);
1024
+ return this.get(tableName, where, options, !Array.isArray(where));
983
1025
  }
984
1026
  finally {
985
1027
  if (renameList.length)
@@ -988,7 +1030,7 @@ export default class Inibase {
988
1030
  }
989
1031
  }
990
1032
  else if (Utils.isObject(where)) {
991
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1033
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
992
1034
  if (returnUpdatedData)
993
1035
  return this.put(tableName, data, lineNumbers, options, returnUpdatedData);
994
1036
  await this.put(tableName, data, lineNumbers, options, returnUpdatedData);
@@ -998,14 +1040,15 @@ export default class Inibase {
998
1040
  }
999
1041
  async delete(tableName, where, _id) {
1000
1042
  const renameList = [];
1001
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1043
+ const tablePath = join(this.databasePath, tableName);
1044
+ await this.throwErrorIfTableEmpty(tableName);
1002
1045
  if (!where) {
1003
1046
  try {
1004
1047
  await File.lock(join(tablePath, ".tmp"));
1005
1048
  await Promise.all((await readdir(tablePath))
1006
1049
  ?.filter((fileName) => fileName.endsWith(".inib"))
1007
1050
  .map(async (file) => unlink(join(tablePath, file))));
1008
- if (Config.isCacheEnabled)
1051
+ if (this.tables[tableName].config.cache)
1009
1052
  await this.clearCache(tablePath);
1010
1053
  }
1011
1054
  finally {
@@ -1015,25 +1058,25 @@ export default class Inibase {
1015
1058
  }
1016
1059
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
1017
1060
  Utils.isValidID(where)) {
1018
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1061
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1019
1062
  return this.delete(tableName, lineNumbers, where);
1020
1063
  }
1021
1064
  if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1022
1065
  Utils.isNumber(where)) {
1023
1066
  // "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()));
1067
+ const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)));
1025
1068
  if (files.length) {
1026
1069
  try {
1027
1070
  await File.lock(join(tablePath, ".tmp"));
1028
1071
  await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
1029
1072
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1030
- if (Config.isCacheEnabled)
1073
+ if (this.tables[tableName].config.cache)
1031
1074
  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))
1075
+ if (await File.isExists(join(tablePath, ".pagination"))) {
1076
+ const [lastId, totalItems] = (await readFile(join(tablePath, ".pagination"), "utf8"))
1034
1077
  .split(",")
1035
1078
  .map(Number);
1036
- await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
1079
+ await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`);
1037
1080
  }
1038
1081
  if (_id)
1039
1082
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
@@ -1047,7 +1090,7 @@ export default class Inibase {
1047
1090
  }
1048
1091
  }
1049
1092
  else if (Utils.isObject(where)) {
1050
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1093
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1051
1094
  return this.delete(tableName, lineNumbers);
1052
1095
  }
1053
1096
  else
@@ -1056,14 +1099,15 @@ export default class Inibase {
1056
1099
  }
1057
1100
  async sum(tableName, columns, where) {
1058
1101
  const RETURN = {};
1059
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1102
+ const tablePath = join(this.databasePath, tableName);
1103
+ await this.throwErrorIfTableEmpty(tableName);
1060
1104
  if (!Array.isArray(columns))
1061
1105
  columns = [columns];
1062
1106
  for await (const column of columns) {
1063
- const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1107
+ const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
1064
1108
  if (await File.isExists(columnPath)) {
1065
1109
  if (where) {
1066
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1110
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1067
1111
  RETURN[column] = lineNumbers
1068
1112
  ? await File.sum(columnPath, lineNumbers)
1069
1113
  : 0;
@@ -1076,14 +1120,15 @@ export default class Inibase {
1076
1120
  }
1077
1121
  async max(tableName, columns, where) {
1078
1122
  const RETURN = {};
1079
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1123
+ const tablePath = join(this.databasePath, tableName);
1124
+ await this.throwErrorIfTableEmpty(tableName);
1080
1125
  if (!Array.isArray(columns))
1081
1126
  columns = [columns];
1082
1127
  for await (const column of columns) {
1083
- const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1128
+ const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
1084
1129
  if (await File.isExists(columnPath)) {
1085
1130
  if (where) {
1086
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1131
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1087
1132
  RETURN[column] = lineNumbers
1088
1133
  ? await File.max(columnPath, lineNumbers)
1089
1134
  : 0;
@@ -1096,14 +1141,15 @@ export default class Inibase {
1096
1141
  }
1097
1142
  async min(tableName, columns, where) {
1098
1143
  const RETURN = {};
1099
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1144
+ const tablePath = join(this.databasePath, tableName);
1145
+ await this.throwErrorIfTableEmpty(tableName);
1100
1146
  if (!Array.isArray(columns))
1101
1147
  columns = [columns];
1102
1148
  for await (const column of columns) {
1103
- const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
1149
+ const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
1104
1150
  if (await File.isExists(columnPath)) {
1105
1151
  if (where) {
1106
- const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1152
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1107
1153
  RETURN[column] = lineNumbers
1108
1154
  ? await File.min(columnPath, lineNumbers)
1109
1155
  : 0;
@@ -1118,7 +1164,7 @@ export default class Inibase {
1118
1164
  page: 1,
1119
1165
  perPage: 15,
1120
1166
  }) {
1121
- const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
1167
+ const tablePath = join(this.databasePath, tableName), schema = (await this.throwErrorIfTableEmpty(tableName)).schema;
1122
1168
  // Default values for page and perPage
1123
1169
  options.page = options.page || 1;
1124
1170
  options.perPage = options.perPage || 15;
@@ -1137,11 +1183,11 @@ export default class Inibase {
1137
1183
  }
1138
1184
  let cacheKey = "";
1139
1185
  // Criteria
1140
- if (Config.isCacheEnabled)
1186
+ if (this.tables[tableName].config.cache)
1141
1187
  cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
1142
1188
  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);
1189
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
1190
+ keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
1145
1191
  isLineNumbers = false;
1146
1192
  if (!keepItems.length)
1147
1193
  throw this.throwError("NO_RESULTS", tableName);
@@ -1151,7 +1197,7 @@ export default class Inibase {
1151
1197
  keepItems = Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
1152
1198
  index +
1153
1199
  1);
1154
- const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension()}`));
1200
+ const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension(tableName)}`));
1155
1201
  // Construct the paste command to merge files and filter lines by IDs
1156
1202
  const pasteCommand = `paste ${filesPathes.join(" ")}`;
1157
1203
  // Construct the sort command dynamically based on the number of files for sorting
@@ -1176,19 +1222,20 @@ export default class Inibase {
1176
1222
  await File.lock(join(tablePath, ".tmp"), cacheKey);
1177
1223
  // Combine the commands
1178
1224
  // Execute the command synchronously
1179
- const { stdout } = await UtilsServer.exec(Config.isCacheEnabled
1225
+ const lines = (await UtilsServer.exec(this.tables[tableName].config.cache
1180
1226
  ? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
1181
1227
  ? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
1182
1228
  : `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
1183
1229
  : `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
1184
1230
  encoding: "utf-8",
1185
- });
1231
+ })).stdout
1232
+ .trim()
1233
+ .split("\n");
1186
1234
  // Parse the result and extract the specified lines
1187
- const lines = stdout.trim().split("\n");
1188
1235
  const outputArray = lines.map((line) => {
1189
1236
  const splitedFileColumns = line.split("\t"); // Assuming tab-separated columns
1190
1237
  const outputObject = {};
1191
- // Extract values for each file, including `id${this.getFileExtension()}`
1238
+ // Extract values for each file, including `id${this.getFileExtension(tableName)}`
1192
1239
  filesPathes.forEach((fileName, index) => {
1193
1240
  const field = Utils.getField(parse(fileName).name, schema);
1194
1241
  if (field)
@@ -1196,7 +1243,7 @@ export default class Inibase {
1196
1243
  });
1197
1244
  return outputObject;
1198
1245
  });
1199
- const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), options, undefined, undefined, schema, true);
1246
+ const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), options, undefined, undefined, true);
1200
1247
  return restOfColumns
1201
1248
  ? outputArray.map((item, index) => ({
1202
1249
  ...item,