helm-env-delta 1.7.1 → 1.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/README.md +41 -7
- package/dist/commandLine.d.ts +1 -0
- package/dist/commandLine.js +6 -0
- package/dist/configFile.d.ts +1 -1
- package/dist/fileDiff.js +2 -2
- package/dist/fileLoader.d.ts +1 -0
- package/dist/fileLoader.js +10 -6
- package/dist/index.js +47 -2
- package/dist/patternUsageValidator.js +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/jsonPath.d.ts +7 -0
- package/dist/utils/jsonPath.js +39 -11
- package/package.json +15 -9
package/README.md
CHANGED
|
@@ -376,24 +376,48 @@ skipPath:
|
|
|
376
376
|
|
|
377
377
|
#### Filter Expressions (Skip by Name)
|
|
378
378
|
|
|
379
|
-
Skip specific array items by property value using filter
|
|
379
|
+
Skip specific array items by property value using CSS-style filter operators:
|
|
380
|
+
|
|
381
|
+
| Operator | Name | Example | Matches |
|
|
382
|
+
| -------- | ---------- | ---------------- | ------------------------- |
|
|
383
|
+
| `=` | equals | `[name=DEBUG]` | Exact match |
|
|
384
|
+
| `^=` | startsWith | `[name^=DB_]` | `DB_HOST`, `DB_PORT` |
|
|
385
|
+
| `$=` | endsWith | `[name$=_KEY]` | `API_KEY`, `SECRET_KEY` |
|
|
386
|
+
| `*=` | contains | `[name*=SECRET]` | `MY_SECRET_KEY`, `SECRET` |
|
|
380
387
|
|
|
381
388
|
```yaml
|
|
382
389
|
skipPath:
|
|
383
390
|
'**/*.yaml':
|
|
391
|
+
# Equals (=) - exact match
|
|
384
392
|
- 'env[name=SECRET_KEY]' # Skip item where name=SECRET_KEY
|
|
385
|
-
- 'env[name=INTERNAL_TOKEN].value' # Skip nested field in matching item
|
|
386
393
|
- 'containers[name=sidecar]' # Skip entire sidecar container
|
|
387
|
-
|
|
394
|
+
|
|
395
|
+
# StartsWith (^=) - prefix match
|
|
396
|
+
- 'env[name^=DB_]' # Skip DB_HOST, DB_PORT, DB_USER
|
|
397
|
+
- 'containers[name^=init-]' # Skip init-db, init-cache
|
|
398
|
+
|
|
399
|
+
# EndsWith ($=) - suffix match
|
|
400
|
+
- 'env[name$=_SECRET]' # Skip API_SECRET, DB_SECRET
|
|
401
|
+
- 'volumes[name$=-data]' # Skip app-data, cache-data
|
|
402
|
+
|
|
403
|
+
# Contains (*=) - substring match
|
|
404
|
+
- 'env[name*=PASSWORD]' # Skip DB_PASSWORD, PASSWORD_HASH
|
|
405
|
+
- 'containers[image*=nginx]' # Skip any nginx image
|
|
406
|
+
|
|
407
|
+
# Nested paths with mixed operators
|
|
408
|
+
- 'spec.containers[name^=sidecar-].env[name$=_KEY]'
|
|
388
409
|
```
|
|
389
410
|
|
|
390
411
|
**Syntax:**
|
|
391
412
|
|
|
392
413
|
- `array[prop=value]` - Match items where property equals value
|
|
414
|
+
- `array[prop^=prefix]` - Match items where property starts with prefix
|
|
415
|
+
- `array[prop$=suffix]` - Match items where property ends with suffix
|
|
416
|
+
- `array[prop*=substring]` - Match items where property contains substring
|
|
393
417
|
- `array[prop="value with spaces"]` - Quoted values for special characters
|
|
394
418
|
- Combine with wildcards: `containers[name=app].env[*].value`
|
|
395
419
|
|
|
396
|
-
**Use cases:** Namespaces, replicas, resource limits, secrets, URLs, environment-specific array items.
|
|
420
|
+
**Use cases:** Namespaces, replicas, resource limits, secrets, URLs, environment-specific array items, batch filtering by naming conventions.
|
|
397
421
|
|
|
398
422
|
---
|
|
399
423
|
|
|
@@ -642,7 +666,8 @@ hed --config <file> [options] # Short alias
|
|
|
642
666
|
| `--diff-json` | Output JSON to stdout (pipe to jq) |
|
|
643
667
|
| `--list-files` | List source/destination files without processing |
|
|
644
668
|
| `--show-config` | Display resolved config after inheritance |
|
|
645
|
-
| `--
|
|
669
|
+
| `--format-only` | Format destination files without syncing |
|
|
670
|
+
| `--skip-format` | Skip YAML formatting during sync |
|
|
646
671
|
| `--no-color` | Disable colored output (CI/accessibility) |
|
|
647
672
|
| `--verbose` | Show detailed debug info |
|
|
648
673
|
| `--quiet` | Suppress output except errors |
|
|
@@ -679,6 +704,12 @@ hed --config config.yaml
|
|
|
679
704
|
|
|
680
705
|
# Force override stop rules
|
|
681
706
|
hed --config config.yaml --force
|
|
707
|
+
|
|
708
|
+
# Format destination files only (no sync)
|
|
709
|
+
hed --config config.yaml --format-only
|
|
710
|
+
|
|
711
|
+
# Preview format changes
|
|
712
|
+
hed --config config.yaml --format-only --dry-run
|
|
682
713
|
```
|
|
683
714
|
|
|
684
715
|
---
|
|
@@ -749,7 +780,7 @@ git push origin main
|
|
|
749
780
|
|
|
750
781
|
✅ **Flexibility** - Per-file patterns. Config inheritance. Regex transforms.
|
|
751
782
|
|
|
752
|
-
✅ **Reliability** -
|
|
783
|
+
✅ **Reliability** - 920 tests, 84% coverage. Battle-tested.
|
|
753
784
|
|
|
754
785
|
---
|
|
755
786
|
|
|
@@ -950,7 +981,10 @@ spec:
|
|
|
950
981
|
# ✅ Correct
|
|
951
982
|
- 'spec.replicas' # No prefix
|
|
952
983
|
- 'env[*].name' # Array wildcard
|
|
953
|
-
- 'env[name=DEBUG]' # Filter by
|
|
984
|
+
- 'env[name=DEBUG]' # Filter by exact value
|
|
985
|
+
- 'env[name^=DB_]' # Filter by prefix (startsWith)
|
|
986
|
+
- 'env[name$=_KEY]' # Filter by suffix (endsWith)
|
|
987
|
+
- 'env[name*=SECRET]' # Filter by substring (contains)
|
|
954
988
|
- 'containers[name=app].env[name=SECRET]' # Nested filters
|
|
955
989
|
```
|
|
956
990
|
|
package/dist/commandLine.d.ts
CHANGED
package/dist/commandLine.js
CHANGED
|
@@ -19,6 +19,7 @@ const parseCommandLine = (argv) => {
|
|
|
19
19
|
.option('--diff-html', 'Generate and open HTML diff report in browser', false)
|
|
20
20
|
.option('--diff-json', 'Output diff as JSON to stdout', false)
|
|
21
21
|
.option('--skip-format', 'Skip YAML formatting (outputFormat section)', false)
|
|
22
|
+
.option('--format-only', 'Format YAML files in destination without syncing', false)
|
|
22
23
|
.option('--validate', 'Validate configuration file and exit', false)
|
|
23
24
|
.option('--list-files', 'List files that would be synced without processing diffs', false)
|
|
24
25
|
.option('--show-config', 'Display resolved configuration after inheritance and exit', false)
|
|
@@ -53,6 +54,10 @@ Documentation: https://github.com/balazscsaba2006/helm-env-delta
|
|
|
53
54
|
console.error('Error: --verbose and --quiet flags are mutually exclusive');
|
|
54
55
|
process.exit(1);
|
|
55
56
|
}
|
|
57
|
+
if (options['formatOnly'] && options['skipFormat']) {
|
|
58
|
+
console.error('Error: --format-only and --skip-format flags are mutually exclusive');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
56
61
|
const threshold = Number.parseFloat(options['suggestThreshold']);
|
|
57
62
|
if (Number.isNaN(threshold) || threshold < 0 || threshold > 1) {
|
|
58
63
|
console.error('Error: --suggest-threshold must be a number between 0 and 1');
|
|
@@ -66,6 +71,7 @@ Documentation: https://github.com/balazscsaba2006/helm-env-delta
|
|
|
66
71
|
diffHtml: options['diffHtml'],
|
|
67
72
|
diffJson: options['diffJson'],
|
|
68
73
|
skipFormat: options['skipFormat'],
|
|
74
|
+
formatOnly: options['formatOnly'],
|
|
69
75
|
validate: options['validate'],
|
|
70
76
|
listFiles: options['listFiles'],
|
|
71
77
|
showConfig: options['showConfig'],
|
package/dist/configFile.d.ts
CHANGED
|
@@ -161,7 +161,6 @@ declare const baseConfigSchema: z.ZodObject<{
|
|
|
161
161
|
}, z.core.$strict>], "type">>>>;
|
|
162
162
|
}, z.core.$strip>;
|
|
163
163
|
declare const finalConfigSchema: z.ZodObject<{
|
|
164
|
-
source: z.ZodNonOptional<z.ZodOptional<z.ZodString>>;
|
|
165
164
|
transforms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
166
165
|
content: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
167
166
|
find: z.ZodString;
|
|
@@ -174,6 +173,7 @@ declare const finalConfigSchema: z.ZodObject<{
|
|
|
174
173
|
contentFile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
175
174
|
filenameFile: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
176
175
|
}, z.core.$strip>>>;
|
|
176
|
+
source: z.ZodNonOptional<z.ZodOptional<z.ZodString>>;
|
|
177
177
|
destination: z.ZodNonOptional<z.ZodOptional<z.ZodString>>;
|
|
178
178
|
skipPath: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
179
179
|
stopRules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
package/dist/fileDiff.js
CHANGED
|
@@ -52,7 +52,7 @@ const deleteJsonPathRecursive = (object, parts, index) => {
|
|
|
52
52
|
const item = object[index_];
|
|
53
53
|
if (item && typeof item === 'object') {
|
|
54
54
|
const itemValue = item[filter.property];
|
|
55
|
-
if (
|
|
55
|
+
if ((0, jsonPath_1.matchesFilter)(itemValue, filter))
|
|
56
56
|
object.splice(index_, 1);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -60,7 +60,7 @@ const deleteJsonPathRecursive = (object, parts, index) => {
|
|
|
60
60
|
for (const item of object)
|
|
61
61
|
if (item && typeof item === 'object') {
|
|
62
62
|
const itemValue = item[filter.property];
|
|
63
|
-
if (
|
|
63
|
+
if ((0, jsonPath_1.matchesFilter)(itemValue, filter))
|
|
64
64
|
deleteJsonPathRecursive(item, parts, index + 1);
|
|
65
65
|
}
|
|
66
66
|
return;
|
package/dist/fileLoader.d.ts
CHANGED
package/dist/fileLoader.js
CHANGED
|
@@ -67,10 +67,12 @@ const validateAndResolveBaseDirectory = async (baseDirectory) => {
|
|
|
67
67
|
throw accessError;
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
|
-
const findMatchingFiles = async (baseDirectory, includePatterns, excludePatterns, transforms) => {
|
|
70
|
+
const findMatchingFiles = async (baseDirectory, includePatterns, excludePatterns, transforms, skipExclude) => {
|
|
71
71
|
try {
|
|
72
72
|
if (!transforms) {
|
|
73
|
-
const allPatterns =
|
|
73
|
+
const allPatterns = skipExclude
|
|
74
|
+
? [...includePatterns]
|
|
75
|
+
: [...includePatterns, ...excludePatterns.map((pattern) => `!${pattern}`)];
|
|
74
76
|
const matchedFiles = await (0, tinyglobby_1.glob)(allPatterns, {
|
|
75
77
|
cwd: baseDirectory,
|
|
76
78
|
absolute: true,
|
|
@@ -94,9 +96,11 @@ const findMatchingFiles = async (baseDirectory, includePatterns, excludePatterns
|
|
|
94
96
|
const included = includePatterns.some((pattern) => patternMatcher_1.globalMatcher.match(transformedPath, pattern));
|
|
95
97
|
if (!included)
|
|
96
98
|
continue;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
if (!skipExclude) {
|
|
100
|
+
const excluded = excludePatterns.some((pattern) => patternMatcher_1.globalMatcher.match(transformedPath, pattern));
|
|
101
|
+
if (excluded)
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
100
104
|
filtered.push(absolutePath);
|
|
101
105
|
}
|
|
102
106
|
return filtered;
|
|
@@ -153,7 +157,7 @@ const loadFiles = async (options, logger) => {
|
|
|
153
157
|
const absoluteBaseDirectory = await validateAndResolveBaseDirectory(options.baseDirectory);
|
|
154
158
|
const includePatterns = options.include ?? ['**/*'];
|
|
155
159
|
const excludePatterns = options.exclude ?? [];
|
|
156
|
-
const files = await findMatchingFiles(absoluteBaseDirectory, includePatterns, excludePatterns, options.transforms);
|
|
160
|
+
const files = await findMatchingFiles(absoluteBaseDirectory, includePatterns, excludePatterns, options.transforms, options.skipExclude);
|
|
157
161
|
if (logger?.shouldShow('debug')) {
|
|
158
162
|
logger.debug('Glob matching:');
|
|
159
163
|
logger.debug(` Directory: ${absoluteBaseDirectory}`);
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
const node_fs_1 = require("node:fs");
|
|
40
|
+
const promises_1 = require("node:fs/promises");
|
|
40
41
|
const node_os_1 = require("node:os");
|
|
41
42
|
const node_path_1 = __importDefault(require("node:path"));
|
|
42
43
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -60,7 +61,9 @@ const stopRulesValidator_1 = require("./stopRulesValidator");
|
|
|
60
61
|
const suggestionEngine_1 = require("./suggestionEngine");
|
|
61
62
|
const collisionDetector_1 = require("./utils/collisionDetector");
|
|
62
63
|
const filenameTransformer_1 = require("./utils/filenameTransformer");
|
|
64
|
+
const fileType_1 = require("./utils/fileType");
|
|
63
65
|
const versionChecker_1 = require("./utils/versionChecker");
|
|
66
|
+
const yamlFormatter_1 = require("./yamlFormatter");
|
|
64
67
|
const ZodError_1 = require("./ZodError");
|
|
65
68
|
const main = async () => {
|
|
66
69
|
const command = (0, commandLine_1.parseCommandLine)();
|
|
@@ -102,13 +105,15 @@ const main = async () => {
|
|
|
102
105
|
baseDirectory: config.source,
|
|
103
106
|
include: config.include,
|
|
104
107
|
exclude: config.exclude,
|
|
105
|
-
transforms: config.transforms
|
|
108
|
+
transforms: config.transforms,
|
|
109
|
+
skipExclude: true
|
|
106
110
|
}, logger);
|
|
107
111
|
const sourceFiles = sourceResult.fileMap;
|
|
108
112
|
const destinationResult = await (0, fileLoader_1.loadFiles)({
|
|
109
113
|
baseDirectory: config.destination,
|
|
110
114
|
include: config.include,
|
|
111
|
-
exclude: config.exclude
|
|
115
|
+
exclude: config.exclude,
|
|
116
|
+
skipExclude: true
|
|
112
117
|
}, logger);
|
|
113
118
|
const destinationFiles = destinationResult.fileMap;
|
|
114
119
|
logger.progress(`Loaded ${sourceFiles.size} source, ${destinationFiles.size} destination file(s)`, 'success');
|
|
@@ -171,6 +176,46 @@ const main = async () => {
|
|
|
171
176
|
console.log(` ${chalk_1.default.dim(file)}`);
|
|
172
177
|
return;
|
|
173
178
|
}
|
|
179
|
+
if (command.formatOnly) {
|
|
180
|
+
if (!config.outputFormat) {
|
|
181
|
+
logger.log(chalk_1.default.yellow('\n⚠️ No outputFormat configured. Nothing to format.'));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Formatting files...', 'info'));
|
|
185
|
+
let formattedCount = 0;
|
|
186
|
+
const errors = [];
|
|
187
|
+
for (const [relativePath, content] of destinationFiles) {
|
|
188
|
+
if (!(0, fileType_1.isYamlFile)(relativePath))
|
|
189
|
+
continue;
|
|
190
|
+
try {
|
|
191
|
+
const formatted = (0, yamlFormatter_1.formatYaml)(content, relativePath, config.outputFormat);
|
|
192
|
+
if (formatted !== content) {
|
|
193
|
+
const absolutePath = node_path_1.default.join(config.destination, relativePath);
|
|
194
|
+
if (command.dryRun)
|
|
195
|
+
logger.fileOp('format', relativePath, true);
|
|
196
|
+
else {
|
|
197
|
+
await (0, promises_1.writeFile)(absolutePath, formatted, 'utf8');
|
|
198
|
+
logger.fileOp('format', relativePath, false);
|
|
199
|
+
}
|
|
200
|
+
formattedCount++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
errors.push({ path: relativePath, error: error });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (command.dryRun)
|
|
208
|
+
logger.log(`\n[DRY RUN] Would format ${formattedCount} file(s)`);
|
|
209
|
+
else
|
|
210
|
+
logger.log(`\n✓ Formatted ${formattedCount} file(s)`);
|
|
211
|
+
if (errors.length > 0) {
|
|
212
|
+
logger.error(`\n❌ Encountered ${errors.length} error(s):`, 'critical');
|
|
213
|
+
for (const { path: errorPath, error } of errors)
|
|
214
|
+
logger.error(` ${errorPath}: ${error.message}`, 'critical');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
174
219
|
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Computing differences...', 'info'));
|
|
175
220
|
const diffResult = (0, fileDiff_1.computeFileDiff)(sourceFiles, destinationFiles, config, logger, originalPaths);
|
|
176
221
|
if (logger.shouldShow('debug'))
|
|
@@ -114,7 +114,7 @@ const pathCouldMatch = (object, pathParts) => {
|
|
|
114
114
|
if (!item || typeof item !== 'object')
|
|
115
115
|
return false;
|
|
116
116
|
const itemValue = item[filter.property];
|
|
117
|
-
return
|
|
117
|
+
return (0, jsonPath_1.matchesFilter)(itemValue, filter);
|
|
118
118
|
});
|
|
119
119
|
if (!matched)
|
|
120
120
|
return false;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export type { ErrorOptions } from './errors';
|
|
|
2
2
|
export { createErrorClass, createErrorTypeGuard } from './errors';
|
|
3
3
|
export { deepEqual } from './deepEqual';
|
|
4
4
|
export { normalizeForComparison, serializeForDiff } from './serialization';
|
|
5
|
-
export {
|
|
5
|
+
export type { FilterOperator } from './jsonPath';
|
|
6
|
+
export { clearJsonPathCache, getValueAtPath, isFilterSegment, matchesFilter, parseFilterSegment, parseJsonPath } from './jsonPath';
|
|
6
7
|
export { isYamlFile } from './fileType';
|
|
7
8
|
export { globalMatcher, PatternMatcher } from './patternMatcher';
|
|
8
9
|
export { generateUnifiedDiff } from './diffGenerator';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.UUID_PATTERN = exports.SEMVER_PATTERN = exports.SEMANTIC_PATTERNS = exports.SEMANTIC_KEYWORDS = exports.PROBLEMATIC_REGEX_CHARS = void 0;
|
|
3
|
+
exports.NUMERIC_MIN_FLOOR = exports.MAX_EXAMPLES_PER_SUGGESTION = exports.ISO_TIMESTAMP_PATTERN = exports.FILTER_THRESHOLDS = exports.CONSTRAINT_FIELD_NAMES = exports.CONFIDENCE_DEFAULTS = exports.ARRAY_KEY_FIELDS = exports.ANTONYM_PAIRS = exports.isYamlSeq = exports.isYamlMap = exports.isYamlCollection = exports.isScalar = exports.extractScalarValue = exports.extractKeyValue = exports.validateVersionString = exports.applyRegexRulesSequentially = exports.validateTargetedRegex = exports.validatePathlessRegex = exports.getAllValuesRecursive = exports.RegexPatternFileLoaderError = exports.loadRegexPatternsFromKeys = exports.loadRegexPatternArray = exports.isRegexPatternFileLoaderError = exports.TransformFileLoaderError = exports.loadTransformFiles = exports.loadTransformFile = exports.isTransformFileLoaderError = exports.YamlFileLoaderError = exports.loadYamlFile = exports.isYamlFileLoaderError = exports.escapeRegex = exports.VersionCheckerError = exports.isVersionCheckerError = exports.checkForUpdates = exports.detectArrayChanges = exports.generateUnifiedDiff = exports.PatternMatcher = exports.globalMatcher = exports.isYamlFile = exports.parseJsonPath = exports.parseFilterSegment = exports.matchesFilter = exports.isFilterSegment = exports.getValueAtPath = exports.clearJsonPathCache = exports.serializeForDiff = exports.normalizeForComparison = exports.deepEqual = exports.createErrorTypeGuard = exports.createErrorClass = void 0;
|
|
4
|
+
exports.UUID_PATTERN = exports.SEMVER_PATTERN = exports.SEMANTIC_PATTERNS = exports.SEMANTIC_KEYWORDS = exports.PROBLEMATIC_REGEX_CHARS = exports.NUMERIC_MIN_MULTIPLIER = void 0;
|
|
5
5
|
var errors_1 = require("./errors");
|
|
6
6
|
Object.defineProperty(exports, "createErrorClass", { enumerable: true, get: function () { return errors_1.createErrorClass; } });
|
|
7
7
|
Object.defineProperty(exports, "createErrorTypeGuard", { enumerable: true, get: function () { return errors_1.createErrorTypeGuard; } });
|
|
@@ -14,6 +14,7 @@ var jsonPath_1 = require("./jsonPath");
|
|
|
14
14
|
Object.defineProperty(exports, "clearJsonPathCache", { enumerable: true, get: function () { return jsonPath_1.clearJsonPathCache; } });
|
|
15
15
|
Object.defineProperty(exports, "getValueAtPath", { enumerable: true, get: function () { return jsonPath_1.getValueAtPath; } });
|
|
16
16
|
Object.defineProperty(exports, "isFilterSegment", { enumerable: true, get: function () { return jsonPath_1.isFilterSegment; } });
|
|
17
|
+
Object.defineProperty(exports, "matchesFilter", { enumerable: true, get: function () { return jsonPath_1.matchesFilter; } });
|
|
17
18
|
Object.defineProperty(exports, "parseFilterSegment", { enumerable: true, get: function () { return jsonPath_1.parseFilterSegment; } });
|
|
18
19
|
Object.defineProperty(exports, "parseJsonPath", { enumerable: true, get: function () { return jsonPath_1.parseJsonPath; } });
|
|
19
20
|
var fileType_1 = require("./fileType");
|
package/dist/utils/jsonPath.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
export type FilterOperator = 'eq' | 'startsWith' | 'endsWith' | 'contains';
|
|
1
2
|
export declare const isFilterSegment: (segment: string) => boolean;
|
|
2
3
|
export declare const parseFilterSegment: (segment: string) => {
|
|
3
4
|
property: string;
|
|
4
5
|
value: string;
|
|
6
|
+
operator: FilterOperator;
|
|
5
7
|
} | undefined;
|
|
8
|
+
export declare const matchesFilter: (itemValue: unknown, filter: {
|
|
9
|
+
property: string;
|
|
10
|
+
value: string;
|
|
11
|
+
operator: FilterOperator;
|
|
12
|
+
}) => boolean;
|
|
6
13
|
export declare const parseJsonPath: (path: string) => string[];
|
|
7
14
|
export declare const clearJsonPathCache: () => void;
|
|
8
15
|
export declare const getValueAtPath: (object: unknown, path: string[]) => unknown;
|
package/dist/utils/jsonPath.js
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getValueAtPath = exports.clearJsonPathCache = exports.parseJsonPath = exports.parseFilterSegment = exports.isFilterSegment = void 0;
|
|
3
|
+
exports.getValueAtPath = exports.clearJsonPathCache = exports.parseJsonPath = exports.matchesFilter = exports.parseFilterSegment = exports.isFilterSegment = void 0;
|
|
4
4
|
const pathCache = new Map();
|
|
5
5
|
const FILTER_PREFIX = 'filter:';
|
|
6
|
-
const
|
|
6
|
+
const OPERATOR_MAP = {
|
|
7
|
+
'=': 'eq',
|
|
8
|
+
'^=': 'startsWith',
|
|
9
|
+
'$=': 'endsWith',
|
|
10
|
+
'*=': 'contains'
|
|
11
|
+
};
|
|
12
|
+
const FILTER_REGEX = /^\[([A-Z_a-z]\w*)(\^=|\$=|\*=|=)("([^"]*)"|([^\]]*))]/;
|
|
7
13
|
const isFilterSegment = (segment) => segment.startsWith(FILTER_PREFIX);
|
|
8
14
|
exports.isFilterSegment = isFilterSegment;
|
|
9
15
|
const parseFilterSegment = (segment) => {
|
|
10
16
|
if (!(0, exports.isFilterSegment)(segment))
|
|
11
17
|
return undefined;
|
|
12
18
|
const content = segment.slice(FILTER_PREFIX.length);
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
19
|
+
const firstColon = content.indexOf(':');
|
|
20
|
+
if (firstColon === -1)
|
|
21
|
+
return undefined;
|
|
22
|
+
const property = content.slice(0, firstColon);
|
|
23
|
+
const rest = content.slice(firstColon + 1);
|
|
24
|
+
const secondColon = rest.indexOf(':');
|
|
25
|
+
if (secondColon === -1)
|
|
26
|
+
return undefined;
|
|
27
|
+
const operator = rest.slice(0, secondColon);
|
|
28
|
+
const value = rest.slice(secondColon + 1);
|
|
29
|
+
if (!['eq', 'startsWith', 'endsWith', 'contains'].includes(operator))
|
|
15
30
|
return undefined;
|
|
16
|
-
return {
|
|
17
|
-
property: content.slice(0, equalIndex),
|
|
18
|
-
value: content.slice(equalIndex + 1)
|
|
19
|
-
};
|
|
31
|
+
return { property, value, operator };
|
|
20
32
|
};
|
|
21
33
|
exports.parseFilterSegment = parseFilterSegment;
|
|
34
|
+
const matchesFilter = (itemValue, filter) => {
|
|
35
|
+
const stringValue = String(itemValue);
|
|
36
|
+
switch (filter.operator) {
|
|
37
|
+
case 'eq':
|
|
38
|
+
return stringValue === filter.value;
|
|
39
|
+
case 'startsWith':
|
|
40
|
+
return stringValue.startsWith(filter.value);
|
|
41
|
+
case 'endsWith':
|
|
42
|
+
return stringValue.endsWith(filter.value);
|
|
43
|
+
case 'contains':
|
|
44
|
+
return stringValue.includes(filter.value);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.matchesFilter = matchesFilter;
|
|
22
48
|
const parseJsonPath = (path) => {
|
|
23
49
|
const cached = pathCache.get(path);
|
|
24
50
|
if (cached !== undefined)
|
|
@@ -44,8 +70,10 @@ const parseJsonPath = (path) => {
|
|
|
44
70
|
const filterMatch = FILTER_REGEX.exec(remaining);
|
|
45
71
|
if (filterMatch) {
|
|
46
72
|
const property = filterMatch[1];
|
|
47
|
-
const
|
|
48
|
-
|
|
73
|
+
const operatorString = filterMatch[2];
|
|
74
|
+
const value = filterMatch[4] === undefined ? filterMatch[5] : filterMatch[4];
|
|
75
|
+
const operator = OPERATOR_MAP[operatorString] ?? 'eq';
|
|
76
|
+
result.push(`${FILTER_PREFIX}${property}:${operator}:${value}`);
|
|
49
77
|
index += filterMatch[0].length;
|
|
50
78
|
continue;
|
|
51
79
|
}
|
|
@@ -88,7 +116,7 @@ const getValueAtPath = (object, path) => {
|
|
|
88
116
|
if (!item || typeof item !== 'object')
|
|
89
117
|
return false;
|
|
90
118
|
const itemValue = item[filter.property];
|
|
91
|
-
return
|
|
119
|
+
return (0, exports.matchesFilter)(itemValue, filter);
|
|
92
120
|
});
|
|
93
121
|
current = matched;
|
|
94
122
|
continue;
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helm-env-delta",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "HelmEnvDelta – environment-aware YAML delta and sync for GitOps",
|
|
5
5
|
"author": "BCsabaEngine",
|
|
6
6
|
"license": "ISC",
|
|
7
|
-
"exports":
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
8
13
|
"main": "./dist/index.js",
|
|
9
14
|
"types": "./dist/index.d.ts",
|
|
15
|
+
"sideEffects": false,
|
|
10
16
|
"engines": {
|
|
11
17
|
"node": ">=22",
|
|
12
18
|
"npm": ">=9"
|
|
@@ -62,19 +68,19 @@
|
|
|
62
68
|
},
|
|
63
69
|
"devDependencies": {
|
|
64
70
|
"@types/hogan.js": "^3.0.5",
|
|
65
|
-
"@types/node": "^25.0.
|
|
71
|
+
"@types/node": "^25.0.10",
|
|
66
72
|
"@types/picomatch": "^4.0.2",
|
|
67
|
-
"@typescript-eslint/eslint-plugin": "^8.53.
|
|
68
|
-
"@typescript-eslint/parser": "^8.53.
|
|
69
|
-
"@vitest/coverage-v8": "^4.0.
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.53.1",
|
|
74
|
+
"@typescript-eslint/parser": "^8.53.1",
|
|
75
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
70
76
|
"eslint": "^9.39.2",
|
|
71
77
|
"eslint-config-prettier": "^10.1.8",
|
|
72
78
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
73
79
|
"eslint-plugin-unicorn": "^62.0.0",
|
|
74
|
-
"prettier": "^3.8.
|
|
80
|
+
"prettier": "^3.8.1",
|
|
75
81
|
"tsx": "^4.21.0",
|
|
76
82
|
"typescript": "^5.9.3",
|
|
77
|
-
"vitest": "^4.0.
|
|
83
|
+
"vitest": "^4.0.18"
|
|
78
84
|
},
|
|
79
85
|
"dependencies": {
|
|
80
86
|
"chalk": "^5.6.2",
|
|
@@ -85,6 +91,6 @@
|
|
|
85
91
|
"picomatch": "^4.0.3",
|
|
86
92
|
"tinyglobby": "^0.2.15",
|
|
87
93
|
"yaml": "^2.8.2",
|
|
88
|
-
"zod": "^4.3.
|
|
94
|
+
"zod": "^4.3.6"
|
|
89
95
|
}
|
|
90
96
|
}
|