inibase 1.0.0-rc.106 → 1.0.0-rc.108

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
@@ -667,21 +667,22 @@ await db.get("user", undefined, { sort: {age: -1, username: "asc"} });
667
667
 
668
668
  ### Bulk
669
669
 
670
- | | 10 | 100 | 1000 |
671
- |--------|-----------------|-----------------|-----------------|
672
- | POST | 11 ms (0.65 mb) | 19 ms (1.00 mb) | 85 ms (4.58 mb) |
673
- | GET | 14 ms (2.77 mb) | 12 ms (3.16 mb) | 34 ms (1.38 mb) |
674
- | PUT | 6 ms (1.11 mb) | 5 ms (1.37 mb) | 10 ms (1.12 mb) |
675
- | DELETE | 17 ms (1.68 mb) | 14 ms (5.45 mb) | 25 ms (5.94 mb) |
670
+ | | 10 | 100 | 1000 |
671
+ |--------|-------------------|-------------------|-------------------|
672
+ | POST | 11 ms (0.66 mb) | 5 ms (1.02 mb) | 24 ms (1.44 mb) |
673
+ | GET | 29 ms (2.86 mb) | 24 ms (2.81 mb) | 36 ms (0.89 mb) |
674
+ | PUT | 21 ms (2.68 mb) | 16 ms (2.90 mb) | 12 ms (0.63 mb) |
675
+ | DELETE | 14 ms (0.82 mb) | 13 ms (0.84 mb) | 2 ms (0.17 mb) |
676
+
676
677
 
677
678
  ### Single
678
679
 
679
- | | 10 | 100 | 1000 |
680
- |--------|-------------------|--------------------|--------------------|
681
- | POST | 43 ms (4.70 mb) | 387 ms (6.36 mb) | 5341 ms (24.73 mb) |
682
- | GET | 99 ms (12.51 mb) | 846 ms (30.68 mb) | 7103 ms (30.86 mb) |
683
- | PUT | 33 ms (10.29 mb) | 312 ms (11.06 mb) | 3539 ms (14.87 mb) |
684
- | DELETE | 134 ms (13.50 mb) | 1224 ms (16.57 mb) | 7339 ms (11.46 mb) |
680
+ | | 10 | 100 | 1000 |
681
+ |--------|---------------------|--------------------|--------------------|
682
+ | POST | 45 ms (1.07 mb) | 12 ms (0.52 mb) | 11 ms (0.37 mb) |
683
+ | GET | 200 ms (2.15 mb) | 192 ms (2.72 mb) | 190 ms (2.31 mb) |
684
+ | PUT | 49 ms (3.22 mb) | 17 ms (2.98 mb) | 17 ms (3.06 mb) |
685
+ | DELETE | 118 ms (0.59 mb) | 113 ms (0.51 mb) | 103 ms (3.14 mb) |
685
686
 
686
687
  > Default testing uses a table with username, email, and password fields, ensuring password encryption is included in the process<br>
