@nmakarov/cli-toolkit 0.11.4 → 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.
@@ -34,7 +34,9 @@ __export(filedatabase_exports, {
34
34
  FileDatabaseError: () => FileDatabaseError,
35
35
  defaultFileSynopsisFunction: () => defaultFileSynopsisFunction,
36
36
  defaultVersionSynopsisFunction: () => defaultVersionSynopsisFunction,
37
- fileDatabaseInit: () => fileDatabaseInit
37
+ fileDatabaseInit: () => fileDatabaseInit,
38
+ listSources: () => listSources,
39
+ listTables: () => listTables
38
40
  });
39
41
  module.exports = __toCommonJS(filedatabase_exports);
40
42
 
@@ -650,20 +652,48 @@ var FileDatabase = class {
650
652
  }
651
653
  /**
652
654
  * Figure out what data to write and which file to use (for pagination)
655
+ * @param data - Data to write
656
+ * @param targetFileIndex - Optional index of existing file to overwrite (when customMetadata matches)
657
+ * @param forceNewFile - If true, always create a new file (when customMetadata provided but no match)
653
658
  */
654
- figureOutDataAndFileToWrite(data) {
659
+ figureOutDataAndFileToWrite(data, targetFileIndex = null, forceNewFile = false) {
655
660
  let dataToWrite;
656
661
  let dataLeftOver;
657
662
  const incomingDataType = detectDataType(data);
658
663
  if (this.metadata.dataType !== incomingDataType) {
659
664
  this.metadata.dataType = incomingDataType;
660
665
  }
661
- if (this.metadata.files.length === 0) {
666
+ if (targetFileIndex !== null && targetFileIndex < this.metadata.files.length) {
667
+ const targetFile = this.metadata.files[targetFileIndex];
668
+ if (!Array.isArray(data)) {
669
+ dataToWrite = data;
670
+ dataLeftOver = null;
671
+ return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
672
+ } else {
673
+ dataToWrite = data.slice(0, this.pageSize);
674
+ dataLeftOver = data.slice(this.pageSize);
675
+ this.lastFileData = dataToWrite;
676
+ return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
677
+ }
678
+ }
679
+ let newlyCreatedFileIndex = null;
680
+ if (forceNewFile) {
681
+ const filesBeforeCreate = this.metadata.files.length;
682
+ this.makeNewFile();
683
+ newlyCreatedFileIndex = filesBeforeCreate;
684
+ this.logger.debug?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
685
+ } else if (this.metadata.files.length === 0) {
662
686
  this.makeNewFile();
663
687
  }
664
688
  const lastFile = this.metadata.files[this.metadata.files.length - 1];
665
689
  const lastFileRecordsCount = lastFile.recordsCount;
666
- if (!Array.isArray(data)) {
690
+ if (forceNewFile && newlyCreatedFileIndex !== null) {
691
+ const newlyCreatedFile = this.metadata.files[newlyCreatedFileIndex];
692
+ if (newlyCreatedFile && newlyCreatedFile.fileName !== lastFile.fileName) {
693
+ this.logger.warn?.(`[FileDatabase] Warning: Newly created file ${newlyCreatedFile.fileName} doesn't match last file ${lastFile.fileName}`);
694
+ }
695
+ }
696
+ if (!Array.isArray(data) && !forceNewFile) {
667
697
  const lastFileExtension = import_path3.default.extname(lastFile.fileName);
668
698
  const expectedExtension = `.${getFileExtension(incomingDataType)}`;
669
699
  if (lastFileExtension !== expectedExtension) {
@@ -673,24 +703,35 @@ var FileDatabase = class {
673
703
  lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
674
704
  }
675
705
  }
706
+ } else if (!Array.isArray(data) && forceNewFile) {
707
+ const lastFileExtension = import_path3.default.extname(lastFile.fileName);
708
+ const expectedExtension = `.${getFileExtension(incomingDataType)}`;
709
+ if (lastFileExtension !== expectedExtension) {
710
+ lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
711
+ }
676
712
  }
677
713
  if (Array.isArray(data)) {
678
- if (lastFileRecordsCount < this.pageSize) {
714
+ if (forceNewFile) {
715
+ dataToWrite = data.slice(0, this.pageSize);
716
+ dataLeftOver = data.slice(this.pageSize);
717
+ this.lastFileData = dataToWrite;
718
+ } else if (lastFileRecordsCount < this.pageSize) {
679
719
  dataToWrite = [...this.lastFileData || [], ...data.slice(0, this.pageSize - lastFileRecordsCount)];
680
720
  dataLeftOver = data.slice(this.pageSize - lastFileRecordsCount);
721
+ this.lastFileData = dataToWrite;
681
722
  } else {
682
723
  this.makeNewFile();
683
724
  dataToWrite = data.slice(0, this.pageSize);
684
725
  dataLeftOver = data.slice(this.pageSize);
726
+ this.lastFileData = dataToWrite;
685
727
  }
686
- this.lastFileData = dataToWrite;
687
728
  } else {
688
729
  dataToWrite = data;
689
730
  dataLeftOver = null;
690
731
  }
691
732
  const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;
692
733
  this.logger.debug?.(
693
- `[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
734
+ `[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, forceNewFile=${forceNewFile}, targetFileIndex=${targetFileIndex}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
694
735
  );
695
736
  return { dataToWrite, dataLeftOver, fileName };
696
737
  }
@@ -718,7 +759,7 @@ var FileDatabase = class {
718
759
  /**
719
760
  * Update metadata after writing data
720
761
  */
721
- updateMetadata(dataToWrite, fileName) {
762
+ updateMetadata(dataToWrite, fileName, customMetadata) {
722
763
  let currentFile;
723
764
  if (fileName) {
724
765
  const foundFile = this.metadata.files.find((file) => file.fileName === fileName);
@@ -733,6 +774,9 @@ var FileDatabase = class {
733
774
  }
734
775
  const recordsCount = Array.isArray(dataToWrite) ? dataToWrite.length : 1;
735
776
  currentFile.recordsCount = recordsCount;
777
+ if (customMetadata) {
778
+ Object.assign(currentFile, customMetadata);
779
+ }
736
780
  const fileIndex = this.metadata.files.indexOf(currentFile);
737
781
  if (fileIndex !== -1) {
738
782
  this.calculateFileSynopsis(dataToWrite, fileIndex);
@@ -785,6 +829,11 @@ var FileDatabase = class {
785
829
  } else {
786
830
  if (!this.metadata.files.length) {
787
831
  this.metadata = await this.figureMetadata(this.currentVersion);
832
+ if (this.metadata.files && this.metadata.files.length > 0) {
833
+ this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
834
+ } else {
835
+ this.currentFileNumber = 0;
836
+ }
788
837
  }
789
838
  }
790
839
  } else {
@@ -795,16 +844,22 @@ var FileDatabase = class {
795
844
  try {
796
845
  const rawData = await import_fs3.default.promises.readFile(metadataPath, "utf8");
797
846
  this.metadata = JSON.parse(rawData);
847
+ if (this.metadata.files && this.metadata.files.length > 0) {
848
+ this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
849
+ } else {
850
+ this.currentFileNumber = 0;
851
+ }
798
852
  } catch (e) {
799
853
  this.metadata = this.getDefaultMetadata();
854
+ this.currentFileNumber = 0;
800
855
  }
801
856
  } else {
802
857
  this.metadata = this.getDefaultMetadata();
803
- this.makeNewFile();
858
+ this.currentFileNumber = 0;
804
859
  }
805
860
  } else {
806
861
  this.metadata = this.getDefaultMetadata();
807
- this.makeNewFile();
862
+ this.currentFileNumber = 0;
808
863
  }
809
864
  }
810
865
  } else if (read) {
@@ -823,6 +878,11 @@ var FileDatabase = class {
823
878
  }
824
879
  if (!this.metadata.files.length) {
825
880
  this.metadata = await this.figureMetadata(this.currentVersion);
881
+ if (this.metadata.files && this.metadata.files.length > 0) {
882
+ this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
883
+ } else {
884
+ this.currentFileNumber = 0;
885
+ }
826
886
  }
827
887
  } else {
828
888
  this.currentVersion = null;
@@ -836,6 +896,11 @@ var FileDatabase = class {
836
896
  try {
837
897
  const rawData = await import_fs3.default.promises.readFile(metadataPath, "utf8");
838
898
  this.metadata = JSON.parse(rawData);
899
+ if (this.metadata.files && this.metadata.files.length > 0) {
900
+ this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
901
+ } else {
902
+ this.currentFileNumber = 0;
903
+ }
839
904
  } catch (e) {
840
905
  throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
841
906
  }
@@ -844,6 +909,11 @@ var FileDatabase = class {
844
909
  }
845
910
  } else {
846
911
  this.metadata = await this.figureMetadataFromVersionFiles("");
912
+ if (this.metadata.files && this.metadata.files.length > 0) {
913
+ this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
914
+ } else {
915
+ this.currentFileNumber = 0;
916
+ }
847
917
  }
848
918
  }
849
919
  }
@@ -865,14 +935,44 @@ var FileDatabase = class {
865
935
  this.metadata.dataType = incomingDataType;
866
936
  this.makeNewFile();
867
937
  }
868
- let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data);
938
+ let targetFileIndex = null;
939
+ const hasCustomMetadata = options.customMetadata && Object.keys(options.customMetadata).length > 0;
940
+ if (hasCustomMetadata) {
941
+ for (let i = 0; i < this.metadata.files.length; i++) {
942
+ const fileEntry = this.metadata.files[i];
943
+ const matches = Object.keys(options.customMetadata).every((key) => {
944
+ return key in fileEntry && fileEntry[key] === options.customMetadata[key];
945
+ });
946
+ if (matches) {
947
+ targetFileIndex = i;
948
+ this.logger.debug?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
949
+ break;
950
+ } else {
951
+ this.logger.debug?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
952
+ }
953
+ }
954
+ if (targetFileIndex === null) {
955
+ this.logger.debug?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
956
+ }
957
+ } else {
958
+ this.logger.debug?.(`[FileDatabase] No custom metadata provided, will create new file`);
959
+ }
960
+ if (targetFileIndex !== null) {
961
+ const targetFile = this.metadata.files[targetFileIndex];
962
+ this.currentFileNumber = targetFile.number;
963
+ this.lastFileData = null;
964
+ this.currentRecord = 0;
965
+ this.hasReadFirstPage = false;
966
+ }
967
+ const forceNewFile = hasCustomMetadata && targetFileIndex === null;
968
+ let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data, targetFileIndex, forceNewFile);
869
969
  const destPath = this.getDestinationPath(this.currentVersion || void 0);
870
970
  await this.safeWrite(import_path3.default.join(destPath, fileName), dataToWrite);
871
- this.updateMetadata(dataToWrite, fileName);
872
- while (dataLeftOver && dataLeftOver.length > 0) {
971
+ this.updateMetadata(dataToWrite, fileName, options.customMetadata);
972
+ while (dataLeftOver && dataLeftOver.length > 0 && targetFileIndex === null) {
873
973
  const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);
874
974
  await this.safeWrite(import_path3.default.join(destPath, writeContext.fileName), writeContext.dataToWrite);
875
- this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);
975
+ this.updateMetadata(writeContext.dataToWrite, writeContext.fileName, options.customMetadata);
876
976
  dataLeftOver = writeContext.dataLeftOver;
877
977
  }
878
978
  this.calculateVersionSynopsis();
@@ -989,7 +1089,85 @@ var FileDatabase = class {
989
1089
  getMetadata() {
990
1090
  return { ...this.metadata };
991
1091
  }
1092
+ /**
1093
+ * Find data by custom metadata fields
1094
+ * Searches through all versions and files to find entries matching the search criteria
1095
+ *
1096
+ * @param searchCriteria - Object with field names and values to search for (e.g., { ListingKey: "123", id: "456" })
1097
+ * @returns Array of found entries with their file paths and metadata
1098
+ */
1099
+ async findData(searchCriteria) {
1100
+ const results = [];
1101
+ if (!this.versioned) {
1102
+ await this.prepare({ read: true });
1103
+ const metadata = this.getMetadata();
1104
+ for (const fileEntry of metadata.files) {
1105
+ const matches = Object.keys(searchCriteria).every((key) => {
1106
+ return fileEntry[key] === searchCriteria[key];
1107
+ });
1108
+ if (matches) {
1109
+ const destPath = this.getDestinationPath();
1110
+ const filePath = import_path3.default.join(destPath, fileEntry.fileName);
1111
+ const fileData = await import_fs3.default.promises.readFile(filePath, "utf8");
1112
+ const data = deserializeData(fileData, metadata.dataType || "json-object");
1113
+ results.push({
1114
+ filePath,
1115
+ fileName: fileEntry.fileName,
1116
+ version: null,
1117
+ metadata: fileEntry,
1118
+ data
1119
+ });
1120
+ }
1121
+ }
1122
+ } else {
1123
+ const versions = await this.getVersions();
1124
+ for (const version of versions) {
1125
+ await this.prepare({ read: true, version });
1126
+ const metadata = this.getMetadata();
1127
+ for (const fileEntry of metadata.files) {
1128
+ const matches = Object.keys(searchCriteria).every((key) => {
1129
+ return fileEntry[key] === searchCriteria[key];
1130
+ });
1131
+ if (matches) {
1132
+ const destPath = this.getDestinationPath(version);
1133
+ const filePath = import_path3.default.join(destPath, fileEntry.fileName);
1134
+ const fileData = await import_fs3.default.promises.readFile(filePath, "utf8");
1135
+ const data = deserializeData(fileData, metadata.dataType || "json-object");
1136
+ results.push({
1137
+ filePath,
1138
+ fileName: fileEntry.fileName,
1139
+ version,
1140
+ metadata: fileEntry,
1141
+ data
1142
+ });
1143
+ }
1144
+ }
1145
+ }
1146
+ }
1147
+ return results;
1148
+ }
992
1149
  };
1150
+ function listTables(basePath, namespace) {
1151
+ const namespacePath = import_path3.default.join(basePath, namespace);
1152
+ if (!import_fs3.default.existsSync(namespacePath)) {
1153
+ return [];
1154
+ }
1155
+ try {
1156
+ return import_fs3.default.readdirSync(namespacePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1157
+ } catch (error) {
1158
+ return [];
1159
+ }
1160
+ }
1161
+ function listSources(basePath) {
1162
+ if (!import_fs3.default.existsSync(basePath)) {
1163
+ return [];
1164
+ }
1165
+ try {
1166
+ return import_fs3.default.readdirSync(basePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1167
+ } catch (error) {
1168
+ return [];
1169
+ }
1170
+ }
993
1171
  function fileDatabaseInit(context, options = {}) {
994
1172
  return new FileDatabase(context, options);
995
1173
  }
@@ -999,6 +1177,8 @@ function fileDatabaseInit(context, options = {}) {
999
1177
  FileDatabaseError,
1000
1178
  defaultFileSynopsisFunction,
1001
1179
  defaultVersionSynopsisFunction,
1002
- fileDatabaseInit
1180
+ fileDatabaseInit,
1181
+ listSources,
1182
+ listTables
1003
1183
  });
1004
1184
  //# sourceMappingURL=filedatabase.cjs.map