inibase 1.2.9 → 1.2.11

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.d.ts CHANGED
@@ -122,6 +122,7 @@ export default class Inibase {
122
122
  private processObjectField;
123
123
  private isTableField;
124
124
  private processTableField;
125
+ private _setNestedKey;
125
126
  private applyCriteria;
126
127
  private _filterSchemaByColumns;
127
128
  /**
package/dist/index.js CHANGED
@@ -696,19 +696,19 @@ export default class Inibase {
696
696
  for (const field of schema) {
697
697
  // If the field is of simple type (non-recursive), process it directly
698
698
  if (this.isSimpleField(field.type)) {
699
- await this.processSimpleField(tableName, field, linesNumber, RETURN, options, prefix);
699
+ await this.processSimpleField(tableName, field, RETURN, linesNumber, prefix);
700
700
  }
701
701
  else if (this.isArrayField(field.type)) {
702
702
  // Process array fields (recursive if needed)
703
- await this.processArrayField(tableName, field, linesNumber, RETURN, options, prefix);
703
+ await this.processArrayField(tableName, field, RETURN, linesNumber, options, prefix);
704
704
  }
705
705
  else if (this.isObjectField(field.type)) {
706
706
  // Process object fields (recursive if needed)
707
- await this.processObjectField(tableName, field, linesNumber, RETURN, options, prefix);
707
+ await this.processObjectField(tableName, field, RETURN, linesNumber, options, prefix);
708
708
  }
709
709
  else if (this.isTableField(field.type)) {
710
710
  // Process table reference fields
711
- await this.processTableField(tableName, field, linesNumber, RETURN, options, prefix);
711
+ await this.processTableField(tableName, field, RETURN, linesNumber, options, prefix);
712
712
  }
713
713
  }
714
714
  return RETURN;
@@ -721,7 +721,7 @@ export default class Inibase {
721
721
  return !complexTypes.includes(fieldType);
722
722
  }
723
723
  // Process a simple field (non-recursive)
724
- async processSimpleField(tableName, field, linesNumber, RETURN, _options, prefix) {
724
+ async processSimpleField(tableName, field, RETURN, linesNumber, prefix) {
725
725
  const fieldPath = join(this.databasePath, tableName, `${prefix ?? ""}${field.key}${this.getFileExtension(tableName)}`);
726
726
  if (await File.isExists(fieldPath)) {
727
727
  const items = await File.get(fieldPath, linesNumber, {
@@ -751,13 +751,13 @@ export default class Inibase {
751
751
  fieldType === "array");
752
752
  }
753
753
  // Process array fields (recursive if needed)
754
- async processArrayField(tableName, field, linesNumber, RETURN, options, prefix) {
754
+ async processArrayField(tableName, field, RETURN, linesNumber, options, prefix) {
755
755
  if (Array.isArray(field.children)) {
756
756
  if (this.isSimpleField(field.children)) {
757
- await this.processSimpleField(tableName, field, linesNumber, RETURN, options, prefix);
757
+ await this.processSimpleField(tableName, field, RETURN, linesNumber, prefix);
758
758
  }
759
759
  else if (this.isTableField(field.children)) {
760
- await this.processTableField(tableName, field, linesNumber, RETURN, options, prefix);
760
+ await this.processTableField(tableName, field, RETURN, linesNumber, options, prefix);
761
761
  }
762
762
  else {
763
763
  let _fieldChildren = field.children;
@@ -835,10 +835,10 @@ export default class Inibase {
835
835
  }
836
836
  else if (this.isSimpleField(field.children)) {
837
837
  // If `children` is FieldType, handle it as an array of simple types (no recursion needed here)
838
- await this.processSimpleField(tableName, field, linesNumber, RETURN, options, prefix);
838
+ await this.processSimpleField(tableName, field, RETURN, linesNumber, prefix);
839
839
  }
840
840
  else if (this.isTableField(field.children)) {
841
- await this.processTableField(tableName, field, linesNumber, RETURN, options, prefix);
841
+ await this.processTableField(tableName, field, RETURN, linesNumber, options, prefix);
842
842
  }
843
843
  }
844
844
  // Helper function to check if the field type is object
@@ -849,7 +849,7 @@ export default class Inibase {
849
849
  fieldType.includes("object")));
850
850
  }
851
851
  // Process object fields (recursive if needed)
852
- async processObjectField(tableName, field, linesNumber, RETURN, options, prefix) {
852
+ async processObjectField(tableName, field, RETURN, linesNumber, options, prefix) {
853
853
  if (Array.isArray(field.children)) {
854
854
  // If `children` is a Schema (array of Field objects), recurse
855
855
  const items = await this.processSchemaData(tableName, field.children, linesNumber, options, `${prefix ?? ""}${field.key}.`);
@@ -873,7 +873,7 @@ export default class Inibase {
873
873
  fieldType.includes("table")));
874
874
  }
875
875
  // Process table reference fields
876
- async processTableField(tableName, field, linesNumber, RETURN, options, prefix) {
876
+ async processTableField(tableName, field, RETURN, linesNumber, options, prefix) {
877
877
  if (field.table &&
878
878
  (await File.isExists(join(this.databasePath, field.table)))) {
879
879
  const fieldPath = join(this.databasePath, tableName, `${prefix ?? ""}${field.key}${this.getFileExtension(tableName)}`);
@@ -899,7 +899,7 @@ export default class Inibase {
899
899
  .flat()
900
900
  .filter((item) => item), {
901
901
  ...options,
902
- perPage: Number.POSITIVE_INFINITY,
902
+ perPage: -1,
903
903
  columns: options.columns
904
904
  ?.filter((column) => column.includes(`${field.key}.`))
905
905
  .map((column) => column.replace(`${field.key}.`, "")),
@@ -927,47 +927,49 @@ export default class Inibase {
927
927
  }
928
928
  }
929
929
  }
930
+ _setNestedKey(obj, path, value) {
931
+ const keys = path.split(".");
932
+ const lastKey = keys.pop();
933
+ const target = keys.reduce((acc, key) => {
934
+ if (typeof acc[key] !== "object" || acc[key] === null) {
935
+ acc[key] = {};
936
+ }
937
+ return acc[key];
938
+ }, obj);
939
+ target[lastKey] = value;
940
+ }
930
941
  async applyCriteria(tableName, options, criteria, allTrue, searchIn) {
931
942
  const tablePath = join(this.databasePath, tableName);
932
- /** result container – we mutate rows in-place instead of deep-merging */
933
- const RETURN = {};
934
- /* ---------- fast exit ---------- */
943
+ let RETURN = {};
935
944
  if (!criteria || Object.keys(criteria).length === 0)
936
945
  return null;
937
- /* ---------- split top-level logical groups ---------- */
938
946
  const criteriaAND = criteria.and;
939
- const criteriaOR = criteria.or;
940
947
  if (criteriaAND)
941
948
  delete criteria.and;
949
+ const criteriaOR = criteria.or;
942
950
  if (criteriaOR)
943
951
  delete criteria.or;
944
- /** cache table schema once – Utils.getField() is cheap but we call it a lot */
945
952
  const schema = globalConfig[this.databasePath].tables.get(tableName).schema;
946
- /* ---------- MAIN LOOP (top-level criteria only) ---------- */
947
953
  if (Object.keys(criteria).length) {
948
954
  if (allTrue === undefined)
949
955
  allTrue = true;
950
- for (const [key, rawValue] of Object.entries(criteria)) {
951
- /* bail early if no candidates left for an “AND” chain */
952
- if (allTrue && searchIn && searchIn.size === 0)
953
- return null;
956
+ for await (let [key, value] of Object.entries(criteria)) {
954
957
  const field = Utils.getField(key, schema);
955
958
  if (!field)
956
959
  continue;
957
- let value = rawValue;
958
- /* ----- resolve relational sub-queries ----- */
959
960
  if (field.table && Utils.isObject(value)) {
960
- const rel = await this.get(field.table, value, {
961
+ const items = await this.get(field.table, value, {
961
962
  columns: "id",
963
+ perPage: -1,
962
964
  });
963
- value = rel?.length ? `[]${rel.map(({ id }) => id)}` : undefined; // ⟵ no match → abort whole AND-chain
964
- if (value === undefined && allTrue)
965
+ if (items?.length)
966
+ value = `[]${items.map(({ id }) => id)}`;
967
+ else if (allTrue)
965
968
  return null;
966
969
  }
967
- /* ----- determine operator / compare value ----- */
968
- let searchOperator;
969
- let searchComparedAtValue;
970
- let searchLogicalOperator;
970
+ let searchOperator = undefined;
971
+ let searchComparedAtValue = undefined;
972
+ let searchLogicalOperator = undefined;
971
973
  if (Utils.isObject(value)) {
972
974
  /* nested object with .and / .or inside */
973
975
  const nestedAnd = value.and;
@@ -985,6 +987,7 @@ export default class Inibase {
985
987
  searchComparedAtValue = crit.map((c) => c[1]);
986
988
  searchLogicalOperator = logic;
987
989
  }
990
+ delete value[logic];
988
991
  }
989
992
  }
990
993
  else if (Array.isArray(value)) {
@@ -1006,45 +1009,53 @@ export default class Inibase {
1006
1009
  searchOperator = "=";
1007
1010
  searchComparedAtValue = value;
1008
1011
  }
1009
- /* ---------- disk search ---------- */
1010
- const [hitRows, totalLines, lineSet] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, searchIn, {
1012
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue, searchLogicalOperator, searchIn, {
1011
1013
  ...field,
1012
1014
  databasePath: this.databasePath,
1013
1015
  table: field.table ?? tableName,
1014
- }, options.perPage, (options.page - 1) * options.perPage +
1015
- (options.page > 1 ? 1 : 0), true);
1016
- if (!hitRows) {
1016
+ }, options.perPage < 0 ? undefined : options.perPage, options.perPage < 0
1017
+ ? undefined
1018
+ : (options.page - 1) * options.perPage +
1019
+ (options.page > 1 ? 1 : 0), true);
1020
+ if (!searchResult) {
1017
1021
  if (allTrue)
1018
- return null; // AND-chain broken
1019
- continue; // OR-chain: just skip
1020
- }
1021
- /* ---------- map rows into RETURN without deep-merge ---------- */
1022
- for (const [lnStr, val] of Object.entries(hitRows)) {
1023
- const ln = +lnStr;
1024
- const row = (RETURN[ln] ??= {});
1025
- row[key] = val;
1022
+ return null;
1023
+ continue;
1026
1024
  }
1025
+ const formatedSearchResult = Object.fromEntries(Object.entries(searchResult).map(([id, value]) => {
1026
+ const nestedObj = {};
1027
+ this._setNestedKey(nestedObj, key, value);
1028
+ return [id, nestedObj];
1029
+ }));
1030
+ RETURN = allTrue
1031
+ ? formatedSearchResult
1032
+ : Utils.deepMerge(RETURN, formatedSearchResult);
1027
1033
  this.totalItems.set(`${tableName}-${key}`, totalLines);
1028
- /* shrink search domain for next AND condition */
1029
- if (lineSet?.size && allTrue)
1030
- searchIn = lineSet;
1034
+ if (linesNumbers?.size && allTrue)
1035
+ searchIn = linesNumbers;
1031
1036
  }
1032
1037
  }
1033
- /* ---------- process nested .and / .or recursively (unchanged) ---------- */
1034
1038
  if (criteriaAND && Utils.isObject(criteriaAND)) {
1035
- const res = await this.applyCriteria(tableName, options, criteriaAND, true, searchIn);
1036
- if (!res)
1039
+ const searchResult = await this.applyCriteria(tableName, options, criteriaAND, true, searchIn);
1040
+ if (searchResult)
1041
+ RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).filter((key) => Object.keys(criteriaAND).includes(key)).length)));
1042
+ else
1037
1043
  return null;