687
688
  > To run benchmarks, install _typescript_ & _[tsx](https://github.com/privatenumber/tsx)_ globally and run `benchmark` by default bulk, for single use `benchmark --single|-s`
package/dist/file.d.ts CHANGED
@@ -108,15 +108,6 @@ export declare const remove: (filePath: string, linesToDelete: number | number[]
108
108
  * Note: Decodes each line for comparison and can handle complex queries with multiple conditions.
109
109
  */
110
110
  export declare const search: (filePath: string, operator: ComparisonOperator | ComparisonOperator[], comparedAtValue: string | number | boolean | null | (string | number | boolean | null)[], logicalOperator?: "and" | "or", fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[] | Schema, limit?: number, offset?: number, readWholeFile?: boolean, secretKey?: string | Buffer) => Promise<[Record<number, string | number | boolean | null | (string | number | boolean | null)[]> | null, number, Set<number> | null]>;
111
- /**
112
- * Asynchronously counts the number of lines in a file.
113
- *
114
- * @param filePath - Path of the file to count lines in.
115
- * @returns Promise<number>. The number of lines in the file.
116
- *
117
- * Note: Reads through the file line by line to count the total number of lines.
118
- */
119
- export declare const count: (filePath: string) => Promise<number>;
120
111
  /**
121
112
  * Asynchronously calculates the sum of numerical values from specified lines in a file.
122
113
  *
package/dist/file.js CHANGED
@@ -176,7 +176,7 @@ export const decode = (input, fieldType, fieldChildrenType, secretKey) => {
176
176
  if (!fieldType)
177
177
  return null;
178
178
  if (input === null || input === "")
179
- return null;
179
+ return undefined;
180
180
  // Detect the fieldType based on the input and the provided array of possible types.
181
181
  if (Array.isArray(fieldType))
182
182
  fieldType = detectFieldType(String(input), fieldType);
@@ -187,6 +187,29 @@ export const decode = (input, fieldType, fieldChildrenType, secretKey) => {
187
187
  : unSecureString(input)
188
188
  : input, fieldType, fieldChildrenType, secretKey);
189
189
  };
190
+ function _groupIntoRanges(arr, action = "p") {
191
+ if (arr.length === 0)
192
+ return [];
193
+ arr.sort((a, b) => a - b); // Ensure the array is sorted
194
+ const ranges = [];
195
+ let start = arr[0];
196
+ let end = arr[0];
197
+ for (let i = 1; i < arr.length; i++) {
198
+ if (arr[i] === end + 1) {
199
+ // Continue the range
200
+ end = arr[i];
201
+ }
202
+ else {
203
+ // End the current range and start a new one
204
+ ranges.push(start === end ? `${start}` : `${start},${end}`);
205
+ start = arr[i];
206
+ end = arr[i];
207
+ }
208
+ }
209
+ // Push the last range
210
+ ranges.push(start === end ? `${start}` : `${start},${end}`);
211
+ return ranges.map((range) => `${range}${action}`).join(";");
212
+ }
190
213
  export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, secretKey, readWholeFile = false) {
191
214
  let fileHandle = null;
192
215
  try {
@@ -224,8 +247,8 @@ export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, s
224
247
  }
225
248
  const escapedFilePath = `${escapeShellPath(filePath)}`;
226
249
  const command = filePath.endsWith(".gz")
227
- ? `zcat "${escapedFilePath}" | sed -n '${lineNumbers.join("p;")}p'`
228
- : `sed -n '${lineNumbers.join("p;")}p' "${escapedFilePath}"`, foundedLines = (await exec(command)).stdout.trimEnd().split("\n");
250
+ ? `zcat "${escapedFilePath}" | sed -n '${_groupIntoRanges(lineNumbers)}'`
251
+ : `sed -n '${_groupIntoRanges(lineNumbers)}' "${escapedFilePath}"`, foundedLines = (await exec(command)).stdout.trimEnd().split("\n");
229
252
  let index = 0;
230
253
  for (const line of foundedLines) {
231
254
  lines[lineNumbers[index]] = decode(line, fieldType, fieldChildrenType, secretKey);
@@ -454,8 +477,8 @@ export const remove = async (filePath, linesToDelete) => {
454
477
  const escapedFilePath = `${escapeShellPath(filePath)}`;
455
478
  const escapedFileTempPath = `${escapeShellPath(fileTempPath)}`;
456
479
  const command = filePath.endsWith(".gz")
457
- ? `zcat ${escapedFilePath} | sed "${linesToDelete.join("d;")}d" | gzip > ${fileTempPath}`
458
- : `sed "${linesToDelete.join("d;")}d" ${escapedFilePath} > ${escapedFileTempPath}`;
480
+ ? `zcat ${escapedFilePath} | sed '${_groupIntoRanges(linesToDelete, "d")}' | gzip > ${fileTempPath}`
481
+ : `sed '${_groupIntoRanges(linesToDelete, "d")}' ${escapedFilePath} > ${escapedFileTempPath}`;
459
482
  await exec(command);
460
483
  return [fileTempPath, filePath];
461
484
  }
@@ -535,31 +558,6 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
535
558
  await fileHandle?.close();
536
559
  }
537
560
  };
538
- /**
539
- * Asynchronously counts the number of lines in a file.
540
- *
541
- * @param filePath - Path of the file to count lines in.
542
- * @returns Promise<number>. The number of lines in the file.
543
- *
544
- * Note: Reads through the file line by line to count the total number of lines.
545
- */
546
- export const count = async (filePath) => {
547
- // Number((await exec(`wc -l < ${filePath}`)).stdout.trimEnd());
548
- let linesCount = 0;
549
- if (await isExists(filePath)) {
550
- let fileHandle = null;
551
- try {
552
- fileHandle = await open(filePath, "r");
553
- const rl = createReadLineInternface(filePath, fileHandle);
554
- for await (const _ of rl)
555
- linesCount++;
556
- }
557
- finally {
558
- await fileHandle?.close();
559
- }
560
- }
561
- return linesCount;
562
- };
563
561
  /**
564
562
  * Asynchronously calculates the sum of numerical values from specified lines in a file.
565
563
  *
package/dist/index.d.ts CHANGED
@@ -100,8 +100,16 @@ export default class Inibase {
100
100
  private _combineObjectsToArray;
101
101
  private _CombineData;
102
102
  private joinPathesContents;
103
- private _getItemsFromSchemaHelper;
104
- private getItemsFromSchema;
103
+ private _processSchemaDataHelper;
104
+ private processSchemaData;
105
+ private isSimpleField;
106
+ private processSimpleField;
107
+ private isArrayField;
108
+ private processArrayField;
109
+ private isObjectField;
110
+ private processObjectField;
111
+ private isTableField;
112
+ private processTableField;
105
113
  private applyCriteria;
106
114
  private _filterSchemaByColumns;
107
115
  /**
package/dist/index.js CHANGED
@@ -142,7 +142,9 @@ export default class Inibase {
142
142
  let schemaIdFilePath;
143
143
  for await (const filePath of glob("*.schema", { cwd: this.databasePath }))
144
144
  schemaIdFilePath = filePath;
145
- const lastSchemaId = Number(parse(schemaIdFilePath).name);
145
+ const lastSchemaId = schemaIdFilePath
146
+ ? Number(parse(schemaIdFilePath).name)
147
+ : 0;
146
148
  if (await File.isExists(join(tablePath, "schema.json"))) {
147
149
  // update columns files names based on field id
148
150
  schema = UtilsServer.addIdToSchema(schema, lastSchemaId, this.salt);
@@ -158,7 +160,10 @@ export default class Inibase {
158
160
  else
159
161
  schema = UtilsServer.addIdToSchema(schema, lastSchemaId, this.salt);
160
162
  await writeFile(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2));
161
- await rename(schemaIdFilePath, join(tablePath, `${lastSchemaId}.schema`));
163
+ if (schemaIdFilePath)
164
+ await rename(schemaIdFilePath, join(tablePath, `${lastSchemaId}.schema`));
165
+ else
166
+ await writeFile(join(tablePath, `${lastSchemaId}.schema`), "");
162
167
  }
163
168
  if (config) {
164
169
  if (config.compression !== undefined &&
@@ -433,15 +438,6 @@ export default class Inibase {
433
438
  });
434
439
  switch (field.type) {
435
440
  case "array":
436
- return Utils.isArrayOfObjects(field.children)
437
- ? [
438
- this.getDefaultValue({
439
- ...field,
440
- type: "object",
441
- children: field.children,
442
- }),
443
- ]
444
- : null;
445
441
  case "object": {
446
442
  if (!field.children || !Utils.isArrayOfObjects(field.children))
447
443
  return null;
@@ -491,12 +487,14 @@ export default class Inibase {
491
487
  newCombinedData[join(tablePath, `${key}${this.getFileExtension(tableName)}`)] = value;
492
488
  return newCombinedData;
493
489
  }
494
- _getItemsFromSchemaHelper(RETURN, item, index, field) {
490
+ _processSchemaDataHelper(RETURN, item, index, field) {
491
+ // If the item is an object, we need to process its children
495
492
  if (Utils.isObject(item)) {
496
493
  if (!RETURN[index])
497
- RETURN[index] = {};
494
+ RETURN[index] = {}; // Ensure the index exists
498
495
  if (!RETURN[index][field.key])
499
496
  RETURN[index][field.key] = [];
497
+ // Process children fields (recursive if needed)
500
498
  for (const child_field of field.children.filter((children) => children.type === "array" &&
501
499
  Utils.isArrayOfObjects(children.children))) {
502
500
  if (Utils.isObject(item[child_field.key])) {
@@ -517,8 +515,8 @@ export default class Inibase {
517
515
  }
518
516
  else {
519
517
  value[_i].forEach((_element, _index) => {
520
- // Recursive call
521
- this._getItemsFromSchemaHelper(RETURN[index][field.key][_i][child_field.key][_index], _element, _index, child_field);
518
+ // Recursive call to handle nested structure
519
+ this._processSchemaDataHelper(RETURN, _element, _index, child_field);
522
520
  // Perform property assignments
523
521
  if (!RETURN[index][field.key][_i][child_field.key][_index])
524
522
  RETURN[index][field.key][_i][child_field.key][_index] = {};
@@ -532,175 +530,228 @@ export default class Inibase {
532
530
  }
533
531
  }
534
532
  }
535
- async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
536
- const tablePath = join(this.databasePath, tableName);
533
+ async processSchemaData(tableName, schema, linesNumber, options, prefix) {
537
534
  const RETURN = {};
538
- for await (const field of schema) {
539
- if ((field.type === "array" ||
540
- (Array.isArray(field.type) && field.type.includes("array"))) &&
541
- field.children) {
542
- if (Utils.isArrayOfObjects(field.children)) {
543
- if (field.children.filter((children) => children.type === "array" &&
544
- Utils.isArrayOfObjects(children.children)).length) {
545
- // one of children has array field type and has children array of object = Schema
546
- const childItems = await this.getItemsFromSchema(tableName, field.children.filter((children) => children.type === "array" &&
547
- Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`);
548
- if (childItems)
549
- for (const [index, item] of Object.entries(childItems))
550
- this._getItemsFromSchemaHelper(RETURN, item, index, field);
551
- field.children = field.children.filter((children) => children.type !== "array" ||
552
- !Utils.isArrayOfObjects(children.children));
553
- }
554
- const fieldItems = await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
555
- if (fieldItems)
556
- for (const [index, item] of Object.entries(fieldItems)) {
557
- if (!RETURN[index])
558
- RETURN[index] = {};
559
- if (Utils.isObject(item)) {
560
- if (!Utils.isArrayOfNulls(Object.values(item))) {
561
- if (RETURN[index][field.key])
562
- Object.entries(item).forEach(([key, value], _index) => {
563
- for (let _index = 0; _index < value.length; _index++)
564
- if (RETURN[index][field.key][_index])
565
- Object.assign(RETURN[index][field.key][_index], {
566
- [key]: value[_index],
567
- });
568
- else
569
- RETURN[index][field.key][_index] = {
570
- [key]: value[_index],
571
- };
572
- });
573
- else if (Object.values(item).every((_i) => Utils.isArrayOfArrays(_i) || Array.isArray(_i)) &&
574
- prefix)
575
- RETURN[index][field.key] = item;
576
- else {
577
- RETURN[index][field.key] = [];
578
- Object.entries(item).forEach(([key, value], _ind) => {
579
- if (!Array.isArray(value)) {
580
- RETURN[index][field.key][_ind] = {};
581
- RETURN[index][field.key][_ind][key] = value;
582
- }
535
+ for (const field of schema) {
536
+ // If the field is of simple type (non-recursive), process it directly
537
+ if (this.isSimpleField(field.type)) {
538
+ await this.processSimpleField(tableName, field, linesNumber, RETURN, options, prefix);
539
+ }
540
+ else if (this.isArrayField(field.type)) {
541
+ // Process array fields (recursive if needed)
542
+ await this.processArrayField(tableName, field, linesNumber, RETURN, options, prefix);
543
+ }
544
+ else if (this.isObjectField(field.type)) {
545
+ // Process object fields (recursive if needed)
546
+ await this.processObjectField(tableName, field, linesNumber, RETURN, options, prefix);
547
+ }
548
+ else if (this.isTableField(field.type)) {
549
+ // Process table reference fields
550
+ await this.processTableField(tableName, field, linesNumber, RETURN, options, prefix);
551
+ }
552
+ }
553
+ return RETURN;
554
+ }
555
+ // Helper function to determine if a field is simple
556
+ isSimpleField(fieldType) {
557
+ const simpleTypes = [
558
+ "string",
559
+ "number",
560
+ "boolean",
561
+ "date",
562
+ "email",
563
+ "password",
564
+ "html",
565
+ "ip",
566
+ "json",
567
+ "id",
568
+ ];
569
+ if (Array.isArray(fieldType))
570
+ return fieldType.every((type) => typeof type === "string" && simpleTypes.includes(type));
571
+ return simpleTypes.includes(fieldType);
572
+ }
573
+ // Process a simple field (non-recursive)
574
+ async processSimpleField(tableName, field, linesNumber, RETURN, _options, prefix) {
575
+ const fieldPath = join(this.databasePath, tableName, `${prefix ?? ""}${field.key}${this.getFileExtension(tableName)}`);
576
+ if (await File.isExists(fieldPath)) {
577
+ const items = await File.get(fieldPath, linesNumber, field.type, field.children, this.salt);
578
+ if (items) {
579
+ for (const [index, item] of Object.entries(items)) {
580
+ if (typeof item === "undefined")
581
+ continue; // Skip undefined items
582
+ if (!RETURN[index])
583
+ RETURN[index] = {}; // Ensure the index exists
584
+ RETURN[index][field.key] = item; // Assign item to the RETURN object
585
+ }
586
+ }
587
+ }
588
+ }
589
+ // Helper function to check if the field type is array
590
+ isArrayField(fieldType) {
591
+ return ((Array.isArray(fieldType) &&
592
+ fieldType.every((type) => typeof type === "string") &&
593
+ fieldType.includes("array")) ||
594
+ fieldType === "array");
595
+ }
596
+ // Process array fields (recursive if needed)
597
+ async processArrayField(tableName, field, linesNumber, RETURN, options, prefix) {
598
+ if (Array.isArray(field.children)) {
599
+ if (this.isSimpleField(field.children)) {
600
+ await this.processSimpleField(tableName, field, linesNumber, RETURN, options, prefix);
601
+ }
602
+ else if (this.isTableField(field.children)) {
603
+ await this.processTableField(tableName, field, linesNumber, RETURN, options, prefix);
604
+ }
605
+ else {
606
+ // Handling array of objects and filtering nested arrays
607
+ const nestedArrayFields = field.children.filter((child) => child.type === "array" &&
608
+ Utils.isArrayOfObjects(child.children));
609
+ if (nestedArrayFields.length > 0) {
610
+ // one of children has array field type and has children array of object = Schema
611
+ const childItems = await this.processSchemaData(tableName, field.children.filter((children) => children.type === "array" &&
612
+ Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`);
613
+ if (childItems)
614
+ for (const [index, item] of Object.entries(childItems))
615
+ this._processSchemaDataHelper(RETURN, item, index, field);
616
+ // Remove nested arrays after processing
617
+ field.children = field.children.filter((child) => child.type === "array" &&
618
+ Utils.isArrayOfObjects(child.children));
619
+ }
620
+ // Process remaining items for the field's children
621
+ const items = await this.processSchemaData(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
622
+ // Process the items after retrieval
623
+ if (items) {
624
+ for (const [index, item] of Object.entries(items)) {
625
+ if (typeof item === "undefined")
626
+ continue; // Skip undefined items
627
+ if (!RETURN[index])
628
+ RETURN[index] = {};
629
+ if (Utils.isObject(item)) {
630
+ if (!Utils.isArrayOfNulls(Object.values(item))) {
631
+ if (RETURN[index][field.key])
632
+ Object.entries(item).forEach(([key, value], _index) => {
633
+ for (let _index = 0; _index < value.length; _index++)
634
+ if (RETURN[index][field.key][_index])
635
+ Object.assign(RETURN[index][field.key][_index], {
636
+ [key]: value[_index],
637
+ });
583
638
  else
584
- for (let _i = 0; _i < value.length; _i++) {
585
- if (value[_i] === null ||
586
- (Array.isArray(value[_i]) &&
587
- Utils.isArrayOfNulls(value[_i])))
588
- continue;
589
- if (!RETURN[index][field.key][_i])
590
- RETURN[index][field.key][_i] = {};
591
- RETURN[index][field.key][_i][key] = value[_i];
592
- }
593
- });
594
- }
639
+ RETURN[index][field.key][_index] = {
640
+ [key]: value[_index],
641
+ };
642
+ });
643
+ else if (Object.values(item).every((_i) => Utils.isArrayOfArrays(_i) || Array.isArray(_i)) &&
644
+ prefix)
645
+ RETURN[index][field.key] = item;
646
+ else {
647
+ RETURN[index][field.key] = [];
648
+ Object.entries(item).forEach(([key, value], _ind) => {
649
+ if (!Array.isArray(value)) {
650
+ RETURN[index][field.key][_ind] = {};
651
+ RETURN[index][field.key][_ind][key] = value;
652
+ }
653
+ else
654
+ for (let _i = 0; _i < value.length; _i++) {
655
+ if (value[_i] === null ||
656
+ (Array.isArray(value[_i]) &&
657
+ Utils.isArrayOfNulls(value[_i])))
658
+ continue;
659
+ if (!RETURN[index][field.key][_i])
660
+ RETURN[index][field.key][_i] = {};
661
+ RETURN[index][field.key][_i][key] = value[_i];
662
+ }
663
+ });
595
664
  }
596
- else
597
- RETURN[index][field.key] = null;
598
665
  }
599
666
  else
600
- RETURN[index][field.key] = item;
601
- }
602
- }
603
- else if (field.children === "table" ||
604
- (Array.isArray(field.type) && field.type.includes("table")) ||
605
- (Array.isArray(field.children) && field.children.includes("table"))) {
606
- if (field.table &&
607
- (await File.isExists(join(this.databasePath, field.table))) &&
608
- (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
609
- const itemsIDs = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
610
- if (itemsIDs) {
611
- const searchableIDs = new Map();
612
- for (const [lineNumber, lineContent] of Object.entries(itemsIDs)) {
613
- if (!RETURN[lineNumber])
614
- RETURN[lineNumber] = {};
615
- if (lineContent !== null && lineContent !== undefined)
616
- searchableIDs.set(lineNumber, lineContent);
617
- }
618
- if (searchableIDs.size) {
619
- const items = await this.get(field.table, Array.from(new Set(Array.from(searchableIDs.values()).flat())), {
620
- ...options,
621
- perPage: Number.POSITIVE_INFINITY,
622
- columns: options.columns
623
- ?.filter((column) => column.includes(`${field.key}.`))
624
- .map((column) => column.replace(`${field.key}.`, "")),
625
- });
626
- if (items) {
627
- for (const [lineNumber, lineContent,] of searchableIDs.entries()) {
628
- const foundedItems = items.filter(({ id }) => lineContent.includes(id));
629
- if (foundedItems)
630
- RETURN[lineNumber][field.key] = foundedItems;
631
- }
632
- }
633
- }
667
+ RETURN[index][field.key] = null;
634
668
  }
669
+ else
670
+ RETURN[index][field.key] = item;
635
671
  }
636
672
  }
637
- else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
638
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
639
- if (items)
640
- for (const [index, item] of Object.entries(items)) {
641
- if (!RETURN[index])
642
- RETURN[index] = {};
643
- if (item !== null && item !== undefined)
644
- RETURN[index][field.key] = item;
645
- }
673
+ }
674
+ }
675
+ else if (this.isSimpleField(field.children)) {
676
+ // If `children` is FieldType, handle it as an array of simple types (no recursion needed here)
677
+ await this.processSimpleField(tableName, field, linesNumber, RETURN, options, prefix);
678
+ }
679
+ else if (this.isTableField(field.children)) {
680
+ await this.processTableField(tableName, field, linesNumber, RETURN, options, prefix);
681
+ }
682
+ }
683
+ // Helper function to check if the field type is object
684
+ isObjectField(fieldType) {
685
+ return (fieldType === "object" ||
686
+ (Array.isArray(fieldType) &&
687
+ fieldType.every((type) => typeof type === "string") &&
688
+ fieldType.includes("object")));
689
+ }
690
+ // Process object fields (recursive if needed)
691
+ async processObjectField(tableName, field, linesNumber, RETURN, options, prefix) {
692
+ if (Array.isArray(field.children)) {
693
+ // If `children` is a Schema (array of Field objects), recurse
694
+ const items = await this.processSchemaData(tableName, field.children, linesNumber, options, `${prefix ?? ""}${field.key}.`);
695
+ for (const [index, item] of Object.entries(items)) {
696
+ if (typeof item === "undefined")
697
+ continue; // Skip undefined items
698
+ if (!RETURN[index])
699
+ RETURN[index] = {};
700
+ if (Utils.isObject(item)) {
701
+ if (!Object.values(item).every((i) => i === null))
702
+ RETURN[index][field.key] = item;
646
703
  }
647
704
  }
648
- else if (field.type === "object") {
649
- const fieldItems = await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
650
- if (fieldItems)
651
- for (const [index, item] of Object.entries(fieldItems)) {
652
- if (!RETURN[index])
653
- RETURN[index] = {};
654
- if (Utils.isObject(item)) {
655
- if (!Object.values(item).every((i) => i === null))
656
- RETURN[index][field.key] = item;
657
- }
705
+ }
706
+ }
707
+ // Helper function to check if the field type is table
708
+ isTableField(fieldType) {
709
+ return (fieldType === "table" ||
710
+ (Array.isArray(fieldType) &&
711
+ fieldType.every((type) => typeof type === "string") &&
712
+ fieldType.includes("table")));
713
+ }
714
+ // Process table reference fields
715
+ async processTableField(tableName, field, linesNumber, RETURN, options, prefix) {
716
+ if (field.table &&
717
+ (await File.isExists(join(this.databasePath, field.table)))) {
718
+ const fieldPath = join(this.databasePath, tableName, `${prefix ?? ""}${field.key}${this.getFileExtension(tableName)}`);
719
+ if (await File.isExists(fieldPath)) {
720
+ const itemsIDs = await File.get(fieldPath, linesNumber, field.type, field.children, this.salt);
721
+ const isArrayField = this.isArrayField(field.type);
722
+ if (itemsIDs) {
723
+ const searchableIDs = new Map();
724
+ for (const [lineNumber, lineContent] of Object.entries(itemsIDs)) {
725
+ if (typeof lineContent === "undefined")
726
+ continue; // Skip undefined items
727
+ if (!RETURN[lineNumber])
728
+ RETURN[lineNumber] = {};
729
+ if (lineContent !== null && lineContent !== undefined)
730
+ searchableIDs.set(lineNumber, lineContent);
658
731
  }
659
- }
660
- else if (field.type === "table") {
661
- if (field.table &&
662
- (await File.isExists(join(this.databasePath, field.table))) &&
663
- (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
664
- const itemsIDs = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
665
- if (itemsIDs) {
666
- const searchableIDs = new Map();
667
- for (const [lineNumber, lineContent] of Object.entries(itemsIDs)) {
668
- if (!RETURN[lineNumber])
669
- RETURN[lineNumber] = {};
670
- if (lineContent !== null && lineContent !== undefined)
671
- searchableIDs.set(lineNumber, lineContent);
672
- }
673
- if (searchableIDs.size) {
674
- const items = await this.get(field.table, Array.from(new Set(searchableIDs.values())), {
675
- ...options,
676
- perPage: Number.POSITIVE_INFINITY,
677
- columns: options.columns
678
- ?.filter((column) => column.includes(`${field.key}.`))
679
- .map((column) => column.replace(`${field.key}.`, "")),
680
- });
681
- if (items) {
682
- for (const [lineNumber, lineContent,] of searchableIDs.entries()) {
683
- const foundedItem = items.find(({ id }) => id === lineContent);
684
- if (foundedItem)
685
- RETURN[lineNumber][field.key] = foundedItem;
686
- }
732
+ if (searchableIDs.size) {
733
+ const items = await this.get(field.table, isArrayField
734
+ ? Array.from(new Set(Array.from(searchableIDs.values()).flat()))
735
+ : Array.from(new Set(searchableIDs.values())), {
736
+ ...options,
737
+ perPage: Number.POSITIVE_INFINITY,
738
+ columns: options.columns
739
+ ?.filter((column) => column.includes(`${field.key}.`))
740
+ .map((column) => column.replace(`${field.key}.`, "")),
741
+ });
742
+ if (items) {
743
+ for (const [lineNumber, lineContent] of searchableIDs.entries()) {
744
+ const foundedItem = isArrayField
745
+ ? items.filter(({ id }) => lineContent.includes(id))
746
+ : items.find(({ id }) => id === lineContent);
747
+ if (foundedItem)
748
+ RETURN[lineNumber][field.key] = foundedItem;
687
749
  }
688
750
  }
689
751
  }
690
752
  }
691
753
  }
692
- else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
693
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
694
- if (items)
695
- for (const [index, item] of Object.entries(items)) {
696
- if (!RETURN[index])
697
- RETURN[index] = {};
698
- if (item !== null && item !== undefined)
699
- RETURN[index][field.key] = item;
700
- }
701
- }
702
754
  }
703
- return RETURN;
704
755
  }
705
756
  async applyCriteria(tableName, schema, options, criteria, allTrue) {
706
757
  const tablePath = join(this.databasePath, tableName);
@@ -968,7 +1019,7 @@ export default class Inibase {
968
1019
  }
969
1020
  if (!where) {
970
1021
  // Display all data
971
- RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
1022
+ RETURN = Object.values(await this.processSchemaData(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
972
1023
  index +
973
1024
  1), options));
974
1025
  if (!this.totalItems[`${tableName}-*`])
@@ -985,7 +1036,7 @@ export default class Inibase {
985
1036
  // useless
986
1037
  if (onlyLinesNumbers)
987
1038
  return lineNumbers;
988
- RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, lineNumbers, options)) ?? {});
1039
+ RETURN = Object.values((await this.processSchemaData(tableName, schema, lineNumbers, options)) ?? {});
989
1040
  if (RETURN?.length && !Array.isArray(where))
990
1041
  RETURN = RETURN[0];
991
1042
  }
@@ -1008,7 +1059,7 @@ export default class Inibase {
1008
1059
  if (!options.columns?.length)
1009
1060
  options.columns = undefined;
1010
1061
  }
1011
- RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
1062
+ RETURN = Object.values((await this.processSchemaData(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
1012
1063
  if (RETURN?.length && !Array.isArray(where))
1013
1064
  RETURN = RETURN[0];
1014
1065
  }
@@ -1040,7 +1091,7 @@ export default class Inibase {
1040
1091
  const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]), alreadyExistsColumnsIDs = Utils.flattenSchema(schema)
1041
1092
  .filter(({ key }) => alreadyExistsColumns.includes(key))
1042
1093
  .map(({ id }) => id);
1043
- RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, Utils.filterSchema(schema, ({ id, type, children }) => !alreadyExistsColumnsIDs.includes(id) ||
1094
+ RETURN = Object.values(Utils.deepMerge(RETURN, await this.processSchemaData(tableName, Utils.filterSchema(schema, ({ id, type, children }) => !alreadyExistsColumnsIDs.includes(id) ||
1044
1095
  Utils.isFieldType("table", type, children)), Object.keys(RETURN).map(Number), options)));
1045
1096
  if (this.tables[tableName].config.cache)
1046
1097
  await writeFile(cachedFilePath, Array.from(linesNumbers).join(","));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.106",
3
+ "version": "1.0.0-rc.108",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",
@@ -75,7 +75,7 @@
75
75
  },
76
76
  "dependencies": {
77
77
  "dotenv": "^16.4.5",
78
- "inison": "1.0.0-rc.4"
78
+ "inison": "latest"
79
79
  },
80
80
  "scripts": {
81
81
  "prepublish": "npx tsc",