@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.
- 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 +193 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +191 -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/index.cjs
CHANGED
|
@@ -1120,6 +1120,8 @@ __export(src_exports, {
|
|
|
1120
1120
|
h: () => import_react5.createElement,
|
|
1121
1121
|
joiEdateType: () => joiEdateType,
|
|
1122
1122
|
joiStringArrayType: () => joiStringArrayType,
|
|
1123
|
+
listSources: () => listSources,
|
|
1124
|
+
listTables: () => listTables,
|
|
1123
1125
|
load: () => load,
|
|
1124
1126
|
organizeFooterMessages: () => organizeFooterMessages,
|
|
1125
1127
|
setupContext: () => setupContext,
|
|
@@ -2587,20 +2589,48 @@ var FileDatabase = class {
|
|
|
2587
2589
|
}
|
|
2588
2590
|
/**
|
|
2589
2591
|
* Figure out what data to write and which file to use (for pagination)
|
|
2592
|
+
* @param data - Data to write
|
|
2593
|
+
* @param targetFileIndex - Optional index of existing file to overwrite (when customMetadata matches)
|
|
2594
|
+
* @param forceNewFile - If true, always create a new file (when customMetadata provided but no match)
|
|
2590
2595
|
*/
|
|
2591
|
-
figureOutDataAndFileToWrite(data) {
|
|
2596
|
+
figureOutDataAndFileToWrite(data, targetFileIndex = null, forceNewFile = false) {
|
|
2592
2597
|
let dataToWrite;
|
|
2593
2598
|
let dataLeftOver;
|
|
2594
2599
|
const incomingDataType = detectDataType(data);
|
|
2595
2600
|
if (this.metadata.dataType !== incomingDataType) {
|
|
2596
2601
|
this.metadata.dataType = incomingDataType;
|
|
2597
2602
|
}
|
|
2598
|
-
if (this.metadata.files.length
|
|
2603
|
+
if (targetFileIndex !== null && targetFileIndex < this.metadata.files.length) {
|
|
2604
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
2605
|
+
if (!Array.isArray(data)) {
|
|
2606
|
+
dataToWrite = data;
|
|
2607
|
+
dataLeftOver = null;
|
|
2608
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
2609
|
+
} else {
|
|
2610
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
2611
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
2612
|
+
this.lastFileData = dataToWrite;
|
|
2613
|
+
return { dataToWrite, dataLeftOver, fileName: targetFile.fileName };
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
let newlyCreatedFileIndex = null;
|
|
2617
|
+
if (forceNewFile) {
|
|
2618
|
+
const filesBeforeCreate = this.metadata.files.length;
|
|
2619
|
+
this.makeNewFile();
|
|
2620
|
+
newlyCreatedFileIndex = filesBeforeCreate;
|
|
2621
|
+
this.logger.debug?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
|
|
2622
|
+
} else if (this.metadata.files.length === 0) {
|
|
2599
2623
|
this.makeNewFile();
|
|
2600
2624
|
}
|
|
2601
2625
|
const lastFile = this.metadata.files[this.metadata.files.length - 1];
|
|
2602
2626
|
const lastFileRecordsCount = lastFile.recordsCount;
|
|
2603
|
-
if (
|
|
2627
|
+
if (forceNewFile && newlyCreatedFileIndex !== null) {
|
|
2628
|
+
const newlyCreatedFile = this.metadata.files[newlyCreatedFileIndex];
|
|
2629
|
+
if (newlyCreatedFile && newlyCreatedFile.fileName !== lastFile.fileName) {
|
|
2630
|
+
this.logger.warn?.(`[FileDatabase] Warning: Newly created file ${newlyCreatedFile.fileName} doesn't match last file ${lastFile.fileName}`);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
if (!Array.isArray(data) && !forceNewFile) {
|
|
2604
2634
|
const lastFileExtension = import_path4.default.extname(lastFile.fileName);
|
|
2605
2635
|
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
2606
2636
|
if (lastFileExtension !== expectedExtension) {
|
|
@@ -2610,24 +2640,35 @@ var FileDatabase = class {
|
|
|
2610
2640
|
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
2611
2641
|
}
|
|
2612
2642
|
}
|
|
2643
|
+
} else if (!Array.isArray(data) && forceNewFile) {
|
|
2644
|
+
const lastFileExtension = import_path4.default.extname(lastFile.fileName);
|
|
2645
|
+
const expectedExtension = `.${getFileExtension(incomingDataType)}`;
|
|
2646
|
+
if (lastFileExtension !== expectedExtension) {
|
|
2647
|
+
lastFile.fileName = `${this.currentFileNumber.toString().padStart(6, "0")}.${getFileExtension(incomingDataType)}`;
|
|
2648
|
+
}
|
|
2613
2649
|
}
|
|
2614
2650
|
if (Array.isArray(data)) {
|
|
2615
|
-
if (
|
|
2651
|
+
if (forceNewFile) {
|
|
2652
|
+
dataToWrite = data.slice(0, this.pageSize);
|
|
2653
|
+
dataLeftOver = data.slice(this.pageSize);
|
|
2654
|
+
this.lastFileData = dataToWrite;
|
|
2655
|
+
} else if (lastFileRecordsCount < this.pageSize) {
|
|
2616
2656
|
dataToWrite = [...this.lastFileData || [], ...data.slice(0, this.pageSize - lastFileRecordsCount)];
|
|
2617
2657
|
dataLeftOver = data.slice(this.pageSize - lastFileRecordsCount);
|
|
2658
|
+
this.lastFileData = dataToWrite;
|
|
2618
2659
|
} else {
|
|
2619
2660
|
this.makeNewFile();
|
|
2620
2661
|
dataToWrite = data.slice(0, this.pageSize);
|
|
2621
2662
|
dataLeftOver = data.slice(this.pageSize);
|
|
2663
|
+
this.lastFileData = dataToWrite;
|
|
2622
2664
|
}
|
|
2623
|
-
this.lastFileData = dataToWrite;
|
|
2624
2665
|
} else {
|
|
2625
2666
|
dataToWrite = data;
|
|
2626
2667
|
dataLeftOver = null;
|
|
2627
2668
|
}
|
|
2628
2669
|
const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;
|
|
2629
2670
|
this.logger.debug?.(
|
|
2630
|
-
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
2671
|
+
`[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, forceNewFile=${forceNewFile}, targetFileIndex=${targetFileIndex}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
|
|
2631
2672
|
);
|
|
2632
2673
|
return { dataToWrite, dataLeftOver, fileName };
|
|
2633
2674
|
}
|
|
@@ -2655,7 +2696,7 @@ var FileDatabase = class {
|
|
|
2655
2696
|
/**
|
|
2656
2697
|
* Update metadata after writing data
|
|
2657
2698
|
*/
|
|
2658
|
-
updateMetadata(dataToWrite, fileName) {
|
|
2699
|
+
updateMetadata(dataToWrite, fileName, customMetadata) {
|
|
2659
2700
|
let currentFile;
|
|
2660
2701
|
if (fileName) {
|
|
2661
2702
|
const foundFile = this.metadata.files.find((file) => file.fileName === fileName);
|
|
@@ -2670,6 +2711,9 @@ var FileDatabase = class {
|
|
|
2670
2711
|
}
|
|
2671
2712
|
const recordsCount = Array.isArray(dataToWrite) ? dataToWrite.length : 1;
|
|
2672
2713
|
currentFile.recordsCount = recordsCount;
|
|
2714
|
+
if (customMetadata) {
|
|
2715
|
+
Object.assign(currentFile, customMetadata);
|
|
2716
|
+
}
|
|
2673
2717
|
const fileIndex = this.metadata.files.indexOf(currentFile);
|
|
2674
2718
|
if (fileIndex !== -1) {
|
|
2675
2719
|
this.calculateFileSynopsis(dataToWrite, fileIndex);
|
|
@@ -2722,6 +2766,11 @@ var FileDatabase = class {
|
|
|
2722
2766
|
} else {
|
|
2723
2767
|
if (!this.metadata.files.length) {
|
|
2724
2768
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
2769
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
2770
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
2771
|
+
} else {
|
|
2772
|
+
this.currentFileNumber = 0;
|
|
2773
|
+
}
|
|
2725
2774
|
}
|
|
2726
2775
|
}
|
|
2727
2776
|
} else {
|
|
@@ -2732,16 +2781,22 @@ var FileDatabase = class {
|
|
|
2732
2781
|
try {
|
|
2733
2782
|
const rawData = await import_fs4.default.promises.readFile(metadataPath, "utf8");
|
|
2734
2783
|
this.metadata = JSON.parse(rawData);
|
|
2784
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
2785
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
2786
|
+
} else {
|
|
2787
|
+
this.currentFileNumber = 0;
|
|
2788
|
+
}
|
|
2735
2789
|
} catch (e) {
|
|
2736
2790
|
this.metadata = this.getDefaultMetadata();
|
|
2791
|
+
this.currentFileNumber = 0;
|
|
2737
2792
|
}
|
|
2738
2793
|
} else {
|
|
2739
2794
|
this.metadata = this.getDefaultMetadata();
|
|
2740
|
-
this.
|
|
2795
|
+
this.currentFileNumber = 0;
|
|
2741
2796
|
}
|
|
2742
2797
|
} else {
|
|
2743
2798
|
this.metadata = this.getDefaultMetadata();
|
|
2744
|
-
this.
|
|
2799
|
+
this.currentFileNumber = 0;
|
|
2745
2800
|
}
|
|
2746
2801
|
}
|
|
2747
2802
|
} else if (read) {
|
|
@@ -2760,6 +2815,11 @@ var FileDatabase = class {
|
|
|
2760
2815
|
}
|
|
2761
2816
|
if (!this.metadata.files.length) {
|
|
2762
2817
|
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
2818
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
2819
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
2820
|
+
} else {
|
|
2821
|
+
this.currentFileNumber = 0;
|
|
2822
|
+
}
|
|
2763
2823
|
}
|
|
2764
2824
|
} else {
|
|
2765
2825
|
this.currentVersion = null;
|
|
@@ -2773,6 +2833,11 @@ var FileDatabase = class {
|
|
|
2773
2833
|
try {
|
|
2774
2834
|
const rawData = await import_fs4.default.promises.readFile(metadataPath, "utf8");
|
|
2775
2835
|
this.metadata = JSON.parse(rawData);
|
|
2836
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
2837
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
2838
|
+
} else {
|
|
2839
|
+
this.currentFileNumber = 0;
|
|
2840
|
+
}
|
|
2776
2841
|
} catch (e) {
|
|
2777
2842
|
throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
|
|
2778
2843
|
}
|
|
@@ -2781,6 +2846,11 @@ var FileDatabase = class {
|
|
|
2781
2846
|
}
|
|
2782
2847
|
} else {
|
|
2783
2848
|
this.metadata = await this.figureMetadataFromVersionFiles("");
|
|
2849
|
+
if (this.metadata.files && this.metadata.files.length > 0) {
|
|
2850
|
+
this.currentFileNumber = Math.max(...this.metadata.files.map((f) => f.number || 0));
|
|
2851
|
+
} else {
|
|
2852
|
+
this.currentFileNumber = 0;
|
|
2853
|
+
}
|
|
2784
2854
|
}
|
|
2785
2855
|
}
|
|
2786
2856
|
}
|
|
@@ -2802,14 +2872,44 @@ var FileDatabase = class {
|
|
|
2802
2872
|
this.metadata.dataType = incomingDataType;
|
|
2803
2873
|
this.makeNewFile();
|
|
2804
2874
|
}
|
|
2805
|
-
let
|
|
2875
|
+
let targetFileIndex = null;
|
|
2876
|
+
const hasCustomMetadata = options.customMetadata && Object.keys(options.customMetadata).length > 0;
|
|
2877
|
+
if (hasCustomMetadata) {
|
|
2878
|
+
for (let i = 0; i < this.metadata.files.length; i++) {
|
|
2879
|
+
const fileEntry = this.metadata.files[i];
|
|
2880
|
+
const matches = Object.keys(options.customMetadata).every((key) => {
|
|
2881
|
+
return key in fileEntry && fileEntry[key] === options.customMetadata[key];
|
|
2882
|
+
});
|
|
2883
|
+
if (matches) {
|
|
2884
|
+
targetFileIndex = i;
|
|
2885
|
+
this.logger.debug?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
2886
|
+
break;
|
|
2887
|
+
} else {
|
|
2888
|
+
this.logger.debug?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
if (targetFileIndex === null) {
|
|
2892
|
+
this.logger.debug?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
|
|
2893
|
+
}
|
|
2894
|
+
} else {
|
|
2895
|
+
this.logger.debug?.(`[FileDatabase] No custom metadata provided, will create new file`);
|
|
2896
|
+
}
|
|
2897
|
+
if (targetFileIndex !== null) {
|
|
2898
|
+
const targetFile = this.metadata.files[targetFileIndex];
|
|
2899
|
+
this.currentFileNumber = targetFile.number;
|
|
2900
|
+
this.lastFileData = null;
|
|
2901
|
+
this.currentRecord = 0;
|
|
2902
|
+
this.hasReadFirstPage = false;
|
|
2903
|
+
}
|
|
2904
|
+
const forceNewFile = hasCustomMetadata && targetFileIndex === null;
|
|
2905
|
+
let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data, targetFileIndex, forceNewFile);
|
|
2806
2906
|
const destPath = this.getDestinationPath(this.currentVersion || void 0);
|
|
2807
2907
|
await this.safeWrite(import_path4.default.join(destPath, fileName), dataToWrite);
|
|
2808
|
-
this.updateMetadata(dataToWrite, fileName);
|
|
2809
|
-
while (dataLeftOver && dataLeftOver.length > 0) {
|
|
2908
|
+
this.updateMetadata(dataToWrite, fileName, options.customMetadata);
|
|
2909
|
+
while (dataLeftOver && dataLeftOver.length > 0 && targetFileIndex === null) {
|
|
2810
2910
|
const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);
|
|
2811
2911
|
await this.safeWrite(import_path4.default.join(destPath, writeContext.fileName), writeContext.dataToWrite);
|
|
2812
|
-
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);
|
|
2912
|
+
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName, options.customMetadata);
|
|
2813
2913
|
dataLeftOver = writeContext.dataLeftOver;
|
|
2814
2914
|
}
|
|
2815
2915
|
this.calculateVersionSynopsis();
|
|
@@ -2926,7 +3026,85 @@ var FileDatabase = class {
|
|
|
2926
3026
|
getMetadata() {
|
|
2927
3027
|
return { ...this.metadata };
|
|
2928
3028
|
}
|
|
3029
|
+
/**
|
|
3030
|
+
* Find data by custom metadata fields
|
|
3031
|
+
* Searches through all versions and files to find entries matching the search criteria
|
|
3032
|
+
*
|
|
3033
|
+
* @param searchCriteria - Object with field names and values to search for (e.g., { ListingKey: "123", id: "456" })
|
|
3034
|
+
* @returns Array of found entries with their file paths and metadata
|
|
3035
|
+
*/
|
|
3036
|
+
async findData(searchCriteria) {
|
|
3037
|
+
const results = [];
|
|
3038
|
+
if (!this.versioned) {
|
|
3039
|
+
await this.prepare({ read: true });
|
|
3040
|
+
const metadata = this.getMetadata();
|
|
3041
|
+
for (const fileEntry of metadata.files) {
|
|
3042
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
3043
|
+
return fileEntry[key] === searchCriteria[key];
|
|
3044
|
+
});
|
|
3045
|
+
if (matches) {
|
|
3046
|
+
const destPath = this.getDestinationPath();
|
|
3047
|
+
const filePath = import_path4.default.join(destPath, fileEntry.fileName);
|
|
3048
|
+
const fileData = await import_fs4.default.promises.readFile(filePath, "utf8");
|
|
3049
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
3050
|
+
results.push({
|
|
3051
|
+
filePath,
|
|
3052
|
+
fileName: fileEntry.fileName,
|
|
3053
|
+
version: null,
|
|
3054
|
+
metadata: fileEntry,
|
|
3055
|
+
data
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
} else {
|
|
3060
|
+
const versions = await this.getVersions();
|
|
3061
|
+
for (const version of versions) {
|
|
3062
|
+
await this.prepare({ read: true, version });
|
|
3063
|
+
const metadata = this.getMetadata();
|
|
3064
|
+
for (const fileEntry of metadata.files) {
|
|
3065
|
+
const matches = Object.keys(searchCriteria).every((key) => {
|
|
3066
|
+
return fileEntry[key] === searchCriteria[key];
|
|
3067
|
+
});
|
|
3068
|
+
if (matches) {
|
|
3069
|
+
const destPath = this.getDestinationPath(version);
|
|
3070
|
+
const filePath = import_path4.default.join(destPath, fileEntry.fileName);
|
|
3071
|
+
const fileData = await import_fs4.default.promises.readFile(filePath, "utf8");
|
|
3072
|
+
const data = deserializeData(fileData, metadata.dataType || "json-object");
|
|
3073
|
+
results.push({
|
|
3074
|
+
filePath,
|
|
3075
|
+
fileName: fileEntry.fileName,
|
|
3076
|
+
version,
|
|
3077
|
+
metadata: fileEntry,
|
|
3078
|
+
data
|
|
3079
|
+
});
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
return results;
|
|
3085
|
+
}
|
|
2929
3086
|
};
|
|
3087
|
+
function listTables(basePath, namespace) {
|
|
3088
|
+
const namespacePath = import_path4.default.join(basePath, namespace);
|
|
3089
|
+
if (!import_fs4.default.existsSync(namespacePath)) {
|
|
3090
|
+
return [];
|
|
3091
|
+
}
|
|
3092
|
+
try {
|
|
3093
|
+
return import_fs4.default.readdirSync(namespacePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
3094
|
+
} catch (error) {
|
|
3095
|
+
return [];
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
function listSources(basePath) {
|
|
3099
|
+
if (!import_fs4.default.existsSync(basePath)) {
|
|
3100
|
+
return [];
|
|
3101
|
+
}
|
|
3102
|
+
try {
|
|
3103
|
+
return import_fs4.default.readdirSync(basePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
3104
|
+
} catch (error) {
|
|
3105
|
+
return [];
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
2930
3108
|
function fileDatabaseInit(context, options = {}) {
|
|
2931
3109
|
return new FileDatabase(context, options);
|
|
2932
3110
|
}
|
|
@@ -3719,6 +3897,8 @@ function setupContext(opts = {}) {
|
|
|
3719
3897
|
h,
|
|
3720
3898
|
joiEdateType,
|
|
3721
3899
|
joiStringArrayType,
|
|
3900
|
+
listSources,
|
|
3901
|
+
listTables,
|
|
3722
3902
|
load,
|
|
3723
3903
|
organizeFooterMessages,
|
|
3724
3904
|
setupContext,
|