@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.js
CHANGED
|
@@ -547,20 +547,48 @@ var FileDatabase = class {
|
|
|
547
547
|
}
|
|
548
548
|
/**
|
|
549
549
|
* Figure out what data to write and which file to use (for pagination)
|
|
550
|
+
* @param data - Data to write
|
|
551
|
+
* @param targetFileIndex - Optional index of existing file to overwrite (when customMetadata matches)
|
|
552
|
+
* @param forceNewFile - If true, always create a new file (when customMetadata provided but no match)
|
|
550
553
|
*/
|
|
551
|
-
figureOutDataAndFileToWrite(data) {
|
|
554
|
+
figureOutDataAndFileToWrite(data, targetFileIndex = null, forceNewFile = false) {
|
|
552
555
|
let dataToWrite;
|
|
553
556
|
let dataLeftOver;
|
|
554
557
|
const incomingDataType = detectDataType(data);
|
|
555
558
|
if (this.metadata.dataType !== incomingDataType) {
|
|
556
559
|
this.metadata.dataType = incomingDataType;
|
|
557
560
|
}
|
|
558
|
-
if (this.metadata.files.length
|
|
561
|
+
if (targetFileIndex !== null && targetFileIndex < this.metadata.files.length) {
|
|
562
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
563
|
+
if (!Array.isArray(data)) {
|
|
564
|
+
dataToWrite = data;
|
|
565
|
+
dataLeftOver = null;
|
|
566
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
567
|
+
} else {
|
|
568
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
569
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
570
|
+
this.lastFileData = dataToWrite;
|
|
571
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
let newlyCreatedFileIndex = null;
|
|
575
|
+
if (forceNewFile) {
|
|
576
|
+
const filesBeforeCreate = this.metadata.files.length;
|
|
577
|
+
this.makeNewFile();
|
|
578
|
+
newlyCreatedFileIndex = filesBeforeCreate;
|
|
579
|
+
this.logger.debug?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
|
|
580
|
+
} else if (this.metadata.files.length === 0) {
|
|
559
581
|
this.makeNewFile();
|
|
560
582
|
}
|
|
561
583
|
const lastFile = this.metadata.files[this.metadata.files.length - 1];
|
|
562
584
|
const lastFileRecordsCount = lastFile.recordsCount;
|
|
563
|
-
if (
|
|
585
|
+
if (forceNewFile && newlyCreatedFileIndex !== null) {
|
|
586
|
+
const newlyCreatedFile = this.metadata.files[newlyCreatedFileIndex];
|
|
587
|
+
if (newlyCreatedFile && newlyCreatedFile.fileName !== lastFile.fileName) {
|
|
588
|
+
this.logger.warn?.(`[FileDatabase] Warning: Newly created file ${newlyCreatedFile.fileName} doesn't match last file ${lastFile.fileName}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (!Array.isArray(data) && !forceNewFile) {
|
|
564
592
|
const lastFileExtension = path3.extname(lastFile.fileName);
|
|
565
593
|
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
566
594
|
if (lastFileExtension !== expectedExtension) {
|
|
@@ -570,24 +598,35 @@ var FileDatabase = class {
|
|
|
570
598
|
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
571
599
|
}
|
|
572
600
|
}
|
|
601
|
+
} else if (!Array.isArray(data) && forceNewFile) {
|
|
602
|
+
const lastFileExtension = path3.extname(lastFile.fileName);
|
|
603
|
+
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
604
|
+
if (lastFileExtension !== expectedExtension) {
|
|
605
|
+
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
606
|
+
}
|
|
573
607
|
}
|
|
574
608
|
if (Array.isArray(data)) {
|
|
575
|
-
if (
|
|
609
|
+
if (forceNewFile) {
|
|
610
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
611
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
612
|
+
this.lastFileData = dataToWrite;
|
|
613
|
+
} else if (lastFileRecordsCount < this.pageSize) {
|
|
576
614
|
dataToWrite = [...this.lastFileData || [], ...data.slice(0, this.pageSize - lastFileRecordsCount)];
|
|
577
615
|
dataLeftOver = data.slice(this.pageSize - lastFileRecordsCount);
|
|
616
|
+
this.lastFileData = dataToWrite;
|
|
578
617
|
} else {
|
|
579
618
|
this.makeNewFile();
|
|
580
619
|
dataToWrite = data.slice(0, this.pageSize);
|
|
581
620
|
dataLeftOver = data.slice(this.pageSize);
|
|
621
|
+
this.lastFileData = dataToWrite;
|
|
582
622
|
}
|
|
583
|
-
this.lastFileData = dataToWrite;
|
|
584
623
|
} else {
|
|
585
624
|
dataToWrite = data;
|
|
586
625
|
dataLeftOver = null;
|
|
587
626
|
}
|
|
588
627
|
const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;
|
|
589
628
|
this.logger.debug?.(
|
|
590
|
-
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
629
|
+
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, forceNewFile=${forceNewFile}, targetFileIndex=${targetFileIndex}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
591
630
|
);
|
|
592
631
|
return { dataToWrite, dataLeftOver, fileName };
|
|
593
632
|
}
|
|
@@ -615,7 +654,7 @@ var FileDatabase = class {
|
|
|
615
654
|
/**
|
|
616
655
|
* Update metadata after writing data
|
|
617
656
|
*/
|
|
618
|
-
updateMetadata(dataToWrite, fileName) {
|
|
657
|
+
updateMetadata(dataToWrite, fileName, customMetadata) {
|
|
619
658
|
let currentFile;
|
|
620
659
|
if (fileName) {
|
|
621
660
|
const foundFile = this.metadata.files.find((file) => file.fileName === fileName);
|
|
@@ -630,6 +669,9 @@ var FileDatabase = class {
|
|
|
630
669
|
}
|
|
631
670
|
const recordsCount = Array.isArray(dataToWrite) ? dataToWrite.length : 1;
|
|
632
671
|
currentFile.recordsCount = recordsCount;
|
|
672
|
+
if (customMetadata) {
|
|
673
|
+
Object.assign(currentFile, customMetadata);
|
|
674
|
+
}
|
|
633
675
|
const fileIndex = this.metadata.files.indexOf(currentFile);
|
|
634
676
|
if (fileIndex !== -1) {
|
|
635
677
|
this.calculateFileSynopsis(dataToWrite, fileIndex);
|
|
@@ -682,6 +724,11 @@ var FileDatabase = class {
|
|
|
682
724
|
} else {
|
|
683
725
|
if (!this.metadata.files.length) {
|
|
684
726
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
727
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
728
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
729
|
+
} else {
|
|
730
|
+
this.currentFileNumber = 0;
|
|
731
|
+
}
|
|
685
732
|
}
|
|
686
733
|
}
|
|
687
734
|
} else {
|
|
@@ -692,16 +739,22 @@ var FileDatabase = class {
|
|
|
692
739
|
try {
|
|
693
740
|
const rawData = await fs3.promises.readFile(metadataPath, "utf8");
|
|
694
741
|
this.metadata = JSON.parse(rawData);
|
|
742
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
743
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
744
|
+
} else {
|
|
745
|
+
this.currentFileNumber = 0;
|
|
746
|
+
}
|
|
695
747
|
} catch (e) {
|
|
696
748
|
this.metadata = this.getDefaultMetadata();
|
|
749
|
+
this.currentFileNumber = 0;
|
|
697
750
|
}
|
|
698
751
|
} else {
|
|
699
752
|
this.metadata = this.getDefaultMetadata();
|
|
700
|
-
this.
|
|
753
|
+
this.currentFileNumber = 0;
|
|
701
754
|
}
|
|
702
755
|
} else {
|
|
703
756
|
this.metadata = this.getDefaultMetadata();
|
|
704
|
-
this.
|
|
757
|
+
this.currentFileNumber = 0;
|
|
705
758
|
}
|
|
706
759
|
}
|
|
707
760
|
} else if (read) {
|
|
@@ -720,6 +773,11 @@ var FileDatabase = class {
|
|
|
720
773
|
}
|
|
721
774
|
if (!this.metadata.files.length) {
|
|
722
775
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
776
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
777
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
778
|
+
} else {
|
|
779
|
+
this.currentFileNumber = 0;
|
|
780
|
+
}
|
|
723
781
|
}
|
|
724
782
|
} else {
|
|
725
783
|
this.currentVersion = null;
|
|
@@ -733,6 +791,11 @@ var FileDatabase = class {
|
|
|
733
791
|
try {
|
|
734
792
|
const rawData = await fs3.promises.readFile(metadataPath, "utf8");
|
|
735
793
|
this.metadata = JSON.parse(rawData);
|
|
794
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
795
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
796
|
+
} else {
|
|
797
|
+
this.currentFileNumber = 0;
|
|
798
|
+
}
|
|
736
799
|
} catch (e) {
|
|
737
800
|
throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
|
|
738
801
|
}
|
|
@@ -741,6 +804,11 @@ var FileDatabase = class {
|
|
|
741
804
|
}
|
|
742
805
|
} else {
|
|
743
806
|
this.metadata = await this.figureMetadataFromVersionFiles("");
|
|
807
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
808
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
809
|
+
} else {
|
|
810
|
+
this.currentFileNumber = 0;
|
|
811
|
+
}
|
|
744
812
|
}
|
|
745
813
|
}
|
|
746
814
|
}
|
|
@@ -762,14 +830,44 @@ var FileDatabase = class {
|
|
|
762
830
|
this.metadata.dataType = incomingDataType;
|
|
763
831
|
this.makeNewFile();
|
|
764
832
|
}
|
|
765
|
-
let
|
|
833
|
+
let targetFileIndex = null;
|
|
834
|
+
const hasCustomMetadata = options.customMetadata && Object.keys(options.customMetadata).length > 0;
|
|
835
|
+
if (hasCustomMetadata) {
|
|
836
|
+
for (let i = 0; i < this.metadata.files.length; i++) {
|
|
837
|
+
const fileEntry = this.metadata.files[i];
|
|
838
|
+
const matches = Object.keys(options.customMetadata).every((key) => {
|
|
839
|
+
return key in fileEntry && fileEntry[key] === options.customMetadata[key];
|
|
840
|
+
});
|
|
841
|
+
if (matches) {
|
|
842
|
+
targetFileIndex = i;
|
|
843
|
+
this.logger.debug?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
844
|
+
break;
|
|
845
|
+
} else {
|
|
846
|
+
this.logger.debug?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (targetFileIndex === null) {
|
|
850
|
+
this.logger.debug?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
|
|
851
|
+
}
|
|
852
|
+
} else {
|
|
853
|
+
this.logger.debug?.(`[FileDatabase] No custom metadata provided, will create new file`);
|
|
854
|
+
}
|
|
855
|
+
if (targetFileIndex !== null) {
|
|
856
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
857
|
+
this.currentFileNumber = targetFile.number;
|
|
858
|
+
this.lastFileData = null;
|
|
859
|
+
this.currentRecord = 0;
|
|
860
|
+
this.hasReadFirstPage = false;
|
|
861
|
+
}
|
|
862
|
+
const forceNewFile = hasCustomMetadata && targetFileIndex === null;
|
|
863
|
+
let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data, targetFileIndex, forceNewFile);
|
|
766
864
|
const destPath = this.getDestinationPath(this.currentVersion || void 0);
|
|
767
865
|
await this.safeWrite(path3.join(destPath, fileName), dataToWrite);
|
|
768
|
-
this.updateMetadata(dataToWrite, fileName);
|
|
769
|
-
while (dataLeftOver && dataLeftOver.length > 0) {
|
|
866
|
+
this.updateMetadata(dataToWrite, fileName, options.customMetadata);
|
|
867
|
+
while (dataLeftOver && dataLeftOver.length > 0 && targetFileIndex === null) {
|
|
770
868
|
const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);
|
|
771
869
|
await this.safeWrite(path3.join(destPath, writeContext.fileName), writeContext.dataToWrite);
|
|
772
|
-
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);
|
|
870
|
+
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName, options.customMetadata);
|
|
773
871
|
dataLeftOver = writeContext.dataLeftOver;
|
|
774
872
|
}
|
|
775
873
|
this.calculateVersionSynopsis();
|
|
@@ -886,6 +984,63 @@ var FileDatabase = class {
|
|
|
886
984
|
getMetadata() {
|
|
887
985
|
return { ...this.metadata };
|
|
888
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* Find data by custom metadata fields
|
|
989
|
+
* Searches through all versions and files to find entries matching the search criteria
|
|
990
|
+
*
|
|
991
|
+
* @param searchCriteria - Object with field names and values to search for (e.g., { ListingKey: "123", id: "456" })
|
|
992
|
+
* @returns Array of found entries with their file paths and metadata
|
|
993
|
+
*/
|
|
994
|
+
async findData(searchCriteria) {
|
|
995
|
+
const results = [];
|
|
996
|
+
if (!this.versioned) {
|
|
997
|
+
await this.prepare({ read: true });
|
|
998
|
+
const metadata = this.getMetadata();
|
|
999
|
+
for (const fileEntry of metadata.files) {
|
|
1000
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
1001
|
+
return fileEntry[key] === searchCriteria[key];
|
|
1002
|
+
});
|
|
1003
|
+
if (matches) {
|
|
1004
|
+
const destPath = this.getDestinationPath();
|
|
1005
|
+
const filePath = path3.join(destPath, fileEntry.fileName);
|
|
1006
|
+
const fileData = await fs3.promises.readFile(filePath, "utf8");
|
|
1007
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
1008
|
+
results.push({
|
|
1009
|
+
filePath,
|
|
1010
|
+
fileName: fileEntry.fileName,
|
|
1011
|
+
version: null,
|
|
1012
|
+
metadata: fileEntry,
|
|
1013
|
+
data
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
} else {
|
|
1018
|
+
const versions = await this.getVersions();
|
|
1019
|
+
for (const version of versions) {
|
|
1020
|
+
await this.prepare({ read: true, version });
|
|
1021
|
+
const metadata = this.getMetadata();
|
|
1022
|
+
for (const fileEntry of metadata.files) {
|
|
1023
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
1024
|
+
return fileEntry[key] === searchCriteria[key];
|
|
1025
|
+
});
|
|
1026
|
+
if (matches) {
|
|
1027
|
+
const destPath = this.getDestinationPath(version);
|
|
1028
|
+
const filePath = path3.join(destPath, fileEntry.fileName);
|
|
1029
|
+
const fileData = await fs3.promises.readFile(filePath, "utf8");
|
|
1030
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
1031
|
+
results.push({
|
|
1032
|
+
filePath,
|
|
1033
|
+
fileName: fileEntry.fileName,
|
|
1034
|
+
version,
|
|
1035
|
+
metadata: fileEntry,
|
|
1036
|
+
data
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return results;
|
|
1043
|
+
}
|
|
889
1044
|
};
|
|
890
1045
|
|
|
891
1046
|
// src/mock-server/catalog.ts
|