@nmakarov/cli-toolkit 0.7.2 → 0.8.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.js CHANGED
@@ -1057,7 +1057,7 @@ var init_screen = __esm({
1057
1057
  import { readFileSync, existsSync } from "fs";
1058
1058
  import { resolve, dirname, basename, extname, join, isAbsolute } from "path";
1059
1059
  import { config } from "dotenv";
1060
- var Args = class {
1060
+ var Args = class _Args {
1061
1061
  args = {};
1062
1062
  flags = {};
1063
1063
  options = {};
@@ -1072,10 +1072,13 @@ var Args = class {
1072
1072
  configsLoaded = [];
1073
1073
  env = "local";
1074
1074
  constructor(config2 = {}) {
1075
- this.aliases = config2.aliases || {};
1076
- this.overrides = config2.overrides || {};
1077
- this.defaults = config2.defaults || {};
1078
- this.prefixes = config2.prefixes || ["not", "no"];
1075
+ this.aliases = {};
1076
+ this.overrides = {};
1077
+ this.defaults = {};
1078
+ this.prefixes = ["not", "no"];
1079
+ if (Object.keys(config2).length > 0) {
1080
+ this.configure(config2);
1081
+ }
1079
1082
  const args = config2.args || process.argv.slice(2);
1080
1083
  this.parseArgs(args);
1081
1084
  this.env = this.get("env")?.toLowerCase() || "local";
@@ -1083,6 +1086,33 @@ var Args = class {
1083
1086
  this.loadConfigFiles();
1084
1087
  this.checkConflicts();
1085
1088
  }
1089
+ /**
1090
+ * Configure Args options
1091
+ * Only parameters present in config are updated
1092
+ * Note: Args is special - it's initialized first, so it can't take context
1093
+ */
1094
+ configure(config2) {
1095
+ if (config2.aliases !== void 0) {
1096
+ this.aliases = config2.aliases;
1097
+ }
1098
+ if (config2.overrides !== void 0) {
1099
+ this.overrides = config2.overrides;
1100
+ }
1101
+ if (config2.defaults !== void 0) {
1102
+ this.defaults = config2.defaults;
1103
+ }
1104
+ if (config2.prefixes !== void 0) {
1105
+ this.prefixes = config2.prefixes;
1106
+ }
1107
+ }
1108
+ /**
1109
+ * Initialize Args instance
1110
+ * Note: Args is special - it's initialized first, so it can't take context
1111
+ * This static method is for consistency with other components
1112
+ */
1113
+ static init(config2 = {}) {
1114
+ return new _Args(config2);
1115
+ }
1086
1116
  /**
1087
1117
  * Parse command line arguments
1088
1118
  */
@@ -1596,18 +1626,76 @@ var joiStringArrayType = (type) => (value, helpers) => {
1596
1626
  };
1597
1627
 
1598
1628
  // src/params/index.ts
1599
- var Params = class {
1629
+ var Params = class _Params {
1630
+ context;
1631
+ // Partial context during initialization
1600
1632
  params = {};
1601
1633
  definitions = {};
1602
1634
  args;
1603
1635
  paramSetters = [];
1604
1636
  paramGetters = [];
1605
- constructor({ args }, opts = {}) {
1606
- this.args = args;
1607
- for (const [k, v] of Object.entries(opts)) {
1637
+ trackedParams = [];
1638
+ constructor(context, options = {}) {
1639
+ this.context = context;
1640
+ this.args = context.args;
1641
+ if (Object.keys(options).length > 0) {
1642
+ this.configure(options);
1643
+ }
1644
+ }
1645
+ /**
1646
+ * Configure parameters
1647
+ * Only parameters present in options are updated
1648
+ */
1649
+ configure(options) {
1650
+ for (const [k, v] of Object.entries(options)) {
1608
1651
  this.params[k] = v;
1609
1652
  }
1610
1653
  }
1654
+ /**
1655
+ * Initialize Params from context and CLI parameters
1656
+ * Note: Params is special - it's initialized early with partial context
1657
+ */
1658
+ static init(context, options) {
1659
+ return new _Params(context, options || {});
1660
+ }
1661
+ /**
1662
+ * Track a parameter request for --stopAfter=init feature
1663
+ */
1664
+ trackParam(key, definition, value, source) {
1665
+ this.trackedParams.push({
1666
+ key,
1667
+ definition,
1668
+ value,
1669
+ source
1670
+ });
1671
+ }
1672
+ /**
1673
+ * Get all tracked parameters (for --stopAfter=init)
1674
+ */
1675
+ getTrackedParams() {
1676
+ return [...this.trackedParams];
1677
+ }
1678
+ /**
1679
+ * Get all figured parameters as a record
1680
+ * Returns all parameters that were collected during initialization,
1681
+ * whether from CLI args, options, or defaults
1682
+ */
1683
+ getAllFigured() {
1684
+ const result = {};
1685
+ for (const param of this.trackedParams) {
1686
+ result[param.key] = {
1687
+ value: param.value,
1688
+ source: param.source
1689
+ };
1690
+ }
1691
+ return result;
1692
+ }
1693
+ /**
1694
+ * Clear tracked parameters
1695
+ */
1696
+ clearTrackedParams() {
1697
+ this.trackedParams = [];
1698
+ }
1611
1699
  /**
1612
1700
  * Assign a parameter definition
1613
1701
  */
@@ -1681,6 +1769,8 @@ var Params = class {
1681
1769
  type = type.default(defValObj.value);
1682
1770
  } else if (str.match(/required/)) {
1683
1771
  type = type.required();
1772
+ } else {
1773
+ type = type.optional();
1684
1774
  }
1685
1775
  return type;
1686
1776
  }
@@ -1688,7 +1778,12 @@ var Params = class {
1688
1778
  * Validate a value against a definition
1689
1779
  */
1690
1780
  validate(key, val, def) {
1691
- const { value, error } = def.type.validate(val, { context: { params: this.params } });
1781
+ const normalizedVal = val === null ? void 0 : val;
1782
+ const { value, error } = def.type.validate(normalizedVal, {
1783
+ context: { params: this.params },
1784
+ abortEarly: false,
1785
+ allowUnknown: false
1786
+ });
1692
1787
  if (error) {
1693
1788
  const errs = error.details.map((el) => el.message).join(", ");
1694
1789
  throw new ParamError(`"${key}" validation error: ${errs}`);
@@ -1706,11 +1801,26 @@ var Params = class {
1706
1801
  }
1707
1802
  const valFromArgs = this.args.get(key);
1708
1803
  const valFromParams = this.params[key];
1709
- const res = valFromGetters ? this.validate(key, valFromGetters, def) : valFromArgs ? this.validate(key, valFromArgs, def) : this.validate(key, valFromParams, def);
1710
- if (res !== void 0 && def.values && !def.values.includes(res)) {
1804
+ let source = "default";
1805
+ let value;
1806
+ if (valFromGetters !== void 0 && valFromGetters !== null) {
1807
+ value = this.validate(key, valFromGetters, def);
1808
+ source = "options";
1809
+ } else if (valFromArgs !== void 0 && valFromArgs !== null) {
1810
+ value = this.validate(key, valFromArgs, def);
1811
+ source = "cli";
1812
+ } else if (valFromParams !== void 0 && valFromParams !== null) {
1813
+ value = this.validate(key, valFromParams, def);
1814
+ source = "options";
1815
+ } else {
1816
+ value = this.validate(key, void 0, def);
1817
+ source = "default";
1818
+ }
1819
+ this.trackParam(key, definition || "string", value, source);
1820
+ if (value !== void 0 && def.values && !def.values.includes(value)) {
1711
1821
  throw new ParamError(`key ${key} should be one of ${def.values}`);
1712
1822
  }
1713
- return res;
1823
+ return value;
1714
1824
  }
1715
1825
  /**
1716
1826
  * Set a parameter value with validation
@@ -1744,10 +1854,10 @@ var Params = class {
1744
1854
  * Run all registered getters for a key
1745
1855
  */
1746
1856
  runAllRegisteredGetters(key) {
1747
- let val = null;
1857
+ let val = void 0;
1748
1858
  for (const getter of this.paramGetters) {
1749
1859
  val = getter(key, this.definitions[key]);
1750
- if (val !== void 0) {
1860
+ if (val !== void 0 && val !== null) {
1751
1861
  break;
1752
1862
  }
1753
1863
  }
@@ -1779,8 +1889,6 @@ var Params = class {
1779
1889
  this.paramSetters.push(fn);
1780
1890
  }
1781
1891
  };
1782
- var paramsInstance = null;
1783
- var getParamsInstance = () => paramsInstance;
1784
1892
 
1785
1893
  // src/screen.ts
1786
1894
  init_screen();
@@ -3006,6 +3114,7 @@ var ALL_LEVELS = [
3006
3114
  "response",
3007
3115
  "progress"
3008
3116
  ];
3117
+ var MAX_LEVEL_LENGTH = Math.max(...ALL_LEVELS.map((level) => level.toUpperCase().length));
3009
3118
  var LEVEL_COLORS = {
3010
3119
  error: chalk.red.bold,
3011
3120
  warn: chalk.rgb(255, 165, 0),
@@ -3019,13 +3128,104 @@ var LEVEL_COLORS = {
3019
3128
  progress: chalk.green,
3020
3129
  results: chalk.magenta
3021
3130
  };
3022
- var CliToolkitLogger = class {
3131
+ var Logger = class _Logger {
3132
+ context;
3133
+ // Partial context during initialization
3023
3134
  options;
3024
3135
  transport;
3025
3136
  startTimes = {};
3026
3137
  lastProgressTimes = {};
3027
- constructor(options = {}) {
3028
- this.options = this.normalizeOptions(options);
3138
+ constructor(context, options = {}) {
3139
+ this.context = context;
3140
+ this.options = this.getDefaultOptions();
3141
+ if (options) {
3142
+ this.configure(options);
3143
+ }
3144
+ this.updateTransport();
3145
+ }
3146
+ /**
3147
+ * Configure logger options
3148
+ * Only parameters present in options are updated
3149
+ */
3150
+ configure(options) {
3151
+ if (options.mode !== void 0) {
3152
+ this.options.mode = this.isValidMode(options.mode) ? options.mode : "text";
3153
+ }
3154
+ if (options.route !== void 0) {
3155
+ this.options.route = options.route;
3156
+ this.updateTransport();
3157
+ }
3158
+ if (options.prefix !== void 0) {
3159
+ this.options.prefix = options.prefix;
3160
+ }
3161
+ if (options.silent !== void 0) {
3162
+ this.options.silent = options.silent;
3163
+ }
3164
+ if (options.showLevel !== void 0) {
3165
+ this.options.showLevel = options.showLevel;
3166
+ }
3167
+ if (options.timestamp !== void 0) {
3168
+ this.options.timestamp = options.timestamp;
3169
+ }
3170
+ if (options.levels !== void 0) {
3171
+ this.options.levels = this.normalizeLevels(options.levels);
3172
+ }
3173
+ if (options.progress !== void 0) {
3174
+ if (options.progress.withTimes !== void 0) {
3175
+ this.options.progressTimes = options.progress.withTimes;
3176
+ }
3177
+ if (options.progress.throttleMs !== void 0) {
3178
+ this.options.progressThrottle = options.progress.throttleMs;
3179
+ }
3180
+ }
3181
+ }
3182
+ /**
3183
+ * Initialize logger from context and CLI parameters
3184
+ */
3185
+ static init(context, options) {
3186
+ const paramDefs = {
3187
+ mode: "string default text",
3188
+ route: "string default console",
3189
+ prefix: "string",
3190
+ silent: "boolean default false",
3191
+ showLevel: "boolean default true",
3192
+ timestamp: "boolean default false",
3193
+ levels: "string",
3194
+ progressWithTimes: "boolean default false",
3195
+ progressThrottleMs: "number"
3196
+ };
3197
+ const cliParams = context.params.getAll(paramDefs);
3198
+ const config2 = {
3199
+ mode: options?.mode ?? cliParams.mode,
3200
+ route: options?.route ?? cliParams.route,
3201
+ prefix: options?.prefix ?? cliParams.prefix,
3202
+ silent: options?.silent ?? cliParams.silent,
3203
+ showLevel: options?.showLevel ?? cliParams.showLevel,
3204
+ timestamp: options?.timestamp ?? cliParams.timestamp,
3205
+ levels: options?.levels ?? (cliParams.levels ? cliParams.levels.split(",") : void 0),
3206
+ progress: options?.progress ?? {
3207
+ withTimes: cliParams.progressWithTimes,
3208
+ throttleMs: cliParams.progressThrottleMs
3209
+ }
3210
+ };
3211
+ const logger = new _Logger(context, config2);
3212
+ context.logger = logger;
3213
+ return logger;
3214
+ }
3215
+ getDefaultOptions() {
3216
+ return {
3217
+ mode: "text",
3218
+ route: this.shouldUseIpcRoute() ? "ipc" : "console",
3219
+ prefix: void 0,
3220
+ silent: false,
3221
+ showLevel: true,
3222
+ timestamp: false,
3223
+ levels: ALL_LEVELS,
3224
+ progressTimes: false,
3225
+ progressThrottle: void 0
3226
+ };
3227
+ }
3228
+ updateTransport() {
3029
3229
  this.transport = this.options.route === "ipc" ? new ParentProcessTransport() : new ConsoleTransport();
3030
3230
  }
3031
3231
  setMode(mode) {
@@ -3134,7 +3334,7 @@ var CliToolkitLogger = class {
3134
3334
  parts.push(now.toISOString());
3135
3335
  }
3136
3336
  if (this.options.showLevel) {
3137
- parts.push(struct.level.toUpperCase());
3337
+ parts.push(struct.level.toUpperCase().padEnd(MAX_LEVEL_LENGTH));
3138
3338
  }
3139
3339
  if (struct.level === "progress") {
3140
3340
  if (struct.prefix) {
@@ -3168,22 +3368,6 @@ var CliToolkitLogger = class {
3168
3368
  inspectChunks(chunks) {
3169
3369
  return chunks.map((chunk) => util.inspect(chunk, { colors: true, depth: null })).join(" ");
3170
3370
  }
3171
- normalizeOptions(options) {
3172
- const { route, mode, prefix, silent, showLevel, timestamp, levels, progress } = options;
3173
- const shouldUseIpc = this.shouldUseIpcRoute();
3174
- const normalized = {
3175
- mode: this.isValidMode(mode) ? mode : "text",
3176
- route: route ?? (shouldUseIpc ? "ipc" : "console"),
3177
- prefix,
3178
- silent: silent ?? false,
3179
- showLevel: showLevel ?? true,
3180
- timestamp: timestamp ?? false,
3181
- levels: this.normalizeLevels(levels),
3182
- progressTimes: progress?.withTimes ?? false,
3183
- progressThrottle: progress?.throttleMs
3184
- };
3185
- return normalized;
3186
- }
3187
3371
  shouldUseIpcRoute() {
3188
3372
  if (process.env.VITEST || process.env.NODE_ENV === "test") {
3189
3373
  return false;
@@ -3214,35 +3398,44 @@ var CliToolkitLogger = class {
3214
3398
 
3215
3399
  // src/init/index.ts
3216
3400
  import { EventEmitter } from "events";
3401
+ function extractComponentOptions(opts, componentName) {
3402
+ const reservedKeys = ["overrides", "defaults", "modules"];
3403
+ const componentOptions = {};
3404
+ for (const [key, value] of Object.entries(opts)) {
3405
+ if (!reservedKeys.includes(key)) {
3406
+ componentOptions[key] = value;
3407
+ }
3408
+ }
3409
+ return componentOptions;
3410
+ }
3217
3411
  function setup(opts = {}) {
3218
- const args = new Args({
3412
+ const args = Args.init({
3219
3413
  overrides: opts.overrides || {},
3220
3414
  defaults: opts.defaults || {}
3221
3415
  });
3222
- const params = new Params({ args }, opts.overrides || {});
3223
- const loggerOptions = opts.logger || {};
3224
- const logger = new CliToolkitLogger({
3225
- mode: loggerOptions.mode || "text",
3226
- route: loggerOptions.route || "console",
3227
- prefix: loggerOptions.prefix,
3228
- silent: loggerOptions.silent,
3229
- showLevel: loggerOptions.showLevel,
3230
- timestamp: loggerOptions.timestamp,
3231
- levels: loggerOptions.levels
3232
- });
3233
- const cleanupFunctions = [];
3234
- const context = {
3416
+ const partialContext = {
3235
3417
  args,
3236
- params,
3237
- logger,
3238
3418
  emitter: new EventEmitter(),
3239
3419
  isStop: () => false,
3240
- // Will be set in init function
3241
- cleanupFunctions,
3420
+ cleanupFunctions: [],
3242
3421
  registerCleanup: (fn) => {
3243
- cleanupFunctions.push(fn);
3422
+ partialContext.cleanupFunctions.push(fn);
3244
3423
  }
3245
3424
  };
3425
+ const params = Params.init(partialContext, opts.overrides || {});
3426
+ partialContext.params = params;
3427
+ const loggerOptions = extractComponentOptions(opts, "logger");
3428
+ const logger = Logger.init(partialContext, loggerOptions);
3429
+ partialContext.logger = logger;
3430
+ const context = {
3431
+ args,
3432
+ params,
3433
+ logger,
3434
+ emitter: partialContext.emitter,
3435
+ isStop: partialContext.isStop,
3436
+ cleanupFunctions: partialContext.cleanupFunctions,
3437
+ registerCleanup: partialContext.registerCleanup
3438
+ };
3246
3439
  logger.debug("[setup] completed successfully");
3247
3440
  return context;
3248
3441
  }
@@ -3279,7 +3472,6 @@ export {
3279
3472
  defaultFileSynopsisFunction,
3280
3473
  defaultVersionSynopsisFunction,
3281
3474
  getArgsInstance,
3282
- getParamsInstance,
3283
3475
  createElement2 as h,
3284
3476
  joiEdateType,
3285
3477
  joiStringArrayType,