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 +13 -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 +222 -171
- package/package.json +2 -2
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
|
|
671
|
-
|
|
672
|
-
| POST | 11 ms (0.
|
|
673
|
-
| GET |
|
|
674
|
-
| PUT |
|
|
675
|
-
| DELETE |
|
|
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
|
|
680
|
-
|
|
681
|
-
| POST |
|
|
682
|
-
| GET |
|
|
683
|
-
| PUT |
|
|
684
|
-
| DELETE |
|
|
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
|
|
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
|
@@ -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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
536
|
-
const tablePath = join(this.databasePath, tableName);
|
|
533
|
+
async processSchemaData(tableName, schema, linesNumber, options, prefix) {
|
|
537
534
|
const RETURN = {};
|
|
538
|
-
for
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
field
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
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
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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] =
|
|
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
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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": "
|
|
78
|
+
"inison": "latest"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"prepublish": "npx tsc",
|