@nmakarov/cli-toolkit 0.14.2 → 0.18.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.
Files changed (57) hide show
  1. package/dist/args.cjs +49 -9
  2. package/dist/args.cjs.map +1 -1
  3. package/dist/args.js +49 -9
  4. package/dist/args.js.map +1 -1
  5. package/dist/cli-runner.cjs +5006 -0
  6. package/dist/cli-runner.cjs.map +1 -0
  7. package/dist/cli-runner.js +4989 -0
  8. package/dist/cli-runner.js.map +1 -0
  9. package/dist/db.cjs +9 -2
  10. package/dist/db.cjs.map +1 -1
  11. package/dist/db.js +9 -2
  12. package/dist/db.js.map +1 -1
  13. package/dist/errors.cjs +17 -0
  14. package/dist/errors.cjs.map +1 -1
  15. package/dist/errors.js +15 -0
  16. package/dist/errors.js.map +1 -1
  17. package/dist/filedatabase.cjs +110 -37
  18. package/dist/filedatabase.cjs.map +1 -1
  19. package/dist/filedatabase.js +110 -36
  20. package/dist/filedatabase.js.map +1 -1
  21. package/dist/http-client.cjs +44 -34
  22. package/dist/http-client.cjs.map +1 -1
  23. package/dist/http-client.js +44 -34
  24. package/dist/http-client.js.map +1 -1
  25. package/dist/http-client2.cjs +1728 -0
  26. package/dist/http-client2.cjs.map +1 -0
  27. package/dist/http-client2.js +1690 -0
  28. package/dist/http-client2.js.map +1 -0
  29. package/dist/index.cjs +1456 -112
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +1436 -110
  32. package/dist/index.js.map +1 -1
  33. package/dist/init.cjs +199 -82
  34. package/dist/init.cjs.map +1 -1
  35. package/dist/init.js +199 -82
  36. package/dist/init.js.map +1 -1
  37. package/dist/logger.cjs +28 -42
  38. package/dist/logger.cjs.map +1 -1
  39. package/dist/logger.js +28 -42
  40. package/dist/logger.js.map +1 -1
  41. package/dist/mock-server.cjs +205 -359
  42. package/dist/mock-server.cjs.map +1 -1
  43. package/dist/mock-server.js +203 -359
  44. package/dist/mock-server.js.map +1 -1
  45. package/dist/params.cjs +114 -15
  46. package/dist/params.cjs.map +1 -1
  47. package/dist/params.js +114 -15
  48. package/dist/params.js.map +1 -1
  49. package/dist/tasks.cjs +2295 -0
  50. package/dist/tasks.cjs.map +1 -0
  51. package/dist/tasks.js +2240 -0
  52. package/dist/tasks.js.map +1 -0
  53. package/dist/utils.cjs +15 -2
  54. package/dist/utils.cjs.map +1 -1
  55. package/dist/utils.js +12 -1
  56. package/dist/utils.js.map +1 -1
  57. package/package.json +18 -3
@@ -34,7 +34,6 @@ __export(filedatabase_exports, {
34
34
  FileDatabaseError: () => FileDatabaseError,
35
35
  defaultFileSynopsisFunction: () => defaultFileSynopsisFunction,
36
36
  defaultVersionSynopsisFunction: () => defaultVersionSynopsisFunction,
37
- fileDatabaseInit: () => fileDatabaseInit,
38
37
  listSources: () => listSources,
39
38
  listTables: () => listTables
40
39
  });
@@ -161,6 +160,12 @@ var ParamError = class extends FrameworkError {
161
160
  this.name = "ParamError";
162
161
  }
163
162
  };
163
+ var FileDatabaseError = class extends FrameworkError {
164
+ constructor(message) {
165
+ super(message);
166
+ this.name = "FileDatabaseError";
167
+ }
168
+ };
164
169
 
165
170
  // src/filedatabase/synopsis-functions.ts
