timonel 2.10.1 → 2.11.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 (35) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +5 -9
  3. package/dist/cli.js +22 -48
  4. package/dist/index.d.ts +1 -2
  5. package/dist/index.js +1 -2
  6. package/dist/lib/helmChartWriter.js +17 -2
  7. package/dist/lib/rutter.d.ts +1 -0
  8. package/dist/lib/rutter.js +64 -11
  9. package/dist/lib/templates/flexible-subchart.d.ts +1 -0
  10. package/dist/lib/templates/flexible-subchart.js +101 -0
  11. package/dist/lib/templates/umbrella-chart.d.ts +13 -2
  12. package/dist/lib/templates/umbrella-chart.js +128 -42
  13. package/dist/lib/utils/helmHelpers/envHelpers.d.ts +2 -0
  14. package/dist/lib/utils/helmHelpers/envHelpers.js +97 -0
  15. package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +2 -0
  16. package/dist/lib/utils/helmHelpers/gitopsHelpers.js +132 -0
  17. package/dist/lib/utils/helmHelpers/index.d.ts +23 -0
  18. package/dist/lib/utils/helmHelpers/index.js +132 -0
  19. package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +2 -0
  20. package/dist/lib/utils/helmHelpers/observabilityHelpers.js +221 -0
  21. package/dist/lib/utils/helmHelpers/types.d.ts +16 -0
  22. package/dist/lib/utils/helmHelpers/types.js +1 -0
  23. package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +2 -0
  24. package/dist/lib/utils/helmHelpers/validationHelpers.js +201 -0
  25. package/dist/lib/utils/helmHelpers.d.ts +11 -1
  26. package/dist/lib/utils/helmHelpers.js +20 -7
  27. package/dist/lib/utils/helmYamlSerializer.d.ts +53 -1
  28. package/dist/lib/utils/helmYamlSerializer.js +325 -29
  29. package/dist/lib/utils/logger.d.ts +49 -0
  30. package/dist/lib/utils/logger.js +418 -0
  31. package/package.json +24 -12
  32. package/dist/lib/templates/basic-chart.d.ts +0 -19
  33. package/dist/lib/templates/basic-chart.js +0 -210
  34. package/dist/lib/templates/subchart.d.ts +0 -27
  35. package/dist/lib/templates/subchart.js +0 -219
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [2.11.0](https://github.com/KenkoGeek/timonel/compare/v2.10.2...v2.11.0) (2025-09-26)
2
+
3
+ ### Features
4
+
5
+ - **helm:** add comprehensive helm helpers with Helm template validation ([#123](https://github.com/KenkoGeek/timonel/issues/123)) ([bda8115](https://github.com/KenkoGeek/timonel/commit/bda811546d308eaa3ee5aec7fd8356b4a508940e))
6
+
7
+ ## [2.10.2](https://github.com/KenkoGeek/timonel/compare/v2.10.1...v2.10.2) (2025-09-20)
8
+
9
+ ### Bug Fixes
10
+
11
+ - **cli:** consolidate path validation and improve error handling ([239a50f](https://github.com/KenkoGeek/timonel/commit/239a50fb30be451301d2af6a586b77748a3db6ec))
12
+
1
13
  ## [2.10.1](https://github.com/KenkoGeek/timonel/compare/v2.10.0...v2.10.1) (2025-09-20)
2
14
 
3
15
  ### Bug Fixes
package/README.md CHANGED
@@ -15,22 +15,18 @@ Helm charts using cdk8s. Define Kubernetes resources with type-safe classes and
15
15
  Helm charts with `Chart.yaml`, `values.yaml`, environment-specific values files, and `templates/`
16
16
  directory.
17
17
 
18
- > 🚀 **New in v2.9.2**: Enhanced umbrella chart support with flexible subchart templates, improved
19
- > CLI version handling, and optimized GitHub workflows.
20
-
21
18
  ## ✨ Key Features
22
19
 
23
20
  - **🔒 Type-safe API** with strict TypeScript and cdk8s constructs
24
21
  - **🔧 Flexible resource creation** with built-in methods and `addManifest()` for custom resources
25
22
  - **🌍 Multi-environment support** with automatic values files generation
26
23
  - **☂️ Umbrella Charts** for managing multiple subcharts as a single unit
24
+ - **🛠️ Enhanced Helm Helpers** with environment, GitOps, observability, and validation helpers
27
25
  - **☁️ Cloud integrations**:
28
- - **AWS**: EBS/EFS StorageClass, ALB Ingress, IRSA ServiceAccount
29
- - **Azure**: Azure Disk, Azure Files, AKS integrations
30
- - **GCP**: GCE Persistent Disk, GKE integrations
26
+ - **AWS**: EBS/EFS StorageClass, ALB Ingress, IRSA ServiceAccount, ECR, and Karpenter
31
27
  - **🛡️ Security-first approach** with NetworkPolicies and best practices
32
28
  - **⚡ Minimal CLI** (`tl`) for scaffolding and chart generation
33
- - **📦 Flexible subchart templates** supporting cdk8s, cdk8s-plus-33, and raw manifests
29
+ - **📦 Flexible subchart templates** supporting cdk8s and cdk8s-plus-33
34
30
 
35
31
  ## 🚀 Quick Start
36
32
 
@@ -246,5 +242,5 @@ MIT
246
242
  [maintained-url]: https://github.com/kenkogeek/
247
243
  [ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml/badge.svg?branch=main
248
244
  [ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml
249
- [codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yaml/badge.svg
250
- [codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yaml
245
+ [codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml/badge.svg
246
+ [codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml
package/dist/cli.js CHANGED
@@ -4,28 +4,12 @@ import fs from 'fs';
4
4
  import path from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { SecurityUtils } from './lib/security.js';
7
+ import { createLogger } from './lib/utils/logger.js';
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = path.dirname(__filename);
9
10
  const UMBRELLA_CONFIG_FILE = 'umbrella.config.json';
10
11
  const UMBRELLA_FILE_NAME = 'umbrella.ts';
11
12
  const PACKAGE_JSON_FILE = 'package.json';
12
- function isValidPath(filePath, expectedFileName) {
13
- try {
14
- const resolvedPath = path.resolve(filePath);
15
- const normalizedPath = path.normalize(resolvedPath);
16
- if (normalizedPath.includes('..') || normalizedPath.includes('~')) {
17
- return false;
18
- }
19
- if (!normalizedPath.endsWith(expectedFileName)) {
20
- return false;
21
- }
22
- const suspiciousPatterns = ['\\', '..', '~', '$', '`', '|', '&', ';', '(', ')'];
23
- return !suspiciousPatterns.some((pattern) => normalizedPath.includes(pattern));
24
- }
25
- catch {
26
- return false;
27
- }
28
- }
29
13
  function getVersion() {
30
14
  try {
31
15
  const possiblePaths = [
@@ -35,18 +19,21 @@ function getVersion() {
35
19
  path.resolve(__dirname, PACKAGE_JSON_FILE),
36
20
  ];
37
21
  for (const packagePath of possiblePaths) {
38
- if (!isValidPath(packagePath, PACKAGE_JSON_FILE)) {
39
- continue;
40
- }
41
- if (fs.existsSync(packagePath)) {
42
- try {
43
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
44
- return packageJson.version || 'unknown';
45
- }
46
- catch {
47
- continue;
22
+ try {
23
+ const validatedPath = SecurityUtils.validatePath(packagePath, process.cwd());
24
+ if (fs.existsSync(validatedPath)) {
25
+ try {
26
+ const packageJson = JSON.parse(fs.readFileSync(validatedPath, 'utf8'));
27
+ return packageJson.version || 'unknown';
28
+ }
29
+ catch {
30
+ continue;
31
+ }
48
32
  }
49
33
  }
34
+ catch {
35
+ continue;
36
+ }
50
37
  }
51
38
  if (process.env.npm_package_version) {
52
39
  return process.env.npm_package_version;
@@ -57,14 +44,15 @@ function getVersion() {
57
44
  return 'unknown';
58
45
  }
59
46
  }
47
+ const cliLogger = createLogger('cli');
60
48
  function log(msg, silent = false) {
61
49
  if (!silent) {
62
- console.log(msg);
50
+ cliLogger.info(msg, { operation: 'cli_log' });
63
51
  }
64
52
  }
65
53
  function logError(msg, silent = false) {
66
54
  if (!silent) {
67
- console.error(msg);
55
+ cliLogger.error(msg, { operation: 'cli_error' });
68
56
  }
69
57
  }
70
58
  function usageAndExit(msg, silent = false) {
@@ -94,7 +82,6 @@ function usageAndExit(msg, silent = false) {
94
82
  ' --env <environment> Use environment-specific values',
95
83
  ' --set <key=value> Override values (can be used multiple times)',
96
84
  ' --help, -h Show this help message',
97
- ' --version, -v Show version information',
98
85
  '',
99
86
  'Examples:',
100
87
  ' tl init my-app',
@@ -118,8 +105,8 @@ async function cmdInit(name, silent = false) {
118
105
  const base = path.join(process.cwd(), validName);
119
106
  const chartFile = path.join(base, 'chart.ts');
120
107
  fs.mkdirSync(base, { recursive: true });
121
- const { generateBasicChart } = await import('./lib/templates/basic-chart.js');
122
- fs.writeFileSync(chartFile, generateBasicChart(validName));
108
+ const { generateFlexibleSubchartTemplate } = await import('./lib/templates/flexible-subchart.js');
109
+ fs.writeFileSync(chartFile, generateFlexibleSubchartTemplate(validName));
123
110
  log(`Chart created at ${base}`, silent);
124
111
  log(`Generated chart.ts file`, silent);
125
112
  log(`Run 'tl synth ${validName}' to generate complete Helm chart`, silent);
@@ -359,8 +346,8 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
359
346
  const subchartDir = path.join(process.cwd(), 'charts', validSubchartPath);
360
347
  fs.mkdirSync(subchartDir, { recursive: true });
361
348
  const chartFile = path.join(subchartDir, 'chart.ts');
362
- const { generateSubchartTemplate } = await import('./lib/templates/subchart.js');
363
- const subchartContent = generateSubchartTemplate(subchartName);
349
+ const { generateFlexibleSubchartTemplate } = await import('./lib/templates/flexible-subchart.js');
350
+ const subchartContent = generateFlexibleSubchartTemplate(subchartName);
364
351
  fs.writeFileSync(chartFile, subchartContent);
365
352
  config.subcharts.push({
366
353
  name: subchartName,
@@ -443,13 +430,6 @@ function parseFlags(args) {
443
430
  }
444
431
  break;
445
432
  }
446
- case '--version':
447
- case '-v':
448
- if (!flags.silent) {
449
- console.log(`Timonel v${getVersion()}`);
450
- }
451
- process.exit(0);
452
- break;
453
433
  case '--help':
454
434
  case '-h':
455
435
  usageAndExit(undefined, flags.silent);
@@ -495,12 +475,6 @@ async function main() {
495
475
  usageAndExit(undefined, isSilent);
496
476
  return;
497
477
  }
498
- if (args.includes('--version') || args.includes('-v')) {
499
- if (!isSilent) {
500
- console.log(`Timonel v${getVersion()}`);
501
- }
502
- process.exit(0);
503
- }
504
478
  const command = args.shift();
505
479
  const flags = parseFlags(args);
506
480
  await executeCommand(command, args, flags);
@@ -508,7 +482,7 @@ async function main() {
508
482
  main().catch((error) => {
509
483
  const flags = parseFlags(process.argv.slice(2));
510
484
  if (!flags.silent) {
511
- console.error('Error:', error.message);
485
+ console.error('Error:', SecurityUtils.sanitizeLogMessage(error.message));
512
486
  }
513
487
  process.exit(1);
514
488
  });
package/dist/index.d.ts CHANGED
@@ -3,12 +3,11 @@ export * from './lib/helmChartWriter.js';
3
3
  export * from './lib/rutter.js';
4
4
  export * from './lib/security.js';
5
5
  export * from './lib/umbrella.js';
6
- export { BasicChart } from './lib/templates/basic-chart.js';
7
6
  export { FlexibleSubchart, createFlexibleSubchart } from './lib/templates/flexible-subchart.js';
8
- export { Subchart } from './lib/templates/subchart.js';
9
7
  export { UmbrellaChartTemplate as UmbrellaChart } from './lib/templates/umbrella-chart.js';
10
8
  export type { ChartProps, SubchartProps } from './lib/types.js';
11
9
  export type { AWSALBIngressSpec, AWSEBSStorageClassSpec, AWSEFSStorageClassSpec, AWSIRSAServiceAccountSpec, } from './lib/resources/cloud/aws/awsResources.js';
12
10
  export type { KarpenterDisruption, KarpenterDisruptionBudget, KarpenterEC2NodeClassSpec, KarpenterNodeClaimSpec, KarpenterNodePoolSpec, } from './lib/resources/cloud/aws/karpenterResources.js';
13
11
  export { DEFAULT_TERMINATION_GRACE_PERIOD, isValidDisruptionBudget, isValidKubernetesDuration, KarpenterVersionUtils, } from './lib/resources/cloud/aws/karpenterResources.js';
14
12
  export { AWS_HELPERS, createHelper, FILE_ACCESS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, getHelpersByCategory, KUBERNETES_HELPERS, SPRIG_HELPERS, STANDARD_HELPERS, TEMPLATE_FUNCTION_HELPERS, type HelperDefinition, } from './lib/utils/helmHelpers.js';
13
+ export { createLogger, logger, LogLevel, TimonelLogger, type LogContext, type LoggerConfig, } from './lib/utils/logger.js';
package/dist/index.js CHANGED
@@ -3,9 +3,8 @@ export * from './lib/helmChartWriter.js';
3
3
  export * from './lib/rutter.js';
4
4
  export * from './lib/security.js';
5
5
  export * from './lib/umbrella.js';
6
- export { BasicChart } from './lib/templates/basic-chart.js';
7
6
  export { FlexibleSubchart, createFlexibleSubchart } from './lib/templates/flexible-subchart.js';
8
- export { Subchart } from './lib/templates/subchart.js';
9
7
  export { UmbrellaChartTemplate as UmbrellaChart } from './lib/templates/umbrella-chart.js';
10
8
  export { DEFAULT_TERMINATION_GRACE_PERIOD, isValidDisruptionBudget, isValidKubernetesDuration, KarpenterVersionUtils, } from './lib/resources/cloud/aws/karpenterResources.js';
11
9
  export { AWS_HELPERS, createHelper, FILE_ACCESS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, getHelpersByCategory, KUBERNETES_HELPERS, SPRIG_HELPERS, STANDARD_HELPERS, TEMPLATE_FUNCTION_HELPERS, } from './lib/utils/helmHelpers.js';
10
+ export { createLogger, logger, LogLevel, TimonelLogger, } from './lib/utils/logger.js';
@@ -1,11 +1,20 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { SecurityUtils } from './security.js';
4
+ import { createLogger } from './utils/logger.js';
4
5
  import { dumpHelmAwareYaml } from './utils/helmYamlSerializer.js';
5
6
  export class HelmChartWriter {
6
7
  static write(opts) {
7
8
  const { outDir, meta, defaultValues = {}, envValues = {}, assets, helpersTpl, notesTpl, valuesSchema, } = opts;
8
- console.log(`Writing chart: ${meta.name} v${meta.version} to ${outDir}`);
9
+ const logger = createLogger('helm-chart-writer');
10
+ const timer = logger.time('helm_chart_write');
11
+ logger.info('Starting Helm chart write operation', {
12
+ chartName: meta.name,
13
+ version: meta.version,
14
+ outputDirectory: outDir,
15
+ assetCount: assets.length,
16
+ operation: 'helm_write_start',
17
+ });
9
18
  const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd());
10
19
  this.createDirectories(validatedOutDir);
11
20
  this.writeChartYaml(validatedOutDir, meta);
@@ -15,7 +24,13 @@ export class HelmChartWriter {
15
24
  this.writeNotes(validatedOutDir, notesTpl);
16
25
  this.writeSchema(validatedOutDir, valuesSchema);
17
26
  this.writeHelmIgnore(validatedOutDir);
18
- console.log(`Chart written successfully to ${validatedOutDir}`);
27
+ logger.info('Helm chart write operation completed successfully', {
28
+ chartName: meta.name,
29
+ version: meta.version,
30
+ outputDirectory: validatedOutDir,
31
+ operation: 'helm_write_complete',
32
+ });
33
+ timer();
19
34
  }
20
35
  static createDirectories(outDir) {
21
36
  fs.mkdirSync(path.join(outDir, 'templates'), { recursive: true });
@@ -16,6 +16,7 @@ export declare class Rutter {
16
16
  private readonly karpenterResources;
17
17
  private readonly meta;
18
18
  private readonly props;
19
+ private readonly logger;
19
20
  constructor(props: RutterProps);
20
21
  addAWSEBSStorageClass(spec: AWSEBSStorageClassSpec): ApiObject;
21
22
  addAWSEFSStorageClass(spec: AWSEFSStorageClassSpec): ApiObject;
@@ -3,17 +3,24 @@ import * as jsYaml from 'js-yaml';
3
3
  import { include } from './helm.js';
4
4
  import { HelmChartWriter } from './helmChartWriter.js';
5
5
  import { AWSResources } from './resources/cloud/aws/awsResources.js';
6
+ import { createLogger } from './utils/logger.js';
6
7
  import { KarpenterResources } from './resources/cloud/aws/karpenterResources.js';
7
8
  import { dumpHelmAwareYaml } from './utils/helmYamlSerializer.js';
8
9
  import { generateHelpersTemplate } from './utils/helmHelpers.js';
9
10
  export class Rutter {
10
11
  constructor(props) {
11
12
  this.assets = [];
12
- console.log(`Initializing chart: ${props.meta.name} v${props.meta.version}`);
13
+ this.logger = createLogger('rutter');
13
14
  this.defaultValues = props.defaultValues ?? {};
14
15
  this.envValues = props.envValues ?? {};
15
16
  this.meta = props.meta;
16
17
  this.props = props;
18
+ this.logger.info('Initializing chart', {
19
+ chartName: props.meta.name,
20
+ version: props.meta.version,
21
+ namespace: props.namespace,
22
+ operation: 'chart_initialization',
23
+ });
17
24
  this.app = new App();
18
25
  this.chart = new Chart(this.app, props.meta.name, {
19
26
  ...props.chartProps,
@@ -134,24 +141,40 @@ ${yamlContent.trim()}
134
141
  }
135
142
  validateManifestStructure(manifest) {
136
143
  if (!manifest.apiVersion) {
137
- console.error('Validation failed: Missing apiVersion');
144
+ this.logger.error('Validation failed: Missing apiVersion', {
145
+ operation: 'manifest_validation',
146
+ issue: 'missing_api_version',
147
+ });
138
148
  throw new Error('Manifest must have an apiVersion');
139
149
  }
140
150
  if (!manifest.kind) {
141
- console.error('Validation failed: Missing kind');
151
+ this.logger.error('Validation failed: Missing kind', {
152
+ operation: 'manifest_validation',
153
+ issue: 'missing_kind',
154
+ });
142
155
  throw new Error('Manifest must have a kind');
143
156
  }
144
157
  if (!manifest.metadata) {
145
- console.error('Validation failed: Missing metadata');
158
+ this.logger.error('Validation failed: Missing metadata', {
159
+ operation: 'manifest_validation',
160
+ issue: 'missing_metadata',
161
+ });
146
162
  throw new Error('Manifest must have metadata');
147
163
  }
148
164
  const metadata = manifest.metadata;
149
165
  if (!metadata.name) {
150
- console.error('Validation failed: Missing metadata.name');
166
+ this.logger.error('Validation failed: Missing metadata.name', {
167
+ operation: 'manifest_validation',
168
+ issue: 'missing_metadata_name',
169
+ });
151
170
  throw new Error('Manifest metadata must have a name');
152
171
  }
153
172
  if (typeof metadata.name !== 'string') {
154
- console.error('Validation failed: metadata.name must be a string');
173
+ this.logger.error('Validation failed: metadata.name must be a string', {
174
+ operation: 'manifest_validation',
175
+ issue: 'invalid_metadata_name_type',
176
+ actual_type: typeof metadata.name,
177
+ });
155
178
  throw new Error('Manifest metadata.name must be a string');
156
179
  }
157
180
  }
@@ -168,7 +191,11 @@ ${yamlContent.trim()}
168
191
  return [...this.assets];
169
192
  }
170
193
  toSynthArray() {
171
- console.log(`Synthesizing chart assets for: ${this.meta.name}`);
194
+ const timer = this.logger.time('chart_synthesis');
195
+ this.logger.debug('Starting chart synthesis', {
196
+ chartName: this.meta.name,
197
+ operation: 'synthesis_start',
198
+ });
172
199
  const apiObjectIds = [];
173
200
  for (const child of this.chart.node.children) {
174
201
  if (child instanceof ApiObject && !child.node.id.endsWith('-placeholder')) {
@@ -184,7 +211,12 @@ ${yamlContent.trim()}
184
211
  }
185
212
  return true;
186
213
  });
187
- console.log(`Found ${manifestObjs.length} manifest objects to process`);
214
+ this.logger.info('Processing manifest objects', {
215
+ chartName: this.meta.name,
216
+ manifestCount: manifestObjs.length,
217
+ apiObjectCount: apiObjectIds.length,
218
+ operation: 'manifest_processing',
219
+ });
188
220
  const enriched = manifestObjs.map((obj) => {
189
221
  if (obj && typeof obj === 'object') {
190
222
  const o = obj;
@@ -229,6 +261,13 @@ ${yamlContent.trim()}
229
261
  this.assets.forEach((asset) => {
230
262
  synthAssets.push({ id: asset.id, yaml: asset.yaml });
231
263
  });
264
+ this.logger.info('Chart synthesis completed', {
265
+ chartName: this.meta.name,
266
+ totalAssets: synthAssets.length,
267
+ additionalAssets: this.assets.length,
268
+ operation: 'synthesis_complete',
269
+ });
270
+ timer();
232
271
  return synthAssets;
233
272
  }
234
273
  processHelmTemplates(yaml) {
@@ -399,7 +438,12 @@ ${yamlContent.trim()}
399
438
  return processed;
400
439
  }
401
440
  write(outDir) {
402
- console.log(`Writing chart '${this.meta.name}' to: ${outDir}`);
441
+ const timer = this.logger.time('chart_write');
442
+ this.logger.info('Starting chart write operation', {
443
+ chartName: this.meta.name,
444
+ outputDirectory: outDir,
445
+ operation: 'chart_write_start',
446
+ });
403
447
  let helpersContent;
404
448
  if (this.props.helpersTpl) {
405
449
  if (typeof this.props.helpersTpl === 'string') {
@@ -420,7 +464,11 @@ ${helper.template}
420
464
  helpersContent = generateHelpersTemplate(this.props.cloudProvider);
421
465
  }
422
466
  const synthAssets = this.toSynthArray();
423
- console.log(`Generated ${synthAssets.length} assets for chart`);
467
+ this.logger.info('Generated assets for chart', {
468
+ chartName: this.meta.name,
469
+ assetCount: synthAssets.length,
470
+ operation: 'assets_generated',
471
+ });
424
472
  HelmChartWriter.write({
425
473
  outDir,
426
474
  meta: this.meta,
@@ -429,7 +477,12 @@ ${helper.template}
429
477
  assets: synthAssets,
430
478
  helpersTpl: helpersContent,
431
479
  });
432
- console.log(`Chart '${this.meta.name}' written successfully`);
480
+ this.logger.info('Chart write operation completed successfully', {
481
+ chartName: this.meta.name,
482
+ outputDirectory: outDir,
483
+ operation: 'chart_write_complete',
484
+ });
485
+ timer();
433
486
  }
434
487
  }
435
488
  Rutter.HELPER_NAME = 'chart.name';
@@ -25,3 +25,4 @@ export declare function createFlexibleSubchart(scope: Chart, id: string, config:
25
25
  description?: string;
26
26
  [key: string]: unknown;
27
27
  }): FlexibleSubchart;
28
+ export declare function generateFlexibleSubchartTemplate(name: string): string;
@@ -105,3 +105,104 @@ export class FlexibleSubchart extends Chart {
105
105
  export function createFlexibleSubchart(scope, id, config) {
106
106
  return new FlexibleSubchart(scope, id, config);
107
107
  }
108
+ export function generateFlexibleSubchartTemplate(name) {
109
+ return `import { App } from 'cdk8s';
110
+ import { Rutter } from 'timonel';
111
+
112
+ /**
113
+ * Creates a new chart with simple Rutter implementation
114
+ * @returns Rutter instance for Helm chart generation
115
+ * @since 2.11.0
116
+ */
117
+ export default function createChart() {
118
+ const app = new App({ outdir: 'dist' });
119
+
120
+ const rutter = new Rutter({
121
+ meta: {
122
+ name: '${name}',
123
+ version: '1.0.0',
124
+ description: '${name} subchart'
125
+ },
126
+ scope: app,
127
+ defaultValues: {
128
+ appName: '${name}',
129
+ image: 'nginx:latest',
130
+ port: 80,
131
+ replicas: 1,
132
+ },
133
+ });
134
+
135
+ // Add simple deployment
136
+ rutter.addManifest({
137
+ apiVersion: 'apps/v1',
138
+ kind: 'Deployment',
139
+ metadata: {
140
+ name: '${name}',
141
+ labels: {
142
+ 'app.kubernetes.io/name': '${name}'
143
+ }
144
+ },
145
+ spec: {
146
+ replicas: 1,
147
+ selector: {
148
+ matchLabels: {
149
+ 'app.kubernetes.io/name': '${name}'
150
+ }
151
+ },
152
+ template: {
153
+ metadata: {
154
+ labels: {
155
+ 'app.kubernetes.io/name': '${name}'
156
+ }
157
+ },
158
+ spec: {
159
+ containers: [{
160
+ name: '${name}',
161
+ image: 'nginx:latest',
162
+ ports: [{
163
+ containerPort: 80
164
+ }],
165
+ env: [
166
+ { name: 'APP_NAME', value: '${name}' },
167
+ { name: 'PORT', value: '80' }
168
+ ]
169
+ }]
170
+ }
171
+ }
172
+ }
173
+ }, 'deployment');
174
+
175
+ // Add Service
176
+ rutter.addManifest({
177
+ apiVersion: 'v1',
178
+ kind: 'Service',
179
+ metadata: {
180
+ name: '${name}',
181
+ labels: {
182
+ 'app.kubernetes.io/name': '${name}'
183
+ }
184
+ },
185
+ spec: {
186
+ type: 'ClusterIP',
187
+ ports: [{
188
+ port: 80,
189
+ targetPort: 80,
190
+ protocol: 'TCP',
191
+ name: 'http'
192
+ }],
193
+ selector: {
194
+ 'app.kubernetes.io/name': '${name}'
195
+ }
196
+ }
197
+ }, 'service');
198
+
199
+ return rutter;
200
+ }
201
+
202
+ // Auto-execute when run directly
203
+ if (import.meta.url === new URL(import.meta.url).href) {
204
+ const chart = createChart();
205
+ chart.write('dist');
206
+ }
207
+ `;
208
+ }
@@ -1,5 +1,4 @@
1
- import type { App } from 'cdk8s';
2
- import { Chart } from 'cdk8s';
1
+ import { App, Chart } from 'cdk8s';
3
2
  import type { ChartProps } from '../types.js';
4
3
  export declare function generateUmbrellaChart(name: string): string;
5
4
  export declare class UmbrellaChartTemplate extends Chart {
@@ -9,6 +8,18 @@ export declare class UmbrellaChartTemplate extends Chart {
9
8
  private configure;
10
9
  private _addService;
11
10
  private _addSubchart;
11
+ private _createFlexibleSubchart;
12
+ private _createFallbackSubchart;
13
+ private _handleFunctionChart;
14
+ private _handleObjectChart;
15
+ private _isRutterInstance;
16
+ private _isChartInstance;
17
+ private _processRutterInstance;
18
+ private _processChartInstance;
19
+ private _copyTemplates;
20
+ private _ensureDirectoryExists;
21
+ private _processAssets;
22
+ private _cleanupTempDir;
12
23
  private _createBasicSubchart;
13
24
  writeHelmChart(outputDir: string): void;
14
25
  }