helm-env-delta 1.15.2 → 2.0.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 (38) hide show
  1. package/README.md +279 -95
  2. package/config.schema.json +496 -0
  3. package/dist/commandLine.d.ts +9 -11
  4. package/dist/commandLine.js +288 -101
  5. package/dist/config/ZodError.d.ts +2 -2
  6. package/dist/config/configFile.d.ts +1 -1
  7. package/dist/config/configFile.js +87 -41
  8. package/dist/config/configLoader.d.ts +2 -1
  9. package/dist/config/configMerger.d.ts +2 -1
  10. package/dist/consoleFormatter.d.ts +1 -1
  11. package/dist/consoleFormatter.js +12 -12
  12. package/dist/exitCodes.d.ts +5 -0
  13. package/dist/exitCodes.js +8 -0
  14. package/dist/index.js +82 -77
  15. package/dist/logger.d.ts +3 -3
  16. package/dist/pipeline/fileDiff.d.ts +6 -5
  17. package/dist/pipeline/fileLoader.d.ts +2 -1
  18. package/dist/pipeline/fileLoader.js +2 -2
  19. package/dist/pipeline/fileUpdater.d.ts +4 -4
  20. package/dist/pipeline/fileUpdater.js +1 -1
  21. package/dist/pipeline/stopRulesValidator.d.ts +2 -1
  22. package/dist/pipeline/stopRulesValidator.js +2 -4
  23. package/dist/pipeline/yamlFormatter.d.ts +1 -1
  24. package/dist/pipeline/yamlFormatter.js +9 -9
  25. package/dist/reporters/browserLauncher.js +1 -34
  26. package/dist/reporters/consoleDiffReporter.d.ts +2 -2
  27. package/dist/reporters/consoleDiffReporter.js +26 -26
  28. package/dist/reporters/htmlReporter.d.ts +4 -3
  29. package/dist/reporters/htmlReporter.js +20 -10
  30. package/dist/reporters/htmlTemplate.d.ts +1 -1
  31. package/dist/reporters/jsonReporter.d.ts +2 -2
  32. package/dist/reporters/treeRenderer.d.ts +1 -1
  33. package/dist/suggestionEngine.d.ts +2 -2
  34. package/dist/suggestionEngine.js +2 -2
  35. package/dist/utils/arrayMerger.d.ts +1 -1
  36. package/dist/utils/patternMatcher.d.ts +1 -1
  37. package/dist/utils/versionChecker.js +3 -3
  38. package/package.json +19 -17
@@ -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;
@@ -1,6 +1,7 @@
1
- import { Config } from '../config';
1
+ import { type Config } from '../config';
2
+ import type { Logger } from '../logger';
2
3
  import type { ValidationResult } from '../pipeline';
3
- import { FileDiffResult } from '../pipeline';
4
+ import { type FileDiffResult } from '../pipeline';
4
5
  export type { DiffStats, ReportMetadata } from './htmlTemplate';
