inibase 1.0.0-rc.74 → 1.0.0-rc.76
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 +3 -3
- package/dist/cli.js +9 -1
- package/dist/file.js +4 -1
- package/dist/index.d.ts +7 -20
- package/dist/index.js +97 -113
- package/dist/utils.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -632,11 +632,11 @@ import Inibase from "inibase";
|
|
|
632
632
|
const db = new Inibase("/databaseName");
|
|
633
633
|
|
|
634
634
|
// order users by the age column
|
|
635
|
-
await db.
|
|
635
|
+
await db.get("user", undefined, { sort: "age" });
|
|
636
636
|
|
|
637
637
|
// order users by the age and username columns
|
|
638
|
-
await db.
|
|
639
|
-
await db.
|
|
638
|
+
await db.get("user", undefined, { sort: ["age", "username"] });
|
|
639
|
+
await db.get("user", undefined, { sort: {age: -1, username: "asc"} });
|
|
640
640
|
```
|
|
641
641
|
</blockquote>
|
|
642
642
|
</details>
|
package/dist/cli.js
CHANGED
|
@@ -196,7 +196,7 @@ rl.on("line", async (input) => {
|
|
|
196
196
|
console.log(`${textRed(" Err:")} Please specify table name`);
|
|
197
197
|
break;
|
|
198
198
|
}
|
|
199
|
-
let where = undefined, page = undefined, perPage = undefined, columns = undefined, data = undefined;
|
|
199
|
+
let where = undefined, page = undefined, perPage = undefined, columns = undefined, sort = undefined, data = undefined;
|
|
200
200
|
if (splitedInput.toSpliced(0, 1).length) {
|
|
201
201
|
const parsedArgs = parseArgs({
|
|
202
202
|
args: splitedInput.toSpliced(0, table ? 1 : 2),
|
|
@@ -204,6 +204,7 @@ rl.on("line", async (input) => {
|
|
|
204
204
|
where: { type: "string", short: "w" },
|
|
205
205
|
page: { type: "string", short: "p" },
|
|
206
206
|
perPage: { type: "string", short: "l" },
|
|
207
|
+
sort: { type: "string", short: "s" },
|
|
207
208
|
columns: { type: "string", short: "c", multiple: true },
|
|
208
209
|
data: { type: "string", short: "d" },
|
|
209
210
|
},
|
|
@@ -216,6 +217,12 @@ rl.on("line", async (input) => {
|
|
|
216
217
|
else if (isJSON(parsedArgs.where))
|
|
217
218
|
where = Inison.unstringify(parsedArgs.where);
|
|
218
219
|
}
|
|
220
|
+
if (parsedArgs.sort) {
|
|
221
|
+
if (isJSON(parsedArgs.sort))
|
|
222
|
+
sort = Inison.unstringify(parsedArgs.sort);
|
|
223
|
+
else
|
|
224
|
+
sort = parsedArgs.sort;
|
|
225
|
+
}
|
|
219
226
|
page = Number(parsedArgs.page) ?? undefined;
|
|
220
227
|
perPage = Number(parsedArgs.perPage) ?? undefined;
|
|
221
228
|
columns = parsedArgs.columns;
|
|
@@ -229,6 +236,7 @@ rl.on("line", async (input) => {
|
|
|
229
236
|
page: Number(page) ?? 1,
|
|
230
237
|
perPage: Number(perPage) ?? 15,
|
|
231
238
|
columns,
|
|
239
|
+
sort,
|
|
232
240
|
}));
|
|
233
241
|
break;
|
|
234
242
|
case "p":
|
package/dist/file.js
CHANGED
|
@@ -76,8 +76,11 @@ export const isExists = async (path) => {
|
|
|
76
76
|
const secureString = (input) => {
|
|
77
77
|
if (["true", "false"].includes(String(input)))
|
|
78
78
|
return input ? 1 : 0;
|
|
79
|
-
if (typeof input !== "string")
|
|
79
|
+
if (typeof input !== "string") {
|
|
80
|
+
if (input === null || input === undefined)
|
|
81
|
+
return "";
|
|
80
82
|
return input;
|
|
83
|
+
}
|
|
81
84
|
let decodedInput = null;
|
|
82
85
|
try {
|
|
83
86
|
decodedInput = decodeURIComponent(input);
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface Options {
|
|
|
21
21
|
page?: number;
|
|
22
22
|
perPage?: number;
|
|
23
23
|
columns?: string[] | string;
|
|
24
|
-
|
|
24
|
+
sort?: Record<string, 1 | -1 | "asc" | "ASC" | "desc" | "DESC"> | string[] | string;
|
|
25
25
|
}
|
|
26
26
|
export interface Config {
|
|
27
27
|
compression?: boolean;
|
|
@@ -113,13 +113,14 @@ export default class Inibase {
|
|
|
113
113
|
* @param {string} tableName
|
|
114
114
|
* @param {(string | number | (string | number)[] | Criteria | undefined)} [where]
|
|
115
115
|
* @param {(Options | undefined)} [options]
|
|
116
|
-
* @param {
|
|
117
|
-
* @param {
|
|
118
|
-
* @param {boolean} [_skipIdColumn]
|
|
116
|
+
* @param {boolean} [onlyOne]
|
|
117
|
+
* @param {boolean} [onlyLinesNumbers]
|
|
119
118
|
* @return {*} {(Promise<Data[] | Data | number[] | null>)}
|
|
120
119
|
*/
|
|
121
|
-
get(tableName: string, where
|
|
122
|
-
get(tableName: string, where
|
|
120
|
+
get(tableName: string, where: string | number | (string | number)[] | Criteria | undefined, options: Options | undefined, onlyOne: true, onlyLinesNumbers?: false): Promise<Data | null>;
|
|
121
|
+
get(tableName: string, where: string | number, options?: Options, onlyOne?: boolean, onlyLinesNumbers?: false): Promise<Data | null>;
|
|
122
|
+
get(tableName: string, where?: string | number | (string | number)[] | Criteria, options?: Options, onlyOne?: boolean, onlyLinesNumbers?: false): Promise<Data[] | null>;
|
|
123
|
+
get(tableName: string, where: string | number | (string | number)[] | Criteria | undefined, options: Options | undefined, onlyOne: boolean | undefined, onlyLinesNumbers: true): Promise<number[]>;
|
|
123
124
|
/**
|
|
124
125
|
* Create new item(s) in a table
|
|
125
126
|
*
|
|
@@ -183,18 +184,4 @@ export default class Inibase {
|
|
|
183
184
|
*/
|
|
184
185
|
min(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
|
185
186
|
min(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
|
|
186
|
-
/**
|
|
187
|
-
* Sort column(s) of a table
|
|
188
|
-
*
|
|
189
|
-
* @param {string} tableName
|
|
190
|
-
* @param {(string
|
|
191
|
-
* | string[]
|
|
192
|
-
* | Record<string, 1 | -1 | "asc" | "ASC" | "desc" | "DESC">)} columns
|
|
193
|
-
* @param {(string | number | (string | number)[] | Criteria)} [where]
|
|
194
|
-
* @param {Options} [options={
|
|
195
|
-
* page: 1,
|
|
196
|
-
* perPage: 15,
|
|
197
|
-
* }]
|
|
198
|
-
*/
|
|
199
|
-
sort(tableName: string, columns: string | string[] | Record<string, 1 | -1 | "asc" | "ASC" | "desc" | "DESC">, where?: string | number | (string | number)[] | Criteria, options?: Options): Promise<any[]>;
|
|
200
187
|
}
|
package/dist/index.js
CHANGED
|
@@ -794,16 +794,14 @@ export default class Inibase {
|
|
|
794
794
|
async get(tableName, where, options = {
|
|
795
795
|
page: 1,
|
|
796
796
|
perPage: 15,
|
|
797
|
-
}, onlyOne, onlyLinesNumbers
|
|
797
|
+
}, onlyOne, onlyLinesNumbers) {
|
|
798
798
|
const tablePath = join(this.databasePath, tableName);
|
|
799
799
|
// Ensure options.columns is an array
|
|
800
800
|
if (options.columns) {
|
|
801
801
|
options.columns = Array.isArray(options.columns)
|
|
802
802
|
? options.columns
|
|
803
803
|
: [options.columns];
|
|
804
|
-
if (
|
|
805
|
-
options.columns.length &&
|
|
806
|
-
!options.columns.includes("id"))
|
|
804
|
+
if (options.columns.length && !options.columns.includes("id"))
|
|
807
805
|
options.columns.push("id");
|
|
808
806
|
}
|
|
809
807
|
// Default values for page and perPage
|
|
@@ -821,6 +819,96 @@ export default class Inibase {
|
|
|
821
819
|
((Array.isArray(where) && !where.length) ||
|
|
822
820
|
(Utils.isObject(where) && !Object.keys(where).length)))
|
|
823
821
|
where = undefined;
|
|
822
|
+
if (options.sort) {
|
|
823
|
+
let sortArray, isLineNumbers = true, keepItems = [];
|
|
824
|
+
if (Utils.isObject(options.sort) && !Array.isArray(options.sort)) {
|
|
825
|
+
// {name: "ASC", age: "DESC"}
|
|
826
|
+
sortArray = Object.entries(options.sort).map(([key, value]) => [
|
|
827
|
+
key,
|
|
828
|
+
typeof value === "string" ? value.toLowerCase() === "asc" : value > 0,
|
|
829
|
+
]);
|
|
830
|
+
}
|
|
831
|
+
else
|
|
832
|
+
sortArray = []
|
|
833
|
+
.concat(options.sort)
|
|
834
|
+
.map((column) => [column, true]);
|
|
835
|
+
let cacheKey = "";
|
|
836
|
+
// Criteria
|
|
837
|
+
if (this.tables[tableName].config.cache)
|
|
838
|
+
cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
|
|
839
|
+
if (where) {
|
|
840
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
841
|
+
keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
|
|
842
|
+
isLineNumbers = false;
|
|
843
|
+
if (!keepItems.length)
|
|
844
|
+
throw this.throwError("NO_RESULTS", tableName);
|
|
845
|
+
keepItems = keepItems.slice((options.page - 1) * options.perPage, options.page * options.perPage);
|
|
846
|
+
}
|
|
847
|
+
if (!keepItems.length)
|
|
848
|
+
keepItems = Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
849
|
+
index +
|
|
850
|
+
1);
|
|
851
|
+
const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension(tableName)}`));
|
|
852
|
+
for await (const path of filesPathes.slice(1))
|
|
853
|
+
if (!(await File.isExists(path)))
|
|
854
|
+
return null;
|
|
855
|
+
// Construct the paste command to merge files and filter lines by IDs
|
|
856
|
+
const pasteCommand = `paste ${filesPathes.join(" ")}`;
|
|
857
|
+
// Construct the sort command dynamically based on the number of files for sorting
|
|
858
|
+
const index = 2;
|
|
859
|
+
const sortColumns = sortArray
|
|
860
|
+
.map(([key, ascending], i) => {
|
|
861
|
+
const field = Utils.getField(key, schema);
|
|
862
|
+
if (field)
|
|
863
|
+
return `-k${i + index},${i + index}${Utils.isFieldType(["id", "number", "date"], field.type, field.children)
|
|
864
|
+
? "n"
|
|
865
|
+
: ""}${!ascending ? "r" : ""}`;
|
|
866
|
+
return "";
|
|
867
|
+
})
|
|
868
|
+
.join(" ");
|
|
869
|
+
const sortCommand = `sort ${sortColumns} -T=${join(tablePath, ".tmp")}`;
|
|
870
|
+
// Construct the awk command to keep only the specified lines after sorting
|
|
871
|
+
const awkCommand = isLineNumbers
|
|
872
|
+
? `awk '${keepItems.map((lineNumber) => `NR==${lineNumber}`).join(" || ")}'`
|
|
873
|
+
: `awk '${keepItems.map((id) => `$1 ~ /^${id} */`).join(" || ")}'`;
|
|
874
|
+
try {
|
|
875
|
+
if (cacheKey)
|
|
876
|
+
await File.lock(join(tablePath, ".tmp"), cacheKey);
|
|
877
|
+
// Combine && Execute the commands synchronously
|
|
878
|
+
const lines = (await UtilsServer.exec(this.tables[tableName].config.cache
|
|
879
|
+
? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
|
|
880
|
+
? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
|
|
881
|
+
: `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
|
|
882
|
+
: `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
|
|
883
|
+
encoding: "utf-8",
|
|
884
|
+
})).stdout
|
|
885
|
+
.trim()
|
|
886
|
+
.split("\n");
|
|
887
|
+
// Parse the result and extract the specified lines
|
|
888
|
+
const outputArray = lines.map((line) => {
|
|
889
|
+
const splitedFileColumns = line.split("\t"); // Assuming tab-separated columns
|
|
890
|
+
const outputObject = {};
|
|
891
|
+
// Extract values for each file, including `id${this.getFileExtension(tableName)}`
|
|
892
|
+
filesPathes.forEach((fileName, index) => {
|
|
893
|
+
const field = Utils.getField(parse(fileName).name, schema);
|
|
894
|
+
if (field)
|
|
895
|
+
outputObject[field.key] = File.decode(splitedFileColumns[index], field?.type, field?.children, this.salt);
|
|
896
|
+
});
|
|
897
|
+
return outputObject;
|
|
898
|
+
});
|
|
899
|
+
const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), (({ sort, ...rest }) => rest)(options));
|
|
900
|
+
return restOfColumns
|
|
901
|
+
? outputArray.map((item) => ({
|
|
902
|
+
...item,
|
|
903
|
+
...restOfColumns.find(({ id }) => id === item.id),
|
|
904
|
+
}))
|
|
905
|
+
: outputArray;
|
|
906
|
+
}
|
|
907
|
+
finally {
|
|
908
|
+
if (cacheKey)
|
|
909
|
+
await File.unlock(join(tablePath, ".tmp"), cacheKey);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
824
912
|
if (!where) {
|
|
825
913
|
// Display all data
|
|
826
914
|
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
@@ -861,6 +949,11 @@ export default class Inibase {
|
|
|
861
949
|
return Object.keys(lineNumbers).length
|
|
862
950
|
? Object.keys(lineNumbers).map(Number)
|
|
863
951
|
: null;
|
|
952
|
+
if (options.columns) {
|
|
953
|
+
options.columns = options.columns.filter((column) => column !== "id");
|
|
954
|
+
if (!options.columns?.length)
|
|
955
|
+
options.columns = undefined;
|
|
956
|
+
}
|
|
864
957
|
RETURN = Object.values((await this.getItemsFromSchema(tableName, schema, Object.keys(lineNumbers).map(Number), options)) ?? {});
|
|
865
958
|
if (!this.totalItems[`${tableName}-*`])
|
|
866
959
|
this.totalItems[`${tableName}-*`] = countItems;
|
|
@@ -1214,113 +1307,4 @@ export default class Inibase {
|
|
|
1214
1307
|
}
|
|
1215
1308
|
return RETURN;
|
|
1216
1309
|
}
|
|
1217
|
-
/**
|
|
1218
|
-
* Sort column(s) of a table
|
|
1219
|
-
*
|
|
1220
|
-
* @param {string} tableName
|
|
1221
|
-
* @param {(string
|
|
1222
|
-
* | string[]
|
|
1223
|
-
* | Record<string, 1 | -1 | "asc" | "ASC" | "desc" | "DESC">)} columns
|
|
1224
|
-
* @param {(string | number | (string | number)[] | Criteria)} [where]
|
|
1225
|
-
* @param {Options} [options={
|
|
1226
|
-
* page: 1,
|
|
1227
|
-
* perPage: 15,
|
|
1228
|
-
* }]
|
|
1229
|
-
*/
|
|
1230
|
-
async sort(tableName, columns, where, options = {
|
|
1231
|
-
page: 1,
|
|
1232
|
-
perPage: 15,
|
|
1233
|
-
}) {
|
|
1234
|
-
const tablePath = join(this.databasePath, tableName), schema = (await this.throwErrorIfTableEmpty(tableName)).schema;
|
|
1235
|
-
// Default values for page and perPage
|
|
1236
|
-
options.page = options.page || 1;
|
|
1237
|
-
options.perPage = options.perPage || 15;
|
|
1238
|
-
let sortArray, isLineNumbers = true, keepItems = [];
|
|
1239
|
-
if (Utils.isObject(columns) && !Array.isArray(columns)) {
|
|
1240
|
-
// {name: "ASC", age: "DESC"}
|
|
1241
|
-
sortArray = Object.entries(columns).map(([key, value]) => [
|
|
1242
|
-
key,
|
|
1243
|
-
typeof value === "string" ? value.toLowerCase() === "asc" : value > 0,
|
|
1244
|
-
]);
|
|
1245
|
-
}
|
|
1246
|
-
else {
|
|
1247
|
-
if (!Array.isArray(columns))
|
|
1248
|
-
columns = [columns];
|
|
1249
|
-
sortArray = columns.map((column) => [column, true]);
|
|
1250
|
-
}
|
|
1251
|
-
let cacheKey = "";
|
|
1252
|
-
// Criteria
|
|
1253
|
-
if (this.tables[tableName].config.cache)
|
|
1254
|
-
cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
|
|
1255
|
-
if (where) {
|
|
1256
|
-
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1257
|
-
keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
|
|
1258
|
-
isLineNumbers = false;
|
|
1259
|
-
if (!keepItems.length)
|
|
1260
|
-
throw this.throwError("NO_RESULTS", tableName);
|
|
1261
|
-
keepItems = keepItems.slice((options.page - 1) * options.perPage, options.page * options.perPage);
|
|
1262
|
-
}
|
|
1263
|
-
if (!keepItems.length)
|
|
1264
|
-
keepItems = Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
1265
|
-
index +
|
|
1266
|
-
1);
|
|
1267
|
-
const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension(tableName)}`));
|
|
1268
|
-
// Construct the paste command to merge files and filter lines by IDs
|
|
1269
|
-
const pasteCommand = `paste ${filesPathes.join(" ")}`;
|
|
1270
|
-
// Construct the sort command dynamically based on the number of files for sorting
|
|
1271
|
-
const index = 2;
|
|
1272
|
-
const sortColumns = sortArray
|
|
1273
|
-
.map(([key, ascending], i) => {
|
|
1274
|
-
const field = Utils.getField(key, schema);
|
|
1275
|
-
if (field)
|
|
1276
|
-
return `-k${i + index},${i + index}${field.type === "number" ? "n" : ""}${!ascending ? "r" : ""}`;
|
|
1277
|
-
return "";
|
|
1278
|
-
})
|
|
1279
|
-
.join(" ");
|
|
1280
|
-
const sortCommand = `sort ${sortColumns}`;
|
|
1281
|
-
// Construct the awk command to keep only the specified lines after sorting
|
|
1282
|
-
const awkCommand = isLineNumbers
|
|
1283
|
-
? `awk '${keepItems.map((line) => `NR==${line}`).join(" || ")}'`
|
|
1284
|
-
: `awk 'NR==${keepItems[0]}${keepItems
|
|
1285
|
-
.map((num) => `||NR==${num}`)
|
|
1286
|
-
.join("")}'`;
|
|
1287
|
-
try {
|
|
1288
|
-
if (cacheKey)
|
|
1289
|
-
await File.lock(join(tablePath, ".tmp"), cacheKey);
|
|
1290
|
-
// Combine the commands
|
|
1291
|
-
// Execute the command synchronously
|
|
1292
|
-
const lines = (await UtilsServer.exec(this.tables[tableName].config.cache
|
|
1293
|
-
? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
|
|
1294
|
-
? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
|
|
1295
|
-
: `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
|
|
1296
|
-
: `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
|
|
1297
|
-
encoding: "utf-8",
|
|
1298
|
-
})).stdout
|
|
1299
|
-
.trim()
|
|
1300
|
-
.split("\n");
|
|
1301
|
-
// Parse the result and extract the specified lines
|
|
1302
|
-
const outputArray = lines.map((line) => {
|
|
1303
|
-
const splitedFileColumns = line.split("\t"); // Assuming tab-separated columns
|
|
1304
|
-
const outputObject = {};
|
|
1305
|
-
// Extract values for each file, including `id${this.getFileExtension(tableName)}`
|
|
1306
|
-
filesPathes.forEach((fileName, index) => {
|
|
1307
|
-
const field = Utils.getField(parse(fileName).name, schema);
|
|
1308
|
-
if (field)
|
|
1309
|
-
outputObject[field.key] = File.decode(splitedFileColumns[index], field?.type, field?.children, this.salt);
|
|
1310
|
-
});
|
|
1311
|
-
return outputObject;
|
|
1312
|
-
});
|
|
1313
|
-
const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), options, undefined, undefined, true);
|
|
1314
|
-
return restOfColumns
|
|
1315
|
-
? outputArray.map((item, index) => ({
|
|
1316
|
-
...item,
|
|
1317
|
-
...restOfColumns[index],
|
|
1318
|
-
}))
|
|
1319
|
-
: outputArray;
|
|
1320
|
-
}
|
|
1321
|
-
finally {
|
|
1322
|
-
if (cacheKey)
|
|
1323
|
-
await File.unlock(join(tablePath, ".tmp"), cacheKey);
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
1310
|
}
|
package/dist/utils.js
CHANGED
|
@@ -417,10 +417,12 @@ export function FormatObjectCriteriaValue(value) {
|
|
|
417
417
|
* @param {Schema} schema
|
|
418
418
|
*/
|
|
419
419
|
export function getField(keyPath, schema) {
|
|
420
|
-
let RETURN =
|
|
420
|
+
let RETURN = schema;
|
|
421
421
|
const keyPathSplited = keyPath.split(".");
|
|
422
422
|
for (const [index, key] of keyPathSplited.entries()) {
|
|
423
|
-
|
|
423
|
+
if (!isArrayOfObjects(RETURN))
|
|
424
|
+
return null;
|
|
425
|
+
const foundItem = RETURN.find((item) => item.key === key);
|
|
424
426
|
if (!foundItem)
|
|
425
427
|
return null;
|
|
426
428
|
if (index === keyPathSplited.length - 1)
|