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.
- package/README.md +279 -95
- package/config.schema.json +496 -0
- package/dist/commandLine.d.ts +9 -11
- package/dist/commandLine.js +288 -101
- package/dist/config/ZodError.d.ts +2 -2
- package/dist/config/configFile.d.ts +1 -1
- package/dist/config/configFile.js +87 -41
- package/dist/config/configLoader.d.ts +2 -1
- package/dist/config/configMerger.d.ts +2 -1
- package/dist/consoleFormatter.d.ts +1 -1
- package/dist/consoleFormatter.js +12 -12
- package/dist/exitCodes.d.ts +5 -0
- package/dist/exitCodes.js +8 -0
- package/dist/index.js +82 -77
- package/dist/logger.d.ts +3 -3
- package/dist/pipeline/fileDiff.d.ts +6 -5
- package/dist/pipeline/fileLoader.d.ts +2 -1
- package/dist/pipeline/fileLoader.js +2 -2
- package/dist/pipeline/fileUpdater.d.ts +4 -4
- package/dist/pipeline/fileUpdater.js +1 -1
- package/dist/pipeline/stopRulesValidator.d.ts +2 -1
- package/dist/pipeline/stopRulesValidator.js +2 -4
- package/dist/pipeline/yamlFormatter.d.ts +1 -1
- package/dist/pipeline/yamlFormatter.js +9 -9
- package/dist/reporters/browserLauncher.js +1 -34
- package/dist/reporters/consoleDiffReporter.d.ts +2 -2
- package/dist/reporters/consoleDiffReporter.js +26 -26
- package/dist/reporters/htmlReporter.d.ts +4 -3
- package/dist/reporters/htmlReporter.js +20 -10
- package/dist/reporters/htmlTemplate.d.ts +1 -1
- package/dist/reporters/jsonReporter.d.ts +2 -2
- package/dist/reporters/treeRenderer.d.ts +1 -1
- package/dist/suggestionEngine.d.ts +2 -2
- package/dist/suggestionEngine.js +2 -2
- package/dist/utils/arrayMerger.d.ts +1 -1
- package/dist/utils/patternMatcher.d.ts +1 -1
- package/dist/utils/versionChecker.js +3 -3
- package/package.json +19 -17
package/dist/commandLine.js
CHANGED
|
@@ -6,122 +6,309 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.parseCommandLine = void 0;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
9
|
+
const exitCodes_1 = require("./exitCodes");
|
|
10
|
+
const exitOverrideFunction = (error) => {
|
|
11
|
+
if (error.exitCode === 0)
|
|
12
|
+
process.exit(0);
|
|
13
|
+
process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
|
|
14
|
+
};
|
|
15
|
+
const validModes = ['new', 'modified', 'deleted', 'all'];
|
|
16
|
+
const parseMode = (mode) => {
|
|
17
|
+
if (!validModes.includes(mode)) {
|
|
18
|
+
console.error('Error: --mode must be one of: ' + validModes.join(', '));
|
|
19
|
+
process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
|
|
20
|
+
}
|
|
21
|
+
return mode;
|
|
22
|
+
};
|
|
23
|
+
const parseMyDays = (myRaw) => {
|
|
24
|
+
if (myRaw === undefined || myRaw === false)
|
|
25
|
+
return { my: false, myDays: 30 };
|
|
26
|
+
if (myRaw === true)
|
|
27
|
+
return { my: true, myDays: 30 };
|
|
28
|
+
const parsed = Number.parseInt(myRaw, 10);
|
|
29
|
+
if (Number.isNaN(parsed) || parsed < 1) {
|
|
30
|
+
console.error('Error: --my days must be a positive integer');
|
|
31
|
+
process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
|
|
32
|
+
}
|
|
33
|
+
return { my: true, myDays: parsed };
|
|
34
|
+
};
|
|
35
|
+
const checkVerboseQuiet = (verbose, quiet) => {
|
|
36
|
+
if (verbose && quiet) {
|
|
37
|
+
console.error('Error: --verbose and --quiet flags are mutually exclusive');
|
|
38
|
+
process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const addGlobalOptions = (cmd) => cmd
|
|
42
|
+
.option('--verbose', 'Show detailed debug information', false)
|
|
43
|
+
.option('--quiet', 'Suppress all output except critical errors', false)
|
|
44
|
+
.option('--no-color', 'Disable colored output');
|
|
9
45
|
const parseCommandLine = (argv) => {
|
|
46
|
+
let result;
|
|
10
47
|
const program = new commander_1.Command();
|
|
11
48
|
program
|
|
12
49
|
.name('helm-env-delta')
|
|
13
50
|
.description('Environment-aware YAML delta and sync for GitOps workflows')
|
|
14
51
|
.version(package_json_1.default.version)
|
|
52
|
+
.showSuggestionAfterError(true)
|
|
53
|
+
.exitOverride(exitOverrideFunction);
|
|
54
|
+
addGlobalOptions(program
|
|
55
|
+
.command('run')
|
|
56
|
+
.description('Sync source YAML changes to destination')
|
|
15
57
|
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
16
58
|
.option('-D, --dry-run', 'Preview changes without writing files', false)
|
|
17
59
|
.option('--force', 'Override stop rules and proceed with changes', false)
|
|
18
|
-
.option('-d, --diff', 'Display console diff for changed files', false)
|
|
19
|
-
.option('-H, --diff-html', 'Generate and open HTML diff report in browser', false)
|
|
20
|
-
.option('-J, --diff-json', 'Output diff as JSON to stdout', false)
|
|
21
60
|
.option('-S, --skip-format', 'Skip YAML formatting (outputFormat section)', false)
|
|
22
|
-
.option('--
|
|
23
|
-
.option('--
|
|
24
|
-
.option('
|
|
25
|
-
.
|
|
26
|
-
|
|
61
|
+
.option('-f, --filter <string>', 'Filter files by name or content (supports , for OR, + for AND)')
|
|
62
|
+
.option('-m, --mode <type>', 'Filter by change type: new, modified, deleted, all', 'all')
|
|
63
|
+
.option('--my [days]', 'Limit to files you modified in the last N days (default: 30)')
|
|
64
|
+
.addHelpText('after', `
|
|
65
|
+
Examples:
|
|
66
|
+
$ helm-env-delta run -c config.yaml
|
|
67
|
+
$ helm-env-delta run -c config.yaml --dry-run
|
|
68
|
+
$ helm-env-delta run -c config.yaml --force
|
|
69
|
+
$ helm-env-delta run -c config.yaml --mode new
|
|
70
|
+
$ helm-env-delta run -c config.yaml -f prod --my 7
|
|
71
|
+
`)
|
|
72
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
73
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
74
|
+
const { my, myDays } = parseMyDays(options['my']);
|
|
75
|
+
result = {
|
|
76
|
+
commandName: 'run',
|
|
77
|
+
config: options['config'],
|
|
78
|
+
dryRun: options['dryRun'],
|
|
79
|
+
force: options['force'],
|
|
80
|
+
strict: false,
|
|
81
|
+
html: false,
|
|
82
|
+
json: false,
|
|
83
|
+
skipFormat: options['skipFormat'],
|
|
84
|
+
suggestThreshold: 0.3,
|
|
85
|
+
filter: options['filter'],
|
|
86
|
+
mode: parseMode(options['mode']),
|
|
87
|
+
my,
|
|
88
|
+
myDays,
|
|
89
|
+
verbose: options['verbose'],
|
|
90
|
+
quiet: options['quiet'],
|
|
91
|
+
noColor: !options['color']
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
addGlobalOptions(program
|
|
95
|
+
.command('validate')
|
|
96
|
+
.description('Validate configuration and patterns against source files')
|
|
97
|
+
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
98
|
+
.option('-f, --filter <string>', 'Filter files by name or content')
|
|
99
|
+
.option('--my [days]', 'Limit to files you modified in the last N days (default: 30)')
|
|
100
|
+
.option('--strict', 'Exit non-zero if any warnings are found', false)
|
|
101
|
+
.addHelpText('after', `
|
|
102
|
+
Examples:
|
|
103
|
+
$ helm-env-delta validate -c config.yaml
|
|
104
|
+
$ helm-env-delta validate -c config.yaml -f prod
|
|
105
|
+
$ helm-env-delta validate -c config.yaml --strict
|
|
106
|
+
`)
|
|
107
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
108
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
109
|
+
const { my, myDays } = parseMyDays(options['my']);
|
|
110
|
+
result = {
|
|
111
|
+
commandName: 'validate',
|
|
112
|
+
config: options['config'],
|
|
113
|
+
dryRun: false,
|
|
114
|
+
force: false,
|
|
115
|
+
strict: options['strict'],
|
|
116
|
+
html: false,
|
|
117
|
+
json: false,
|
|
118
|
+
skipFormat: false,
|
|
119
|
+
suggestThreshold: 0.3,
|
|
120
|
+
filter: options['filter'],
|
|
121
|
+
mode: 'all',
|
|
122
|
+
my,
|
|
123
|
+
myDays,
|
|
124
|
+
verbose: options['verbose'],
|
|
125
|
+
quiet: options['quiet'],
|
|
126
|
+
noColor: !options['color']
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
addGlobalOptions(program
|
|
130
|
+
.command('format')
|
|
131
|
+
.description('Format YAML files in destination without syncing')
|
|
132
|
+
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
133
|
+
.option('-D, --dry-run', 'Preview formatting changes without writing files', false)
|
|
134
|
+
.option('-f, --filter <string>', 'Filter files by name or content')
|
|
135
|
+
.addHelpText('after', `
|
|
136
|
+
Examples:
|
|
137
|
+
$ helm-env-delta format -c config.yaml
|
|
138
|
+
$ helm-env-delta format -c config.yaml --dry-run
|
|
139
|
+
$ helm-env-delta format -c config.yaml -f prod
|
|
140
|
+
`)
|
|
141
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
142
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
143
|
+
result = {
|
|
144
|
+
commandName: 'format',
|
|
145
|
+
config: options['config'],
|
|
146
|
+
dryRun: options['dryRun'],
|
|
147
|
+
force: false,
|
|
148
|
+
strict: false,
|
|
149
|
+
html: false,
|
|
150
|
+
json: false,
|
|
151
|
+
skipFormat: false,
|
|
152
|
+
suggestThreshold: 0.3,
|
|
153
|
+
filter: options['filter'],
|
|
154
|
+
mode: 'all',
|
|
155
|
+
my: false,
|
|
156
|
+
myDays: 30,
|
|
157
|
+
verbose: options['verbose'],
|
|
158
|
+
quiet: options['quiet'],
|
|
159
|
+
noColor: !options['color']
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
addGlobalOptions(program
|
|
163
|
+
.command('suggest')
|
|
164
|
+
.description('Analyze differences and suggest transforms and stop rules')
|
|
165
|
+
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
27
166
|
.option('--suggest-threshold <number>', 'Minimum confidence for suggestions (0-1, default: 0.3)', '0.3')
|
|
28
|
-
.option('--
|
|
29
|
-
.option('-f, --filter <string>', 'Filter files by filename or content (supports , for OR, + for AND)')
|
|
167
|
+
.option('-f, --filter <string>', 'Filter files by name or content')
|
|
30
168
|
.option('-m, --mode <type>', 'Filter by change type: new, modified, deleted, all', 'all')
|
|
31
|
-
.option('--my [days]', '
|
|
32
|
-
.option('--verbose', 'Show detailed debug information', false)
|
|
33
|
-
.option('--quiet', 'Suppress all output except critical errors', false)
|
|
169
|
+
.option('--my [days]', 'Limit to files you modified in the last N days (default: 30)')
|
|
34
170
|
.addHelpText('after', `
|
|
35
171
|
Examples:
|
|
36
|
-
|
|
37
|
-
$ helm-env-delta
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
$ helm-env-delta --config config.yaml --validate
|
|
47
|
-
|
|
48
|
-
# CI/CD usage with JSON output
|
|
49
|
-
$ helm-env-delta --config config.yaml --diff-json | jq '.summary'
|
|
50
|
-
|
|
51
|
-
# Filter to only process files matching 'prod'
|
|
52
|
-
$ helm-env-delta --config config.yaml -f prod --diff
|
|
53
|
-
|
|
54
|
-
# Filter with OR: files matching 'prod' OR 'staging'
|
|
55
|
-
$ helm-env-delta --config config.yaml -f prod,staging --diff
|
|
56
|
-
|
|
57
|
-
# Filter with AND: files matching 'values' AND 'prod'
|
|
58
|
-
$ helm-env-delta --config config.yaml -f values+prod --diff
|
|
59
|
-
|
|
60
|
-
# Sync only new files
|
|
61
|
-
$ helm-env-delta --config config.yaml --mode new
|
|
62
|
-
|
|
63
|
-
# Preview modified files only
|
|
64
|
-
$ helm-env-delta --config config.yaml --mode modified --dry-run --diff
|
|
65
|
-
|
|
66
|
-
# Sync only files you modified in the last 30 days
|
|
67
|
-
$ helm-env-delta --config config.yaml --my --dry-run --diff
|
|
68
|
-
|
|
69
|
-
# Sync only files you modified in the last 7 days
|
|
70
|
-
$ helm-env-delta --config config.yaml --my 7 --diff
|
|
71
|
-
|
|
72
|
-
Documentation: https://github.com/balazscsaba2006/helm-env-delta
|
|
73
|
-
`);
|
|
74
|
-
program.showSuggestionAfterError(true);
|
|
75
|
-
program.parse(argv || process.argv);
|
|
76
|
-
const options = program.opts();
|
|
77
|
-
if (options['verbose'] && options['quiet']) {
|
|
78
|
-
console.error('Error: --verbose and --quiet flags are mutually exclusive');
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
if (options['formatOnly'] && options['skipFormat']) {
|
|
82
|
-
console.error('Error: --format-only and --skip-format flags are mutually exclusive');
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
const threshold = Number.parseFloat(options['suggestThreshold']);
|
|
86
|
-
if (Number.isNaN(threshold) || threshold < 0 || threshold > 1) {
|
|
87
|
-
console.error('Error: --suggest-threshold must be a number between 0 and 1');
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
90
|
-
const validModes = ['new', 'modified', 'deleted', 'all'];
|
|
91
|
-
if (!validModes.includes(options['mode'])) {
|
|
92
|
-
console.error('Error: --mode must be one of: ' + validModes.join(', '));
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
let myDays = 30;
|
|
96
|
-
if (options['my'] !== undefined && options['my'] !== true) {
|
|
97
|
-
const parsed = Number.parseInt(options['my'], 10);
|
|
98
|
-
if (Number.isNaN(parsed) || parsed < 1) {
|
|
99
|
-
console.error('Error: --my days must be a positive integer');
|
|
100
|
-
process.exit(1);
|
|
172
|
+
$ helm-env-delta suggest -c config.yaml
|
|
173
|
+
$ helm-env-delta suggest -c config.yaml --suggest-threshold 0.5
|
|
174
|
+
$ helm-env-delta suggest -c config.yaml -f prod
|
|
175
|
+
`)
|
|
176
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
177
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
178
|
+
const threshold = Number.parseFloat(options['suggestThreshold']);
|
|
179
|
+
if (Number.isNaN(threshold) || threshold < 0 || threshold > 1) {
|
|
180
|
+
console.error('Error: --suggest-threshold must be a number between 0 and 1');
|
|
181
|
+
process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
|
|
101
182
|
}
|
|
102
|
-
myDays =
|
|
183
|
+
const { my, myDays } = parseMyDays(options['my']);
|
|
184
|
+
result = {
|
|
185
|
+
commandName: 'suggest',
|
|
186
|
+
config: options['config'],
|
|
187
|
+
dryRun: false,
|
|
188
|
+
force: false,
|
|
189
|
+
strict: false,
|
|
190
|
+
html: false,
|
|
191
|
+
json: false,
|
|
192
|
+
skipFormat: false,
|
|
193
|
+
suggestThreshold: threshold,
|
|
194
|
+
filter: options['filter'],
|
|
195
|
+
mode: parseMode(options['mode']),
|
|
196
|
+
my,
|
|
197
|
+
myDays,
|
|
198
|
+
verbose: options['verbose'],
|
|
199
|
+
quiet: options['quiet'],
|
|
200
|
+
noColor: !options['color']
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
addGlobalOptions(program
|
|
204
|
+
.command('diff')
|
|
205
|
+
.description('Show changes between source and destination (read-only)')
|
|
206
|
+
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
207
|
+
.option('-H, --html', 'Generate and open HTML diff report in browser', false)
|
|
208
|
+
.option('-J, --json', 'Output diff as JSON to stdout', false)
|
|
209
|
+
.option('--report-output <path>', 'Save HTML report to a file or directory (suppresses browser auto-open)')
|
|
210
|
+
.option('-f, --filter <string>', 'Filter files by name or content')
|
|
211
|
+
.option('-m, --mode <type>', 'Filter by change type: new, modified, deleted, all', 'all')
|
|
212
|
+
.option('--my [days]', 'Limit to files you modified in the last N days (default: 30)')
|
|
213
|
+
.addHelpText('after', `
|
|
214
|
+
Examples:
|
|
215
|
+
$ helm-env-delta diff -c config.yaml
|
|
216
|
+
$ helm-env-delta diff -c config.yaml --html
|
|
217
|
+
$ helm-env-delta diff -c config.yaml --json | jq '.summary'
|
|
218
|
+
$ helm-env-delta diff -c config.yaml --report-output ./reports/
|
|
219
|
+
$ helm-env-delta diff -c config.yaml -f prod --mode modified
|
|
220
|
+
`)
|
|
221
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
222
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
223
|
+
const { my, myDays } = parseMyDays(options['my']);
|
|
224
|
+
result = {
|
|
225
|
+
commandName: 'diff',
|
|
226
|
+
config: options['config'],
|
|
227
|
+
dryRun: false,
|
|
228
|
+
force: false,
|
|
229
|
+
strict: false,
|
|
230
|
+
html: options['html'],
|
|
231
|
+
json: options['json'],
|
|
232
|
+
reportOutput: options['reportOutput'],
|
|
233
|
+
skipFormat: false,
|
|
234
|
+
suggestThreshold: 0.3,
|
|
235
|
+
filter: options['filter'],
|
|
236
|
+
mode: parseMode(options['mode']),
|
|
237
|
+
my,
|
|
238
|
+
myDays,
|
|
239
|
+
verbose: options['verbose'],
|
|
240
|
+
quiet: options['quiet'],
|
|
241
|
+
noColor: !options['color']
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
addGlobalOptions(program
|
|
245
|
+
.command('list-files')
|
|
246
|
+
.description('List files that would be loaded without processing diffs')
|
|
247
|
+
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
248
|
+
.option('-f, --filter <string>', 'Filter files by name or content')
|
|
249
|
+
.option('--my [days]', 'Limit to files you modified in the last N days (default: 30)')
|
|
250
|
+
.addHelpText('after', `
|
|
251
|
+
Examples:
|
|
252
|
+
$ helm-env-delta list-files -c config.yaml
|
|
253
|
+
$ helm-env-delta list-files -c config.yaml -f prod
|
|
254
|
+
$ helm-env-delta list-files -c config.yaml --my 7
|
|
255
|
+
`)
|
|
256
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
257
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
258
|
+
const { my, myDays } = parseMyDays(options['my']);
|
|
259
|
+
result = {
|
|
260
|
+
commandName: 'list-files',
|
|
261
|
+
config: options['config'],
|
|
262
|
+
dryRun: false,
|
|
263
|
+
force: false,
|
|
264
|
+
strict: false,
|
|
265
|
+
html: false,
|
|
266
|
+
json: false,
|
|
267
|
+
skipFormat: false,
|
|
268
|
+
suggestThreshold: 0.3,
|
|
269
|
+
filter: options['filter'],
|
|
270
|
+
mode: 'all',
|
|
271
|
+
my,
|
|
272
|
+
myDays,
|
|
273
|
+
verbose: options['verbose'],
|
|
274
|
+
quiet: options['quiet'],
|
|
275
|
+
noColor: !options['color']
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
addGlobalOptions(program
|
|
279
|
+
.command('show-config')
|
|
280
|
+
.description('Display resolved configuration after inheritance')
|
|
281
|
+
.requiredOption('-c, --config <file>', 'Path to YAML configuration file')
|
|
282
|
+
.addHelpText('after', `
|
|
283
|
+
Examples:
|
|
284
|
+
$ helm-env-delta show-config -c config.yaml
|
|
285
|
+
`)
|
|
286
|
+
.exitOverride(exitOverrideFunction)).action(function (options) {
|
|
287
|
+
checkVerboseQuiet(options['verbose'], options['quiet']);
|
|
288
|
+
result = {
|
|
289
|
+
commandName: 'show-config',
|
|
290
|
+
config: options['config'],
|
|
291
|
+
dryRun: false,
|
|
292
|
+
force: false,
|
|
293
|
+
strict: false,
|
|
294
|
+
html: false,
|
|
295
|
+
json: false,
|
|
296
|
+
skipFormat: false,
|
|
297
|
+
suggestThreshold: 0.3,
|
|
298
|
+
filter: undefined,
|
|
299
|
+
mode: 'all',
|
|
300
|
+
my: false,
|
|
301
|
+
myDays: 30,
|
|
302
|
+
verbose: options['verbose'],
|
|
303
|
+
quiet: options['quiet'],
|
|
304
|
+
noColor: !options['color']
|
|
305
|
+
};
|
|
306
|
+
});
|
|
307
|
+
program.parse(argv ?? process.argv);
|
|
308
|
+
if (!result) {
|
|
309
|
+
program.help();
|
|
310
|
+
process.exit(exitCodes_1.EXIT_CONFIG_ERROR);
|
|
103
311
|
}
|
|
104
|
-
return
|
|
105
|
-
config: options['config'],
|
|
106
|
-
dryRun: options['dryRun'],
|
|
107
|
-
force: options['force'],
|
|
108
|
-
diff: options['diff'],
|
|
109
|
-
diffHtml: options['diffHtml'],
|
|
110
|
-
diffJson: options['diffJson'],
|
|
111
|
-
skipFormat: options['skipFormat'],
|
|
112
|
-
formatOnly: options['formatOnly'],
|
|
113
|
-
validate: options['validate'],
|
|
114
|
-
listFiles: options['listFiles'],
|
|
115
|
-
showConfig: options['showConfig'],
|
|
116
|
-
noColor: !options['color'],
|
|
117
|
-
verbose: options['verbose'],
|
|
118
|
-
quiet: options['quiet'],
|
|
119
|
-
suggest: options['suggest'],
|
|
120
|
-
suggestThreshold: threshold,
|
|
121
|
-
filter: options['filter'],
|
|
122
|
-
mode: options['mode'],
|
|
123
|
-
my: options['my'] !== undefined && options['my'] !== false,
|
|
124
|
-
myDays
|
|
125
|
-
};
|
|
312
|
+
return result;
|
|
126
313
|
};
|
|
127
314
|
exports.parseCommandLine = parseCommandLine;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import { type z } from 'zod';
|
|
2
2
|
export declare class ZodValidationError extends Error {
|
|
3
3
|
readonly zodError: z.ZodError;
|
|
4
4
|
readonly sourcePath?: string | undefined;
|
|
5
5
|
constructor(zodError: z.ZodError, sourcePath?: string | undefined);
|
|
6
|
-
private static formatError;
|
|
6
|
+
private static readonly formatError;
|
|
7
7
|
}
|
|
8
8
|
export declare const isZodValidationError: (error: unknown) => error is ZodValidationError;
|
|
@@ -100,7 +100,7 @@ declare const transformRulesSchema: z.ZodObject<{
|
|
|
100
100
|
contentFile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
101
101
|
filenameFile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
102
102
|
}, z.core.$strip>;
|
|
103
|
-
declare const baseConfigSchema: z.ZodObject<{
|
|
103
|
+
export declare const baseConfigSchema: z.ZodObject<{
|
|
104
104
|
extends: z.ZodOptional<z.ZodString>;
|
|
105
105
|
requiredVersion: z.ZodOptional<z.ZodString>;
|
|
106
106
|
source: z.ZodOptional<z.ZodString>;
|
|
@@ -3,25 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isConfigValidationError = exports.ConfigValidationError = exports.parseConfig = exports.parseFormatOnlyConfig = exports.parseFinalConfig = exports.parseBaseConfig = void 0;
|
|
6
|
+
exports.isConfigValidationError = exports.ConfigValidationError = exports.parseConfig = exports.parseFormatOnlyConfig = exports.parseFinalConfig = exports.parseBaseConfig = exports.baseConfigSchema = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const zod_1 = require("zod");
|
|
9
9
|
const regexSafety_1 = require("../utils/regexSafety");
|
|
10
10
|
const ZodError_1 = require("./ZodError");
|
|
11
11
|
const semverMajorUpgradeRuleSchema = zod_1.z.object({
|
|
12
12
|
type: zod_1.z.literal('semverMajorUpgrade'),
|
|
13
|
-
path: zod_1.z.string().min(1)
|
|
13
|
+
path: zod_1.z.string().min(1).describe('JSONPath to the version field to check for major upgrades')
|
|
14
14
|
});
|
|
15
15
|
const semverDowngradeRuleSchema = zod_1.z.object({
|
|
16
16
|
type: zod_1.z.literal('semverDowngrade'),
|
|
17
|
-
path: zod_1.z.string().min(1)
|
|
17
|
+
path: zod_1.z.string().min(1).describe('JSONPath to the version field to check for any downgrade')
|
|
18
18
|
});
|
|
19
19
|
const numericRuleSchema = zod_1.z
|
|
20
20
|
.object({
|
|
21
21
|
type: zod_1.z.literal('numeric'),
|
|
22
|
-
path: zod_1.z.string().min(1),
|
|
23
|
-
min: zod_1.z.number().optional(),
|
|
24
|
-
max: zod_1.z.number().optional()
|
|
22
|
+
path: zod_1.z.string().min(1).describe('JSONPath to the numeric field to validate'),
|
|
23
|
+
min: zod_1.z.number().optional().describe('Minimum allowed value (inclusive)'),
|
|
24
|
+
max: zod_1.z.number().optional().describe('Maximum allowed value (inclusive)')
|
|
25
25
|
})
|
|
26
26
|
.refine((data) => {
|
|
27
27
|
if (data.min !== undefined && data.max !== undefined)
|
|
@@ -34,8 +34,8 @@ const numericRuleSchema = zod_1.z
|
|
|
34
34
|
const regexRuleSchema = zod_1.z
|
|
35
35
|
.object({
|
|
36
36
|
type: zod_1.z.literal('regex'),
|
|
37
|
-
path: zod_1.z.string().min(1).optional(),
|
|
38
|
-
regex: zod_1.z.string().min(1)
|
|
37
|
+
path: zod_1.z.string().min(1).optional().describe('JSONPath to check (omit to scan ALL values recursively)'),
|
|
38
|
+
regex: zod_1.z.string().min(1).describe('Regex pattern that must NOT match (blocks sync if matched)')
|
|
39
39
|
})
|
|
40
40
|
.refine((data) => {
|
|
41
41
|
try {
|
|
@@ -55,19 +55,22 @@ const regexRuleSchema = zod_1.z
|
|
|
55
55
|
});
|
|
56
56
|
const regexFileRuleSchema = zod_1.z.object({
|
|
57
57
|
type: zod_1.z.literal('regexFile'),
|
|
58
|
-
path: zod_1.z.string().min(1).optional(),
|
|
59
|
-
file: zod_1.z.string().min(1)
|
|
58
|
+
path: zod_1.z.string().min(1).optional().describe('JSONPath to check (omit to scan ALL values recursively)'),
|
|
59
|
+
file: zod_1.z.string().min(1).describe('Path to a YAML file containing an array of regex patterns')
|
|
60
60
|
});
|
|
61
61
|
const regexFileKeyRuleSchema = zod_1.z.object({
|
|
62
62
|
type: zod_1.z.literal('regexFileKey'),
|
|
63
|
-
path: zod_1.z.string().min(1).optional(),
|
|
64
|
-
file: zod_1.z.string().min(1)
|
|
63
|
+
path: zod_1.z.string().min(1).optional().describe('JSONPath to check (omit to scan ALL values recursively)'),
|
|
64
|
+
file: zod_1.z.string().min(1).describe('Path to a YAML file whose keys are used as regex patterns')
|
|
65
65
|
});
|
|
66
66
|
const versionFormatRuleSchema = zod_1.z
|
|
67
67
|
.object({
|
|
68
68
|
type: zod_1.z.literal('versionFormat'),
|
|
69
|
-
path: zod_1.z.string().min(1),
|
|
70
|
-
vPrefix: zod_1.z
|
|
69
|
+
path: zod_1.z.string().min(1).describe('JSONPath to the version field to validate format'),
|
|
70
|
+
vPrefix: zod_1.z
|
|
71
|
+
.enum(['required', 'allowed', 'forbidden'])
|
|
72
|
+
.default('allowed')
|
|
73
|
+
.describe('"required" = must start with v, "allowed" = either, "forbidden" = no v (default: "allowed")')
|
|
71
74
|
})
|
|
72
75
|
.strict();
|
|
73
76
|
const stopRuleSchema = zod_1.z.discriminatedUnion('type', [
|
|
@@ -80,11 +83,17 @@ const stopRuleSchema = zod_1.z.discriminatedUnion('type', [
|
|
|
80
83
|
versionFormatRuleSchema
|
|
81
84
|
]);
|
|
82
85
|
const arraySortRuleSchema = zod_1.z.object({
|
|
83
|
-
path: zod_1.z.string().min(1),
|
|
84
|
-
sortBy: zod_1.z
|
|
85
|
-
|
|
86
|
+
path: zod_1.z.string().min(1).describe('JSONPath to the array to sort'),
|
|
87
|
+
sortBy: zod_1.z
|
|
88
|
+
.string()
|
|
89
|
+
.min(1)
|
|
90
|
+
.optional()
|
|
91
|
+
.describe('Object property to sort by (required for arrays of objects; omit for scalar arrays)'),
|
|
92
|
+
order: zod_1.z.enum(['asc', 'desc']).default('asc').describe('Sort direction: "asc" or "desc" (default: "asc")')
|
|
93
|
+
});
|
|
94
|
+
const keySortRuleSchema = zod_1.z.object({
|
|
95
|
+
path: zod_1.z.string().min(1).describe('JSONPath to the object whose keys should be sorted alphabetically')
|
|
86
96
|
});
|
|
87
|
-
const keySortRuleSchema = zod_1.z.object({ path: zod_1.z.string().min(1) });
|
|
88
97
|
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
89
98
|
const hasDangerousKeys = (value) => {
|
|
90
99
|
if (value === null || typeof value !== 'object')
|
|
@@ -139,37 +148,74 @@ const transformRulesSchema = zod_1.z
|
|
|
139
148
|
data.filenameFile !== undefined, {
|
|
140
149
|
message: 'At least one of content, filename, contentFile, or filenameFile must be specified'
|
|
141
150
|
});
|
|
142
|
-
|
|
143
|
-
extends: zod_1.z.string().min(1).optional(),
|
|
151
|
+
exports.baseConfigSchema = zod_1.z.object({
|
|
152
|
+
extends: zod_1.z.string().min(1).optional().describe('Path to a parent config file to inherit from (up to 5 levels deep)'),
|
|
144
153
|
requiredVersion: zod_1.z
|
|
145
154
|
.string()
|
|
146
155
|
.min(1)
|
|
147
156
|
.regex(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/, {
|
|
148
157
|
message: 'Must be a valid semver version (e.g., "1.2.3" or "v1.2.3")'
|
|
149
158
|
})
|
|
150
|
-
.optional()
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
.optional()
|
|
160
|
+
.describe('Minimum required hed CLI version (e.g. "1.2.3"); also suppresses auto-update notifications'),
|
|
161
|
+
source: zod_1.z.string().min(1).optional().describe('Path to the source directory (environment to copy FROM)'),
|
|
162
|
+
destination: zod_1.z.string().min(1).optional().describe('Path to the destination directory (environment to sync INTO)'),
|
|
163
|
+
include: zod_1.z.array(zod_1.z.string().min(1)).optional().describe('Glob patterns for files to include (default: ["**/*"])'),
|
|
164
|
+
exclude: zod_1.z.array(zod_1.z.string().min(1)).optional().describe('Glob patterns for files to exclude from processing'),
|
|
165
|
+
prune: zod_1.z.boolean().optional().describe('Delete destination files that no longer exist in source (default: false)'),
|
|
166
|
+
confirmationDelay: zod_1.z
|
|
167
|
+
.number()
|
|
168
|
+
.int()
|
|
169
|
+
.min(0)
|
|
170
|
+
.optional()
|
|
171
|
+
.describe('Milliseconds to pause before applying changes (default: 3000; 0 = no delay)'),
|
|
172
|
+
skipPath: zod_1.z
|
|
173
|
+
.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string()))
|
|
174
|
+
.optional()
|
|
175
|
+
.describe('Per-file-glob map of JSONPath patterns whose destination values are preserved'),
|
|
158
176
|
outputFormat: zod_1.z
|
|
159
177
|
.object({
|
|
160
|
-
indent: zod_1.z
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
indent: zod_1.z
|
|
179
|
+
.number()
|
|
180
|
+
.int()
|
|
181
|
+
.min(1)
|
|
182
|
+
.max(10)
|
|
183
|
+
.optional()
|
|
184
|
+
.describe('Number of spaces for YAML indentation (1–10, default: 2)'),
|
|
185
|
+
keySeparator: zod_1.z.boolean().optional().describe('Insert a blank line between top-level keys (default: false)'),
|
|
186
|
+
quoteValues: zod_1.z
|
|
187
|
+
.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string()))
|
|
188
|
+
.optional()
|
|
189
|
+
.describe('Per-file-glob map of JSONPath patterns whose values should be quoted'),
|
|
190
|
+
keyOrders: zod_1.z
|
|
191
|
+
.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string()))
|
|
192
|
+
.optional()
|
|
193
|
+
.describe('Per-file-glob explicit key ordering arrays'),
|
|
194
|
+
keySort: zod_1.z
|
|
195
|
+
.record(zod_1.z.string(), zod_1.z.array(keySortRuleSchema))
|
|
196
|
+
.optional()
|
|
197
|
+
.describe('Per-file-glob rules for alphabetically sorting object keys at a path'),
|
|
198
|
+
arraySort: zod_1.z
|
|
199
|
+
.record(zod_1.z.string(), zod_1.z.array(arraySortRuleSchema))
|
|
200
|
+
.optional()
|
|
201
|
+
.describe('Per-file-glob rules for sorting array elements at a path')
|
|
166
202
|
})
|
|
167
|
-
.optional()
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
203
|
+
.optional()
|
|
204
|
+
.describe('YAML output formatting rules applied after merge'),
|
|
205
|
+
transforms: zod_1.z
|
|
206
|
+
.record(zod_1.z.string(), transformRulesSchema)
|
|
207
|
+
.optional()
|
|
208
|
+
.describe('Per-file-glob regex and file-based content/filename transform rules'),
|
|
209
|
+
stopRules: zod_1.z
|
|
210
|
+
.record(zod_1.z.string(), zod_1.z.array(stopRuleSchema))
|
|
211
|
+
.optional()
|
|
212
|
+
.describe('Per-file-glob validation rules that block sync if triggered (use --force to override)'),
|
|
213
|
+
fixedValues: zod_1.z
|
|
214
|
+
.record(zod_1.z.string(), zod_1.z.array(fixedValueRuleSchema))
|
|
215
|
+
.optional()
|
|
216
|
+
.describe('Per-file-glob rules that pin specific JSONPath locations to constant values after merge')
|
|
171
217
|
});
|
|
172
|
-
const finalConfigSchema = baseConfigSchema
|
|
218
|
+
const finalConfigSchema = exports.baseConfigSchema
|
|
173
219
|
.omit({ extends: true })
|
|
174
220
|
.required({ source: true, destination: true })
|
|
175
221
|
.extend({
|
|
@@ -197,7 +243,7 @@ const finalConfigSchema = baseConfigSchema
|
|
|
197
243
|
message: 'Source and destination folders cannot be the same',
|
|
198
244
|
path: ['destination']
|
|
199
245
|
});
|
|
200
|
-
const formatOnlyConfigSchema = baseConfigSchema
|
|
246
|
+
const formatOnlyConfigSchema = exports.baseConfigSchema
|
|
201
247
|
.omit({ extends: true })
|
|
202
248
|
.required({ destination: true })
|
|
203
249
|
.extend({
|
|
@@ -218,7 +264,7 @@ const formatOnlyConfigSchema = baseConfigSchema
|
|
|
218
264
|
.default({ indent: 2, keySeparator: false })
|
|
219
265
|
});
|
|
220
266
|
const parseBaseConfig = (data, configPath) => {
|
|
221
|
-
const result = baseConfigSchema.safeParse(data);
|
|
267
|
+
const result = exports.baseConfigSchema.safeParse(data);
|
|
222
268
|
if (!result.success)
|
|
223
269
|
throw new ZodError_1.ZodValidationError(result.error, configPath);
|
|
224
270
|
return result.data;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from '../logger';
|
|
1
2
|
import { type FinalConfig, type FormatOnlyConfig } from './configFile';
|
|
2
3
|
declare const ConfigLoaderErrorClass: {
|
|
3
4
|
new (message: string, options?: import("../utils").ErrorOptions): {
|
|
@@ -21,5 +22,5 @@ export type Config = FinalConfig;
|
|
|
21
22
|
export type LoadConfigOptions = {
|
|
22
23
|
formatOnly?: boolean;
|
|
23
24
|
};
|
|
24
|
-
export declare const loadConfigFile: (configPath: string, quiet?: boolean, logger?:
|
|
25
|
+
export declare const loadConfigFile: (configPath: string, quiet?: boolean, logger?: Logger, options?: LoadConfigOptions) => FinalConfig | FormatOnlyConfig;
|
|
25
26
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from '../logger';
|
|
1
2
|
import { type BaseConfig } from './configFile';
|
|
2
3
|
declare const ConfigMergerErrorClass: {
|
|
3
4
|
new (message: string, options?: import("../utils/errors").ErrorOptions): {
|
|
@@ -18,5 +19,5 @@ export declare class ConfigMergerError extends ConfigMergerErrorClass {
|
|
|
18
19
|
}
|
|
19
20
|
export declare const isConfigMergerError: (error: unknown) => error is ConfigMergerError;
|
|
20
21
|
export declare const mergeConfigs: (parent: BaseConfig, child: BaseConfig) => BaseConfig;
|
|
21
|
-
export declare const resolveConfigWithExtends: (configPath: string, visited?: Set<string>, depth?: number, logger?:
|
|
22
|
+
export declare const resolveConfigWithExtends: (configPath: string, visited?: Set<string>, depth?: number, logger?: Logger) => BaseConfig;
|
|
22
23
|
export {};
|