@nmakarov/cli-toolkit 0.11.4 → 0.14.2
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/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 +195 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +193 -13
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +2 -0
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +2 -0
- package/dist/init.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/screen.cjs +2 -0
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +2 -0
- package/dist/screen.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/filedatabase.js
CHANGED
|
@@ -610,20 +610,48 @@ var FileDatabase = class {
|
|
|
610
610
|
}
|
|
611
611
|
/**
|
|
612
612
|
* Figure out what data to write and which file to use (for pagination)
|
|
613
|
+
* @param data - Data to write
|
|
614
|
+
* @param targetFileIndex - Optional index of existing file to overwrite (when customMetadata matches)
|
|
615
|
+
* @param forceNewFile - If true, always create a new file (when customMetadata provided but no match)
|
|
613
616
|
*/
|
|
614
|
-
figureOutDataAndFileToWrite(data) {
|
|
617
|
+
figureOutDataAndFileToWrite(data, targetFileIndex = null, forceNewFile = false) {
|
|
615
618
|
let dataToWrite;
|
|
616
619
|
let dataLeftOver;
|
|
617
620
|
const incomingDataType = detectDataType(data);
|
|
618
621
|
if (this.metadata.dataType !== incomingDataType) {
|
|
619
622
|
this.metadata.dataType = incomingDataType;
|
|
620
623
|
}
|
|
621
|
-
if (this.metadata.files.length
|
|
624
|
+
if (targetFileIndex !== null && targetFileIndex < this.metadata.files.length) {
|
|
625
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
626
|
+
if (!Array.isArray(data)) {
|
|
627
|
+
dataToWrite = data;
|
|
628
|
+
dataLeftOver = null;
|
|
629
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
630
|
+
} else {
|
|
631
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
632
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
633
|
+
this.lastFileData = dataToWrite;
|
|
634
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
let newlyCreatedFileIndex = null;
|
|
638
|
+
if (forceNewFile) {
|
|
639
|
+
const filesBeforeCreate = this.metadata.files.length;
|
|
640
|
+
this.makeNewFile();
|
|
641
|
+
newlyCreatedFileIndex = filesBeforeCreate;
|
|
642
|
+
this.logger.debug?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
|
|
643
|
+
} else if (this.metadata.files.length === 0) {
|
|
622
644
|
this.makeNewFile();
|
|
623
645
|
}
|
|
624
646
|
const lastFile = this.metadata.files[this.metadata.files.length - 1];
|
|
625
647
|
const lastFileRecordsCount = lastFile.recordsCount;
|
|
626
|
-
if (
|
|
648
|
+
if (forceNewFile && newlyCreatedFileIndex !== null) {
|
|
649
|
+
const newlyCreatedFile = this.metadata.files[newlyCreatedFileIndex];
|
|
650
|
+
if (newlyCreatedFile && newlyCreatedFile.fileName !== lastFile.fileName) {
|
|
651
|
+
this.logger.warn?.(`[FileDatabase] Warning: Newly created file ${newlyCreatedFile.fileName} doesn't match last file ${lastFile.fileName}`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
if (!Array.isArray(data) && !forceNewFile) {
|
|
627
655
|
const lastFileExtension = path3.extname(lastFile.fileName);
|
|
628
656
|
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
629
657
|
if (lastFileExtension !== expectedExtension) {
|
|
@@ -633,24 +661,35 @@ var FileDatabase = class {
|
|
|
633
661
|
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
634
662
|
}
|
|
635
663
|
}
|
|
664
|
+
} else if (!Array.isArray(data) && forceNewFile) {
|
|
665
|
+
const lastFileExtension = path3.extname(lastFile.fileName);
|
|
666
|
+
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
667
|
+
if (lastFileExtension !== expectedExtension) {
|
|
668
|
+
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
669
|
+
}
|
|
636
670
|
}
|
|
637
671
|
if (Array.isArray(data)) {
|
|
638
|
-
if (
|
|
672
|
+
if (forceNewFile) {
|
|
673
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
674
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
675
|
+
this.lastFileData = dataToWrite;
|
|
676
|
+
} else if (lastFileRecordsCount < this.pageSize) {
|
|
639
677
|
dataToWrite = [...this.lastFileData || [], ...data.slice(0, this.pageSize - lastFileRecordsCount)];
|
|
640
678
|
dataLeftOver = data.slice(this.pageSize - lastFileRecordsCount);
|
|
679
|
+
this.lastFileData = dataToWrite;
|
|
641
680
|
} else {
|
|
642
681
|
this.makeNewFile();
|
|
643
682
|
dataToWrite = data.slice(0, this.pageSize);
|
|
644
683
|
dataLeftOver = data.slice(this.pageSize);
|
|
684
|
+
this.lastFileData = dataToWrite;
|
|
645
685
|
}
|
|
646
|
-
this.lastFileData = dataToWrite;
|
|
647
686
|
} else {
|
|
648
687
|
dataToWrite = data;
|
|
649
688
|
dataLeftOver = null;
|
|
650
689
|
}
|
|
651
690
|
const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;
|
|
652
691
|
this.logger.debug?.(
|
|
653
|
-
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
692
|
+
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, forceNewFile=${forceNewFile}, targetFileIndex=${targetFileIndex}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
654
693
|
);
|
|
655
694
|
return { dataToWrite, dataLeftOver, fileName };
|
|
656
695
|
}
|
|
@@ -678,7 +717,7 @@ var FileDatabase = class {
|
|
|
678
717
|
/**
|
|
679
718
|
* Update metadata after writing data
|
|
680
719
|
*/
|
|
681
|
-
updateMetadata(dataToWrite, fileName) {
|
|
720
|
+
updateMetadata(dataToWrite, fileName, customMetadata) {
|
|
682
721
|
let currentFile;
|
|
683
722
|
if (fileName) {
|
|
684
723
|
const foundFile = this.metadata.files.find((file) => file.fileName === fileName);
|
|
@@ -693,6 +732,9 @@ var FileDatabase = class {
|
|
|
693
732
|
}
|
|
694
733
|
const recordsCount = Array.isArray(dataToWrite) ? dataToWrite.length : 1;
|
|
695
734
|
currentFile.recordsCount = recordsCount;
|
|
735
|
+
if (customMetadata) {
|
|
736
|
+
Object.assign(currentFile, customMetadata);
|
|
737
|
+
}
|
|
696
738
|
const fileIndex = this.metadata.files.indexOf(currentFile);
|
|
697
739
|
if (fileIndex !== -1) {
|
|
698
740
|
this.calculateFileSynopsis(dataToWrite, fileIndex);
|
|
@@ -745,6 +787,11 @@ var FileDatabase = class {
|
|
|
745
787
|
} else {
|
|
746
788
|
if (!this.metadata.files.length) {
|
|
747
789
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
790
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
791
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
792
|
+
} else {
|
|
793
|
+
this.currentFileNumber = 0;
|
|
794
|
+
}
|
|
748
795
|
}
|
|
749
796
|
}
|
|
750
797
|
} else {
|
|
@@ -755,16 +802,22 @@ var FileDatabase = class {
|
|
|
755
802
|
try {
|
|
756
803
|
const rawData = await fs3.promises.readFile(metadataPath, "utf8");
|
|
757
804
|
this.metadata = JSON.parse(rawData);
|
|
805
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
806
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
807
|
+
} else {
|
|
808
|
+
this.currentFileNumber = 0;
|
|
809
|
+
}
|
|
758
810
|
} catch (e) {
|
|
759
811
|
this.metadata = this.getDefaultMetadata();
|
|
812
|
+
this.currentFileNumber = 0;
|
|
760
813
|
}
|
|
761
814
|
} else {
|
|
762
815
|
this.metadata = this.getDefaultMetadata();
|
|
763
|
-
this.
|
|
816
|
+
this.currentFileNumber = 0;
|
|
764
817
|
}
|
|
765
818
|
} else {
|
|
766
819
|
this.metadata = this.getDefaultMetadata();
|
|
767
|
-
this.
|
|
820
|
+
this.currentFileNumber = 0;
|
|
768
821
|
}
|
|
769
822
|
}
|
|
770
823
|
} else if (read) {
|
|
@@ -783,6 +836,11 @@ var FileDatabase = class {
|
|
|
783
836
|
}
|
|
784
837
|
if (!this.metadata.files.length) {
|
|
785
838
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
839
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
840
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
841
|
+
} else {
|
|
842
|
+
this.currentFileNumber = 0;
|
|
843
|
+
}
|
|
786
844
|
}
|
|
787
845
|
} else {
|
|
788
846
|
this.currentVersion = null;
|
|
@@ -796,6 +854,11 @@ var FileDatabase = class {
|
|
|
796
854
|
try {
|
|
797
855
|
const rawData = await fs3.promises.readFile(metadataPath, "utf8");
|
|
798
856
|
this.metadata = JSON.parse(rawData);
|
|
857
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
858
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
859
|
+
} else {
|
|
860
|
+
this.currentFileNumber = 0;
|
|
861
|
+
}
|
|
799
862
|
} catch (e) {
|
|
800
863
|
throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
|
|
801
864
|
}
|
|
@@ -804,6 +867,11 @@ var FileDatabase = class {
|
|
|
804
867
|
}
|
|
805
868
|
} else {
|
|
806
869
|
this.metadata = await this.figureMetadataFromVersionFiles("");
|
|
870
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
871
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
872
|
+
} else {
|
|
873
|
+
this.currentFileNumber = 0;
|
|
874
|
+
}
|
|
807
875
|
}
|
|
808
876
|
}
|
|
809
877
|
}
|
|
@@ -825,14 +893,44 @@ var FileDatabase = class {
|
|
|
825
893
|
this.metadata.dataType = incomingDataType;
|
|
826
894
|
this.makeNewFile();
|
|
827
895
|
}
|
|
828
|
-
let
|
|
896
|
+
let targetFileIndex = null;
|
|
897
|
+
const hasCustomMetadata = options.customMetadata && Object.keys(options.customMetadata).length > 0;
|
|
898
|
+
if (hasCustomMetadata) {
|
|
899
|
+
for (let i = 0; i < this.metadata.files.length; i++) {
|
|
900
|
+
const fileEntry = this.metadata.files[i];
|
|
901
|
+
const matches = Object.keys(options.customMetadata).every((key) => {
|
|
902
|
+
return key in fileEntry && fileEntry[key] === options.customMetadata[key];
|
|
903
|
+
});
|
|
904
|
+
if (matches) {
|
|
905
|
+
targetFileIndex = i;
|
|
906
|
+
this.logger.debug?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
907
|
+
break;
|
|
908
|
+
} else {
|
|
909
|
+
this.logger.debug?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (targetFileIndex === null) {
|
|
913
|
+
this.logger.debug?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
|
|
914
|
+
}
|
|
915
|
+
} else {
|
|
916
|
+
this.logger.debug?.(`[FileDatabase] No custom metadata provided, will create new file`);
|
|
917
|
+
}
|
|
918
|
+
if (targetFileIndex !== null) {
|
|
919
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
920
|
+
this.currentFileNumber = targetFile.number;
|
|
921
|
+
this.lastFileData = null;
|
|
922
|
+
this.currentRecord = 0;
|
|
923
|
+
this.hasReadFirstPage = false;
|
|
924
|
+
}
|
|
925
|
+
const forceNewFile = hasCustomMetadata && targetFileIndex === null;
|
|
926
|
+
let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data, targetFileIndex, forceNewFile);
|
|
829
927
|
const destPath = this.getDestinationPath(this.currentVersion || void 0);
|
|
830
928
|
await this.safeWrite(path3.join(destPath, fileName), dataToWrite);
|
|
831
|
-
this.updateMetadata(dataToWrite, fileName);
|
|
832
|
-
while (dataLeftOver && dataLeftOver.length > 0) {
|
|
929
|
+
this.updateMetadata(dataToWrite, fileName, options.customMetadata);
|
|
930
|
+
while (dataLeftOver && dataLeftOver.length > 0 && targetFileIndex === null) {
|
|
833
931
|
const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);
|
|
834
932
|
await this.safeWrite(path3.join(destPath, writeContext.fileName), writeContext.dataToWrite);
|
|
835
|
-
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);
|
|
933
|
+
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName, options.customMetadata);
|
|
836
934
|
dataLeftOver = writeContext.dataLeftOver;
|
|
837
935
|
}
|
|
838
936
|
this.calculateVersionSynopsis();
|
|
@@ -949,7 +1047,85 @@ var FileDatabase = class {
|
|
|
949
1047
|
getMetadata() {
|
|
950
1048
|
return { ...this.metadata };
|
|
951
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Find data by custom metadata fields
|
|
1052
|
+
* Searches through all versions and files to find entries matching the search criteria
|
|
1053
|
+
*
|
|
1054
|
+
* @param searchCriteria - Object with field names and values to search for (e.g., { ListingKey: "123", id: "456" })
|
|
1055
|
+
* @returns Array of found entries with their file paths and metadata
|
|
1056
|
+
*/
|
|
1057
|
+
async findData(searchCriteria) {
|
|
1058
|
+
const results = [];
|
|
1059
|
+
if (!this.versioned) {
|
|
1060
|
+
await this.prepare({ read: true });
|
|
1061
|
+
const metadata = this.getMetadata();
|
|
1062
|
+
for (const fileEntry of metadata.files) {
|
|
1063
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
1064
|
+
return fileEntry[key] === searchCriteria[key];
|
|
1065
|
+
});
|
|
1066
|
+
if (matches) {
|
|
1067
|
+
const destPath = this.getDestinationPath();
|
|
1068
|
+
const filePath = path3.join(destPath, fileEntry.fileName);
|
|
1069
|
+
const fileData = await fs3.promises.readFile(filePath, "utf8");
|
|
1070
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
1071
|
+
results.push({
|
|
1072
|
+
filePath,
|
|
1073
|
+
fileName: fileEntry.fileName,
|
|
1074
|
+
version: null,
|
|
1075
|
+
metadata: fileEntry,
|
|
1076
|
+
data
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
} else {
|
|
1081
|
+
const versions = await this.getVersions();
|
|
1082
|
+
for (const version of versions) {
|
|
1083
|
+
await this.prepare({ read: true, version });
|
|
1084
|
+
const metadata = this.getMetadata();
|
|
1085
|
+
for (const fileEntry of metadata.files) {
|
|
1086
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
1087
|
+
return fileEntry[key] === searchCriteria[key];
|
|
1088
|
+
});
|
|
1089
|
+
if (matches) {
|
|
1090
|
+
const destPath = this.getDestinationPath(version);
|
|
1091
|
+
const filePath = path3.join(destPath, fileEntry.fileName);
|
|
1092
|
+
const fileData = await fs3.promises.readFile(filePath, "utf8");
|
|
1093
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
1094
|
+
results.push({
|
|
1095
|
+
filePath,
|
|
1096
|
+
fileName: fileEntry.fileName,
|
|
1097
|
+
version,
|
|
1098
|
+
metadata: fileEntry,
|
|
1099
|
+
data
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
return results;
|
|
1106
|
+
}
|
|
952
1107
|
};
|
|
1108
|
+
function listTables(basePath, namespace) {
|
|
1109
|
+
const namespacePath = path3.join(basePath, namespace);
|
|
1110
|
+
if (!fs3.existsSync(namespacePath)) {
|
|
1111
|
+
return [];
|
|
1112
|
+
}
|
|
1113
|
+
try {
|
|
1114
|
+
return fs3.readdirSync(namespacePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
return [];
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
function listSources(basePath) {
|
|
1120
|
+
if (!fs3.existsSync(basePath)) {
|
|
1121
|
+
return [];
|
|
1122
|
+
}
|
|
1123
|
+
try {
|
|
1124
|
+
return fs3.readdirSync(basePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
return [];
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
953
1129
|
function fileDatabaseInit(context, options = {}) {
|
|
954
1130
|
return new FileDatabase(context, options);
|
|
955
1131
|
}
|
|
@@ -958,6 +1134,8 @@ export {
|
|
|
958
1134
|
FileDatabaseError,
|
|
959
1135
|
defaultFileSynopsisFunction,
|
|
960
1136
|
defaultVersionSynopsisFunction,
|
|
961
|
-
fileDatabaseInit
|
|
1137
|
+
fileDatabaseInit,
|
|
1138
|
+
listSources,
|
|
1139
|
+
listTables
|
|
962
1140
|
};
|
|
963
1141
|
//# sourceMappingURL=filedatabase.js.map
|