helm-env-delta 1.15.1 → 1.15.3

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/README.md CHANGED
@@ -66,7 +66,7 @@ HelmEnvDelta (`hed`) automates environment synchronization for GitOps workflows
66
66
 
67
67
  ⚔ **High Performance** - Intelligent caching and parallel processing. Formatting rules, compiled regexes, and array normalization are all cached for fast repeated runs.
68
68
 
69
- šŸ” **Security Hardened** - Regex inputs (stop rules, transforms, pattern files) are validated against ReDoS (catastrophic backtracking). Fixed values are validated against prototype pollution attacks.
69
+ šŸ” **Security Hardened** - Regex inputs (stop rules, transforms, pattern files) are validated against ReDoS (catastrophic backtracking) — covers nested quantifiers, optional groups, and alternation patterns. Fixed values and YAML file content are guarded against prototype pollution. HTML report paths are HTML-escaped to prevent XSS from filename transforms.
70
70
 
71
71
  šŸ”” **Auto Updates** - Notifies when newer versions are available (skips in CI/CD).
72
72
 
@@ -428,7 +428,7 @@ requiredVersion: '1.10.0' # Optional: Minimum tool version required to process t
428
428
 
429
429
  **Note:** Source and destination paths cannot resolve to the same folder.
430
430
 
431
- **`requiredVersion`:** When set, the CLI checks that the installed version of helm-env-delta meets this minimum. If the installed version is older, the CLI exits immediately with a clear upgrade message. This prevents running configs that depend on newer features with an outdated tool version. Supports `"1.2.3"` or `"v1.2.3"` format.
431
+ **`requiredVersion`:** When set, the CLI checks that the installed version of helm-env-delta meets this minimum. If the installed version is older, the CLI exits immediately with a clear upgrade message. This prevents running configs that depend on newer features with an outdated tool version. Supports `"1.2.3"` or `"v1.2.3"` format. Setting `requiredVersion` also suppresses the auto-update notification — a pinned version requirement signals intentional version targeting.
432
432
 
433
433
  ---
434
434
 
@@ -680,7 +680,7 @@ stopRules:
680
680
 
681
681
  **Override:** Use `--force` to bypass stop rules when needed.
682
682
 
683
- **Regex safety:** All `regex` patterns (inline and from files) are validated against catastrophic backtracking (ReDoS). Patterns with nested quantifiers on groups (e.g., `(a+)+`) are rejected at config load time.
683
+ **Regex safety:** All `regex` patterns (inline and from files) are validated against catastrophic backtracking (ReDoS). Rejected patterns include: nested quantifiers on groups (e.g., `(a+)+`), optional groups with inner quantifiers (e.g., `(a+)?`), and alternation groups with outer repetition (e.g., `(a|ab)*`).
684
684
 
685
685
  **Visibility:** Stop rule violations appear in console output, JSON reports, and HTML reports (dry-run mode only, as a collapsible table in the header area).
686
686
 
