inibase 1.0.0-rc.39 → 1.0.0-rc.40

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/README.md CHANGED
@@ -519,9 +519,9 @@ await Promise.all(
519
519
 
520
520
  </details>
521
521
 
522
- ## Config
522
+ ## Config (.env)
523
523
 
524
- The `.env` file supports the following parameters (make sure to run command with flag --env-file=.env)
524
+ The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
525
525
 
526
526
  ```ini
527
527
  # Auto generated secret key, will be using for encrypting the IDs
@@ -529,6 +529,9 @@ INIBASE_SECRET=
529
529
 
530
530
  INIBASE_COMPRESSION=true
531
531
  INIBASE_CACHE=true
532
+
533
+ # Prepend new items to the beginning of file
534
+ INIBASE_REVERSE=true
532
535
  ```
533
536
 
534
537
  ## License
package/dist/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export default class Config {
2
2
  static isCompressionEnabled: boolean;
3
3
  static isCacheEnabled: boolean;
4
+ static isReverseEnabled: boolean;
4
5
  }
package/dist/config.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export default class Config {
2
2
  static isCompressionEnabled = process.env.INIBASE_COMPRESSION === "true";
3
3
  static isCacheEnabled = process.env.INIBASE_CACHE === "true";
4
+ static isReverseEnabled = process.env.INIBASE_REVERSE === "true";
4
5
  }
package/dist/file.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import { ComparisonOperator, FieldType } from "./index.js";
3
- export declare const lock: (folderPath: string) => Promise<void>;
4
- export declare const unlock: (folderPath: string) => Promise<void>;
3
+ export declare const lock: (folderPath: string, prefix?: string) => Promise<void>;
4
+ export declare const unlock: (folderPath: string, prefix?: string) => Promise<void>;
5
5
  export declare const write: (filePath: string, data: any, disableCompression?: boolean) => Promise<void>;
6
6
  export declare const read: (filePath: string, disableCompression?: boolean) => Promise<string>;
7
7
  /**
@@ -170,7 +170,7 @@ export default class File {
170
170
  static count: (filePath: string) => Promise<number>;
171
171
  static write: (filePath: string, data: any, disableCompression?: boolean) => Promise<void>;
172
172
  static read: (filePath: string, disableCompression?: boolean) => Promise<string>;
173
- static lock: (folderPath: string) => Promise<void>;
174
- static unlock: (folderPath: string) => Promise<void>;
173
+ static lock: (folderPath: string, prefix?: string | undefined) => Promise<void>;
174
+ static unlock: (folderPath: string, prefix?: string | undefined) => Promise<void>;
175
175
  static createWorker: typeof createWorker;
176
176
  }
package/dist/file.js CHANGED
@@ -1,4 +1,4 @@
1
- import { open, access, writeFile, readFile, constants as fsConstants, unlink, } from "node:fs/promises";
1
+ import { open, access, writeFile, readFile, constants as fsConstants, unlink, copyFile, appendFile, } from "node:fs/promises";
2
2
  import { createInterface } from "node:readline";
3
3
  import { Transform } from "node:stream";
4
4
  import { pipeline } from "node:stream/promises";
@@ -11,23 +11,23 @@ import { encodeID, comparePassword } from "./utils.server.js";
11
11
  import Config from "./config.js";
12
12
  const gzip = promisify(gzipAsync);
13
13
  const gunzip = promisify(gunzipAsync);
14
- export const lock = async (folderPath) => {
15
- let lockFile, lockFilePath = join(folderPath, "locked.inib");
14
+ export const lock = async (folderPath, prefix) => {
15
+ let lockFile, lockFilePath = join(folderPath, `${prefix ?? ""}.locked`);
16
16
  try {
17
17
  lockFile = await open(lockFilePath, "wx");
18
18
  return;
19
19
  }
20
20
  catch ({ message }) {
21
21
  if (message.split(":")[0] === "EEXIST")
22
- return await new Promise((resolve, reject) => setTimeout(() => resolve(lock(folderPath)), 13));
22
+ return await new Promise((resolve, reject) => setTimeout(() => resolve(lock(folderPath, prefix)), 13));
23
23
  }
24
24
  finally {
25
25
  await lockFile?.close();
26
26
  }
27
27
  };
28
- export const unlock = async (folderPath) => {
28
+ export const unlock = async (folderPath, prefix) => {
29
29
  try {
30
- await unlink(join(folderPath, "locked.inib"));
30
+ await unlink(join(folderPath, `${prefix ?? ""}.locked`));
31
31
  }
32
32
  catch { }
33
33
  };
@@ -184,7 +184,10 @@ const unSecureString = (input) => {
184
184
  "\\r": "\r",
185
185
  };
186
186
  // Replace encoded characters with their original symbols using the defined mapping.
187
- return (input.replace(/%(2C|7C|26|24|23|40|5E|3A|21|3B|\\n|\\r)/g, (match) => replacements[match]) || null);
187
+ const decodedString = input.replace(/%(2C|7C|26|24|23|40|5E|3A|21|3B|\\n|\\r)/g, (match) => replacements[match]) || null;
188
+ if (decodedString === null)
189
+ return null;
190
+ return isNumber(decodedString) ? Number(decodedString) : decodedString;
188
191
  };
189
192
  /**
190
193
  * Reverses the process of 'joinMultidimensionalArray', splitting a string back into a multidimensional array.
@@ -370,29 +373,35 @@ export const replace = async (filePath, replacements) => {
370
373
  export const append = async (filePath, data) => {
371
374
  const fileTempPath = filePath.replace(/([^/]+)\/?$/, `.tmp/$1`);
372
375
  if (await isExists(filePath)) {
373
- let fileHandle, fileTempHandle, rl;
374
- try {
375
- fileHandle = await open(filePath, "r");
376
- fileTempHandle = await open(fileTempPath, "w");
377
- rl = readLineInternface(fileHandle);
378
- let isAppended = false;
379
- await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
380
- transform(line, encoding, callback) {
381
- if (!isAppended) {
382
- isAppended = true;
383
- return callback(null, `${Array.isArray(data) ? data.join("\n") : data}\n` +
384
- (line.length ? `${line}\n` : ""));
385
- }
386
- else
387
- return callback(null, `${line}\n`);
388
- },
389
- }));
376
+ if (!Config.isReverseEnabled && !Config.isCompressionEnabled) {
377
+ await copyFile(filePath, fileTempPath);
378
+ await appendFile(fileTempPath, `${Array.isArray(data) ? data.join("\n") : data}\n`);
390
379
  }
391
- finally {
392
- // Ensure that file handles are closed, even if an error occurred
393
- rl?.close();
394
- await fileHandle?.close();
395
- await fileTempHandle?.close();
380
+ else {
381
+ let fileHandle, fileTempHandle, rl;
382
+ try {
383
+ fileHandle = await open(filePath, "r");
384
+ fileTempHandle = await open(fileTempPath, "w");
385
+ rl = readLineInternface(fileHandle);
386
+ let isAppended = false;
387
+ await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
388
+ transform(line, encoding, callback) {
389
+ if (!isAppended) {
390
+ isAppended = true;
391
+ return callback(null, `${Array.isArray(data) ? data.join("\n") : data}\n` +
392
+ (line.length ? `${line}\n` : ""));
393
+ }
394
+ else
395
+ return callback(null, `${line}\n`);
396
+ },
397
+ }));
398
+ }
399
+ finally {
400
+ // Ensure that file handles are closed, even if an error occurred
401
+ rl?.close();
402
+ await fileHandle?.close();
403
+ await fileTempHandle?.close();
404
+ }
396
405
  }
397
406
  }
398
407
  else
package/dist/index.d.ts CHANGED
@@ -57,6 +57,8 @@ declare global {
57
57
  entries<T extends object>(o: T): Entries<T>;
58
58
  }
59
59
  }
60
+ export type ErrorCodes = "FIELD_REQUIRED" | "NO_SCHEMA" | "NO_ITEMS" | "NO_RESULTS" | "INVALID_ID" | "INVALID_TYPE" | "INVALID_PARAMETERS";
61
+ export type ErrorLang = "en";
60
62
  export default class Inibase {
61
63
  folder: string;
62
64
  database: string;
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import File from "./file.js";
7
7
  import Utils from "./utils.js";
8
8
  import UtilsServer from "./utils.server.js";
9
9
  import Config from "./config.js";
10
+ import { inspect } from "node:util";
10
11
  export default class Inibase {
11
12
  folder;
12
13
  database;
@@ -32,27 +33,24 @@ export default class Inibase {
32
33
  throwError(code, variable, language = "en") {
33
34
  const errorMessages = {
34
35
  en: {
35
- FIELD_REQUIRED: "REQUIRED: {variable}",
36
- NO_SCHEMA: "NO_SCHEMA: {variable}",
37
- NO_ITEMS: "NO_ITEMS: {variable}",
38
- NO_DATA: "NO_DATA: {variable}",
39
- INVALID_ID: "INVALID_ID: {variable}",
40
- INVALID_TYPE: "INVALID_TYPE: {variable}",
41
- INVALID_OPERATOR: "INVALID_OPERATOR: {variable}",
42
- INVALID_PARAMETERS: "PARAMETERS: {variable}",
36
+ FIELD_REQUIRED: "Field {variable} is required",
37
+ NO_SCHEMA: "Table {variable} does't have a schema",
38
+ NO_ITEMS: "Table {variable} is empty",
39
+ NO_RESULTS: "No results found for table {variable}",
40
+ INVALID_ID: "The given ID(s) is/are not valid(s)",
41
+ INVALID_TYPE: "Expect {variable} to be {variable}, got {variable} instead",
42
+ INVALID_PARAMETERS: "The given parameters are not valid",
43
43
  },
44
44
  // Add more languages and error messages as needed
45
45
  };
46
- let errorMessage = errorMessages[language][code] || code;
47
- if (variable) {
48
- if (typeof variable === "string" ||
49
- typeof variable === "number" ||
50
- Array.isArray(variable))
51
- errorMessage = errorMessage.replaceAll(`{variable}`, Array.isArray(variable) ? variable.join(", ") : variable);
52
- else
53
- Object.keys(variable).forEach((variableKey) => (errorMessage = errorMessage.replaceAll(`{${variableKey}}`, variable[variableKey].toString())));
54
- }
55
- return new Error(errorMessage);
46
+ let errorMessage = errorMessages[language][code];
47
+ if (!errorMessage)
48
+ return new Error("ERR");
49
+ return new Error(variable
50
+ ? Array.isArray(variable)
51
+ ? errorMessage.replace(/\{variable\}/g, () => variable.shift()?.toString() ?? "")
52
+ : errorMessage.replaceAll(`{variable}`, `'${variable.toString()}'`)
53
+ : errorMessage.replaceAll(`{variable}`, ""));
56
54
  }
57
55
  async createWorker(functionName, arg) {
58
56
  return new Promise((resolve, reject) => {
@@ -105,6 +103,8 @@ export default class Inibase {
105
103
  await mkdir(tablePath, { recursive: true });
106
104
  if (!(await File.isExists(join(tablePath, ".tmp"))))
107
105
  await mkdir(join(tablePath, ".tmp"));
106
+ if (!(await File.isExists(join(tablePath, ".cache"))))
107
+ await mkdir(join(tablePath, ".cache"));
108
108
  if (await File.isExists(tableSchemaPath)) {
109
109
  // update columns files names based on field id
110
110
  const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath((await this.getTableSchema(tableName)) ?? []), this._schemaToIdsPath(schema));
@@ -180,7 +180,11 @@ export default class Inibase {
180
180
  !Utils.isArrayOfObjects(field.children)
181
181
  ? field.children
182
182
  : undefined))
183
- throw this.throwError("INVALID_TYPE", [field.key, field.type]);
183
+ throw this.throwError("INVALID_TYPE", [
184
+ field.key,
185
+ field.type,
186
+ typeof data[field.key],
187
+ ]);
184
188
  if ((field.type === "array" || field.type === "object") &&
185
189
  field.children &&
186
190
  Utils.isArrayOfObjects(field.children))
@@ -642,9 +646,9 @@ export default class Inibase {
642
646
  .filter((i) => i);
643
647
  }
644
648
  async clearCache(tablePath) {
645
- await Promise.all((await readdir(join(tablePath, ".tmp")))
646
- ?.filter((fileName) => !["pagination.inib", "locked.inib"].includes(fileName))
647
- .map(async (file) => unlink(join(tablePath, ".tmp", file))));
649
+ await Promise.all((await readdir(join(tablePath, ".cache")))
650
+ ?.filter((fileName) => fileName !== "pagination.inib")
651
+ .map(async (file) => unlink(join(tablePath, ".cache", file))));
648
652
  }
649
653
  async get(tableName, where, options = {
650
654
  page: 1,
@@ -676,15 +680,15 @@ export default class Inibase {
676
680
  index +
677
681
  1), options));
678
682
  if (Config.isCacheEnabled &&
679
- (await File.isExists(join(tablePath, ".tmp", "pagination.inib"))))
680
- this.totalItems[tableName + "-*"] = Number((await File.read(join(tablePath, ".tmp", "pagination.inib"), true)).split(",")[1]);
683
+ (await File.isExists(join(tablePath, ".cache", "pagination.inib"))))
684
+ this.totalItems[tableName + "-*"] = Number((await File.read(join(tablePath, ".cache", "pagination.inib"), true)).split(",")[1]);
681
685
  else {
682
686
  let [lastId, totalItems] = await File.get(join(tablePath, "id.inib"), -1, "number", undefined, this.salt, true);
683
687
  if (lastId)
684
688
  lastId = Number(Object.keys(lastId)[0] ?? 0);
685
689
  this.totalItems[tableName + "-*"] = totalItems;
686
690
  if (Config.isCacheEnabled)
687
- await File.write(join(tablePath, ".tmp", "pagination.inib"), `${lastId},${totalItems}`, true);
691
+ await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems}`, true);
688
692
  }
689
693
  }
690
694
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -708,7 +712,7 @@ export default class Inibase {
708
712
  Ids = [Ids];
709
713
  const [lineNumbers, countItems] = await File.search(join(tablePath, "id.inib"), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[tableName + "-*"], this.salt);
710
714
  if (!lineNumbers)
711
- throw this.throwError("INVALID_ID", where);
715
+ throw this.throwError("NO_RESULTS", tableName);
712
716
  if (onlyLinesNumbers)
713
717
  return Object.keys(lineNumbers).length
714
718
  ? Object.keys(lineNumbers).map(Number)
@@ -723,7 +727,7 @@ export default class Inibase {
723
727
  let cachedFilePath = "";
724
728
  // Criteria
725
729
  if (Config.isCacheEnabled)
726
- cachedFilePath = join(tablePath, ".tmp", `${UtilsServer.hashObject(where)}.inib`);
730
+ cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}.inib`);
727
731
  if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
728
732
  const cachedItems = (await File.read(cachedFilePath, true)).split(",");
729
733
  this.totalItems[tableName + "-*"] = cachedItems.length;
@@ -775,13 +779,14 @@ export default class Inibase {
775
779
  let RETURN;
776
780
  if (!schema)
777
781
  throw this.throwError("NO_SCHEMA", tableName);
782
+ const keys = UtilsServer.hashString(Object.keys(Array.isArray(data) ? data[0] : data).join("."));
778
783
  let lastId = 0, totalItems = 0, renameList = [];
779
784
  try {
780
- await File.lock(join(tablePath, ".tmp"));
785
+ await File.lock(join(tablePath, ".tmp"), keys);
781
786
  if (await File.isExists(join(tablePath, "id.inib"))) {
782
787
  if (Config.isCacheEnabled &&
783
- (await File.isExists(join(tablePath, ".tmp", "pagination.inib"))))
784
- [lastId, totalItems] = (await File.read(join(tablePath, ".tmp", "pagination.inib"), true))
788
+ (await File.isExists(join(tablePath, ".cache", "pagination.inib"))))
789
+ [lastId, totalItems] = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
785
790
  .split(",")
786
791
  .map(Number);
787
792
  else {
@@ -803,10 +808,12 @@ export default class Inibase {
803
808
  ...rest,
804
809
  createdAt: Date.now(),
805
810
  }))(data);
806
- if (!RETURN)
807
- throw this.throwError("NO_DATA");
808
811
  RETURN = this.formatData(RETURN, schema);
809
- const pathesContents = this.joinPathesContents(join(tablePath), Array.isArray(RETURN) ? RETURN.toReversed() : RETURN);
812
+ const pathesContents = this.joinPathesContents(tablePath, Config.isReverseEnabled
813
+ ? Array.isArray(RETURN)
814
+ ? RETURN.toReversed()
815
+ : RETURN
816
+ : RETURN);
810
817
  await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
811
818
  ? await File.createWorker("append", [path, content])
812
819
  : await File.append(path, content))));
@@ -814,7 +821,7 @@ export default class Inibase {
814
821
  renameList = [];
815
822
  if (Config.isCacheEnabled) {
816
823
  await this.clearCache(tablePath);
817
- await File.write(join(tablePath, ".tmp", "pagination.inib"), `${lastId},${totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)}`, true);
824
+ await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems + (Array.isArray(RETURN) ? RETURN.length : 1)}`, true);
818
825
  }
819
826
  if (returnPostedData)
820
827
  return this.get(tableName, Array.isArray(RETURN) ? RETURN.map((_, index) => index + 1) : 1, options, !Utils.isArrayOfObjects(data), // return only one item if data is not array of objects
@@ -823,7 +830,7 @@ export default class Inibase {
823
830
  finally {
824
831
  if (renameList.length)
825
832
  await Promise.allSettled(renameList.map(async ([tempPath, _]) => unlink(tempPath)));
826
- await File.unlock(join(tablePath, ".tmp"));
833
+ await File.unlock(join(tablePath, ".tmp"), keys);
827
834
  }
828
835
  }
829
836
  async put(tableName, data, where, options = {
@@ -852,23 +859,33 @@ export default class Inibase {
852
859
  return this.put(tableName, data, UtilsServer.decodeID(data.id, this.salt));
853
860
  }
854
861
  else {
855
- const pathesContents = this.joinPathesContents(join(tablePath), Utils.isArrayOfObjects(data)
856
- ? data.map((item) => ({
857
- ...(({ id, ...restOfData }) => restOfData)(item),
858
- updatedAt: Date.now(),
859
- }))
860
- : {
861
- ...(({ id, ...restOfData }) => restOfData)(data),
862
- updatedAt: Date.now(),
863
- });
862
+ let totalItems;
863
+ if (Config.isCacheEnabled &&
864
+ (await File.isExists(join(tablePath, ".cache", "pagination.inib"))))
865
+ totalItems = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
866
+ .split(",")
867
+ .map(Number)[1];
868
+ else
869
+ totalItems = await File.count(join(tablePath, "id.inib"));
870
+ const pathesContents = this.joinPathesContents(tablePath, {
871
+ ...(({ id, ...restOfData }) => restOfData)(data),
872
+ updatedAt: Date.now(),
873
+ });
864
874
  try {
865
875
  await File.lock(join(tablePath, ".tmp"));
866
876
  await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
867
- ? await File.createWorker("replace", [path, content])
868
- : await File.replace(path, content))));
877
+ ? await File.createWorker("replace", [
878
+ path,
879
+ Utils.combineObjects([...Array(totalItems)].map((_, i) => ({
880
+ [`${i + 1}`]: content,
881
+ }))),
882
+ ])
883
+ : await File.replace(path, Utils.combineObjects([...Array(totalItems)].map((_, i) => ({
884
+ [`${i + 1}`]: content,
885
+ })))))));
869
886
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
870
887
  if (Config.isCacheEnabled)
871
- await this.clearCache(join(tablePath, ".tmp"));
888
+ await this.clearCache(join(tablePath, ".cache"));
872
889
  if (returnPostedData)
873
890
  return this.get(tableName, where, options, undefined, undefined, schema);
874
891
  }
@@ -891,7 +908,7 @@ export default class Inibase {
891
908
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
892
909
  Utils.isNumber(where)) {
893
910
  // "where" in this case, is the line(s) number(s) and not id(s)
894
- const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(join(tablePath), Utils.isArrayOfObjects(data)
911
+ const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(tablePath, Utils.isArrayOfObjects(data)
895
912
  ? data.map((item) => ({
896
913
  ...item,
897
914
  updatedAt: Date.now(),
@@ -903,8 +920,11 @@ export default class Inibase {
903
920
  [lineNum]: Array.isArray(content) ? content[index] : content,
904
921
  }), {}),
905
922
  ]));
923
+ const keys = UtilsServer.hashString(Object.keys(pathesContents)
924
+ .map((path) => path.replaceAll(".inib", ""))
925
+ .join("."));
906
926
  try {
907
- await File.lock(join(tablePath, ".tmp"));
927
+ await File.lock(join(tablePath, ".tmp"), keys);
908
928
  await Promise.all(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.isThreadEnabled
909
929
  ? await File.createWorker("replace", [path, content])
910
930
  : await File.replace(path, content))));
@@ -918,18 +938,18 @@ export default class Inibase {
918
938
  finally {
919
939
  if (renameList.length)
920
940
  await Promise.allSettled(renameList.map(async ([tempPath, _]) => unlink(tempPath)));
921
- await File.unlock(join(tablePath, ".tmp"));
941
+ await File.unlock(join(tablePath, ".tmp"), keys);
922
942
  }
923
943
  }
924
944
  }
925
945
  else if (Utils.isObject(where)) {
926
946
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
927
947
  if (!lineNumbers || !lineNumbers.length)
928
- throw this.throwError("NO_ITEMS", tableName);
948
+ throw this.throwError("NO_RESULTS", tableName);
929
949
  return this.put(tableName, data, lineNumbers);
930
950
  }
931
951
  else
932
- throw this.throwError("INVALID_PARAMETERS", tableName);
952
+ throw this.throwError("INVALID_PARAMETERS");
933
953
  }
934
954
  async delete(tableName, where, _id) {
935
955
  let renameList = [];
@@ -942,7 +962,7 @@ export default class Inibase {
942
962
  if (!where) {
943
963
  try {
944
964
  await File.lock(join(tablePath, ".tmp"));
945
- await Promise.all((await readdir(join(tablePath)))
965
+ await Promise.all((await readdir(tablePath))
946
966
  ?.filter((fileName) => fileName.endsWith(".inib"))
947
967
  .map(async (file) => unlink(join(tablePath, file))));
948
968
  if (Config.isCacheEnabled)
@@ -967,12 +987,12 @@ export default class Inibase {
967
987
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
968
988
  Utils.isNumber(where)) {
969
989
  // "where" in this case, is the line(s) number(s) and not id(s)
970
- const files = (await readdir(join(tablePath)))?.filter((fileName) => fileName.endsWith(".inib"));
990
+ const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(".inib"));
971
991
  if (files.length) {
972
992
  if (!_id)
973
993
  _id = Object.entries((await File.get(join(tablePath, "id.inib"), where, "number", undefined, this.salt)) ?? {}).map(([_key, id]) => UtilsServer.encodeID(Number(id), this.salt));
974
994
  if (!_id.length)
975
- throw this.throwError("NO_ITEMS", tableName);
995
+ throw this.throwError("NO_RESULTS", tableName);
976
996
  try {
977
997
  await File.lock(join(tablePath, ".tmp"));
978
998
  await Promise.all(files.map(async (file) => renameList.push(this.isThreadEnabled
@@ -984,11 +1004,11 @@ export default class Inibase {
984
1004
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
985
1005
  if (Config.isCacheEnabled) {
986
1006
  await this.clearCache(tablePath);
987
- if (await File.isExists(join(tablePath, ".tmp", "pagination.inib"))) {
988
- let [lastId, totalItems] = (await File.read(join(tablePath, ".tmp", "pagination.inib"), true))
1007
+ if (await File.isExists(join(tablePath, ".cache", "pagination.inib"))) {
1008
+ let [lastId, totalItems] = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
989
1009
  .split(",")
990
1010
  .map(Number);
991
- await File.write(join(tablePath, ".tmp", "pagination.inib"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
1011
+ await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
992
1012
  }
993
1013
  }
994
1014
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
@@ -1004,11 +1024,11 @@ export default class Inibase {
1004
1024
  else if (Utils.isObject(where)) {
1005
1025
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
1006
1026
  if (!lineNumbers || !lineNumbers.length)
1007
- throw this.throwError("NO_ITEMS", tableName);
1027
+ throw this.throwError("NO_RESULTS", tableName);
1008
1028
  return this.delete(tableName, lineNumbers);
1009
1029
  }
1010
1030
  else
1011
- throw this.throwError("INVALID_PARAMETERS", tableName);
1031
+ throw this.throwError("INVALID_PARAMETERS");
1012
1032
  return null;
1013
1033
  }
1014
1034
  async sum(tableName, columns, where) {
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type FieldType, ComparisonOperator } from "./index.js";
1
+ import type { FieldType, ComparisonOperator } from "./index.js";
2
2
  /**
3
3
  * Type guard function to check if the input is an array of objects.
4
4
  *
@@ -70,7 +70,7 @@ export declare const addIdToSchema: (schema: Schema, oldIndex: number | undefine
70
70
  type: "array";
71
71
  children: "string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip" | Schema | ("string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip")[];
72
72
  }))[];
73
- export declare const hashObject: (obj: any) => string;
73
+ export declare const hashString: (str: string) => string;
74
74
  export default class UtilsServer {
75
75
  static encodeID: (id: string | number, secretKeyOrSalt: string | number | Buffer) => string;
76
76
  static decodeID: (input: string, secretKeyOrSalt: string | number | Buffer) => number;
@@ -99,5 +99,5 @@ export default class UtilsServer {
99
99
  type: "array";
100
100
  children: "string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip" | Schema | ("string" | "number" | "boolean" | "object" | "id" | "url" | "html" | "table" | "email" | "json" | "date" | "password" | "ip")[];
101
101
  }))[];
102
- static hashObject: (obj: any) => string;
102
+ static hashString: (str: string) => string;
103
103
  }
@@ -110,35 +110,7 @@ export const addIdToSchema = (schema, oldIndex = 0, secretKeyOrSalt, encodeIDs)
110
110
  }
111
111
  return field;
112
112
  });
113
- function sortObject(obj) {
114
- if (typeof obj !== "object" || obj === null)
115
- return obj;
116
- if (Array.isArray(obj))
117
- return obj.toSorted().map(sortObject);
118
- const sorted = {};
119
- const keys = Object.keys(obj).sort();
120
- const length = keys.length;
121
- for (let i = 0; i < length; i++) {
122
- const key = keys[i];
123
- sorted[key] = sortObject(obj[key]);
124
- }
125
- return sorted;
126
- }
127
- function stringifyObject(obj) {
128
- if (typeof obj !== "object" || obj === null)
129
- return String(obj);
130
- if (Array.isArray(obj))
131
- return "[" + obj.map((value) => stringifyObject(value)).join(",") + "]";
132
- return ("{" +
133
- Object.keys(obj)
134
- .toSorted()
135
- .map((key) => `${key}:${stringifyObject(obj[key])}`)
136
- .join(",") +
137
- "}");
138
- }
139
- export const hashObject = (obj) => createHash("sha256")
140
- .update(stringifyObject(sortObject(obj)))
141
- .digest("hex");
113
+ export const hashString = (str) => createHash("sha256").update(str).digest("hex");
142
114
  export default class UtilsServer {
143
115
  static encodeID = encodeID;
144
116
  static decodeID = decodeID;
@@ -146,5 +118,5 @@ export default class UtilsServer {
146
118
  static comparePassword = comparePassword;
147
119
  static findLastIdNumber = findLastIdNumber;
148
120
  static addIdToSchema = addIdToSchema;
149
- static hashObject = hashObject;
121
+ static hashString = hashString;
150
122
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.39",
3
+ "version": "1.0.0-rc.40",
4
4
  "author": {
5
5
  "name": "Karim Amahtil",
6
6
  "email": "karim.amahtil@gmail.com"
@@ -75,12 +75,14 @@
75
75
  },
76
76
  "devDependencies": {
77
77
  "@types/node": "^20.10.6",
78
+ "tinybench": "^2.6.0",
78
79
  "typescript": "^5.3.3"
79
80
  },
80
81
  "scripts": {
81
82
  "build": "npx tsc",
82
83
  "test": "npx tsx watch --expose-gc --env-file=.env ./index.test",
83
- "benchmark:single": "npx tsx watch --expose-gc --env-file=.env ./benchmark/single",
84
- "benchmark:bulk": "npx tsx watch --expose-gc --env-file=.env ./benchmark/bulk"
84
+ "benchmark": "npx tsx watch --env-file=.env ./benchmark/index",
85
+ "benchmark:single": "npx tsx --expose-gc --env-file=.env ./benchmark/single",
86
+ "benchmark:bulk": "npx tsx --expose-gc --env-file=.env ./benchmark/bulk"
85
87
  }
86
88
  }