166
171
  function defaultFileSynopsisFunction(fileEntry, data) {
@@ -231,13 +236,7 @@ function defaultVersionSynopsisFunction(metadata) {
231
236
  }
232
237
 
233
238
  // src/filedatabase/index.ts
234
- var FileDatabaseError = class extends Error {
235
- constructor(message) {
236
- super(message);
237
- this.name = "FileDatabaseError";
238
- }
239
- };
240
- var FileDatabase = class {
239
+ var FileDatabase = class _FileDatabase {
241
240
  basePath;
242
241
  namespace;
243
242
  tableName = null;
@@ -274,18 +273,8 @@ var FileDatabase = class {
274
273
  maxVersions: "number default 5",
275
274
  pageSize: "number default 5000"
276
275
  };
277
- const paramsConfig = context.params.getAll(defs);
278
- config = {
279
- basePath: opts.basePath ?? paramsConfig.basePath,
280
- namespace: opts.namespace ?? paramsConfig.namespace,
281
- tableName: opts.tableName ?? paramsConfig.tableName ?? null,
282
- versioned: opts.versioned ?? true,
283
- maxVersions: opts.maxVersions ?? paramsConfig.maxVersions,
284
- pageSize: opts.pageSize ?? paramsConfig.pageSize,
285
- useMetadata: opts.useMetadata ?? true,
286
- freeSpaceThreshold: opts.freeSpaceThreshold ?? 100 * 1024 * 1024,
287
- logger: context.logger
288
- };
276
+ const discovered = context.params.getAllForModule(defs);
277
+ config = { ...discovered, ...opts, logger: context.logger };
289
278
  } else {
290
279
  config = contextOrConfig;
291
280
  }
@@ -303,6 +292,13 @@ var FileDatabase = class {
303
292
  this.logger = config.logger || console;
304
293
  this.metadata = this.getDefaultMetadata();
305
294
  }
295
+ /**
296
+ * Initialize FileDatabase from context and options.
297
+ * Params are read via getAllForModule("filedatabase", defs) for --showUsedParams grouping.
298
+ */
299
+ static init(context, options) {
300
+ return new _FileDatabase(context, options ?? {});
301
+ }
306
302
  /**
307
303
  * Get default metadata structure
308
304
  */
@@ -370,7 +366,7 @@ var FileDatabase = class {
370
366
  const versions = await this.getVersions();
371
367
  while (versions.length > this.maxVersions) {
372
368
  const versionToDelete = import_path3.default.resolve(this.getDestinationPath(), versions.shift());
373
- this.logger.debug?.(`[FileDatabase] Deleting old version: ${versionToDelete}`);
369
+ this.logger.silly?.(`[FileDatabase] Deleting old version: ${versionToDelete}`);
374
370
  await import_fs3.default.promises.rm(versionToDelete, { recursive: true, force: true });
375
371
  }
376
372
  return versionName;
@@ -648,7 +644,7 @@ var FileDatabase = class {
648
644
  };
649
645
  this.metadata.files.push(fileEntry);
650
646
  this.lastFileData = null;
651
- this.logger.debug?.(`[FileDatabase] Created new file: ${fileEntry.fileName}, fileNumber: ${this.currentFileNumber}`);
647
+ this.logger.silly?.(`[FileDatabase] Created new file: ${fileEntry.fileName}, fileNumber: ${this.currentFileNumber}`);
652
648
  }
653
649
  /**
654
650
  * Figure out what data to write and which file to use (for pagination)
@@ -681,7 +677,7 @@ var FileDatabase = class {
681
677
  const filesBeforeCreate = this.metadata.files.length;
682
678
  this.makeNewFile();
683
679
  newlyCreatedFileIndex = filesBeforeCreate;
684
- this.logger.debug?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
680
+ this.logger.silly?.(`[FileDatabase] Creating new file for unique custom metadata combination, fileNumber: ${this.currentFileNumber}`);
685
681
  } else if (this.metadata.files.length === 0) {
686
682
  this.makeNewFile();
687
683
  }
@@ -730,7 +726,7 @@ var FileDatabase = class {
730
726
  dataLeftOver = null;
731
727
  }
732
728
  const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;
733
- this.logger.debug?.(
729
+ this.logger.silly?.(
734
730
  `[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, forceNewFile=${forceNewFile}, targetFileIndex=${targetFileIndex}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : "N/A"}, lastFileRecordsCount=${lastFileRecordsCount}`
735
731
  );
736
732
  return { dataToWrite, dataLeftOver, fileName };
@@ -785,7 +781,7 @@ var FileDatabase = class {
785
781
  this.metadata.modifiedAt = (/* @__PURE__ */ new Date()).toISOString();
786
782
  this.metadata.dataType = detectDataType(dataToWrite);
787
783
  this.metadata.totalRecords = this.metadata.files.reduce((sum, file) => sum + (file.recordsCount || 0), 0);
788
- this.logger.debug?.(
784
+ this.logger.silly?.(
789
785
  `[FileDatabase] Updated metadata for file ${currentFile.fileName}: recordsCount=${recordsCount}, totalRecords=${this.metadata.totalRecords}`
790
786
  );
791
787
  }
@@ -809,7 +805,7 @@ var FileDatabase = class {
809
805
  }
810
806
  try {
811
807
  await import_fs3.default.promises.writeFile(filePath, serializedData, "utf8");
812
- this.logger.debug?.(`[FileDatabase] Wrote ${bytesToHumanReadable(requiredBytes)} to ${filePath}`);
808
+ this.logger.silly?.(`[FileDatabase] Wrote ${bytesToHumanReadable(requiredBytes)} to ${filePath}`);
813
809
  } catch (error) {
814
810
  throw new FileDatabaseError(`Failed to write file ${filePath}: ${error.message}`);
815
811
  }
@@ -891,7 +887,8 @@ var FileDatabase = class {
891
887
  this.useMetadata = format.hasMetadata;
892
888
  }
893
889
  if (this.useMetadata) {
894
- const metadataPath = import_path3.default.join(this.getDestinationPath(), "metadata.json");
890
+ const destPath = this.getDestinationPath();
891
+ const metadataPath = import_path3.default.join(destPath, "metadata.json");
895
892
  if (import_fs3.default.existsSync(metadataPath)) {
896
893
  try {
897
894
  const rawData = await import_fs3.default.promises.readFile(metadataPath, "utf8");
@@ -905,7 +902,9 @@ var FileDatabase = class {
905
902
  throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
906
903
  }
907
904
  } else {
908
- throw new FileDatabaseError("[FileDatabase] No metadata found in non-versioned mode");
905
+ throw new FileDatabaseError(
906
+ `[FileDatabase] No metadata found in non-versioned mode. Looked for: ${metadataPath} (table path: ${destPath})`
907
+ );
909
908
  }
910
909
  } else {
911
910
  this.metadata = await this.figureMetadataFromVersionFiles("");
@@ -922,6 +921,13 @@ var FileDatabase = class {
922
921
  * Write data to the file database
923
922
  */
924
923
  async write(data, options = {}) {
924
+ if (options.filename) {
925
+ const destPath2 = this.getDestinationPath();
926
+ await ensurePath(destPath2);
927
+ const filePath = import_path3.default.join(destPath2, options.filename);
928
+ await this.safeWrite(filePath, data);
929
+ return;
930
+ }
925
931
  if (options.forceNewVersion && !this.versioned) {
926
932
  throw new FileDatabaseError("Cannot use forceNewVersion in non-versioned mode");
927
933
  }
@@ -945,17 +951,17 @@ var FileDatabase = class {
945
951
  });
946
952
  if (matches) {
947
953
  targetFileIndex = i;
948
- this.logger.debug?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
954
+ this.logger.silly?.(`[FileDatabase] Found existing file with matching custom metadata: ${fileEntry.fileName}, metadata: ${JSON.stringify(options.customMetadata)}`);
949
955
  break;
950
956
  } else {
951
- this.logger.debug?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
957
+ this.logger.silly?.(`[FileDatabase] File ${fileEntry.fileName} does not match custom metadata: ${JSON.stringify(options.customMetadata)}`);
952
958
  }
953
959
  }
954
960
  if (targetFileIndex === null) {
955
- this.logger.debug?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
961
+ this.logger.silly?.(`[FileDatabase] No existing file found with custom metadata: ${JSON.stringify(options.customMetadata)}, will create new file`);
956
962
  }
957
963
  } else {
958
- this.logger.debug?.(`[FileDatabase] No custom metadata provided, will create new file`);
964
+ this.logger.silly?.(`[FileDatabase] No custom metadata provided, will create new file`);
959
965
  }
960
966
  if (targetFileIndex !== null) {
961
967
  const targetFile = this.metadata.files[targetFileIndex];
@@ -984,7 +990,17 @@ var FileDatabase = class {
984
990
  * Read data from the file database
985
991
  */
986
992
  async read(options = {}) {
987
- const { version, nextPage = false, pageSize } = options;
993
+ const { version, nextPage = false, pageSize, filename } = options;
994
+ if (filename) {
995
+ const destPath = this.getDestinationPath(version);
996
+ const filePath = import_path3.default.join(destPath, filename);
997
+ try {
998
+ const rawData = await import_fs3.default.promises.readFile(filePath, "utf8");
999
+ return JSON.parse(rawData);
1000
+ } catch (error) {
1001
+ throw new FileDatabaseError(`Failed to read file ${filename}: ${error.message}`);
1002
+ }
1003
+ }
988
1004
  await this.prepare({ read: true, version });
989
1005
  const isNonPaginatedData = this.metadata.dataType === "text" || this.metadata.dataType === "xml" || this.metadata.dataType === "json-object";
990
1006
  if (isNonPaginatedData) {
@@ -1065,6 +1081,67 @@ var FileDatabase = class {
1065
1081
  this.currentRecord = 0;
1066
1082
  this.hasReadFirstPage = false;
1067
1083
  }
1084
+ /**
1085
+ * List filenames in the table directory.
1086
+ * For catalog/key-value usage (files written with { filename }).
1087
+ * Returns data file names (.json, .txt, .xml) excluding metadata.json.
1088
+ */
1089
+ async listFilenames() {
1090
+ const destPath = this.versioned && this.currentVersion ? import_path3.default.join(this.getDestinationPath(), this.currentVersion) : this.getDestinationPath();
1091
+ try {
1092
+ const entries = await import_fs3.default.promises.readdir(destPath, { withFileTypes: true });
1093
+ return entries.filter((e) => e.isFile() && e.name !== "metadata.json" && /\.(json|txt|xml)$/i.test(e.name)).map((e) => e.name);
1094
+ } catch (err) {
1095
+ if (err?.code === "ENOENT") return [];
1096
+ throw new FileDatabaseError(`Failed to list files: ${err.message}`);
1097
+ }
1098
+ }
1099
+ /**
1100
+ * Remove a file from the table directory (catalog mode).
1101
+ * Use with listFilenames() to manage individual files.
1102
+ */
1103
+ async removeFile(filename) {
1104
+ const destPath = this.versioned && this.currentVersion ? import_path3.default.join(this.getDestinationPath(), this.currentVersion) : this.getDestinationPath();
1105
+ const filePath = import_path3.default.join(destPath, filename);
1106
+ try {
1107
+ await import_fs3.default.promises.unlink(filePath);
1108
+ } catch (err) {
1109
+ if (err?.code === "ENOENT") return;
1110
+ throw new FileDatabaseError(`Failed to remove file ${filename}: ${err.message}`);
1111
+ }
1112
+ }
1113
+ /**
1114
+ * Remove a file and its metadata entry (non-versioned mode with useMetadata).
1115
+ * Use with findData() to get fileName, then call removeFileEntry to delete.
1116
+ */
1117
+ async removeFileEntry(filename) {
1118
+ if (this.versioned) {
1119
+ throw new FileDatabaseError("removeFileEntry is only supported in non-versioned mode");
1120
+ }
1121
+ await this.prepare({ read: true });
1122
+ const idx = this.metadata.files.findIndex((f) => f.fileName === filename);
1123
+ if (idx === -1) {
1124
+ throw new FileDatabaseError(`File entry ${filename} not found in metadata`);
1125
+ }
1126
+ const entry = this.metadata.files[idx];
1127
+ const recordsCount = entry.recordsCount || 0;
1128
+ this.metadata.files.splice(idx, 1);
1129
+ this.metadata.totalRecords = Math.max(0, (this.metadata.totalRecords || 0) - recordsCount);
1130
+ const destPath = this.getDestinationPath();
1131
+ const filePath = import_path3.default.join(destPath, filename);
1132
+ try {
1133
+ await import_fs3.default.promises.unlink(filePath);
1134
+ } catch (err) {
1135
+ if (err?.code === "ENOENT") {
1136
+ this.logger.warn?.(`[FileDatabase] File ${filename} already missing on disk`);
1137
+ } else {
1138
+ throw new FileDatabaseError(`Failed to remove file ${filename}: ${err.message}`);
1139
+ }
1140
+ }
1141
+ if (this.useMetadata) {
1142
+ await this.saveVersionMetadata(this.metadata);
1143
+ }
1144
+ }
1068
1145
  /**
1069
1146
  * Set file-level synopsis calculation function
1070
1147
  */
@@ -1168,16 +1245,12 @@ function listSources(basePath) {
1168
1245
  return [];
1169
1246
  }
1170
1247
  }
1171
- function fileDatabaseInit(context, options = {}) {
1172
- return new FileDatabase(context, options);
1173
- }
1174
1248
  // Annotate the CommonJS export names for ESM import in node:
1175
1249
  0 && (module.exports = {
1176
1250
  FileDatabase,
1177
1251
  FileDatabaseError,
1178
1252
  defaultFileSynopsisFunction,
1179
1253
  defaultVersionSynopsisFunction,
1180
- fileDatabaseInit,
1181
1254
  listSources,
1182
1255
  listTables
1183
1256
  });