5
6
  declare const HtmlReporterErrorClass: {
6
7
  new (message: string, options?: import("../utils/errors").ErrorOptions): {
@@ -20,4 +21,4 @@ declare const HtmlReporterErrorClass: {
20
21
  export declare class HtmlReporterError extends HtmlReporterErrorClass {
21
22
  }
22
23
  export declare const isHtmlReporterError: (error: unknown) => error is HtmlReporterError;
23
- export declare const generateHtmlReport: (diffResult: FileDiffResult, formattedFiles: string[], config: Config, dryRun: boolean, logger?: import("../logger").Logger, validationResult?: ValidationResult) => Promise<void>;
24
+ export declare const generateHtmlReport: (diffResult: FileDiffResult, formattedFiles: string[], config: Config, dryRun: boolean, logger?: Logger, validationResult?: ValidationResult, outputPath?: string) => Promise<void>;
@@ -32,6 +32,12 @@ const generateTemporaryFilePath = () => {
32
32
  const filename = `helm-env-delta-${timestamp}-${randomName}.html`;
33
33
  return node_path_1.default.join((0, node_os_1.tmpdir)(), filename);
34
34
  };
35
+ const resolveOutputPath = (outputPath) => {
36
+ if (node_path_1.default.extname(outputPath))
37
+ return outputPath;
38
+ const timestamp = new Date().toISOString().replaceAll(/[.:]/g, '-');
39
+ return node_path_1.default.join(outputPath, `helm-env-delta-${timestamp}.html`);
40
+ };
35
41
  const DIFF2HTML_OPTIONS = {
36
42
  drawFileList: false,
37
43
  matching: 'lines',
@@ -119,8 +125,8 @@ const writeHtmlFile = async (htmlContent, outputPath) => {
119
125
  });
120
126
  }
121
127
  };
122
- const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun, logger, validationResult) => {
123
- const reportPath = generateTemporaryFilePath();
128
+ const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun, logger, validationResult, outputPath) => {
129
+ const reportPath = outputPath ? resolveOutputPath(outputPath) : generateTemporaryFilePath();
124
130
  const metadata = {
125
131
  timestamp: new Date().toISOString(),
126
132
  source: config.source,
@@ -167,14 +173,18 @@ const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun, lo
167
173
  : undefined;
168
174
  const htmlContent = (0, htmlTemplate_1.generateHtmlTemplate)(diffResult, formattedFiles, trulyUnchangedFiles, metadata, changedSections, changedFileIds, addedSections, addedFileIds, diffStats, stopRuleViolations);
169
175
  await writeHtmlFile(htmlContent, reportPath);
170
- logger?.log(`✓ HTML report generated: ${reportPath}, opening in browser...`);
171
- try {
172
- await (0, browserLauncher_1.openInBrowser)(reportPath);
173
- }
174
- catch {
175
- const absolutePath = node_path_1.default.resolve(reportPath);
176
- logger?.log('⚠ Could not open browser automatically. Please open manually:');
177
- logger?.log(` file://${absolutePath}`);
176
+ if (outputPath)
177
+ logger?.log(`✓ HTML report saved: ${reportPath}`);
178
+ else {
179
+ logger?.log(`✓ HTML report generated: ${reportPath}, opening in browser...`);
180
+ try {
181
+ await (0, browserLauncher_1.openInBrowser)(reportPath);
182
+ }
183
+ catch {
184
+ const absolutePath = node_path_1.default.resolve(reportPath);
185
+ logger?.log('⚠ Could not open browser automatically. Please open manually:');
186
+ logger?.log(` file://${absolutePath}`);
187
+ }
178
188
  }
179
189
  };
180
190
  exports.generateHtmlReport = generateHtmlReport;
@@ -1,4 +1,4 @@
1
- import { FileDiffResult } from '../pipeline';
1
+ import { type FileDiffResult } from '../pipeline';
2
2
  export interface HtmlStopRuleViolation {
3
3
  file: string;
4
4
  rule: {
@@ -1,5 +1,5 @@
1
- import { Config } from '../config';
2
- import { FileDiffResult, ValidationResult } from '../pipeline';
1
+ import { type Config } from '../config';
2
+ import { type FileDiffResult, type ValidationResult } from '../pipeline';
3
3
  declare const JsonReporterErrorClass: {
4
4
  new (message: string, options?: import("../utils/errors").ErrorOptions): {
5
5
  [key: string]: unknown;
@@ -1,4 +1,4 @@
1
- import { TreeNode } from './treeBuilder';
1
+ import { type TreeNode } from './treeBuilder';
2
2
  export declare const renderTreeview: (nodes: TreeNode[]) => string;
3
3
  export declare const renderSidebarTree: (nodes: TreeNode[], fileIds: Map<string, string>) => string;
4
4
  export declare const escapeHtml: (text: string) => string;
@@ -1,5 +1,5 @@
1
- import { Config, StopRule } from './config';
2
- import { FileDiffResult } from './pipeline';
1
+ import { type Config, type StopRule } from './config';
2
+ import { type FileDiffResult } from './pipeline';
3
3
  export interface ValuePair {
4
4
  oldValue: string;
5
5
  targetValue: string;
@@ -49,7 +49,7 @@ const createEmptySuggestionResult = (diffResult) => ({
49
49
  const extractAllDifferences = (changedFiles) => {
50
50
  const differences = [];
51
51
  for (const file of changedFiles) {
52
- const fileDiffs = walkAndCompare(file.rawParsedSource, file.rawParsedDest, [], file.path, file.skipPaths);
52
+ const fileDiffs = walkAndCompare(file.rawParsedSource, file.rawParsedDest, [], file.path, file.skipPaths ?? []);
53
53
  differences.push(...fileDiffs);
54
54
  }
55
55
  return differences;
@@ -152,7 +152,7 @@ const collectValuesByPath = (changedFiles) => {
152
152
  for (const file of changedFiles) {
153
153
  const valuesMap = extractAllPathValues(file.processedSourceContent, []);
154
154
  for (const [path, values] of valuesMap) {
155
- if (isPathSkipped(path, file.skipPaths))
155
+ if (isPathSkipped(path, file.skipPaths ?? []))
156
156
  continue;
157
157
  if (!map.has(path))
158
158
  map.set(path, { values: [], files: new Set() });
@@ -11,5 +11,5 @@ export declare const itemMatchesAnyFilter: (item: unknown, applicableFilters: Ap
11
11
  matches: boolean;
12
12
  matchedFilter?: ApplicableFilter;
13
13
  };
14
- export declare const findMatchingTargetItem: (sourceItem: unknown, fullTargetArray: unknown[], applicableFilters: ApplicableFilter[]) => unknown | undefined;
14
+ export declare const findMatchingTargetItem: (sourceItem: unknown, fullTargetArray: unknown[], applicableFilters: ApplicableFilter[]) => unknown;
15
15
  export declare const shouldPreserveItem: (item: unknown, applicableFilters: ApplicableFilter[], existingResult: unknown[]) => boolean;
@@ -1,5 +1,5 @@
1
1
  export declare class PatternMatcher {
2
- private cache;
2
+ private readonly cache;
3
3
  compilePattern(pattern: string): RegExp;
4
4
  match(path: string, pattern: string): boolean;
5
5
  clearCache(): void;
@@ -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.2",
3
+ "version": "2.0.0",
4
4
  "description": "HelmEnvDelta – environment-aware YAML delta and sync for GitOps",
5
5
  "author": "BCsabaEngine",
6
6
  "license": "ISC",
@@ -20,7 +20,8 @@
20
20
  "files": [
21
21
  "bin/*.js",
22
22
  "dist/**/*.js",
23
- "dist/**/*.d.ts"
23
+ "dist/**/*.d.ts",
24
+ "config.schema.json"
24
25
  ],
25
26
  "bin": {
26
27
  "helm-env-delta": "bin/index.js",
@@ -35,14 +36,15 @@
35
36
  },
36
37
  "homepage": "https://github.com/BCsabaEngine/helm-env-delta",
37
38
  "scripts": {
38
- "dev": "tsx src/index.ts -c ./example.dev/config.yaml",
39
+ "dev": "tsx src/index.ts diff -c ./example.dev/config.yaml --html",
39
40
  "test": "vitest run --passWithNoTests",
40
41
  "test:coverage": "vitest run --coverage",
41
42
  "test:perf": "vitest bench --run",
42
43
  "test:perf:json": "vitest bench --run --reporter=json --outputFile=benchmark-results.json",
43
44
  "test:all": "node --run test && node --run test:perf",
44
45
  "clean": "tsc --build --clean",
45
- "build": "tsc --build --clean && tsc --build",
46
+ "generate:schema": "tsx scripts/generateSchema.ts",
47
+ "build": "tsc --build --clean && tsc --build && node --run generate:schema",
46
48
  "format:check": "prettier --check .",
47
49
  "format:fix": "prettier --write . | grep -v 'unchanged' | sed G",
48
50
  "lint:check": "eslint .",
@@ -65,28 +67,28 @@
65
67
  ],
66
68
  "devDependencies": {
67
69
  "@eslint/js": "^10.0.1",
68
- "@types/node": "^25.5.0",
69
- "@types/picomatch": "^4.0.2",
70
- "@typescript-eslint/eslint-plugin": "^8.57.2",
71
- "@vitest/coverage-v8": "^4.1.2",
72
- "eslint": "^10.1.0",
70
+ "@types/node": "^25.6.0",
71
+ "@types/picomatch": "^4.0.3",
72
+ "@typescript-eslint/eslint-plugin": "^8.59.1",
73
+ "@vitest/coverage-v8": "^4.1.5",
74
+ "eslint": "^10.2.1",
73
75
  "eslint-config-prettier": "^10.1.8",
74
- "eslint-plugin-simple-import-sort": "^12.1.1",
76
+ "eslint-plugin-simple-import-sort": "^13.0.0",
75
77
  "eslint-plugin-unicorn": "^64.0.0",
76
- "prettier": "^3.8.1",
78
+ "prettier": "^3.8.3",
77
79
  "tsx": "^4.21.0",
78
- "typescript": "^5.9.3",
79
- "vitest": "^4.1.2"
80
+ "typescript": "^6.0.3",
81
+ "vitest": "^4.1.5"
80
82
  },
81
83
  "dependencies": {
82
- "chalk": "^5.6.2",
84
+ "ansi-colors": "^4.1.3",
83
85
  "commander": "^14.0.3",
84
- "diff": "^8.0.4",
86
+ "diff": "^9.0.0",
85
87
  "diff2html": "3.4.56",
86
88
  "open": "^11.0.0",
87
89
  "picomatch": "^4.0.4",
88
- "simple-git": "^3.33.0",
89
- "tinyglobby": "^0.2.15",
90
+ "simple-git": "^3.36.0",
91
+ "tinyglobby": "^0.2.16",
90
92
  "yaml": "^2.8.3",
91
93
  "zod": "^4.3.6"
92
94
  }