@nmakarov/cli-toolkit 0.11.3 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db.cjs +97 -2
- package/dist/db.cjs.map +1 -1
- package/dist/db.js +93 -1
- package/dist/db.js.map +1 -1
- package/dist/filedatabase.cjs +195 -15
- package/dist/filedatabase.cjs.map +1 -1
- package/dist/filedatabase.js +192 -14
- package/dist/filedatabase.js.map +1 -1
- package/dist/index.cjs +288 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +283 -13
- package/dist/index.js.map +1 -1
- package/dist/mock-server.cjs +168 -13
- package/dist/mock-server.cjs.map +1 -1
- package/dist/mock-server.js +168 -13
- package/dist/mock-server.js.map +1 -1
- package/dist/utils.cjs +251 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.js +199 -0
- package/dist/utils.js.map +1 -0
- package/package.json +6 -1
package/dist/mock-server.cjs
CHANGED
|
@@ -584,20 +584,48 @@ var FileDatabase = class {
|
|
|
584
584
|
}
|
|
585
585
|
/**
|
|
586
586
|
* Figure out what data to write and which file to use (for pagination)
|
|
587
|
+
* @param data - Data to write
|
|
588
|
+
* @param targetFileIndex - Optional index of existing file to overwrite (when customMetadata matches)
|
|
589
|
+
* @param forceNewFile - If true, always create a new file (when customMetadata provided but no match)
|
|
587
590
|
*/
|
|
588
|
-
figureOutDataAndFileToWrite(data) {
|
|
591
|
+
figureOutDataAndFileToWrite(data, targetFileIndex = null, forceNewFile = false) {
|
|
589
592
|
let dataToWrite;
|
|
590
593
|
let dataLeftOver;
|
|
591
594
|
const incomingDataType = detectDataType(data);
|
|
592
595
|
if (this.metadata.dataType !== incomingDataType) {
|
|
593
596
|
this.metadata.dataType = incomingDataType;
|
|
594
597
|
}
|
|
595
|
-
if (this.metadata.files.length
|
|
598
|
+
if (targetFileIndex !== null && targetFileIndex < this.metadata.files.length) {
|
|
599
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
600
|
+
if (!Array.isArray(data)) {
|
|
601
|
+
dataToWrite = data;
|
|
602
|
+
dataLeftOver = null;
|
|
603
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
604
|
+
} else {
|
|
605
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
606
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
607
|
+
this.lastFileData = dataToWrite;
|
|
608
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
let newlyCreatedFileIndex = null;
|
|
612
|
+
if (forceNewFile) {
|
|
613
|
+
const filesBeforeCreate = this.metadata.files.length;
|
|
614
|
+
this.makeNewFile();
|
|
615
|
+
newlyCreatedFileIndex = filesBeforeCreate;
|
|
616
|
+
this.logger.debug?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
|
|
617
|
+
} else if (this.metadata.files.length === 0) {
|
|
596
618
|
this.makeNewFile();
|
|
597
619
|
}
|
|
598
620
|
const lastFile = this.metadata.files[this.metadata.files.length - 1];
|
|
599
621
|
const lastFileRecordsCount = lastFile.recordsCount;
|
|
600
|
-
if (
|
|
622
|
+
if (forceNewFile && newlyCreatedFileIndex !== null) {
|
|
623
|
+
const newlyCreatedFile = this.metadata.files[newlyCreatedFileIndex];
|
|
624
|
+
if (newlyCreatedFile && newlyCreatedFile.fileName !== lastFile.fileName) {
|
|
625
|
+
this.logger.warn?.(`[FileDatabase] Warning: Newly created file ${newlyCreatedFile.fileName} doesn't match last file ${lastFile.fileName}`);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (!Array.isArray(data) && !forceNewFile) {
|
|
601
629
|
const lastFileExtension = import_path3.default.extname(lastFile.fileName);
|
|
602
630
|
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
603
631
|
if (lastFileExtension !== expectedExtension) {
|
|
@@ -607,24 +635,35 @@ var FileDatabase = class {
|
|
|
607
635
|
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
608
636
|
}
|
|
609
637
|
}
|
|
638
|
+
} else if (!Array.isArray(data) && forceNewFile) {
|
|
639
|
+
const lastFileExtension = import_path3.default.extname(lastFile.fileName);
|
|
640
|
+
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
641
|
+
if (lastFileExtension !== expectedExtension) {
|
|
642
|
+
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
643
|
+
}
|
|
610
644
|
}
|
|
611
645
|
if (Array.isArray(data)) {
|
|
612
|
-
if (
|
|
646
|
+
if (forceNewFile) {
|
|
647
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
648
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
649
|
+
this.lastFileData = dataToWrite;
|
|
650
|
+
} else if (lastFileRecordsCount < this.pageSize) {
|
|
613
651
|
dataToWrite = [...this.lastFileData || [], ...data.slice(0, this.pageSize - lastFileRecordsCount)];
|
|
614
652
|
dataLeftOver = data.slice(this.pageSize - lastFileRecordsCount);
|
|
653
|
+
this.lastFileData = dataToWrite;
|
|
615
654
|
} else {
|
|
616
655
|
this.makeNewFile();
|
|
617
656
|
dataToWrite = data.slice(0, this.pageSize);
|
|
618
657
|
dataLeftOver = data.slice(this.pageSize);
|
|
658
|
+
this.lastFileData = dataToWrite;
|
|
619
659
|
}
|
|
620
|
-
this.lastFileData = dataToWrite;
|
|
621
660
|
} else {
|
|
622
661
|
dataToWrite = data;
|
|
623
662
|
dataLeftOver = null;
|
|
624
663
|
}
|
|
625
664
|
const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;
|
|
626
665
|
this.logger.debug?.(
|
|
627
|
-
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
666
|
+
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, forceNewFile=${forceNewFile}, targetFileIndex=${targetFileIndex}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
628
667
|
);
|
|
629
668
|
return { dataToWrite, dataLeftOver, fileName };
|
|
630
669
|
}
|
|
@@ -652,7 +691,7 @@ var FileDatabase = class {
|
|
|
652
691
|
/**
|
|
653
692
|
* Update metadata after writing data
|
|
654
693
|
*/
|
|
655
|
-
updateMetadata(dataToWrite, fileName) {
|
|
694
|
+
updateMetadata(dataToWrite, fileName, customMetadata) {
|
|
656
695
|
let currentFile;
|
|
657
696
|
if (fileName) {
|
|
658
697
|
const foundFile = this.metadata.files.find((file) => file.fileName === fileName);
|
|
@@ -667,6 +706,9 @@ var FileDatabase = class {
|
|
|
667
706
|
}
|
|
668
707
|
const recordsCount = Array.isArray(dataToWrite) ? dataToWrite.length : 1;
|
|
669
708
|
currentFile.recordsCount = recordsCount;
|
|
709
|
+
if (customMetadata) {
|
|
710
|
+
Object.assign(currentFile, customMetadata);
|
|
711
|
+
}
|
|
670
712
|
const fileIndex = this.metadata.files.indexOf(currentFile);
|
|
671
713
|
if (fileIndex !== -1) {
|
|
672
714
|
this.calculateFileSynopsis(dataToWrite, fileIndex);
|
|
@@ -719,6 +761,11 @@ var FileDatabase = class {
|
|
|
719
761
|
} else {
|
|
720
762
|
if (!this.metadata.files.length) {
|
|
721
763
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
764
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
765
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
766
|
+
} else {
|
|
767
|
+
this.currentFileNumber = 0;
|
|
768
|
+
}
|
|
722
769
|
}
|
|
723
770
|
}
|
|
724
771
|
} else {
|
|
@@ -729,16 +776,22 @@ var FileDatabase = class {
|
|
|
729
776
|
try {
|
|
730
777
|
const rawData = await import_fs3.default.promises.readFile(metadataPath, "utf8");
|
|
731
778
|
this.metadata = JSON.parse(rawData);
|
|
779
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
780
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
781
|
+
} else {
|
|
782
|
+
this.currentFileNumber = 0;
|
|
783
|
+
}
|
|
732
784
|
} catch (e) {
|
|
733
785
|
this.metadata = this.getDefaultMetadata();
|
|
786
|
+
this.currentFileNumber = 0;
|
|
734
787
|
}
|
|
735
788
|
} else {
|
|
736
789
|
this.metadata = this.getDefaultMetadata();
|
|
737
|
-
this.
|
|
790
|
+
this.currentFileNumber = 0;
|
|
738
791
|
}
|
|
739
792
|
} else {
|
|
740
793
|
this.metadata = this.getDefaultMetadata();
|
|
741
|
-
this.
|
|
794
|
+
this.currentFileNumber = 0;
|
|
742
795
|
}
|
|
743
796
|
}
|
|
744
797
|
} else if (read) {
|
|
@@ -757,6 +810,11 @@ var FileDatabase = class {
|
|
|
757
810
|
}
|
|
758
811
|
if (!this.metadata.files.length) {
|
|
759
812
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
813
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
814
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
815
|
+
} else {
|
|
816
|
+
this.currentFileNumber = 0;
|
|
817
|
+
}
|
|
760
818
|
}
|
|
761
819
|
} else {
|
|
762
820
|
this.currentVersion = null;
|
|
@@ -770,6 +828,11 @@ var FileDatabase = class {
|
|
|
770
828
|
try {
|
|
771
829
|
const rawData = await import_fs3.default.promises.readFile(metadataPath, "utf8");
|
|
772
830
|
this.metadata = JSON.parse(rawData);
|
|
831
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
832
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
833
|
+
} else {
|
|
834
|
+
this.currentFileNumber = 0;
|
|
835
|
+
}
|
|
773
836
|
} catch (e) {
|
|
774
837
|
throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
|
|
775
838
|
}
|
|
@@ -778,6 +841,11 @@ var FileDatabase = class {
|
|
|
778
841
|
}
|
|
779
842
|
} else {
|
|
780
843
|
this.metadata = await this.figureMetadataFromVersionFiles("");
|
|
844
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
845
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
846
|
+
} else {
|
|
847
|
+
this.currentFileNumber = 0;
|
|
848
|
+
}
|
|
781
849
|
}
|
|
782
850
|
}
|
|
783
851
|
}
|
|
@@ -799,14 +867,44 @@ var FileDatabase = class {
|
|
|
799
867
|
this.metadata.dataType = incomingDataType;
|
|
800
868
|
this.makeNewFile();
|
|
801
869
|
}
|
|
802
|
-
let
|
|
870
|
+
let targetFileIndex = null;
|
|
871
|
+
const hasCustomMetadata = options.customMetadata && Object.keys(options.customMetadata).length > 0;
|
|
872
|
+
if (hasCustomMetadata) {
|
|
873
|
+
for (let i = 0; i < this.metadata.files.length; i++) {
|
|
874
|
+
const fileEntry = this.metadata.files[i];
|
|
875
|
+
const matches = Object.keys(options.customMetadata).every((key) => {
|
|
876
|
+
return key in fileEntry && fileEntry[key] === options.customMetadata[key];
|
|
877
|
+
});
|
|
878
|
+
if (matches) {
|
|
879
|
+
targetFileIndex = i;
|
|
880
|
+
this.logger.debug?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
881
|
+
break;
|
|
882
|
+
} else {
|
|
883
|
+
this.logger.debug?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (targetFileIndex === null) {
|
|
887
|
+
this.logger.debug?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
|
|
888
|
+
}
|
|
889
|
+
} else {
|
|
890
|
+
this.logger.debug?.(`[FileDatabase] No custom metadata provided, will create new file`);
|
|
891
|
+
}
|
|
892
|
+
if (targetFileIndex !== null) {
|
|
893
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
894
|
+
this.currentFileNumber = targetFile.number;
|
|
895
|
+
this.lastFileData = null;
|
|
896
|
+
this.currentRecord = 0;
|
|
897
|
+
this.hasReadFirstPage = false;
|
|
898
|
+
}
|
|
899
|
+
const forceNewFile = hasCustomMetadata && targetFileIndex === null;
|
|
900
|
+
let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data, targetFileIndex, forceNewFile);
|
|
803
901
|
const destPath = this.getDestinationPath(this.currentVersion || void 0);
|
|
804
902
|
await this.safeWrite(import_path3.default.join(destPath, fileName), dataToWrite);
|
|
805
|
-
this.updateMetadata(dataToWrite, fileName);
|
|
806
|
-
while (dataLeftOver && dataLeftOver.length > 0) {
|
|
903
|
+
this.updateMetadata(dataToWrite, fileName, options.customMetadata);
|
|
904
|
+
while (dataLeftOver && dataLeftOver.length > 0 && targetFileIndex === null) {
|
|
807
905
|
const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);
|
|
808
906
|
await this.safeWrite(import_path3.default.join(destPath, writeContext.fileName), writeContext.dataToWrite);
|
|
809
|
-
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);
|
|
907
|
+
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName, options.customMetadata);
|
|
810
908
|
dataLeftOver = writeContext.dataLeftOver;
|
|
811
909
|
}
|
|
812
910
|
this.calculateVersionSynopsis();
|
|
@@ -923,6 +1021,63 @@ var FileDatabase = class {
|
|
|
923
1021
|
getMetadata() {
|
|
924
1022
|
return { ...this.metadata };
|
|
925
1023
|
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Find data by custom metadata fields
|
|
1026
|
+
* Searches through all versions and files to find entries matching the search criteria
|
|
1027
|
+
*
|
|
1028
|
+
* @param searchCriteria - Object with field names and values to search for (e.g., { ListingKey: "123", id: "456" })
|
|
1029
|
+
* @returns Array of found entries with their file paths and metadata
|
|
1030
|
+
*/
|
|
1031
|
+
async findData(searchCriteria) {
|
|
1032
|
+
const results = [];
|
|
1033
|
+
if (!this.versioned) {
|
|
1034
|
+
await this.prepare({ read: true });
|
|
1035
|
+
const metadata = this.getMetadata();
|
|
1036
|
+
for (const fileEntry of metadata.files) {
|
|
1037
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
1038
|
+
return fileEntry[key] === searchCriteria[key];
|
|
1039
|
+
});
|
|
1040
|
+
if (matches) {
|
|
1041
|
+
const destPath = this.getDestinationPath();
|
|
1042
|
+
const filePath = import_path3.default.join(destPath, fileEntry.fileName);
|
|
1043
|
+
const fileData = await import_fs3.default.promises.readFile(filePath, "utf8");
|
|
1044
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
1045
|
+
results.push({
|
|
1046
|
+
filePath,
|
|
1047
|
+
fileName: fileEntry.fileName,
|
|
1048
|
+
version: null,
|
|
1049
|
+
metadata: fileEntry,
|
|
1050
|
+
data
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
} else {
|
|
1055
|
+
const versions = await this.getVersions();
|
|
1056
|
+
for (const version of versions) {
|
|
1057
|
+
await this.prepare({ read: true, version });
|
|
1058
|
+
const metadata = this.getMetadata();
|
|
1059
|
+
for (const fileEntry of metadata.files) {
|
|
1060
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
1061
|
+
return fileEntry[key] === searchCriteria[key];
|
|
1062
|
+
});
|
|
1063
|
+
if (matches) {
|
|
1064
|
+
const destPath = this.getDestinationPath(version);
|
|
1065
|
+
const filePath = import_path3.default.join(destPath, fileEntry.fileName);
|
|
1066
|
+
const fileData = await import_fs3.default.promises.readFile(filePath, "utf8");
|
|
1067
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
1068
|
+
results.push({
|
|
1069
|
+
filePath,
|
|
1070
|
+
fileName: fileEntry.fileName,
|
|
1071
|
+
version,
|
|
1072
|
+
metadata: fileEntry,
|
|
1073
|
+
data
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return results;
|
|
1080
|
+
}
|
|
926
1081
|
};
|
|
927
1082
|
|
|
928
1083
|
// src/mock-server/catalog.ts
|