timonel 2.8.1 → 2.8.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cli.d.ts +0 -1
  3. package/dist/cli.js +290 -226
  4. package/dist/index.d.ts +0 -5
  5. package/dist/index.js +0 -6
  6. package/dist/lib/helm.d.ts +0 -472
  7. package/dist/lib/helm.js +0 -481
  8. package/dist/lib/helmChartWriter.d.ts +0 -178
  9. package/dist/lib/helmChartWriter.js +0 -180
  10. package/dist/lib/resources/baseResourceProvider.d.ts +0 -46
  11. package/dist/lib/resources/baseResourceProvider.js +1 -47
  12. package/dist/lib/resources/cloud/aws/awsResources.d.ts +0 -144
  13. package/dist/lib/resources/cloud/aws/awsResources.js +1 -163
  14. package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +0 -132
  15. package/dist/lib/resources/cloud/aws/karpenterResources.js +0 -75
  16. package/dist/lib/rutter.d.ts +0 -324
  17. package/dist/lib/rutter.js +8 -352
  18. package/dist/lib/security.d.ts +0 -119
  19. package/dist/lib/security.js +4 -160
  20. package/dist/lib/umbrella.d.ts +0 -24
  21. package/dist/lib/umbrella.js +0 -24
  22. package/dist/lib/umbrellaRutter.d.ts +0 -71
  23. package/dist/lib/umbrellaRutter.js +2 -82
  24. package/dist/lib/utils/helmHelpers.d.ts +0 -39
  25. package/dist/lib/utils/helmHelpers.js +0 -32
  26. package/package.json +1 -1
  27. package/dist/cli.d.ts.map +0 -1
  28. package/dist/cli.js.map +0 -1
  29. package/dist/index.d.ts.map +0 -1
  30. package/dist/index.js.map +0 -1
  31. package/dist/lib/helm.d.ts.map +0 -1
  32. package/dist/lib/helm.js.map +0 -1
  33. package/dist/lib/helmChartWriter.d.ts.map +0 -1
  34. package/dist/lib/helmChartWriter.js.map +0 -1
  35. package/dist/lib/resources/baseResourceProvider.d.ts.map +0 -1
  36. package/dist/lib/resources/baseResourceProvider.js.map +0 -1
  37. package/dist/lib/resources/cloud/aws/awsResources.d.ts.map +0 -1
  38. package/dist/lib/resources/cloud/aws/awsResources.js.map +0 -1
  39. package/dist/lib/resources/cloud/aws/karpenterResources.d.ts.map +0 -1
  40. package/dist/lib/resources/cloud/aws/karpenterResources.js.map +0 -1
  41. package/dist/lib/rutter.d.ts.map +0 -1
  42. package/dist/lib/rutter.js.map +0 -1
  43. package/dist/lib/security.d.ts.map +0 -1
  44. package/dist/lib/security.js.map +0 -1
  45. package/dist/lib/umbrella.d.ts.map +0 -1
  46. package/dist/lib/umbrella.js.map +0 -1
  47. package/dist/lib/umbrellaRutter.d.ts.map +0 -1
  48. package/dist/lib/umbrellaRutter.js.map +0 -1
  49. package/dist/lib/utils/helmHelpers.d.ts.map +0 -1
  50. package/dist/lib/utils/helmHelpers.js.map +0 -1
@@ -1,77 +1,36 @@
1
- /**
2
- * @fileoverview Security utilities for input validation and sanitization
3
- * Focused on CLI tool security concerns: path traversal, injection prevention
4
- * @since 2.2.0
5
- */
6
1
  import * as path from 'path';