@@ -4,18 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.formatProgressMessage = exports.colorizeFileOperation = exports.formatStopRuleViolation = exports.formatBox = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
7
+ const ansi_colors_1 = __importDefault(require("ansi-colors"));
8
8
  const formatBox = (title, content, style = 'info', width = 60) => {
9
9
  const getColorFunction = (boxStyle) => {
10
10
  switch (boxStyle) {
11
11
  case 'success':
12
- return chalk_1.default.green;
12
+ return ansi_colors_1.default.green;
13
13
  case 'warning':
14
- return chalk_1.default.yellow;
14
+ return ansi_colors_1.default.yellow;
15
15
  case 'error':
16
- return chalk_1.default.red;
16
+ return ansi_colors_1.default.red;
17
17
  default:
18
- return chalk_1.default.cyan;
18
+ return ansi_colors_1.default.cyan;
19
19
  }
20
20
  };
21
21
  const colorFunction = getColorFunction(style);
@@ -44,7 +44,7 @@ const formatStopRuleViolation = (violation, mode) => {
44
44
  return violationMode === 'error' ? 'error' : 'warning';
45
45
  };
46
46
  const labelWidth = 10;
47
- const formatLabel = (label) => chalk_1.default.dim(label.padEnd(labelWidth));
47
+ const formatLabel = (label) => ansi_colors_1.default.dim(label.padEnd(labelWidth));
48
48
  const content = [
49
49
  `${formatLabel('File:')} ${violation.file}`,
50
50
  `${formatLabel('Path:')} ${violation.path}`,
@@ -61,13 +61,13 @@ const colorizeFileOperation = (operation, filePath, isDryRun, alreadyDeleted = f
61
61
  const getOperationDisplay = (op) => {
62
62
  switch (op) {
63
63
  case 'add':
64
- return { symbol: '+', verb: 'add', colorFn: chalk_1.default.green };
64
+ return { symbol: '+', verb: 'add', colorFn: ansi_colors_1.default.green };
65
65
  case 'update':
66
- return { symbol: '~', verb: 'update', colorFn: chalk_1.default.yellow };
66
+ return { symbol: '~', verb: 'update', colorFn: ansi_colors_1.default.yellow };
67
67
  case 'delete':
68
- return { symbol: '-', verb: 'delete', colorFn: chalk_1.default.red };
68
+ return { symbol: '-', verb: 'delete', colorFn: ansi_colors_1.default.red };
69
69
  case 'format':
70
- return { symbol: 'ā‰ˆ', verb: 'format', colorFn: chalk_1.default.cyan };
70
+ return { symbol: 'ā‰ˆ', verb: 'format', colorFn: ansi_colors_1.default.cyan };
71
71
  }
72
72
  };
73
73
  const { symbol, verb, colorFn } = getOperationDisplay(operation);
@@ -91,9 +91,9 @@ const formatProgressMessage = (message, style) => {
91
91
  const getColorFunction = (progressStyle) => {
92
92
  switch (progressStyle) {
93
93
  case 'success':
94
- return chalk_1.default.green;
94
+ return ansi_colors_1.default.green;
95
95
  default:
96
- return chalk_1.default.cyan;
96
+ return ansi_colors_1.default.cyan;
97
97
  }
98
98
  };
99
99
  const icon = getIcon(style);
package/dist/index.js CHANGED
@@ -40,7 +40,7 @@ const node_fs_1 = require("node:fs");
40
40
  const promises_1 = require("node:fs/promises");
41
41
  const node_os_1 = require("node:os");
42
42
  const node_path_1 = __importDefault(require("node:path"));
43
- const chalk_1 = __importDefault(require("chalk"));
43
+ const ansi_colors_1 = __importDefault(require("ansi-colors"));
44
44
  const YAML = __importStar(require("yaml"));
45
45
  const package_json_1 = __importDefault(require("../package.json"));
46
46
  const commandLine_1 = require("./commandLine");
@@ -60,7 +60,7 @@ const versionChecker_1 = require("./utils/versionChecker");
60
60
  const main = async () => {
61
61
  const command = (0, commandLine_1.parseCommandLine)();
62
62
  if (command.noColor)
63
- chalk_1.default.level = 0;
63
+ ansi_colors_1.default.enabled = false;
64
64
  const verbosityLevel = command.verbose ? 'verbose' : command.quiet ? 'quiet' : 'normal';
65
65
  const logger = new logger_1.Logger({ level: verbosityLevel, isDiffJson: command.diffJson });
66
66
  logger.log(`Now you run ${package_json_1.default.name} v${package_json_1.default.version}...`);
@@ -68,18 +68,20 @@ const main = async () => {
68
68
  const firstRunMarker = node_path_1.default.join(configDirectory, 'first-run');
69
69
  const isFirstRun = !(0, node_fs_1.existsSync)(firstRunMarker);
70
70
  if (isFirstRun && !command.quiet) {
71
- console.log(chalk_1.default.cyan('\nšŸ‘‹ First time using helm-env-delta?\n'));
72
- console.log(chalk_1.default.dim(' Tips:'));
73
- console.log(chalk_1.default.dim(' • Always use --dry-run first to preview changes'));
74
- console.log(chalk_1.default.dim(' • Use --diff-html to review diffs in your browser'));
75
- console.log(chalk_1.default.dim(' • See examples: https://github.com/balazscsaba2006/helm-env-delta/tree/main/example'));
76
- console.log(chalk_1.default.dim(' • Run with --help to see all options\n'));
71
+ console.log(ansi_colors_1.default.cyan('\nšŸ‘‹ First time using helm-env-delta?\n'));
72
+ console.log(ansi_colors_1.default.dim(' Tips:'));
73
+ console.log(ansi_colors_1.default.dim(' • Always use --dry-run first to preview changes'));
74
+ console.log(ansi_colors_1.default.dim(' • Use --diff-html to review diffs in your browser'));
75
+ console.log(ansi_colors_1.default.dim(' • See examples: https://github.com/balazscsaba2006/helm-env-delta/tree/main/example'));
76
+ console.log(ansi_colors_1.default.dim(' • Run with --help to see all options\n'));
77
77
  (0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
78
78
  (0, node_fs_1.writeFileSync)(firstRunMarker, new Date().toISOString());
79
79
  }
80
80
  const config = (0, config_1.loadConfigFile)(command.config, command.quiet, logger, { formatOnly: command.formatOnly });
81
+ if (config.requiredVersion)
82
+ configHasRequiredVersion = true;
81
83
  if (command.showConfig) {
82
- console.log(chalk_1.default.cyan('\nāš™ļø Resolved Configuration:\n'));
84
+ console.log(ansi_colors_1.default.cyan('\nāš™ļø Resolved Configuration:\n'));
83
85
  console.log(YAML.stringify(config, { indent: 2 }));
84
86
  return;
85
87
  }
@@ -93,9 +95,9 @@ const main = async () => {
93
95
  const warningResult = (0, config_1.validateConfigWarnings)(validationConfig);
94
96
  let hasAnyWarnings = warningResult.hasWarnings;
95
97
  if (warningResult.hasWarnings) {
96
- console.warn(chalk_1.default.yellow('\nāš ļø Configuration Warnings (non-fatal):\n'));
98
+ console.warn(ansi_colors_1.default.yellow('\nāš ļø Configuration Warnings (non-fatal):\n'));
97
99
  for (const warning of warningResult.warnings)
98
- console.warn(chalk_1.default.yellow(` • ${warning}`));
100
+ console.warn(ansi_colors_1.default.yellow(` • ${warning}`));
99
101
  }
100
102
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files for validation...', 'loading'));
101
103
  const sourceResult = await (0, pipeline_1.loadFiles)({
@@ -133,12 +135,12 @@ const main = async () => {
133
135
  const usageResult = (0, pipeline_1.validatePatternUsage)(validationConfig, sourceFiles, destinationFiles);
134
136
  hasAnyWarnings = hasAnyWarnings || usageResult.hasWarnings;
135
137
  if (usageResult.hasWarnings) {
136
- console.warn(chalk_1.default.yellow('\nāš ļø Pattern Usage Warnings (non-fatal):\n'));
138
+ console.warn(ansi_colors_1.default.yellow('\nāš ļø Pattern Usage Warnings (non-fatal):\n'));
137
139
  for (const warning of usageResult.warnings) {
138
- const contextString = warning.context ? chalk_1.default.dim(` (${warning.context})`) : '';
139
- console.warn(chalk_1.default.yellow(` • ${warning.message}${contextString}`));
140
+ const contextString = warning.context ? ansi_colors_1.default.dim(` (${warning.context})`) : '';
141
+ console.warn(ansi_colors_1.default.yellow(` • ${warning.message}${contextString}`));
140
142
  if (warning.hint)
141
- console.warn(chalk_1.default.dim(` Hint: ${warning.hint}`));
143
+ console.warn(ansi_colors_1.default.dim(` Hint: ${warning.hint}`));
142
144
  }
143
145
  }
144
146
  if (hasAnyWarnings)
@@ -159,7 +161,7 @@ const main = async () => {
159
161
  }
160
162
  if (command.formatOnly) {
161
163
  if (!config.outputFormat) {
162
- logger.log(chalk_1.default.yellow('\nāš ļø No outputFormat configured. Nothing to format.'));
164
+ logger.log(ansi_colors_1.default.yellow('\nāš ļø No outputFormat configured. Nothing to format.'));
163
165
  return;
164
166
  }
165
167
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading destination files...', 'loading'));
@@ -174,10 +176,10 @@ const main = async () => {
174
176
  logger.progress(`Loaded ${destinationFiles.size} destination file(s)`, 'success');
175
177
  if (command.listFiles) {
176
178
  const filesList = [...destinationFiles.keys()].toSorted();
177
- console.log(chalk_1.default.cyan('\nšŸ“‹ Files to be formatted:\n'));
178
- console.log(chalk_1.default.yellow(`Destination files: ${filesList.length}`));
179
+ console.log(ansi_colors_1.default.cyan('\nšŸ“‹ Files to be formatted:\n'));
180
+ console.log(ansi_colors_1.default.yellow(`Destination files: ${filesList.length}`));
179
181
  for (const file of filesList)
180
- console.log(` ${chalk_1.default.dim(file)}`);
182
+ console.log(` ${ansi_colors_1.default.dim(file)}`);
181
183
  return;
182
184
  }
183
185
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Formatting files...', 'info'));
@@ -263,13 +265,13 @@ const main = async () => {
263
265
  if (command.listFiles) {
264
266
  const sourceFilesList = [...sourceFiles.keys()].toSorted();
265
267
  const destinationFilesList = [...destinationFiles.keys()].toSorted();
266
- console.log(chalk_1.default.cyan('\nšŸ“‹ Files to be synced:\n'));
267
- console.log(chalk_1.default.green(`Source files: ${sourceFilesList.length}`));
268
+ console.log(ansi_colors_1.default.cyan('\nšŸ“‹ Files to be synced:\n'));
269
+ console.log(ansi_colors_1.default.green(`Source files: ${sourceFilesList.length}`));
268
270
  for (const file of sourceFilesList)
269
- console.log(` ${chalk_1.default.dim(file)}`);
270
- console.log(chalk_1.default.yellow(`\nDestination files: ${destinationFilesList.length}`));
271
+ console.log(` ${ansi_colors_1.default.dim(file)}`);
272
+ console.log(ansi_colors_1.default.yellow(`\nDestination files: ${destinationFilesList.length}`));
271
273
  for (const file of destinationFilesList)
272
- console.log(` ${chalk_1.default.dim(file)}`);
274
+ console.log(` ${ansi_colors_1.default.dim(file)}`);
273
275
  return;
274
276
  }
275
277
  logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Computing differences...', 'info'));
@@ -290,13 +292,13 @@ const main = async () => {
290
292
  try {
291
293
  const suggestions = (0, suggestionEngine_1.analyzeDifferencesForSuggestions)(diffResult, syncConfig, command.suggestThreshold);
292
294
  const yaml = (0, suggestionEngine_1.formatSuggestionsAsYaml)(suggestions);
293
- console.log(chalk_1.default.cyan('\nšŸ’” Suggested Configuration:\n'));
295
+ console.log(ansi_colors_1.default.cyan('\nšŸ’” Suggested Configuration:\n'));
294
296
  console.log(yaml);
295
297
  if (suggestions.metadata.changedFiles === 0)
296
- console.log(chalk_1.default.yellow('\nā„¹ļø No changes detected. Files are already in sync.'));
298
+ console.log(ansi_colors_1.default.yellow('\nā„¹ļø No changes detected. Files are already in sync.'));
297
299
  else {
298
- console.log(chalk_1.default.dim('\n---'));
299
- console.log(chalk_1.default.dim('šŸ’” Tip: Copy relevant sections to your config.yaml and test with --dry-run'));
300
+ console.log(ansi_colors_1.default.dim('\n---'));
301
+ console.log(ansi_colors_1.default.dim('šŸ’” Tip: Copy relevant sections to your config.yaml and test with --dry-run'));
300
302
  }
301
303
  return;
302
304
  }
@@ -324,29 +326,29 @@ const main = async () => {
324
326
  process.exit(1);
325
327
  }
326
328
  if (!command.dryRun && !command.quiet) {
327
- console.log(chalk_1.default.cyan('\nšŸ“Š Sync Summary:'));
328
- console.log(chalk_1.default.dim('─'.repeat(60)));
329
- console.log(` ${chalk_1.default.green('Added:')} ${diffResult.addedFiles.length} files`);
330
- console.log(` ${chalk_1.default.yellow('Changed:')} ${diffResult.changedFiles.length} files`);
331
- console.log(` ${chalk_1.default.red('Deleted:')} ${diffResult.deletedFiles.length} files (${syncConfig.prune ? 'prune enabled' : 'prune disabled'})`);
332
- console.log(` ${chalk_1.default.blue('Unchanged:')} ${diffResult.unchangedFiles.length} files`);
333
- console.log(chalk_1.default.dim('─'.repeat(60)));
329
+ console.log(ansi_colors_1.default.cyan('\nšŸ“Š Sync Summary:'));
330
+ console.log(ansi_colors_1.default.dim('─'.repeat(60)));
331
+ console.log(` ${ansi_colors_1.default.green('Added:')} ${diffResult.addedFiles.length} files`);
332
+ console.log(` ${ansi_colors_1.default.yellow('Changed:')} ${diffResult.changedFiles.length} files`);
333
+ console.log(` ${ansi_colors_1.default.red('Deleted:')} ${diffResult.deletedFiles.length} files (${syncConfig.prune ? 'prune enabled' : 'prune disabled'})`);
334
+ console.log(` ${ansi_colors_1.default.blue('Unchanged:')} ${diffResult.unchangedFiles.length} files`);
335
+ console.log(ansi_colors_1.default.dim('─'.repeat(60)));
334
336
  if (diffResult.deletedFiles.length > 0 && syncConfig.prune) {
335
- console.warn(chalk_1.default.red('āš ļø Warning: Prune is enabled. The following files will be permanently deleted:'));
337
+ console.warn(ansi_colors_1.default.red('āš ļø Warning: Prune is enabled. The following files will be permanently deleted:'));
336
338
  for (const f of diffResult.deletedFiles)
337
- console.warn(chalk_1.default.red(` - ${f}`));
339
+ console.warn(ansi_colors_1.default.red(` - ${f}`));
338
340
  }
339
341
  if (syncConfig.confirmationDelay > 0) {
340
342
  const totalSeconds = Math.ceil(syncConfig.confirmationDelay / 1000);
341
- console.log(chalk_1.default.dim('\nPress Ctrl+C to cancel.\n'));
343
+ console.log(ansi_colors_1.default.dim('\nPress Ctrl+C to cancel.\n'));
342
344
  for (let remaining = totalSeconds; remaining > 0; remaining--) {
343
- process.stdout.write(chalk_1.default.dim(` Proceeding in ${remaining}s...\r`));
345
+ process.stdout.write(ansi_colors_1.default.dim(` Proceeding in ${remaining}s...\r`));
344
346
  await new Promise((resolve) => setTimeout(resolve, 1000));
345
347
  }
346
348
  process.stdout.write(' '.repeat(40) + '\r');
347
349
  }
348
350
  else
349
- console.log(chalk_1.default.dim('\nPress Ctrl+C to cancel, or use --dry-run to preview changes first.\n'));
351
+ console.log(ansi_colors_1.default.dim('\nPress Ctrl+C to cancel, or use --dry-run to preview changes first.\n'));
350
352
  }
351
353
  const formattedFiles = await (0, pipeline_1.updateFiles)(diffResult, sourceFiles, destinationFiles, syncConfig, command.dryRun, command.skipFormat, logger);
352
354
  if (command.diffHtml && !command.quiet)
@@ -354,6 +356,7 @@ const main = async () => {
354
356
  if (command.diffJson)
355
357
  (0, reporters_1.generateJsonReport)(diffResult, formattedFiles, validationResult, syncConfig, command.dryRun, package_json_1.default.version);
356
358
  };
359
+ let configHasRequiredVersion = false;
357
360
  (async () => {
358
361
  try {
359
362
  await main();
@@ -393,7 +396,7 @@ const main = async () => {
393
396
  }
394
397
  finally {
395
398
  const command = (0, commandLine_1.parseCommandLine)();
396
- if (!command.quiet)
399
+ if (!command.quiet && !configHasRequiredVersion)
397
400
  void (0, versionChecker_1.checkForUpdates)(package_json_1.default.version);
398
401
  }
399
402
  })();
@@ -115,8 +115,9 @@ const deepMerge = (fullTarget, filteredSource, filteredTarget, currentPath = [],
115
115
  const fullTargetObject = fullTarget;
116
116
  const filteredTargetObject = filteredTarget || {};
117
117
  const result = { ...sourceObject };
118
+ const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
118
119
  for (const [key, value] of Object.entries(fullTargetObject))
119
- if (!(key in filteredTargetObject) && !(key in sourceObject))
120
+ if (!DANGEROUS_KEYS.has(key) && !(key in filteredTargetObject) && !(key in sourceObject))
120
121
  result[key] = value;
121
122
  for (const [key, value] of Object.entries(sourceObject))
122
123
  if (key in fullTargetObject)
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -46,7 +13,7 @@ exports.BrowserLauncherError = BrowserLauncherError;
46
13
  exports.isBrowserLauncherError = (0, errors_1.createErrorTypeGuard)(BrowserLauncherError);
47
14
  const openInBrowser = async (filePath) => {
48
15
  try {
49
- const openModule = await Promise.resolve().then(() => __importStar(require('open')));
16
+ const openModule = await import('open');
50
17
  const open = openModule.default;
51
18
  const absolutePath = node_path_1.default.resolve(filePath);
52
19
  await open(absolutePath);
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.showConsoleDiff = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
7
+ const ansi_colors_1 = __importDefault(require("ansi-colors"));
8
8
  const pipeline_1 = require("../pipeline");
9
9
  const diffGenerator_1 = require("../utils/diffGenerator");
10
10
  const fileType_1 = require("../utils/fileType");
@@ -14,36 +14,36 @@ const colorizeUnifiedDiff = (diff) => {
14
14
  .split('\n')
15
15
  .map((line) => {
16
16
  if (line.startsWith('+') && !line.startsWith('+++'))
17
- return chalk_1.default.green(line);
17
+ return ansi_colors_1.default.green(line);
18
18
  if (line.startsWith('-') && !line.startsWith('---'))
19
- return chalk_1.default.red(line);
19
+ return ansi_colors_1.default.red(line);
20
20
  if (line.startsWith('@@'))
21
- return chalk_1.default.cyan(line);
22
- return chalk_1.default.gray(line);
21
+ return ansi_colors_1.default.cyan(line);
22
+ return ansi_colors_1.default.gray(line);
23
23
  })
24
24
  .join('\n');
25
25
  };
26
26
  const formatAddedFiles = (files) => {
27
27
  if (files.length === 0)
28
28
  return '';
29
- const header = chalk_1.default.green.bold(`\nAdded Files (${files.length}):`);
30
- const fileList = files.map((file) => chalk_1.default.green(` + ${file.path}`)).join('\n');
29
+ const header = ansi_colors_1.default.green.bold(`\nAdded Files (${files.length}):`);
30
+ const fileList = files.map((file) => ansi_colors_1.default.green(` + ${file.path}`)).join('\n');
31
31
  return `${header}\n${fileList}\n`;
32
32
  };
33
33
  const formatDeletedFiles = (files) => {
34
34
  if (files.length === 0)
35
35
  return '';
36
- const header = chalk_1.default.red.bold(`\nDeleted Files (${files.length}):`);
37
- const fileList = files.map((file) => chalk_1.default.red(` - ${file}`)).join('\n');
36
+ const header = ansi_colors_1.default.red.bold(`\nDeleted Files (${files.length}):`);
37
+ const fileList = files.map((file) => ansi_colors_1.default.red(` - ${file}`)).join('\n');
38
38
  return `${header}\n${fileList}\n`;
39
39
  };
40
40
  const formatChangedFile = (file, config) => {
41
41
  const isYaml = (0, fileType_1.isYamlFile)(file.path);
42
- const separator = chalk_1.default.yellow('━'.repeat(60));
42
+ const separator = ansi_colors_1.default.yellow('━'.repeat(60));
43
43
  const skipPaths = (0, pipeline_1.getSkipPathsForFile)(file.path, config.skipPath);
44
44
  const skipPathInfo = skipPaths.length > 0
45
- ? chalk_1.default.dim(`SkipPath patterns applied: ${skipPaths.join(', ')}`)
46
- : chalk_1.default.dim('No skipPath patterns applied');
45
+ ? ansi_colors_1.default.dim(`SkipPath patterns applied: ${skipPaths.join(', ')}`)
46
+ : ansi_colors_1.default.dim('No skipPath patterns applied');
47
47
  const destinationContent = isYaml
48
48
  ? (0, serialization_1.serializeForDiff)(file.processedDestContent, true)
49
49
  : String(file.processedDestContent);
@@ -54,7 +54,7 @@ const formatChangedFile = (file, config) => {
54
54
  const colorizedDiff = colorizeUnifiedDiff(unifiedDiff);
55
55
  return `
56
56
  ${separator}
57
- ${chalk_1.default.yellow.bold(`File: ${file.path}`)}
57
+ ${ansi_colors_1.default.yellow.bold(`File: ${file.path}`)}
58
58
  ${skipPathInfo}
59
59
 
60
60
  ${colorizedDiff}
@@ -63,39 +63,39 @@ ${colorizedDiff}
63
63
  const formatChangedFiles = (files, config) => {
64
64
  if (files.length === 0)
65
65
  return '';
66
- const header = chalk_1.default.yellow.bold(`\nChanged Files (${files.length}):`);
66
+ const header = ansi_colors_1.default.yellow.bold(`\nChanged Files (${files.length}):`);
67
67
  const fileContent = files.map((file) => formatChangedFile(file, config)).join('\n');
68
68
  return `${header}\n${fileContent}`;
69
69
  };
70
70
  const formatSummaryBox = (diffResult, pruneEnabled) => {
71
71
  const width = 60;
72
- const topBorder = chalk_1.default.cyan(`╭─ Diff Summary ${'─'.repeat(width - 14)}ā•®`);
73
- const bottomBorder = chalk_1.default.cyan(`ā•°${'─'.repeat(width + 1)}╯`);
74
- const addedLine = chalk_1.default.cyan(`│ ${chalk_1.default.green('✚ Added:')} ${diffResult.addedFiles.length.toString().padEnd(width - 15)} │`);
75
- const changedLine = chalk_1.default.cyan(`│ ${chalk_1.default.yellow('āœŽ Changed:')} ${diffResult.changedFiles.length.toString().padEnd(width - 15)} │`);
72
+ const topBorder = ansi_colors_1.default.cyan(`╭─ Diff Summary ${'─'.repeat(width - 14)}ā•®`);
73
+ const bottomBorder = ansi_colors_1.default.cyan(`ā•°${'─'.repeat(width + 1)}╯`);
74
+ const addedLine = ansi_colors_1.default.cyan(`│ ${ansi_colors_1.default.green('✚ Added:')} ${diffResult.addedFiles.length.toString().padEnd(width - 15)} │`);
75
+ const changedLine = ansi_colors_1.default.cyan(`│ ${ansi_colors_1.default.yellow('āœŽ Changed:')} ${diffResult.changedFiles.length.toString().padEnd(width - 15)} │`);
76
76
  const deletedText = pruneEnabled
77
77
  ? `${diffResult.deletedFiles.length} (prune enabled)`
78
78
  : `${diffResult.deletedFiles.length} (prune disabled)`;
79
- const deletedLine = chalk_1.default.cyan(`│ ${chalk_1.default.red('āœ– Deleted:')} ${deletedText.padEnd(width - 15)} │`);
80
- const unchangedLine = chalk_1.default.cyan(`│ ${chalk_1.default.gray('āœ“ Unchanged:')} ${diffResult.unchangedFiles.length.toString().padEnd(width - 15)} │`);
79
+ const deletedLine = ansi_colors_1.default.cyan(`│ ${ansi_colors_1.default.red('āœ– Deleted:')} ${deletedText.padEnd(width - 15)} │`);
80
+ const unchangedLine = ansi_colors_1.default.cyan(`│ ${ansi_colors_1.default.gray('āœ“ Unchanged:')} ${diffResult.unchangedFiles.length.toString().padEnd(width - 15)} │`);
81
81
  return `${topBorder}\n${addedLine}\n${changedLine}\n${deletedLine}\n${unchangedLine}\n${bottomBorder}\n`;
82
82
  };
83
83
  const showConsoleDiff = (diffResult, config) => {
84
84
  console.log('');
85
85
  console.log(formatSummaryBox(diffResult, config.prune));
86
- console.log(chalk_1.default.bold('\nInclude patterns:'), config.include.join(', '));
87
- console.log(chalk_1.default.bold('Exclude patterns:'), config.exclude.length > 0 ? config.exclude.join(', ') : chalk_1.default.dim('(none)'));
86
+ console.log(ansi_colors_1.default.bold('\nInclude patterns:'), config.include.join(', '));
87
+ console.log(ansi_colors_1.default.bold('Exclude patterns:'), config.exclude.length > 0 ? config.exclude.join(', ') : ansi_colors_1.default.dim('(none)'));
88
88
  if (diffResult.addedFiles.length === 0 &&
89
89
  diffResult.changedFiles.length === 0 &&
90
90
  diffResult.deletedFiles.length === 0) {
91
91
  const totalCompared = diffResult.unchangedFiles.length;
92
92
  const hasSkipPath = config.skipPath && Object.keys(config.skipPath).length > 0;
93
- const skipNote = hasSkipPath ? chalk_1.default.dim(' (some paths may be excluded via skipPath)') : '';
93
+ const skipNote = hasSkipPath ? ansi_colors_1.default.dim(' (some paths may be excluded via skipPath)') : '';
94
94
  if (totalCompared === 0)
95
- console.log(chalk_1.default.yellow.bold('\n⚠ No files matched the include/exclude patterns\n'));
95
+ console.log(ansi_colors_1.default.yellow.bold('\n⚠ No files matched the include/exclude patterns\n'));
96
96
  else
97
- console.log(chalk_1.default.green.bold(`\nāœ“ No differences found`) +
98
- chalk_1.default.dim(` — ${totalCompared} file(s) compared, all identical`) +
97
+ console.log(ansi_colors_1.default.green.bold(`\nāœ“ No differences found`) +
98
+ ansi_colors_1.default.dim(` — ${totalCompared} file(s) compared, all identical`) +
99
99
  skipNote +
100
100
  '\n');
101
101
  return;
@@ -51,13 +51,13 @@ const countDiffLines = (unifiedDiff) => {
51
51
  };
52
52
  const generateFileSummary = (file) => {
53
53
  if (!file.originalPath)
54
- return file.path;
55
- return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
54
+ return (0, treeRenderer_1.escapeHtml)(file.path);
55
+ return `<span class="filename-transform">${(0, treeRenderer_1.escapeHtml)(file.originalPath)} → ${(0, treeRenderer_1.escapeHtml)(file.path)}</span>`;
56
56
  };
57
57
  const generateAddedFileSummary = (file) => {
58
58
  if (!file.originalPath)
59
- return file.path;
60
- return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
59
+ return (0, treeRenderer_1.escapeHtml)(file.path);
60
+ return `<span class="filename-transform">${(0, treeRenderer_1.escapeHtml)(file.originalPath)} → ${(0, treeRenderer_1.escapeHtml)(file.path)}</span>`;
61
61
  };
62
62
  const JUMP_TO_SIDEBAR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M2 2h3v12H2V2zm0-1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H2zm5 4h7v1H7V5zm0 3h7v1H7V8zm0 3h5v1H7v-1z"/></svg>`;
63
63
  const generateAddedFileSection = (file, fileId, open) => {
@@ -548,9 +548,9 @@ const calculateLevenshteinDistance = (string1, string2) => {
548
548
  matrix[0][col] = col;
549
549
  for (let row = 1; row <= string2.length; row++)
550
550
  for (let col = 1; col <= string1.length; col++)
551
- if (string2.charAt(row - 1) === string1.charAt(col - 1))
552
- matrix[row][col] = matrix[row - 1][col - 1];
553
- else
554
- matrix[row][col] = Math.min(matrix[row - 1][col - 1] + 1, matrix[row][col - 1] + 1, matrix[row - 1][col] + 1);
551
+ matrix[row][col] =
552
+ string2.charAt(row - 1) === string1.charAt(col - 1)
553
+ ? matrix[row - 1][col - 1]
554
+ : Math.min(matrix[row - 1][col - 1] + 1, matrix[row][col - 1] + 1, matrix[row - 1][col] + 1);
555
555
  return matrix[string2.length][string1.length];
556
556
  };
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isSafeRegex = void 0;
4
4
  const isSafeRegex = (pattern) => {
5
- if (/\([^()]*[*+][^()]*\)[*+{]/.test(pattern))
5
+ if (/\([^()]*[*+][^()]*\)[*+?{]/.test(pattern))
6
+ return false;
7
+ if (/\([^()]*\|[^()]*\)[*+{]/.test(pattern))
6
8
  return false;
7
9
  return true;
8
10
  };
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.checkForUpdates = exports.isNewerVersion = exports.parseVersion = exports.isVersionCheckerError = exports.VersionCheckerError = void 0;
7
7
  const node_https_1 = __importDefault(require("node:https"));
8
- const chalk_1 = __importDefault(require("chalk"));
8
+ const ansi_colors_1 = __importDefault(require("ansi-colors"));
9
9
  const package_json_1 = __importDefault(require("../../package.json"));
10
10
  const errors_1 = require("./errors");
11
11
  const VersionCheckerErrorClass = (0, errors_1.createErrorClass)('Version Checker Error', {
@@ -102,8 +102,8 @@ const fetchLatestVersion = (packageName, timeout) => {
102
102
  });
103
103
  };
104
104
  const displayUpdateNotification = (currentVersion, latestVersion) => {
105
- console.log('\n' + chalk_1.default.yellow(`⚠ Update available! v${currentVersion} → v${latestVersion}`));
106
- console.log(chalk_1.default.yellow('Run: npm install -g helm-env-delta@latest'));
105
+ console.log('\n' + ansi_colors_1.default.yellow(`⚠ Update available! v${currentVersion} → v${latestVersion}`));
106
+ console.log(ansi_colors_1.default.yellow('Run: npm install -g helm-env-delta@latest'));
107
107
  };
108
108
  const checkForUpdates = async (currentVersion) => {
109
109
  if (isCiEnvironment())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helm-env-delta",
3
- "version": "1.15.1",
3
+ "version": "1.15.3",
4
4
  "description": "HelmEnvDelta – environment-aware YAML delta and sync for GitOps",
5
5
  "author": "BCsabaEngine",
6
6
  "license": "ISC",
@@ -65,29 +65,30 @@
65
65
  ],
66
66
  "devDependencies": {
67
67
  "@eslint/js": "^10.0.1",
68
- "@types/node": "^25.5.0",
69
- "@types/picomatch": "^4.0.2",
70
- "@typescript-eslint/eslint-plugin": "^8.57.1",
71
- "@vitest/coverage-v8": "^4.1.0",
72
- "eslint": "^10.0.3",
68
+ "@types/ansi-colors": "^3.2.0",
69
+ "@types/node": "^25.5.2",
70
+ "@types/picomatch": "^4.0.3",
71
+ "@typescript-eslint/eslint-plugin": "^8.58.0",
72
+ "@vitest/coverage-v8": "^4.1.2",
73
+ "eslint": "^10.2.0",
73
74
  "eslint-config-prettier": "^10.1.8",
74
- "eslint-plugin-simple-import-sort": "^12.1.1",
75
- "eslint-plugin-unicorn": "^63.0.0",
75
+ "eslint-plugin-simple-import-sort": "^13.0.0",
76
+ "eslint-plugin-unicorn": "^64.0.0",
76
77
  "prettier": "^3.8.1",
77
78
  "tsx": "^4.21.0",
78
- "typescript": "^5.9.3",
79
- "vitest": "^4.1.0"
79
+ "typescript": "^6.0.2",
80
+ "vitest": "^4.1.2"
80
81
  },
81
82
  "dependencies": {
82
- "chalk": "^5.6.2",
83
+ "ansi-colors": "^4.1.3",
83
84
  "commander": "^14.0.3",
84
- "diff": "^8.0.3",
85
+ "diff": "^8.0.4",
85
86
  "diff2html": "3.4.56",
86
87
  "open": "^11.0.0",
87
- "picomatch": "^4.0.3",
88
- "simple-git": "^3.33.0",
88
+ "picomatch": "^4.0.4",
89
+ "simple-git": "^3.35.2",
89
90
  "tinyglobby": "^0.2.15",
90
- "yaml": "^2.8.2",
91
+ "yaml": "^2.8.3",
91
92
  "zod": "^4.3.6"
92
93
  }
93
94
  }