@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.
@@ -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 === 0) {
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 (!Array.isArray(data)) {
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 (lastFileRecordsCount < this.pageSize) {
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.makeNewFile();
816
+ this.currentFileNumber = 0;
764
817
  }
765
818
  } else {
766
819
  this.metadata = this.getDefaultMetadata();
767
- this.makeNewFile();
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 { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data);
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