7
- /**
8
- * Security utilities for input validation and sanitization
9
- * Focused on CLI tool security concerns: path traversal, injection prevention
10
- *
11
- * @since 2.2.0
12
- */
13
2
  export class SecurityUtils {
14
- /**
15
- * Validates and sanitizes file paths to prevent path traversal attacks
16
- * @param inputPath - The path to validate
17
- * @param allowedBasePath - The base path that the input should be within
18
- * @returns Sanitized path if valid
19
- * @throws Error if path is invalid or contains traversal sequences
20
- *
21
- * @since 2.2.0
22
- */
23
3
  static validatePath(inputPath, allowedBasePath) {
24
4
  if (!inputPath || typeof inputPath !== 'string') {
25
5
  throw new Error('Invalid path: path must be a non-empty string');
26
6
  }
27
- // Check for path traversal sequences
28
7
  if (inputPath.includes('../') ||
29
8
  inputPath.includes('..\\') ||
30
9
  inputPath.includes('%2e%2e%2f') ||
31
10
  inputPath.includes('%2e%2e%5c')) {
32
11
  throw new Error('Invalid path: path traversal sequences detected');
33
12
  }
34
- // Resolve and normalize paths
35
13
  const resolvedInput = path.resolve(inputPath);
36
14
  const resolvedBase = path.resolve(allowedBasePath);
37
- // Ensure the resolved path is within the allowed base path
38
15
  if (!resolvedInput.startsWith(resolvedBase + path.sep) && resolvedInput !== resolvedBase) {
39
16
  throw new Error(`Invalid path: path must be within ${resolvedBase}`);
40
17
  }
41
18
  return resolvedInput;
42
19
  }
43
- /**
44
- * Sanitizes log messages to prevent log injection attacks
45
- * @param message - The message to sanitize
46
- * @returns Sanitized message with control characters removed
47
- *
48
- * @since 2.2.0
49
- */
50
20
  static sanitizeLogMessage(message) {
51
21
  if (typeof message !== 'string') {
52
22
  return String(message);
53
23
  }
54
- // Remove control characters (ASCII 0-31 and 127) and normalize line endings
55
24
  return (message
56
- // eslint-disable-next-line no-control-regex -- Intentionally removing control characters for security
57
- .replace(/[\x00-\x1F\x7F]/g, '') // Remove control characters
58
- .replace(/\r\n/g, ' ') // Replace CRLF with space
59
- .replace(/[\r\n]/g, ' ') // Replace remaining CR/LF with space
25
+ .replace(/[\x00-\x1F\x7F]/g, '')
26
+ .replace(/\r\n/g, ' ')
27
+ .replace(/[\r\n]/g, ' ')
60
28
  .trim());
61
29
  }
62
- /**
63
- * Sanitizes environment names to prevent path traversal (CWE-22)
64
- * @param env - Environment name to sanitize
65
- * @returns Sanitized environment name
66
- * @throws Error if environment name is invalid
67
- *
68
- * @since 2.2.0
69
- */
70
30
  static sanitizeEnvironmentName(env) {
71
31
  if (!env || typeof env !== 'string') {
72
32
  throw new Error('Environment name must be a non-empty string');
73
33
  }
74
- // Allow only alphanumeric, hyphens, and underscores
75
34
  const sanitized = env.replace(/[^a-zA-Z0-9-_]/g, '');
76
35
  if (sanitized !== env) {
77
36
  throw new Error(`Invalid environment name: ${this.sanitizeLogMessage(env)}`);
@@ -81,98 +40,49 @@ export class SecurityUtils {
81
40
  }
82
41
  return sanitized;
83
42
  }
84
- /**
85
- * Validates TypeScript file extensions for dynamic imports
86
- * @param filePath - The file path to validate
87
- * @returns True if the file has a valid TypeScript extension
88
- *
89
- * @since 2.2.0
90
- */
91
43
  static isValidTypeScriptFile(filePath) {
92
44
  const allowedExtensions = ['.ts', '.tsx'];
93
45
  const ext = path.extname(filePath);
94
46
  return allowedExtensions.includes(ext);
95
47
  }
96
- /**
97
- * Validates if a path is safe for use in Helm templates
98
- * Enhanced validation with better security checks and Helm best practices
99
- * @param templatePath - The template path to validate
100
- * @returns True if the path is valid for Helm templates
101
- *
102
- * @since 2.2.0
103
- */
104
48
  static isValidHelmTemplatePath(templatePath) {
105
49
  if (!templatePath || typeof templatePath !== 'string') {
106
50
  return false;
107
51
  }
108
- // Check length limits (reasonable for Helm paths)
109
52
  if (templatePath.length > 253) {
110
53
  return false;
111
54
  }
112
- // Enhanced validation following Kubernetes naming conventions
113
- // Check if starts and ends with alphanumeric
114
55
  if (!/^[a-zA-Z0-9]/.test(templatePath) || !/[a-zA-Z0-9]$/.test(templatePath)) {
115
56
  return false;
116
57
  }
117
- // Check for valid characters only (safe regex)
118
58
  if (!/^[a-zA-Z0-9._-]+$/.test(templatePath)) {
119
59
  return false;
120
60
  }
121
- // Additional security checks
122
- // Prevent path traversal attempts
123
61
  if (templatePath.includes('..') || templatePath.includes('//')) {
124
62
  return false;
125
63
  }
126
- // Prevent reserved words that could cause issues
127
64
  const reservedWords = ['nil', 'null', 'undefined', 'true', 'false'];
128
65
  const pathSegments = templatePath.split('.');
129
66
  for (const segment of pathSegments) {
130
67
  if (reservedWords.includes(segment.toLowerCase())) {
131
68
  return false;
132
69
  }
133
- // Each segment should not be empty
134
70
  if (segment.length === 0) {
135
71
  return false;
136
72
  }
137
73
  }
138
74
  return true;
139
75
  }
140
- /**
141
- * Validates chart names according to Helm conventions
142
- * @param chartName - Chart name to validate
143
- * @returns True if chart name follows Helm naming rules
144
- *
145
- * @since 2.2.0
146
- */
147
76
  static isValidChartName(chartName) {
148
77
  if (!chartName || typeof chartName !== 'string') {
149
78
  return false;
150
79
  }
151
- // Helm chart name validation (RFC 1123 subdomain) - safe regex
152
- // eslint-disable-next-line security/detect-unsafe-regex -- Simple character class regex is safe
153
- const chartNameRegex = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
80
+ const chartNameRegex = /^[a-z]([-a-z0-9]*[a-z0-9])?$/;
154
81
  return chartNameRegex.test(chartName) && chartName.length <= 63;
155
82
  }
156
- /**
157
- * Validates subchart names according to Helm conventions
158
- * @param subchartName - Subchart name to validate
159
- * @returns True if subchart name is valid
160
- *
161
- * @since 2.2.0
162
- */
163
83
  static isValidSubchartName(subchartName) {
164
84
  return this.isValidChartName(subchartName);
165
85
  }
166
- /**
167
- * Sanitizes environment variable names and values for Kubernetes security
168
- * Prevents injection attacks and ensures compliance with Kubernetes naming rules
169
- * @param name - Environment variable name to sanitize
170
- * @param value - Environment variable value to sanitize
171
- * @returns Object with sanitized name and value
172
- * @throws Error if name or value contains dangerous patterns
173
- *
174
- * @since 2.6.0
175
- */
176
86
  static sanitizeEnvVar(name, value) {
177
87
  if (!name || typeof name !== 'string') {
178
88
  throw new Error('Environment variable name must be a non-empty string');
@@ -180,9 +90,7 @@ export class SecurityUtils {
180
90
  if (typeof value !== 'string') {
181
91
  throw new Error('Environment variable value must be a string');
182
92
  }
183
- // Kubernetes env var name validation (RFC 1123 compatible)
184
93
  let sanitizedName = name.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
185
- // Ensure name starts with letter or underscore
186
94
  if (/^[0-9]/.test(sanitizedName)) {
187
95
  sanitizedName = `_${sanitizedName}`;
188
96
  }
@@ -190,7 +98,6 @@ export class SecurityUtils {
190
98
  if (!nameRegex.test(sanitizedName)) {
191
99
  throw new Error(`Invalid environment variable name: ${this.sanitizeLogMessage(name)}`);
192
100
  }
193
- // Check for dangerous patterns in value using safer string methods
194
101
  const hasDangerousPattern = (val) => {
195
102
  if (val.includes('$(') && val.includes(')'))
196
103
  return 'command substitution';
@@ -198,7 +105,6 @@ export class SecurityUtils {
198
105
  return 'backtick execution';
199
106
  if (val.includes('${') && val.includes('}'))
200
107
  return 'variable expansion';
201
- // Check for control characters
202
108
  for (let i = 0; i < val.length; i++) {
203
109
  const code = val.charCodeAt(i);
204
110
  if ((code >= 0x00 && code <= 0x08) ||
@@ -215,7 +121,6 @@ export class SecurityUtils {
215
121
  if (dangerousPattern) {
216
122
  throw new Error(`Environment variable value contains dangerous pattern (${dangerousPattern}): ${this.sanitizeLogMessage(value)}`);
217
123
  }
218
- // Limit value length to prevent DoS
219
124
  if (value.length > 32768) {
220
125
  throw new Error('Environment variable value exceeds maximum length (32KB)');
221
126
  }
@@ -224,58 +129,31 @@ export class SecurityUtils {
224
129
  value: value.trim(),
225
130
  };
226
131
  }
227
- /**
228
- * Validates container image tags for security best practices
229
- * Prevents use of dangerous tags and ensures proper versioning
230
- * @param tag - Image tag to validate
231
- * @returns True if tag is valid and secure
232
- * @throws Error if tag violates security policies
233
- *
234
- * @since 2.6.0
235
- */
236
132
  static validateImageTag(tag) {
237
133
  if (!tag || typeof tag !== 'string') {
238
134
  throw new Error('Image tag must be a non-empty string');
239
135
  }
240
136
  const trimmedTag = tag.trim();
241
- // Reject dangerous or non-specific tags
242
137
  const dangerousTags = ['latest', 'master', 'main', 'dev', 'development', 'test', 'staging'];
243
138
  if (dangerousTags.includes(trimmedTag.toLowerCase())) {
244
139
  throw new Error(`Insecure image tag detected: '${trimmedTag}'. Use specific version tags for security.`);
245
140
  }
246
- // Validate tag format (Docker tag rules)
247
141
  const tagRegex = /^[a-zA-Z0-9._-]+$/;
248
142
  if (!tagRegex.test(trimmedTag)) {
249
143
  throw new Error(`Invalid image tag format: ${this.sanitizeLogMessage(trimmedTag)}`);
250
144
  }
251
- // Check length limits
252
145
  if (trimmedTag.length > 128) {
253
146
  throw new Error('Image tag exceeds maximum length (128 characters)');
254
147
  }
255
- // Prefer semantic versioning patterns
256
- // eslint-disable-next-line security/detect-unsafe-regex -- Simple regex patterns are safe for validation
257
148
  const semverPattern = /^v?\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
258
149
  const hashPattern = /^[a-f0-9]{7,64}$/;
259
- // eslint-disable-next-line security/detect-unsafe-regex -- Simple date pattern is safe for validation
260
150
  const datePattern = /^\d{4}-\d{2}-\d{2}(-[a-zA-Z0-9.-]+)?$/;
261
151
  if (!semverPattern.test(trimmedTag) &&
262
152
  !hashPattern.test(trimmedTag) &&
263
153
  !datePattern.test(trimmedTag)) {
264
- // Warning for non-standard tags but don't fail
265
- console.warn(`Warning: Image tag '${trimmedTag}' doesn't follow recommended patterns (semver, hash, or date)`);
266
154
  }
267
155
  return true;
268
156
  }
269
- /**
270
- * Generates secure names for Kubernetes secrets
271
- * Ensures names follow RFC 1123 and are safe for Kubernetes
272
- * @param baseName - Base name for the secret
273
- * @param suffix - Optional suffix to append
274
- * @returns Secure secret name
275
- * @throws Error if generated name is invalid
276
- *
277
- * @since 2.6.0
278
- */
279
157
  static generateSecretName(baseName, suffix) {
280
158
  if (!baseName || typeof baseName !== 'string') {
281
159
  throw new Error('Base name must be a non-empty string');
@@ -284,27 +162,16 @@ export class SecurityUtils {
284
162
  if (suffix) {
285
163
  sanitizedBase = this.appendSuffix(sanitizedBase, suffix);
286
164
  }
287
- // Ensure name starts with letter (not number)
288
165
  if (/^[0-9]/.test(sanitizedBase)) {
289
166
  sanitizedBase = `s${sanitizedBase}`;
290
167
  }
291
168
  sanitizedBase = this.truncateIfNeeded(sanitizedBase, suffix);
292
- // Final validation
293
169
  if (!this.isValidChartName(sanitizedBase)) {
294
170
  throw new Error(`Generated secret name is invalid: ${sanitizedBase}`);
295
171
  }
296
172
  return sanitizedBase;
297
173
  }
298
- /**
299
- * Sanitizes the base name for secret generation using safe string methods
300
- * @param baseName - Base name to sanitize
301
- * @returns Sanitized base name
302
- * @throws Error if no valid characters remain
303
- *
304
- * @since 2.6.0
305
- */
306
174
  static sanitizeBaseName(baseName) {
307
- // Use character-by-character processing instead of regex
308
175
  let sanitized = '';
309
176
  for (const char of baseName.toLowerCase()) {
310
177
  if ((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char === '-') {
@@ -314,7 +181,6 @@ export class SecurityUtils {
314
181
  sanitized += '-';
315
182
  }
316
183
  }
317
- // Collapse multiple hyphens using split/filter/join
318
184
  const parts = sanitized.split('-').filter((part) => part.length > 0);
319
185
  sanitized = parts.join('-');
320
186
  if (!sanitized) {
@@ -322,20 +188,10 @@ export class SecurityUtils {
322
188
  }
323
189
  return sanitized;
324
190
  }
325
- /**
326
- * Appends suffix to sanitized base name using safe string methods
327
- * @param baseName - Sanitized base name
328
- * @param suffix - Suffix to append
329
- * @returns Base name with suffix
330
- * @throws Error if suffix is invalid
331
- *
332
- * @since 2.6.0
333
- */
334
191
  static appendSuffix(baseName, suffix) {
335
192
  if (typeof suffix !== 'string') {
336
193
  throw new Error('Suffix must be a string');
337
194
  }
338
- // Use character-by-character processing instead of regex
339
195
  let sanitizedSuffix = '';
340
196
  for (const char of suffix.toLowerCase()) {
341
197
  if ((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char === '-') {
@@ -345,7 +201,6 @@ export class SecurityUtils {
345
201
  sanitizedSuffix += '-';
346
202
  }
347
203
  }
348
- // Collapse multiple hyphens using split/filter/join
349
204
  const parts = sanitizedSuffix.split('-').filter((part) => part.length > 0);
350
205
  sanitizedSuffix = parts.join('-');
351
206
  if (sanitizedSuffix) {
@@ -353,22 +208,12 @@ export class SecurityUtils {
353
208
  }
354
209
  return baseName;
355
210
  }
356
- /**
357
- * Truncates name if it exceeds length limits
358
- * @param name - Name to potentially truncate
359
- * @param suffix - Original suffix for preservation
360
- * @returns Truncated name if needed
361
- *
362
- * @since 2.6.0
363
- */
364
211
  static truncateIfNeeded(name, suffix) {
365
212
  const maxLength = 63;
366
213
  if (name.length <= maxLength) {
367
214
  return name;
368
215
  }
369
- // Truncate but preserve suffix if possible
370
216
  if (suffix) {
371
- // Use safe character processing instead of regex
372
217
  let safeSuffix = '';
373
218
  for (const char of suffix.toLowerCase()) {
374
219
  if ((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char === '-') {
@@ -387,4 +232,3 @@ export class SecurityUtils {
387
232
  return name.substring(0, maxLength);
388
233
  }
389
234
  }
390
- //# sourceMappingURL=security.js.map
@@ -1,27 +1,3 @@
1
- /**
2
- * @fileoverview Umbrella chart utilities for managing multiple subcharts as a single unit
3
- * @since 0.2.0
4
- */
5
1
  import type { UmbrellaRutter, UmbrellaRutterProps } from './umbrellaRutter.js';
6
- /**
7
- * Create an umbrella chart from multiple Rutter instances
8
- *
9
- * @param props - Umbrella chart configuration
10
- * @returns UmbrellaRutter instance for managing subcharts
11
- *
12
- * @example
13
- * ```typescript
14
- * const umbrella = createUmbrella({
15
- * meta: { name: 'my-umbrella', version: '1.0.0' },
16
- * subcharts: [
17
- * { name: 'frontend', rutter: frontendChart },
18
- * { name: 'backend', rutter: backendChart }
19
- * ]
20
- * });
21
- * ```
22
- *
23
- * @since 0.2.0
24
- */
25
2
  export declare function createUmbrella(props: UmbrellaRutterProps): UmbrellaRutter;
26
3
  export type { UmbrellaRutterProps, SubchartSpec } from './umbrellaRutter.js';
27
- //# sourceMappingURL=umbrella.d.ts.map
@@ -1,28 +1,4 @@
1
- /**
2
- * @fileoverview Umbrella chart utilities for managing multiple subcharts as a single unit
3
- * @since 0.2.0
4
- */
5
1
  import { UmbrellaRutter as UmbrellaRutterClass } from './umbrellaRutter.js';
6
- /**
7
- * Create an umbrella chart from multiple Rutter instances
8
- *
9
- * @param props - Umbrella chart configuration
10
- * @returns UmbrellaRutter instance for managing subcharts
11
- *
12
- * @example
13
- * ```typescript
14
- * const umbrella = createUmbrella({
15
- * meta: { name: 'my-umbrella', version: '1.0.0' },
16
- * subcharts: [
17
- * { name: 'frontend', rutter: frontendChart },
18
- * { name: 'backend', rutter: backendChart }
19
- * ]
20
- * });
21
- * ```
22
- *
23
- * @since 0.2.0
24
- */
25
2
  export function createUmbrella(props) {
26
3
  return new UmbrellaRutterClass(props);
27
4
  }
28
- //# sourceMappingURL=umbrella.js.map
@@ -1,97 +1,26 @@
1
- /**
2
- * @fileoverview UmbrellaRutter class for managing Helm umbrella charts with multiple subcharts
3
- * @since 0.2.0
4
- */
5
1
  import type { HelmChartMeta } from './helmChartWriter.js';
6
2
  import type { Rutter } from './rutter.js';
7
- /**
8
- * Configuration for a subchart within an umbrella chart
9
- *
10
- * @since 0.2.0
11
- */
12
3
  export interface SubchartSpec {
13
- /** Name of the subchart */
14
4
  name: string;
15
- /** Rutter instance that generates the subchart */
16
5
  rutter: Rutter;
17
- /** Version of the subchart (optional) */
18
6
  version?: string;
19
- /** Helm condition for enabling/disabling the subchart */
20
7
  condition?: string;
21
- /** Tags for grouping subcharts */
22
8
  tags?: string[];
23
- /** Repository URL for the subchart */
24
9
  repository?: string;
25
10
  }
26
- /**
27
- * Configuration properties for UmbrellaRutter
28
- *
29
- * @since 0.2.0
30
- */
31
11
  export interface UmbrellaRutterProps {
32
- /** Metadata for the umbrella chart */
33
12
  meta: HelmChartMeta;
34
- /** Array of subchart specifications */
35
13
  subcharts: SubchartSpec[];
36
- /** Default values for the umbrella chart */
37
14
  defaultValues?: Record<string, unknown>;
38
- /** Environment-specific values */
39
15
  envValues?: Record<string, Record<string, unknown>>;
40
16
  }
41
- /**
42
- * UmbrellaRutter manages multiple Rutter instances as subcharts
43
- * to create Helm umbrella charts with dependencies.
44
- *
45
- * @example
46
- * ```typescript
47
- * const umbrella = new UmbrellaRutter({
48
- * meta: { name: 'my-app', version: '1.0.0' },
49
- * subcharts: [
50
- * { name: 'frontend', rutter: frontendChart },
51
- * { name: 'backend', rutter: backendChart }
52
- * ]
53
- * });
54
- *
55
- * umbrella.write('./charts/my-app');
56
- * ```
57
- *
58
- * @since 0.2.0
59
- */
60
17
  export declare class UmbrellaRutter {
61
18
  private readonly props;
62
- /**
63
- * Creates a new UmbrellaRutter instance
64
- *
65
- * @param props - Configuration properties for the umbrella chart
66
- * @throws {Error} If chart metadata is invalid
67
- *
68
- * @since 0.2.0
69
- */
70
19
  constructor(props: UmbrellaRutterProps);
71
20
  private validateMetadata;
72
- /**
73
- * Write the umbrella chart with all subcharts to the output directory
74
- *
75
- * Creates the complete umbrella chart structure including:
76
- * - Parent Chart.yaml with dependencies
77
- * - Parent values.yaml with global and subchart values
78
- * - Individual subchart directories under charts/
79
- * - NOTES.txt template
80
- *
81
- * @param outDir - Output directory path for the umbrella chart
82
- * @throws {Error} If output directory path is invalid
83
- *
84
- * @example
85
- * ```typescript
86
- * umbrella.write('./dist/my-umbrella-chart');
87
- * ```
88
- *
89
- * @since 0.2.0
90
- */
91
21
  write(outDir: string): void;
92
22
  private writeParentChart;
93
23
  private writeParentValues;
94
24
  private deepMerge;
95
25
  private generateNotesTemplate;
96
26
  }
97
- //# sourceMappingURL=umbrellaRutter.d.ts.map
@@ -1,101 +1,35 @@
1
- /**
2
- * @fileoverview UmbrellaRutter class for managing Helm umbrella charts with multiple subcharts
3
- * @since 0.2.0
4
- */
5
1
  import { writeFileSync, mkdirSync } from 'fs';
6
2
  import { join } from 'path';
7
3
  import YAML from 'yaml';
8
4
  import { SecurityUtils } from './security.js';
9
- /**
10
- * UmbrellaRutter manages multiple Rutter instances as subcharts
11
- * to create Helm umbrella charts with dependencies.
12
- *
13
- * @example
14
- * ```typescript
15
- * const umbrella = new UmbrellaRutter({
16
- * meta: { name: 'my-app', version: '1.0.0' },
17
- * subcharts: [
18
- * { name: 'frontend', rutter: frontendChart },
19
- * { name: 'backend', rutter: backendChart }
20
- * ]
21
- * });
22
- *
23
- * umbrella.write('./charts/my-app');
24
- * ```
25
- *
26
- * @since 0.2.0
27
- */
28
5
  export class UmbrellaRutter {
29
- /**
30
- * Creates a new UmbrellaRutter instance
31
- *
32
- * @param props - Configuration properties for the umbrella chart
33
- * @throws {Error} If chart metadata is invalid
34
- *
35
- * @since 0.2.0
36
- */
37
6
  constructor(props) {
38
- // Validate chart metadata
39
7
  this.validateMetadata(props.meta);
40
8
  this.props = props;
41
9
  }
42
10
  validateMetadata(meta) {
43
- // Validate chart name follows Helm conventions
44
- // eslint-disable-next-line security/detect-unsafe-regex -- Safe regex for chart name validation
45
11
  if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(meta.name)) {
46
12
  throw new Error('Chart name must contain only lowercase letters, numbers, and dashes');
47
13
  }
48
- // Validate semantic versioning
49
- // eslint-disable-next-line security/detect-unsafe-regex -- Safe regex for semver validation
50
14
  if (!/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(meta.version)) {
51
15
  throw new Error('Chart version must follow semantic versioning (e.g., 1.0.0)');
52
16
  }
53
17
  }
54
- /**
55
- * Write the umbrella chart with all subcharts to the output directory
56
- *
57
- * Creates the complete umbrella chart structure including:
58
- * - Parent Chart.yaml with dependencies
59
- * - Parent values.yaml with global and subchart values
60
- * - Individual subchart directories under charts/
61
- * - NOTES.txt template
62
- *
63
- * @param outDir - Output directory path for the umbrella chart
64
- * @throws {Error} If output directory path is invalid
65
- *
66
- * @example
67
- * ```typescript
68
- * umbrella.write('./dist/my-umbrella-chart');
69
- * ```
70
- *
71
- * @since 0.2.0
72
- */
73
18
  write(outDir) {
74
- // Validate output directory path
75
19
  const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd());
76
- // Create output directory structure
77
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
78
20
  mkdirSync(validatedOutDir, { recursive: true });
79
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
80
21
  mkdirSync(join(validatedOutDir, 'charts'), { recursive: true });
81
- // Write each subchart to charts/ directory
82
22
  for (const subchart of this.props.subcharts) {
83
- // Validate subchart name using centralized validation
84
23
  if (!SecurityUtils.isValidSubchartName(subchart.name)) {
85
24
  throw new Error(`Invalid subchart name: ${SecurityUtils.sanitizeLogMessage(subchart.name)}`);
86
25
  }
87
- const sanitizedName = subchart.name; // Already validated by SecurityUtils
26
+ const sanitizedName = subchart.name;
88
27
  const subchartDir = join(validatedOutDir, 'charts', sanitizedName);
89
28
  subchart.rutter.write(subchartDir);
90
29
  }
91
- // Generate parent Chart.yaml with dependencies
92
30
  this.writeParentChart(validatedOutDir);
93
- // Generate parent values.yaml
94
31
  this.writeParentValues(validatedOutDir);
95
- // Generate empty templates directory (umbrella charts typically don't have templates)
96
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
97
32
  mkdirSync(join(validatedOutDir, 'templates'), { recursive: true });
98
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
99
33
  writeFileSync(join(validatedOutDir, 'templates', 'NOTES.txt'), this.generateNotesTemplate());
100
34
  }
101
35
  writeParentChart(outDir) {
@@ -111,7 +45,6 @@ export class UmbrellaRutter {
111
45
  sources: this.props.meta.sources,
112
46
  maintainers: this.props.meta.maintainers,
113
47
  dependencies: this.props.subcharts.map((subchart) => {
114
- // Subchart name already validated in write() method
115
48
  return {
116
49
  name: subchart.name,
117
50
  version: subchart.version || '0.1.0',
@@ -121,17 +54,13 @@ export class UmbrellaRutter {
121
54
  };
122
55
  }),
123
56
  };
124
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
125
57
  writeFileSync(join(outDir, 'Chart.yaml'), YAML.stringify(chartYaml));
126
58
  }
127
59
  writeParentValues(outDir) {
128
60
  const values = {
129
- // Global values that can be shared across subcharts
130
61
  global: {},
131
- // Individual subchart values
132
62
  ...this.props.defaultValues,
133
63
  };
134
- // Add subchart-specific value sections
135
64
  for (const subchart of this.props.subcharts) {
136
65
  if (!(subchart.name in values)) {
137
66
  values[subchart.name] = {
@@ -139,15 +68,11 @@ export class UmbrellaRutter {
139
68
  };
140
69
  }
141
70
  }
142
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
143
71
  writeFileSync(join(outDir, 'values.yaml'), YAML.stringify(values));
144
- // Write environment-specific values files
145
72
  if (this.props.envValues) {
146
73
  for (const [env, envVals] of Object.entries(this.props.envValues)) {
147
- // Use centralized environment name sanitization
148
74
  const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(env);
149
75
  const envValues = this.deepMerge(values, envVals);
150
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool needs dynamic paths
151
76
  writeFileSync(join(outDir, `values-${sanitizedEnv}.yaml`), YAML.stringify(envValues));
152
77
  }
153
78
  }
@@ -156,13 +81,9 @@ export class UmbrellaRutter {
156
81
  const result = { ...target };
157
82
  for (const [key, value] of Object.entries(source)) {
158
83
  if (value && typeof value === 'object' && !Array.isArray(value)) {
159
- // eslint-disable-next-line security/detect-object-injection -- Safe object property access in deep merge
160
- result[key] = this.deepMerge(
161
- // eslint-disable-next-line security/detect-object-injection -- Safe object property access in deep merge
162
- result[key] || {}, value);
84
+ result[key] = this.deepMerge(result[key] || {}, value);
163
85
  }
164
86
  else {
165
- // eslint-disable-next-line security/detect-object-injection -- Safe object property access in deep merge
166
87
  result[key] = value;
167
88
  }
168
89
  }
@@ -184,4 +105,3 @@ ${this.props.subcharts.map((s) => ` - charts/${s.name}/README.md`).join('\n')}
184
105
  `;
185
106
  }
186
107
  }
187
- //# sourceMappingURL=umbrellaRutter.js.map