@nmakarov/cli-toolkit 0.7.2 → 0.9.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/index.cjs CHANGED
@@ -1113,7 +1113,6 @@ __export(src_exports, {
1113
1113
  defaultFileSynopsisFunction: () => defaultFileSynopsisFunction,
1114
1114
  defaultVersionSynopsisFunction: () => defaultVersionSynopsisFunction,
1115
1115
  getArgsInstance: () => getArgsInstance,
1116
- getParamsInstance: () => getParamsInstance,
1117
1116
  h: () => import_react5.createElement,
1118
1117
  joiEdateType: () => joiEdateType,
1119
1118
  joiStringArrayType: () => joiStringArrayType,
@@ -1139,7 +1138,7 @@ module.exports = __toCommonJS(src_exports);
1139
1138
  var import_fs = require("fs");
1140
1139
  var import_path = require("path");
1141
1140
  var import_dotenv = require("dotenv");
1142
- var Args = class {
1141
+ var Args = class _Args {
1143
1142
  args = {};
1144
1143
  flags = {};
1145
1144
  options = {};
@@ -1154,10 +1153,13 @@ var Args = class {
1154
1153
  configsLoaded = [];
1155
1154
  env = "local";
1156
1155
  constructor(config2 = {}) {
1157
- this.aliases = config2.aliases || {};
1158
- this.overrides = config2.overrides || {};
1159
- this.defaults = config2.defaults || {};
1160
- this.prefixes = config2.prefixes || ["not", "no"];
1156
+ this.aliases = {};
1157
+ this.overrides = {};
1158
+ this.defaults = {};
1159
+ this.prefixes = ["not", "no"];
1160
+ if (Object.keys(config2).length > 0) {
1161
+ this.configure(config2);
1162
+ }
1161
1163
  const args = config2.args || process.argv.slice(2);
1162
1164
  this.parseArgs(args);
1163
1165
  this.env = this.get("env")?.toLowerCase() || "local";
@@ -1165,6 +1167,33 @@ var Args = class {
1165
1167
  this.loadConfigFiles();
1166
1168
  this.checkConflicts();
1167
1169
  }
1170
+ /**
1171
+ * Configure Args options
1172
+ * Only parameters present in config are updated
1173
+ * Note: Args is special - it's initialized first, so it can't take context
1174
+ */
1175
+ configure(config2) {
1176
+ if (config2.aliases !== void 0) {
1177
+ this.aliases = config2.aliases;
1178
+ }
1179
+ if (config2.overrides !== void 0) {
1180
+ this.overrides = config2.overrides;
1181
+ }
1182
+ if (config2.defaults !== void 0) {
1183
+ this.defaults = config2.defaults;
1184
+ }
1185
+ if (config2.prefixes !== void 0) {
1186
+ this.prefixes = config2.prefixes;
1187
+ }
1188
+ }
1189
+ /**
1190
+ * Initialize Args instance
1191
+ * Note: Args is special - it's initialized first, so it can't take context
1192
+ * This static method is for consistency with other components
1193
+ */
1194
+ static init(config2 = {}) {
1195
+ return new _Args(config2);
1196
+ }
1168
1197
  /**
1169
1198
  * Parse command line arguments
1170
1199
  */
@@ -1303,11 +1332,11 @@ var Args = class {
1303
1332
  */
1304
1333
  get(key) {
1305
1334
  const resolvedKey = this.aliases[key] || key;
1306
- this.usedKeys.add(resolvedKey);
1335
+ const lcKey = resolvedKey.toLowerCase();
1336
+ this.usedKeys.add(lcKey);
1307
1337
  if (this.overrides[resolvedKey] !== void 0) {
1308
1338
  return this.overrides[resolvedKey];
1309
1339
  }
1310
- const lcKey = resolvedKey.toLowerCase();
1311
1340
  const lcKeyWithEnv = `${lcKey}${this.env ? `_${this.env.toLowerCase()}` : ""}`;
1312
1341
  if (this.env && this.args[lcKeyWithEnv] !== void 0) {
1313
1342
  return this.args[lcKeyWithEnv];
@@ -1678,18 +1707,76 @@ var joiStringArrayType = (type) => (value, helpers) => {
1678
1707
  };
1679
1708
 
1680
1709
  // src/params/index.ts
1681
- var Params = class {
1710
+ var Params = class _Params {
1711
+ context;
1712
+ // Partial context during initialization
1682
1713
  params = {};
1683
1714
  definitions = {};
1684
1715
  args;
1685
1716
  paramSetters = [];
1686
1717
  paramGetters = [];
1687
- constructor({ args }, opts = {}) {
1688
- this.args = args;
1689
- for (const [k, v] of Object.entries(opts)) {
1718
+ trackedParams = [];
1719
+ constructor(context, options = {}) {
1720
+ this.context = context;
1721
+ this.args = context.args;
1722
+ if (Object.keys(options).length > 0) {
1723
+ this.configure(options);
1724
+ }
1725
+ }
1726
+ /**
1727
+ * Configure parameters
1728
+ * Only parameters present in options are updated
1729
+ */
1730
+ configure(options) {
1731
+ for (const [k, v] of Object.entries(options)) {
1690
1732
  this.params[k] = v;
1691
1733
  }
1692
1734
  }
1735
+ /**
1736
+ * Initialize Params from context and CLI parameters
1737
+ * Note: Params is special - it's initialized early with partial context
1738
+ */
1739
+ static init(context, options) {
1740
+ return new _Params(context, options || {});
1741
+ }
1742
+ /**
1743
+ * Track a parameter request for --stopAfter=init feature
1744
+ */
1745
+ trackParam(key, definition, value, source) {
1746
+ this.trackedParams.push({
1747
+ key,
1748
+ definition,
1749
+ value,
1750
+ source
1751
+ });
1752
+ }
1753
+ /**
1754
+ * Get all tracked parameters (for --stopAfter=init)
1755
+ */
1756
+ getTrackedParams() {
1757
+ return [...this.trackedParams];
1758
+ }
1759
+ /**
1760
+ * Get all figured parameters as a record
1761
+ * Returns all parameters that were collected during initialization,
1762
+ * whether from CLI args, options, or defaults
1763
+ */
1764
+ getAllFigured() {
1765
+ const result = {};
1766
+ for (const param of this.trackedParams) {
1767
+ result[param.key] = {
1768
+ value: param.value,
1769
+ source: param.source
1770
+ };
1771
+ }
1772
+ return result;
1773
+ }
1774
+ /**
1775
+ * Clear tracked parameters
1776
+ */
1777
+ clearTrackedParams() {
1778
+ this.trackedParams = [];
1779
+ }
1693
1780
  /**
1694
1781
  * Assign a parameter definition
1695
1782
  */
@@ -1763,6 +1850,8 @@ var Params = class {
1763
1850
  type = type.default(defValObj.value);
1764
1851
  } else if (str.match(/required/)) {
1765
1852
  type = type.required();
1853
+ } else {
1854
+ type = type.optional();
1766
1855
  }
1767
1856
  return type;
1768
1857
  }
@@ -1770,7 +1859,12 @@ var Params = class {
1770
1859
  * Validate a value against a definition
1771
1860
  */
1772
1861
  validate(key, val, def) {
1773
- const { value, error } = def.type.validate(val, { context: { params: this.params } });
1862
+ const normalizedVal = val === null ? void 0 : val;
1863
+ const { value, error } = def.type.validate(normalizedVal, {
1864
+ context: { params: this.params },
1865
+ abortEarly: false,
1866
+ allowUnknown: false
1867
+ });
1774
1868
  if (error) {
1775
1869
  const errs = error.details.map((el) => el.message).join(", ");
1776
1870
  throw new ParamError(`"${key}" validation error: ${errs}`);
@@ -1788,11 +1882,26 @@ var Params = class {
1788
1882
  }
1789
1883
  const valFromArgs = this.args.get(key);
1790
1884
  const valFromParams = this.params[key];
1791
- const res = valFromGetters ? this.validate(key, valFromGetters, def) : valFromArgs ? this.validate(key, valFromArgs, def) : this.validate(key, valFromParams, def);
1792
- if (res !== void 0 && def.values && !def.values.includes(res)) {
1885
+ let source = "default";
1886
+ let value;
1887
+ if (valFromGetters !== void 0 && valFromGetters !== null) {
1888
+ value = this.validate(key, valFromGetters, def);
1889
+ source = "options";
1890
+ } else if (valFromArgs !== void 0 && valFromArgs !== null) {
1891
+ value = this.validate(key, valFromArgs, def);
1892
+ source = "cli";
1893
+ } else if (valFromParams !== void 0 && valFromParams !== null) {
1894
+ value = this.validate(key, valFromParams, def);
1895
+ source = "options";
1896
+ } else {
1897
+ value = this.validate(key, void 0, def);
1898
+ source = "default";
1899
+ }
1900
+ this.trackParam(key, definition || "string", value, source);
1901
+ if (value !== void 0 && def.values && !def.values.includes(value)) {
1793
1902
  throw new ParamError(`key ${key} should be one of ${def.values}`);
1794
1903
  }
1795
- return res;
1904
+ return value;
1796
1905
  }
1797
1906
  /**
1798
1907
  * Set a parameter value with validation
@@ -1826,10 +1935,10 @@ var Params = class {
1826
1935
  * Run all registered getters for a key
1827
1936
  */
1828
1937
  runAllRegisteredGetters(key) {
1829
- let val = null;
1938
+ let val = void 0;
1830
1939
  for (const getter of this.paramGetters) {
1831
1940
  val = getter(key, this.definitions[key]);
1832
- if (val !== void 0) {
1941
+ if (val !== void 0 && val !== null) {
1833
1942
  break;
1834
1943
  }
1835
1944
  }
@@ -1861,8 +1970,6 @@ var Params = class {
1861
1970
  this.paramSetters.push(fn);
1862
1971
  }
1863
1972
  };
1864
- var paramsInstance = null;
1865
- var getParamsInstance = () => paramsInstance;
1866
1973
 
1867
1974
  // src/screen.ts
1868
1975
  init_screen();
@@ -3088,6 +3195,7 @@ var ALL_LEVELS = [
3088
3195
  "response",
3089
3196
  "progress"
3090
3197
  ];
3198
+ var MAX_LEVEL_LENGTH = Math.max(...ALL_LEVELS.map((level) => level.toUpperCase().length));
3091
3199
  var LEVEL_COLORS = {
3092
3200
  error: import_chalk.default.red.bold,
3093
3201
  warn: import_chalk.default.rgb(255, 165, 0),
@@ -3101,13 +3209,104 @@ var LEVEL_COLORS = {
3101
3209
  progress: import_chalk.default.green,
3102
3210
  results: import_chalk.default.magenta
3103
3211
  };
3104
- var CliToolkitLogger = class {
3212
+ var Logger = class _Logger {
3213
+ context;
3214
+ // Partial context during initialization
3105
3215
  options;
3106
3216
  transport;
3107
3217
  startTimes = {};
3108
3218
  lastProgressTimes = {};
3109
- constructor(options = {}) {
3110
- this.options = this.normalizeOptions(options);
3219
+ constructor(context, options = {}) {
3220
+ this.context = context;
3221
+ this.options = this.getDefaultOptions();
3222
+ if (options) {
3223
+ this.configure(options);
3224
+ }
3225
+ this.updateTransport();
3226
+ }
3227
+ /**
3228
+ * Configure logger options
3229
+ * Only parameters present in options are updated
3230
+ */
3231
+ configure(options) {
3232
+ if (options.mode !== void 0) {
3233
+ this.options.mode = this.isValidMode(options.mode) ? options.mode : "text";
3234
+ }
3235
+ if (options.route !== void 0) {
3236
+ this.options.route = options.route;
3237
+ this.updateTransport();
3238
+ }
3239
+ if (options.prefix !== void 0) {
3240
+ this.options.prefix = options.prefix;
3241
+ }
3242
+ if (options.silent !== void 0) {
3243
+ this.options.silent = options.silent;
3244
+ }
3245
+ if (options.showLevel !== void 0) {
3246
+ this.options.showLevel = options.showLevel;
3247
+ }
3248
+ if (options.timestamp !== void 0) {
3249
+ this.options.timestamp = options.timestamp;
3250
+ }
3251
+ if (options.levels !== void 0) {
3252
+ this.options.levels = this.normalizeLevels(options.levels);
3253
+ }
3254
+ if (options.progress !== void 0) {
3255
+ if (options.progress.withTimes !== void 0) {
3256
+ this.options.progressTimes = options.progress.withTimes;
3257
+ }
3258
+ if (options.progress.throttleMs !== void 0) {
3259
+ this.options.progressThrottle = options.progress.throttleMs;
3260
+ }
3261
+ }
3262
+ }
3263
+ /**
3264
+ * Initialize logger from context and CLI parameters
3265
+ */
3266
+ static init(context, options) {
3267
+ const paramDefs = {
3268
+ mode: "string default text",
3269
+ route: "string default console",
3270
+ prefix: "string",
3271
+ silent: "boolean default false",
3272
+ showLevel: "boolean default true",
3273
+ timestamp: "boolean default false",
3274
+ levels: "string",
3275
+ progressWithTimes: "boolean default false",
3276
+ progressThrottleMs: "number"
3277
+ };
3278
+ const cliParams = context.params.getAll(paramDefs);
3279
+ const config2 = {
3280
+ mode: options?.mode ?? cliParams.mode,
3281
+ route: options?.route ?? cliParams.route,
3282
+ prefix: options?.prefix ?? cliParams.prefix,
3283
+ silent: options?.silent ?? cliParams.silent,
3284
+ showLevel: options?.showLevel ?? cliParams.showLevel,
3285
+ timestamp: options?.timestamp ?? cliParams.timestamp,
3286
+ levels: options?.levels ?? (cliParams.levels ? cliParams.levels.split(",") : void 0),
3287
+ progress: options?.progress ?? {
3288
+ withTimes: cliParams.progressWithTimes,
3289
+ throttleMs: cliParams.progressThrottleMs
3290
+ }
3291
+ };
3292
+ const logger = new _Logger(context, config2);
3293
+ context.logger = logger;
3294
+ return logger;
3295
+ }
3296
+ getDefaultOptions() {
3297
+ return {
3298
+ mode: "text",
3299
+ route: this.shouldUseIpcRoute() ? "ipc" : "console",
3300
+ prefix: void 0,
3301
+ silent: false,
3302
+ showLevel: true,
3303
+ timestamp: false,
3304
+ levels: ALL_LEVELS,
3305
+ progressTimes: false,
3306
+ progressThrottle: void 0
3307
+ };
3308
+ }
3309
+ updateTransport() {
3111
3310
  this.transport = this.options.route === "ipc" ? new ParentProcessTransport() : new ConsoleTransport();
3112
3311
  }
3113
3312
  setMode(mode) {
@@ -3216,7 +3415,7 @@ var CliToolkitLogger = class {
3216
3415
  parts.push(now.toISOString());
3217
3416
  }
3218
3417
  if (this.options.showLevel) {
3219
- parts.push(struct.level.toUpperCase());
3418
+ parts.push(struct.level.toUpperCase().padEnd(MAX_LEVEL_LENGTH));
3220
3419
  }
3221
3420
  if (struct.level === "progress") {
3222
3421
  if (struct.prefix) {
@@ -3250,22 +3449,6 @@ var CliToolkitLogger = class {
3250
3449
  inspectChunks(chunks) {
3251
3450
  return chunks.map((chunk) => import_util.default.inspect(chunk, { colors: true, depth: null })).join(" ");
3252
3451
  }
3253
- normalizeOptions(options) {
3254
- const { route, mode, prefix, silent, showLevel, timestamp, levels, progress } = options;
3255
- const shouldUseIpc = this.shouldUseIpcRoute();
3256
- const normalized = {
3257
- mode: this.isValidMode(mode) ? mode : "text",
3258
- route: route ?? (shouldUseIpc ? "ipc" : "console"),
3259
- prefix,
3260
- silent: silent ?? false,
3261
- showLevel: showLevel ?? true,
3262
- timestamp: timestamp ?? false,
3263
- levels: this.normalizeLevels(levels),
3264
- progressTimes: progress?.withTimes ?? false,
3265
- progressThrottle: progress?.throttleMs
3266
- };
3267
- return normalized;
3268
- }
3269
3452
  shouldUseIpcRoute() {
3270
3453
  if (process.env.VITEST || process.env.NODE_ENV === "test") {
3271
3454
  return false;
@@ -3296,35 +3479,44 @@ var CliToolkitLogger = class {
3296
3479
 
3297
3480
  // src/init/index.ts
3298
3481
  var import_events = require("events");
3482
+ function extractComponentOptions(opts, componentName) {
3483
+ const reservedKeys = ["overrides", "defaults", "modules"];
3484
+ const componentOptions = {};
3485
+ for (const [key, value] of Object.entries(opts)) {
3486
+ if (!reservedKeys.includes(key)) {
3487
+ componentOptions[key] = value;
3488
+ }
3489
+ }
3490
+ return componentOptions;
3491
+ }
3299
3492
  function setup(opts = {}) {
3300
- const args = new Args({
3493
+ const args = Args.init({
3301
3494
  overrides: opts.overrides || {},
3302
3495
  defaults: opts.defaults || {}
3303
3496
  });
3304
- const params = new Params({ args }, opts.overrides || {});
3305
- const loggerOptions = opts.logger || {};
3306
- const logger = new CliToolkitLogger({
3307
- mode: loggerOptions.mode || "text",
3308
- route: loggerOptions.route || "console",
3309
- prefix: loggerOptions.prefix,
3310
- silent: loggerOptions.silent,
3311
- showLevel: loggerOptions.showLevel,
3312
- timestamp: loggerOptions.timestamp,
3313
- levels: loggerOptions.levels
3314
- });
3315
- const cleanupFunctions = [];
3316
- const context = {
3497
+ const partialContext = {
3317
3498
  args,
3318
- params,
3319
- logger,
3320
3499
  emitter: new import_events.EventEmitter(),
3321
3500
  isStop: () => false,
3322
- // Will be set in init function
3323
- cleanupFunctions,
3501
+ cleanupFunctions: [],
3324
3502
  registerCleanup: (fn) => {
3325
- cleanupFunctions.push(fn);
3503
+ partialContext.cleanupFunctions.push(fn);
3326
3504
  }
3327
3505
  };
3506
+ const params = Params.init(partialContext, opts.overrides || {});
3507
+ partialContext.params = params;
3508
+ const loggerOptions = extractComponentOptions(opts, "logger");
3509
+ const logger = Logger.init(partialContext, loggerOptions);
3510
+ partialContext.logger = logger;
3511
+ const context = {
3512
+ args,
3513
+ params,
3514
+ logger,
3515
+ emitter: partialContext.emitter,
3516
+ isStop: partialContext.isStop,
3517
+ cleanupFunctions: partialContext.cleanupFunctions,
3518
+ registerCleanup: partialContext.registerCleanup
3519
+ };
3328
3520
  logger.debug("[setup] completed successfully");
3329
3521
  return context;
3330
3522
  }
@@ -3362,7 +3554,6 @@ function setupContext(opts = {}) {
3362
3554
  defaultFileSynopsisFunction,
3363
3555
  defaultVersionSynopsisFunction,
3364
3556
  getArgsInstance,
3365
- getParamsInstance,
3366
3557
  h,
3367
3558
  joiEdateType,
3368
3559
  joiStringArrayType,