1038
- for (const [lnStr, data] of Object.entries(res))
1039
- (RETURN[+lnStr] ??= {}), Object.assign(RETURN[+lnStr], data);
1040
1044
  }
1041
1045
  if (criteriaOR && Utils.isObject(criteriaOR)) {
1042
- const res = await this.applyCriteria(tableName, options, criteriaOR, false, undefined);
1043
- if (res)
1044
- for (const [lnStr, data] of Object.entries(res))
1045
- (RETURN[+lnStr] ??= {}), Object.assign(RETURN[+lnStr], data);
1046
+ const searchResult = await this.applyCriteria(tableName, options, criteriaOR, false, searchIn);
1047
+ if (searchResult) {
1048
+ RETURN = Utils.deepMerge(RETURN, searchResult);
1049
+ if (!Object.keys(RETURN).length)
1050
+ RETURN = {};
1051
+ RETURN = Object.fromEntries(Object.entries(RETURN).filter(([_index, item]) => Object.keys(item).filter((key) => Object.keys(criteriaOR).includes(key) ||
1052
+ Object.keys(criteriaOR).some((criteriaKey) => criteriaKey.startsWith(`${key}.`))).length));
1053
+ if (!Object.keys(RETURN).length)
1054
+ RETURN = {};
1055
+ }
1056
+ else
1057
+ RETURN = {};
1046
1058
  }
1047
- /* ---------- final answer ---------- */
1048
1059
  return Object.keys(RETURN).length ? RETURN : null;
1049
1060
  }
1050
1061
  _filterSchemaByColumns(schema, columns) {
@@ -1214,9 +1225,11 @@ export default class Inibase {
1214
1225
  }
1215
1226
  if (!where) {
1216
1227
  // Display all data
1217
- RETURN = Object.values(await this.processSchemaData(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
1218
- index +
1219
- 1), options));
1228
+ RETURN = Object.values(await this.processSchemaData(tableName, schema, options.perPage < 0
1229
+ ? undefined
1230
+ : Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
1231
+ index +
1232
+ 1), options));
1220
1233
  if (!this.totalItems.has(`${tableName}-*`))
1221
1234
  this.totalItems.set(`${tableName}-*`, pagination[1]);
1222
1235
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.2.9",
3
+ "version": "1.2.11",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",