inibase 1.0.0-rc.107 → 1.0.0-rc.109
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 +22 -12
- package/dist/file.d.ts +0 -9
- package/dist/file.js +28 -30
- package/dist/index.d.ts +10 -2
- package/dist/index.js +215 -169
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -54,6 +54,15 @@ const users = await db.get("user", { favoriteFoods: "![]Pizza,Burger" });
|
|
|
54
54
|
<npm|pnpm|yarn|bun> install inibase
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
> [!WARNING]
|
|
58
|
+
> If you're using **Windows**, the following Unix commands are required: `zcat`, `sed`, `gzip`, and `echo`.
|
|
59
|
+
>
|
|
60
|
+
> To use the missing commands, you need to install additional tools:
|
|
61
|
+
> - **[GnuWin32](http://gnuwin32.sourceforge.net/)**: Provides individual GNU utilities for Windows.
|
|
62
|
+
> - **[Cygwin](https://www.cygwin.com/)**: Offers a full Unix-like environment for Windows.
|
|
63
|
+
>
|
|
64
|
+
> Alternatively, consider using the **Windows Subsystem for Linux (WSL)** to run a Linux environment on Windows. Learn more [here](https://learn.microsoft.com/en-us/windows/wsl/).
|
|
65
|
+
|
|
57
66
|
## How it works?
|
|
58
67
|
|
|
59
68
|
`Inibase` organizes data into databases, tables, and columns, each stored in separate files.
|
|
@@ -667,21 +676,22 @@ await db.get("user", undefined, { sort: {age: -1, username: "asc"} });
|
|
|
667
676
|
|
|
668
677
|
### Bulk
|
|
669
678
|
|
|
670
|
-
| | 10
|
|
671
|
-
|
|
672
|
-
| POST | 11 ms (0.
|
|
673
|
-
| GET |
|
|
674
|
-
| PUT |
|
|
675
|
-
| DELETE |
|
|
679
|
+
| | 10 | 100 | 1000 |
|
|
680
|
+
|--------|-------------------|-------------------|-------------------|
|
|
681
|
+
| POST | 11 ms (0.66 mb) | 5 ms (1.02 mb) | 24 ms (1.44 mb) |
|
|
682
|
+
| GET | 29 ms (2.86 mb) | 24 ms (2.81 mb) | 36 ms (0.89 mb) |
|
|
683
|
+
| PUT | 21 ms (2.68 mb) | 16 ms (2.90 mb) | 12 ms (0.63 mb) |
|
|
684
|
+
| DELETE | 14 ms (0.82 mb) | 13 ms (0.84 mb) | 2 ms (0.17 mb) |
|
|
685
|
+
|
|
676
686
|
|
|
677
687
|
### Single
|
|
678
688
|
|
|
679
|
-
| | 10
|
|
680
|
-
|
|
681
|
-
| POST |
|
|
682
|
-
| GET |
|
|
683
|
-
| PUT |
|
|
684
|
-
| DELETE |
|
|
689
|
+
| | 10 | 100 | 1000 |
|
|
690
|
+
|--------|---------------------|--------------------|--------------------|
|
|
691
|
+
| POST | 45 ms (1.07 mb) | 12 ms (0.52 mb) | 11 ms (0.37 mb) |
|
|
692
|
+
| GET | 200 ms (2.15 mb) | 192 ms (2.72 mb) | 190 ms (2.31 mb) |
|
|
693
|
+
| PUT | 49 ms (3.22 mb) | 17 ms (2.98 mb) | 17 ms (3.06 mb) |
|
|
694
|
+
| DELETE | 118 ms (0.59 mb) | 113 ms (0.51 mb) | 103 ms (3.14 mb) |
|
|
685
695
|
|
|
686
696
|
> Default testing uses a table with username, email, and password fields, ensuring password encryption is included in the process<br>
|
|
687
697
|
> 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
|
|
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
|
|
228
|
-
: `sed -n '${lineNumbers
|
|
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
|
|
458
|
-
: `sed
|
|
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
|
|
104
|
-
private
|
|
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
|
@@ -438,15 +438,6 @@ export default class Inibase {
|
|
|
438
438
|
});
|
|
439
439
|
switch (field.type) {
|
|
440
440
|
case "array":
|
|
441
|
-
return Utils.isArrayOfObjects(field.children)
|
|
442
|
-
? [
|
|
443
|
-
this.getDefaultValue({
|
|
444
|
-
...field,
|
|
445
|
-
type: "object",
|
|
446
|
-
children: field.children,
|
|
447
|
-
}),
|
|
448
|
-
]
|
|
449
|
-
: null;
|
|
450
441
|
case "object": {
|
|
451
442
|
if (!field.children || !Utils.isArrayOfObjects(field.children))
|
|
452
443
|
return null;
|
|
@@ -496,12 +487,14 @@ export default class Inibase {
|
|
|
496
487
|
newCombinedData[join(tablePath, `${key}${this.getFileExtension(tableName)}`)] = value;
|
|
497
488
|
return newCombinedData;
|
|
498
489
|
}
|
|
499
|
-
|
|
490
|
+
_processSchemaDataHelper(RETURN, item, index, field) {
|
|
491
|
+
// If the item is an object, we need to process its children
|
|
500
492
|
if (Utils.isObject(item)) {
|
|
501
493
|
if (!RETURN[index])
|
|
502
|
-
RETURN[index] = {};
|
|
494
|
+
RETURN[index] = {}; // Ensure the index exists
|
|
503
495
|
if (!RETURN[index][field.key])
|
|
504
496
|
RETURN[index][field.key] = [];
|
|
497
|
+
// Process children fields (recursive if needed)
|
|
505
498
|
for (const child_field of field.children.filter((children) => children.type === "array" &&
|
|
506
499
|
Utils.isArrayOfObjects(children.children))) {
|
|
507
500
|
if (Utils.isObject(item[child_field.key])) {
|
|
@@ -522,8 +515,8 @@ export default class Inibase {
|
|
|
522
515
|
}
|
|
523
516
|
else {
|
|
524
517
|
value[_i].forEach((_element, _index) => {
|
|
525
|
-
// Recursive call
|
|
526
|
-
this.
|
|
518
|
+
// Recursive call to handle nested structure
|
|
519
|
+
this._processSchemaDataHelper(RETURN, _element, _index, child_field);
|
|
527
520
|
// Perform property assignments
|
|
528
521
|
if (!RETURN[index][field.key][_i][child_field.key][_index])
|
|
529
522
|
RETURN[index][field.key][_i][child_field.key][_index] = {};
|
|
@@ -537,175 +530,228 @@ export default class Inibase {
|
|
|
537
530
|
}
|
|
538
531
|
}
|
|
539
532
|
}
|
|
540
|
-
async
|
|
541
|
-
const tablePath = join(this.databasePath, tableName);
|
|
533
|
+
async processSchemaData(tableName, schema, linesNumber, options, prefix) {
|
|
542
534
|
const RETURN = {};
|
|
543
|
-
for
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
field
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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
|
+
});
|
|
588
638
|
else
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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
|
+
});
|
|
600
664
|
}
|
|
601
|
-
else
|
|
602
|
-
RETURN[index][field.key] = null;
|
|
603
665
|
}
|
|
604
666
|
else
|
|
605
|
-
RETURN[index][field.key] =
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
else if (field.children === "table" ||
|
|
609
|
-
(Array.isArray(field.type) && field.type.includes("table")) ||
|
|
610
|
-
(Array.isArray(field.children) && field.children.includes("table"))) {
|
|
611
|
-
if (field.table &&
|
|
612
|
-
(await File.isExists(join(this.databasePath, field.table))) &&
|
|
613
|
-
(await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
|
|
614
|
-
const itemsIDs = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
|
|
615
|
-
if (itemsIDs) {
|
|
616
|
-
const searchableIDs = new Map();
|
|
617
|
-
for (const [lineNumber, lineContent] of Object.entries(itemsIDs)) {
|
|
618
|
-
if (!RETURN[lineNumber])
|
|
619
|
-
RETURN[lineNumber] = {};
|
|
620
|
-
if (lineContent !== null && lineContent !== undefined)
|
|
621
|
-
searchableIDs.set(lineNumber, lineContent);
|
|
622
|
-
}
|
|
623
|
-
if (searchableIDs.size) {
|
|
624
|
-
const items = await this.get(field.table, Array.from(new Set(Array.from(searchableIDs.values()).flat())), {
|
|
625
|
-
...options,
|
|
626
|
-
perPage: Number.POSITIVE_INFINITY,
|
|
627
|
-
columns: options.columns
|
|
628
|
-
?.filter((column) => column.includes(`${field.key}.`))
|
|
629
|
-
.map((column) => column.replace(`${field.key}.`, "")),
|
|
630
|
-
});
|
|
631
|
-
if (items) {
|
|
632
|
-
for (const [lineNumber, lineContent,] of searchableIDs.entries()) {
|
|
633
|
-
const foundedItems = items.filter(({ id }) => lineContent.includes(id));
|
|
634
|
-
if (foundedItems)
|
|
635
|
-
RETURN[lineNumber][field.key] = foundedItems;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
667
|
+
RETURN[index][field.key] = null;
|
|
639
668
|
}
|
|
669
|
+
else
|
|
670
|
+
RETURN[index][field.key] = item;
|
|
640
671
|
}
|
|
641
672
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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;
|
|
651
703
|
}
|
|
652
704
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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);
|
|
663
731
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
perPage: Number.POSITIVE_INFINITY,
|
|
682
|
-
columns: options.columns
|
|
683
|
-
?.filter((column) => column.includes(`${field.key}.`))
|
|
684
|
-
.map((column) => column.replace(`${field.key}.`, "")),
|
|
685
|
-
});
|
|
686
|
-
if (items) {
|
|
687
|
-
for (const [lineNumber, lineContent,] of searchableIDs.entries()) {
|
|
688
|
-
const foundedItem = items.find(({ id }) => id === lineContent);
|
|
689
|
-
if (foundedItem)
|
|
690
|
-
RETURN[lineNumber][field.key] = foundedItem;
|
|
691
|
-
}
|
|
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;
|
|
692
749
|
}
|
|
693
750
|
}
|
|
694
751
|
}
|
|
695
752
|
}
|
|
696
753
|
}
|
|
697
|
-
else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
|
|
698
|
-
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
|
|
699
|
-
if (items)
|
|
700
|
-
for (const [index, item] of Object.entries(items)) {
|
|
701
|
-
if (!RETURN[index])
|
|
702
|
-
RETURN[index] = {};
|
|
703
|
-
if (item !== null && item !== undefined)
|
|
704
|
-
RETURN[index][field.key] = item;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
754
|
}
|
|
708
|
-
return RETURN;
|
|
709
755
|
}
|
|
710
756
|
async applyCriteria(tableName, schema, options, criteria, allTrue) {
|
|
711
757
|
const tablePath = join(this.databasePath, tableName);
|
|
@@ -973,7 +1019,7 @@ export default class Inibase {
|
|
|
973
1019
|
}
|
|
974
1020
|
if (!where) {
|
|
975
1021
|
// Display all data
|
|
976
|
-
RETURN = Object.values(await this.
|
|
1022
|
+
RETURN = Object.values(await this.processSchemaData(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
977
1023
|
index +
|
|
978
1024
|
1), options));
|
|
979
1025
|
if (!this.totalItems[`${tableName}-*`])
|
|
@@ -990,7 +1036,7 @@ export default class Inibase {
|
|
|
990
1036
|
// useless
|
|
991
1037
|
if (onlyLinesNumbers)
|
|
992
1038
|
return lineNumbers;
|
|
993
|
-
RETURN = Object.values((await this.
|
|
1039
|
+
RETURN = Object.values((await this.processSchemaData(tableName, schema, lineNumbers, options)) ?? {});
|
|
994
1040
|
if (RETURN?.length && !Array.isArray(where))
|
|
995
1041
|
RETURN = RETURN[0];
|
|
996
1042
|
}
|
|
@@ -1013,7 +1059,7 @@ export default class Inibase {
|
|
|
1013
1059
|
if (!options.columns?.length)
|
|
1014
1060
|
options.columns = undefined;
|
|
1015
1061
|
}
|
|
1016
|
-
RETURN = Object.values((await this.
|
|
1062
|
+
RETURN = Object.values((await this.processSchemaData(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
|
|
1017
1063
|
if (RETURN?.length && !Array.isArray(where))
|
|
1018
1064
|
RETURN = RETURN[0];
|
|
1019
1065
|
}
|
|
@@ -1045,7 +1091,7 @@ export default class Inibase {
|
|
|
1045
1091
|
const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]), alreadyExistsColumnsIDs = Utils.flattenSchema(schema)
|
|
1046
1092
|
.filter(({ key }) => alreadyExistsColumns.includes(key))
|
|
1047
1093
|
.map(({ id }) => id);
|
|
1048
|
-
RETURN = Object.values(Utils.deepMerge(RETURN, await this.
|
|
1094
|
+
RETURN = Object.values(Utils.deepMerge(RETURN, await this.processSchemaData(tableName, Utils.filterSchema(schema, ({ id, type, children }) => !alreadyExistsColumnsIDs.includes(id) ||
|
|
1049
1095
|
Utils.isFieldType("table", type, children)), Object.keys(RETURN).map(Number), options)));
|
|
1050
1096
|
if (this.tables[tableName].config.cache)
|
|
1051
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.
|
|
3
|
+
"version": "1.0.0-rc.109",
|
|
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": "
|
|
78
|
+
"inison": "latest"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"prepublish": "npx tsc",
|