timonel 3.1.1 → 3.1.2

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 (83) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +323 -695
  3. package/dist/cli.js +275 -15
  4. package/dist/index.d.ts +5 -1
  5. package/dist/index.js +12 -0
  6. package/dist/lib/helm.d.ts +471 -0
  7. package/dist/lib/helm.js +483 -0
  8. package/dist/lib/helmChartWriter.d.ts +157 -0
  9. package/dist/lib/helmChartWriter.js +171 -1
  10. package/dist/lib/policy/configurationLoader.d.ts +132 -0
  11. package/dist/lib/policy/configurationLoader.js +132 -0
  12. package/dist/lib/policy/errorContextGenerator.d.ts +89 -0
  13. package/dist/lib/policy/errorContextGenerator.js +99 -2
  14. package/dist/lib/policy/errors.d.ts +35 -0
  15. package/dist/lib/policy/errors.js +36 -0
  16. package/dist/lib/policy/index.d.ts +9 -0
  17. package/dist/lib/policy/index.js +16 -0
  18. package/dist/lib/policy/parallelExecutor.d.ts +90 -0
  19. package/dist/lib/policy/parallelExecutor.js +86 -3
  20. package/dist/lib/policy/pluginLoader.d.ts +92 -0
  21. package/dist/lib/policy/pluginLoader.js +92 -1
  22. package/dist/lib/policy/pluginRegistry.d.ts +54 -0
  23. package/dist/lib/policy/pluginRegistry.js +56 -0
  24. package/dist/lib/policy/policyEngine.d.ts +137 -0
  25. package/dist/lib/policy/policyEngine.js +191 -5
  26. package/dist/lib/policy/resultAggregator.d.ts +46 -0
  27. package/dist/lib/policy/resultAggregator.js +69 -1
  28. package/dist/lib/policy/resultFormatter.d.ts +88 -0
  29. package/dist/lib/policy/resultFormatter.js +101 -0
  30. package/dist/lib/policy/types.d.ts +136 -0
  31. package/dist/lib/policy/types.js +8 -0
  32. package/dist/lib/policy/validationCache.d.ts +146 -0
  33. package/dist/lib/policy/validationCache.js +142 -6
  34. package/dist/lib/resources/baseResourceProvider.d.ts +45 -0
  35. package/dist/lib/resources/baseResourceProvider.js +48 -1
  36. package/dist/lib/resources/cloud/aws/awsResources.d.ts +192 -0
  37. package/dist/lib/resources/cloud/aws/awsResources.js +163 -1
  38. package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +131 -0
  39. package/dist/lib/resources/cloud/aws/karpenterResources.js +77 -0
  40. package/dist/lib/rutter.d.ts +381 -3
  41. package/dist/lib/rutter.js +439 -28
  42. package/dist/lib/security.d.ts +123 -0
  43. package/dist/lib/security.js +162 -4
  44. package/dist/lib/templates/flexible-subchart.d.ts +52 -0
  45. package/dist/lib/templates/flexible-subchart.js +70 -0
  46. package/dist/lib/templates/umbrella-chart.d.ts +27 -0
  47. package/dist/lib/templates/umbrella-chart.js +89 -0
  48. package/dist/lib/types.d.ts +26 -0
  49. package/dist/lib/umbrella.d.ts +23 -0
  50. package/dist/lib/umbrella.js +23 -0
  51. package/dist/lib/umbrellaRutter.d.ts +75 -0
  52. package/dist/lib/umbrellaRutter.js +82 -2
  53. package/dist/lib/utils/envVarsLoader.d.ts +49 -0
  54. package/dist/lib/utils/envVarsLoader.js +53 -0
  55. package/dist/lib/utils/helmConstructSerializer.d.ts +17 -0
  56. package/dist/lib/utils/helmConstructSerializer.js +22 -0
  57. package/dist/lib/utils/helmControlStructures.d.ts +194 -0
  58. package/dist/lib/utils/helmControlStructures.js +180 -0
  59. package/dist/lib/utils/helmHelpers/envHelpers.d.ts +13 -0
  60. package/dist/lib/utils/helmHelpers/envHelpers.js +13 -0
  61. package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +13 -0
  62. package/dist/lib/utils/helmHelpers/gitopsHelpers.js +13 -0
  63. package/dist/lib/utils/helmHelpers/index.d.ts +74 -0
  64. package/dist/lib/utils/helmHelpers/index.js +85 -1
  65. package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +13 -0
  66. package/dist/lib/utils/helmHelpers/observabilityHelpers.js +13 -0
  67. package/dist/lib/utils/helmHelpers/types.d.ts +23 -0
  68. package/dist/lib/utils/helmHelpers/types.js +4 -0
  69. package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +13 -0
  70. package/dist/lib/utils/helmHelpers/validationHelpers.js +13 -0
  71. package/dist/lib/utils/helmHelpers.d.ts +62 -0
  72. package/dist/lib/utils/helmHelpers.js +77 -0
  73. package/dist/lib/utils/helmYamlSerializer.d.ts +77 -0
  74. package/dist/lib/utils/helmYamlSerializer.js +398 -21
  75. package/dist/lib/utils/logger.d.ts +153 -0
  76. package/dist/lib/utils/logger.js +170 -2
  77. package/dist/lib/utils/valuesRef.d.ts +181 -50
  78. package/dist/lib/utils/valuesRef.js +168 -170
  79. package/dist/lib/validation/inputValidator.d.ts +45 -0
  80. package/dist/lib/validation/inputValidator.js +67 -2
  81. package/dist/types/index.d.ts +34 -0
  82. package/dist/types/index.js +3 -0
  83. package/package.json +31 -38
@@ -1,22 +1,51 @@
1
+ /**
2
+ * @fileoverview Helm chart writer for generating complete Helm chart structures
3
+ * @since 2.8.0+
4
+ */
1
5
  import { type TimonelLogger } from './utils/logger.js';
2
6
  import type { HelperDefinition as ExternalHelperDefinition } from './utils/helmHelpers/types.js';
7
+ /**
8
+ * Helm template helper definition shared across helper modules.
9
+ * @since 2.8.0+ (unified with utils helper definitions in 2.12.2)
10
+ */
3
11
  export type HelperDefinition = ExternalHelperDefinition;
12
+ /**
13
+ * Metadata for a Helm chart
14
+ *
15
+ * Contains all the information needed for Chart.yaml file generation
16
+ * following Helm chart specification.
17
+ *
18
+ * @interface HelmChartMeta
19
+ * @since 2.8.0+
20
+ */
4
21
  export interface HelmChartMeta {
22
+ /** Chart name */
5
23
  name: string;
24
+ /** SemVer chart version */
6
25
  version: string;
26
+ /** Chart description */
7
27
  description?: string;
28
+ /** Underlying application version */
8
29
  appVersion?: string;
30
+ /** Chart type */
9
31
  type?: 'application' | 'library';
32
+ /** Kubernetes version constraint */
10
33
  kubeVersion?: string;
34
+ /** Chart keywords for searchability */
11
35
  keywords?: string[];
36
+ /** Chart home page URL */
12
37
  home?: string;
38
+ /** Source code URLs */
13
39
  sources?: string[];
40
+ /** Chart maintainers */
14
41
  maintainers?: {
15
42
  name: string;
16
43
  email?: string;
17
44
  url?: string;
18
45
  }[];
46
+ /** Chart icon URL */
19
47
  icon?: string;
48
+ /** Chart dependencies */
20
49
  dependencies?: Array<{
21
50
  name: string;
22
51
  version: string;
@@ -27,34 +56,162 @@ export interface HelmChartMeta {
27
56
  importValues?: unknown;
28
57
  }>;
29
58
  }
59
+ /**
60
+ * Environment-specific values mapping
61
+ *
62
+ * @interface EnvValuesMap
63
+ * @since 2.8.0+
64
+ */
30
65
  export interface EnvValuesMap {
31
66
  [env: string]: Record<string, unknown>;
32
67
  }
68
+ /**
69
+ * Synthesized Kubernetes asset for Helm chart
70
+ *
71
+ * Represents a Kubernetes manifest that will be written to the chart's
72
+ * templates or crds directory.
73
+ *
74
+ * @interface SynthAsset
75
+ * @since 2.8.0+
76
+ */
33
77
  export interface SynthAsset {
78
+ /** Filename or logical identifier */
34
79
  id: string;
80
+ /** YAML content, may contain multiple documents separated by --- */
35
81
  yaml: string;
82
+ /** Target directory inside chart */
36
83
  target?: 'templates' | 'crds';
84
+ /** If true, don't split into multiple files even with multiple docs */
37
85
  singleFile?: boolean;
38
86
  }
87
+ /**
88
+ * Options for writing a complete Helm chart
89
+ *
90
+ * @interface HelmChartWriteOptions
91
+ * @since 2.8.0+
92
+ */
39
93
  export interface HelmChartWriteOptions {
94
+ /** Output directory path (e.g., dist/charts/my-chart) */
40
95
  outDir: string;
96
+ /** Chart metadata */
41
97
  meta: HelmChartMeta;
98
+ /** Default values for values.yaml */
42
99
  defaultValues?: Record<string, unknown> | undefined;
100
+ /** Environment-specific values (e.g., { dev: {...}, prod: {...} }) */
43
101
  envValues?: EnvValuesMap | undefined;
102
+ /** Kubernetes manifests to place under templates/ */
44
103
  assets: SynthAsset[];
104
+ /** Helm helpers content for templates/_helpers.tpl */
45
105
  helpersTpl?: string | HelperDefinition[];
106
+ /** NOTES.txt content under templates/ */
46
107
  notesTpl?: string;
108
+ /** JSON schema for values validation */
47
109
  valuesSchema?: Record<string, unknown>;
110
+ /**
111
+ * Custom logger instance
112
+ * @since 2.13.0
113
+ */
48
114
  logger?: TimonelLogger;
49
115
  }
116
+ /**
117
+ * Helm chart writer for generating complete chart structures
118
+ *
119
+ * This class provides static methods to generate a complete Helm chart
120
+ * including Chart.yaml, values.yaml, templates, and auxiliary files.
121
+ *
122
+ * @class HelmChartWriter
123
+ * @since 2.8.0+
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * HelmChartWriter.write({
128
+ * outDir: './dist/my-chart',
129
+ * meta: { name: 'my-app', version: '1.0.0' },
130
+ * assets: []
131
+ * });
132
+ * ```
133
+ */
50
134
  export declare class HelmChartWriter {
135
+ /**
136
+ * Writes a complete Helm chart to the specified directory
137
+ *
138
+ * @param {HelmChartWriteOptions} opts - Chart writing options
139
+ * @throws {Error} If chart cannot be written
140
+ * @since 2.8.0+
141
+ */
51
142
  static write(opts: HelmChartWriteOptions): void;
143
+ /**
144
+ * Creates the necessary directory structure for the chart
145
+ *
146
+ * @private
147
+ * @param {string} outDir - Output directory path (must be pre-validated)
148
+ * @throws {Error} If directories cannot be created
149
+ * @since 2.8.0+
150
+ */
52
151
  private static createDirectories;
152
+ /**
153
+ * Writes the Chart.yaml file with chart metadata
154
+ *
155
+ * @private
156
+ * @param {string} outDir - Output directory path
157
+ * @param {HelmChartMeta} meta - Chart metadata
158
+ * @throws {Error} If Chart.yaml cannot be written
159
+ * @since 2.8.0+
160
+ */
53
161
  private static writeChartYaml;
162
+ /**
163
+ * Writes values.yaml and environment-specific values files
164
+ *
165
+ * @private
166
+ * @param {string} outDir - Output directory path
167
+ * @param {Record<string, unknown>} defaultValues - Default values
168
+ * @param {EnvValuesMap} envValues - Environment-specific values
169
+ * @throws {Error} If values files cannot be written
170
+ * @since 2.8.0+
171
+ */
54
172
  private static writeValuesFiles;
173
+ /**
174
+ * Writes Kubernetes manifest assets to templates directory
175
+ *
176
+ * @private
177
+ * @param {string} outDir - Output directory path
178
+ * @param {SynthAsset[]} assets - Kubernetes manifests to write
179
+ * @since 2.8.0+
180
+ */
55
181
  private static writeAssets;
182
+ /**
183
+ * Writes Helm template helpers to _helpers.tpl file
184
+ *
185
+ * @private
186
+ * @param {string} outDir - Output directory path
187
+ * @param {string | HelperDefinition[]} [helpersTpl] - Helpers content
188
+ * @since 2.8.0+
189
+ */
56
190
  private static writeHelpers;
191
+ /**
192
+ * Writes NOTES.txt file for post-install instructions
193
+ *
194
+ * @private
195
+ * @param {string} outDir - Output directory path
196
+ * @param {string} [notesTpl] - Notes content
197
+ * @since 2.8.0+
198
+ */
57
199
  private static writeNotes;
200
+ /**
201
+ * Writes values.schema.json for values validation
202
+ *
203
+ * @private
204
+ * @param {string} outDir - Output directory path
205
+ * @param {Record<string, unknown>} [valuesSchema] - JSON schema
206
+ * @since 2.8.0+
207
+ */
58
208
  private static writeSchema;
209
+ /**
210
+ * Writes .helmignore file with common ignore patterns
211
+ *
212
+ * @private
213
+ * @param {string} outDir - Output directory path
214
+ * @since 2.8.0+
215
+ */
59
216
  private static writeHelmIgnore;
60
217
  }
@@ -1,9 +1,38 @@
1
+ /**
2
+ * @fileoverview Helm chart writer for generating complete Helm chart structures
3
+ * @since 2.8.0+
4
+ */
1
5
  import * as fs from 'fs';
2
6
  import * as path from 'path';
3
7
  import { SecurityUtils } from './security.js';
4
8
  import { createLogger } from './utils/logger.js';
5
9
  import { dumpHelmAwareYaml, postProcessFieldConditionals } from './utils/helmYamlSerializer.js';
10
+ /**
11
+ * Helm chart writer for generating complete chart structures
12
+ *
13
+ * This class provides static methods to generate a complete Helm chart
14
+ * including Chart.yaml, values.yaml, templates, and auxiliary files.
15
+ *
16
+ * @class HelmChartWriter
17
+ * @since 2.8.0+
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * HelmChartWriter.write({
22
+ * outDir: './dist/my-chart',
23
+ * meta: { name: 'my-app', version: '1.0.0' },
24
+ * assets: []
25
+ * });
26
+ * ```
27
+ */
6
28
  export class HelmChartWriter {
29
+ /**
30
+ * Writes a complete Helm chart to the specified directory
31
+ *
32
+ * @param {HelmChartWriteOptions} opts - Chart writing options
33
+ * @throws {Error} If chart cannot be written
34
+ * @since 2.8.0+
35
+ */
7
36
  static write(opts) {
8
37
  const { outDir, meta, defaultValues = {}, envValues = {}, assets, helpersTpl, notesTpl, valuesSchema, logger: customLogger, } = opts;
9
38
  const logger = customLogger ?? createLogger('helm-chart-writer');
@@ -15,10 +44,13 @@ export class HelmChartWriter {
15
44
  assetCount: assets.length,
16
45
  operation: 'helm_write_start',
17
46
  });
47
+ // Validate output directory path
18
48
  const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd(), {
19
49
  allowAbsolute: true,
20
50
  });
51
+ // Create directory structure
21
52
  this.createDirectories(validatedOutDir);
53
+ // Write chart files
22
54
  this.writeChartYaml(validatedOutDir, meta);
23
55
  this.writeValuesFiles(validatedOutDir, defaultValues, envValues);
24
56
  this.writeAssets(validatedOutDir, assets);
@@ -32,12 +64,31 @@ export class HelmChartWriter {
32
64
  outputDirectory: validatedOutDir,
33
65
  operation: 'helm_write_complete',
34
66
  });
35
- timer();
67
+ timer(); // Complete timing measurement
36
68
  }
69
+ /**
70
+ * Creates the necessary directory structure for the chart
71
+ *
72
+ * @private
73
+ * @param {string} outDir - Output directory path (must be pre-validated)
74
+ * @throws {Error} If directories cannot be created
75
+ * @since 2.8.0+
76
+ */
37
77
  static createDirectories(outDir) {
38
78
  const templatesDir = SecurityUtils.validatePath(path.join(outDir, 'templates'), outDir);
79
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
39
80
  fs.mkdirSync(templatesDir, { recursive: true });
81
+ // Create crds directory only if needed later
40
82
  }
83
+ /**
84
+ * Writes the Chart.yaml file with chart metadata
85
+ *
86
+ * @private
87
+ * @param {string} outDir - Output directory path
88
+ * @param {HelmChartMeta} meta - Chart metadata
89
+ * @throws {Error} If Chart.yaml cannot be written
90
+ * @since 2.8.0+
91
+ */
41
92
  static writeChartYaml(outDir, meta) {
42
93
  const chartYaml = dumpHelmAwareYaml({
43
94
  apiVersion: 'v2',
@@ -55,20 +106,50 @@ export class HelmChartWriter {
55
106
  dependencies: meta.dependencies,
56
107
  });
57
108
  const chartYamlPath = SecurityUtils.validatePath(path.join(outDir, 'Chart.yaml'), outDir);
109
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
58
110
  fs.writeFileSync(chartYamlPath, chartYaml);
59
111
  }
112
+ /**
113
+ * Writes values.yaml and environment-specific values files
114
+ *
115
+ * @private
116
+ * @param {string} outDir - Output directory path
117
+ * @param {Record<string, unknown>} defaultValues - Default values
118
+ * @param {EnvValuesMap} envValues - Environment-specific values
119
+ * @throws {Error} If values files cannot be written
120
+ * @since 2.8.0+
121
+ */
60
122
  static writeValuesFiles(outDir, defaultValues, envValues) {
61
123
  const valuesPath = SecurityUtils.validatePath(path.join(outDir, 'values.yaml'), outDir);
124
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
62
125
  fs.writeFileSync(valuesPath, dumpHelmAwareYaml(defaultValues));
63
126
  for (const [env, values] of Object.entries(envValues)) {
127
+ // Use centralized environment name sanitization
64
128
  const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(env);
65
129
  const envValuesPath = SecurityUtils.validatePath(path.join(outDir, `values-${sanitizedEnv}.yaml`), outDir);
130
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
66
131
  fs.writeFileSync(envValuesPath, dumpHelmAwareYaml(values));
67
132
  }
68
133
  }
134
+ /**
135
+ * Writes Kubernetes manifest assets to templates directory
136
+ *
137
+ * @private
138
+ * @param {string} outDir - Output directory path
139
+ * @param {SynthAsset[]} assets - Kubernetes manifests to write
140
+ * @since 2.8.0+
141
+ */
69
142
  static writeAssets(outDir, assets) {
70
143
  writeAssets(outDir, assets);
71
144
  }
145
+ /**
146
+ * Writes Helm template helpers to _helpers.tpl file
147
+ *
148
+ * @private
149
+ * @param {string} outDir - Output directory path
150
+ * @param {string | HelperDefinition[]} [helpersTpl] - Helpers content
151
+ * @since 2.8.0+
152
+ */
72
153
  static writeHelpers(outDir, helpersTpl) {
73
154
  if (!helpersTpl)
74
155
  return;
@@ -82,22 +163,49 @@ export class HelmChartWriter {
82
163
  .join('\n');
83
164
  }
84
165
  const helpersPath = SecurityUtils.validatePath(path.join(outDir, 'templates', '_helpers.tpl'), outDir);
166
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
85
167
  fs.writeFileSync(helpersPath, content);
86
168
  }
169
+ /**
170
+ * Writes NOTES.txt file for post-install instructions
171
+ *
172
+ * @private
173
+ * @param {string} outDir - Output directory path
174
+ * @param {string} [notesTpl] - Notes content
175
+ * @since 2.8.0+
176
+ */
87
177
  static writeNotes(outDir, notesTpl) {
88
178
  if (!notesTpl)
89
179
  return;
90
180
  const notesPath = SecurityUtils.validatePath(path.join(outDir, 'templates', 'NOTES.txt'), outDir);
181
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
91
182
  fs.writeFileSync(notesPath, notesTpl.endsWith('\n') ? notesTpl : notesTpl + '\n');
92
183
  }
184
+ /**
185
+ * Writes values.schema.json for values validation
186
+ *
187
+ * @private
188
+ * @param {string} outDir - Output directory path
189
+ * @param {Record<string, unknown>} [valuesSchema] - JSON schema
190
+ * @since 2.8.0+
191
+ */
93
192
  static writeSchema(outDir, valuesSchema) {
94
193
  if (!valuesSchema)
95
194
  return;
96
195
  const schemaPath = SecurityUtils.validatePath(path.join(outDir, 'values.schema.json'), outDir);
196
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
97
197
  fs.writeFileSync(schemaPath, JSON.stringify(valuesSchema, null, 2) + '\n');
98
198
  }
199
+ /**
200
+ * Writes .helmignore file with common ignore patterns
201
+ *
202
+ * @private
203
+ * @param {string} outDir - Output directory path
204
+ * @since 2.8.0+
205
+ */
99
206
  static writeHelmIgnore(outDir) {
100
207
  const helmIgnorePath = SecurityUtils.validatePath(path.join(outDir, '.helmignore'), outDir);
208
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
101
209
  if (!fs.existsSync(helmIgnorePath)) {
102
210
  const helmIgnore = [
103
211
  '# VCS',
@@ -121,16 +229,34 @@ export class HelmChartWriter {
121
229
  '*.tgz',
122
230
  '',
123
231
  ].join('\n');
232
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Chart writer needs dynamic paths
124
233
  fs.writeFileSync(helmIgnorePath, helmIgnore);
125
234
  }
126
235
  }
127
236
  }
237
+ /**
238
+ * Splits YAML string into individual documents
239
+ *
240
+ * @private
241
+ * @param {string} yamlStr - YAML string with potential document separators
242
+ * @returns {string[]} Array of individual YAML documents
243
+ * @since 2.8.0+
244
+ */
128
245
  function splitDocs(yamlStr) {
129
246
  return yamlStr
130
247
  .split(/^---\s*$/m)
131
248
  .map((p) => p.trim())
132
249
  .filter((p) => p.length);
133
250
  }
251
+ /**
252
+ * Writes all synthesized assets to the chart directory
253
+ *
254
+ * @private
255
+ * @param {string} outDir - Output directory path
256
+ * @param {SynthAsset[]} assets - Assets to write
257
+ * @throws {Error} If asset ID contains invalid characters
258
+ * @since 2.8.0+
259
+ */
134
260
  function writeAssets(outDir, assets) {
135
261
  for (const asset of assets) {
136
262
  const targetDir = getTargetDirectory(asset.target);
@@ -149,45 +275,89 @@ function writeAssets(outDir, assets) {
149
275
  }
150
276
  }
151
277
  }
278
+ /**
279
+ * Gets the target directory for an asset
280
+ *
281
+ * @private
282
+ * @param {('templates' | 'crds')} [target] - Target directory type
283
+ * @returns {string} Directory name
284
+ * @since 2.8.0+
285
+ */
152
286
  function getTargetDirectory(target) {
153
287
  return target === 'crds' ? 'crds' : 'templates';
154
288
  }
289
+ /**
290
+ * Writes a single asset file without document splitting
291
+ *
292
+ * @private
293
+ * @param {string} outDir - Output directory path
294
+ * @param {string} targetDir - Target subdirectory
295
+ * @param {string} assetId - Asset identifier
296
+ * @param {string} yaml - YAML content
297
+ * @since 2.8.0+
298
+ */
155
299
  function writeSingleAssetFile(outDir, targetDir, directorySegments, fileBaseName, yaml) {
156
300
  const chartSubdir = path.join(outDir, targetDir, ...directorySegments);
157
301
  SecurityUtils.validatePath(chartSubdir, outDir);
302
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
158
303
  fs.mkdirSync(chartSubdir, { recursive: true });
304
+ // Validate fileBaseName to prevent path traversal
159
305
  if (fileBaseName.includes('..') || fileBaseName.includes('/') || fileBaseName.includes('\\')) {
160
306
  throw new Error(`Invalid fileBaseName: ${fileBaseName}`);
161
307
  }
162
308
  const filename = `${fileBaseName}.yaml`;
309
+ // Validate filename to prevent path traversal
163
310
  if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
164
311
  throw new Error(`Invalid filename: ${filename}`);
165
312
  }
166
313
  const absolutePath = path.join(chartSubdir, filename);
167
314
  SecurityUtils.validatePath(absolutePath, outDir);
315
+ // Post-process YAML to transform field-level conditionals
168
316
  const processedYaml = postProcessFieldConditionals(yaml);
317
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
169
318
  fs.writeFileSync(absolutePath, processedYaml.endsWith('\n') ? processedYaml : `${processedYaml}\n`);
170
319
  }
320
+ /**
321
+ * Writes multiple asset files by splitting YAML documents
322
+ *
323
+ * @private
324
+ * @param {string} outDir - Output directory path
325
+ * @param {string} targetDir - Target subdirectory
326
+ * @param {string} assetId - Asset identifier
327
+ * @param {string} yaml - YAML content with potential multiple documents
328
+ * @since 2.8.0+
329
+ */
171
330
  function writeMultipleAssetFiles(outDir, targetDir, directorySegments, fileBaseName, yaml) {
172
331
  const chartSubdir = path.join(outDir, targetDir, ...directorySegments);
173
332
  SecurityUtils.validatePath(chartSubdir, outDir);
333
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Chart writer needs dynamic paths
174
334
  fs.mkdirSync(chartSubdir, { recursive: true });
335
+ // Post-process YAML to transform field-level conditionals
175
336
  const processedYaml = postProcessFieldConditionals(yaml);
176
337
  const parts = splitDocs(processedYaml);
177
338
  parts.forEach((doc, index) => {
339
+ // Validate fileBaseName to prevent path traversal
178
340
  if (fileBaseName.includes('..') || fileBaseName.includes('/') || fileBaseName.includes('\\')) {
179
341
  throw new Error(`Invalid fileBaseName: ${fileBaseName}`);
180
342
  }
181
343
  const suffix = parts.length > 1 ? `-${index + 1}` : '';
182
344
  const filename = `${fileBaseName}${suffix}.yaml`;
345
+ // Validate filename to prevent path traversal
183
346
  if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
184
347
  throw new Error(`Invalid filename: ${filename}`);
185
348
  }
186
349
  const absolutePath = path.join(chartSubdir, filename);
187
350
  SecurityUtils.validatePath(absolutePath, outDir);
351
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
188
352
  fs.writeFileSync(absolutePath, doc.endsWith('\n') ? doc : `${doc}\n`);
189
353
  });
190
354
  }
355
+ /**
356
+ * Validates an asset identifier and converts it into directory segments and file basename.
357
+ * @param assetId - Identifier provided by the synthesizer.
358
+ * @returns Normalized directory segments and filename stem.
359
+ * @since 2.12.2 Preserves human-friendly asset identifiers without compromising safety.
360
+ */
191
361
  function resolveAssetPath(assetId) {
192
362
  if (!assetId || typeof assetId !== 'string') {
193
363
  throw new Error('Asset ID must be a non-empty string');
@@ -1,46 +1,178 @@
1
+ /**
2
+ * Configuration Loader
3
+ *
4
+ * This module provides configuration loading and validation for policy plugins.
5
+ * It supports environment-specific configuration, schema validation, and
6
+ * configuration inheritance.
7
+ *
8
+ * @since 3.0.0
9
+ */
1
10
  import type { JSONSchema, PolicyPlugin, ConfigurationLoaderOptions } from './types.js';
11
+ /**
12
+ * Configuration source types
13
+ */
2
14
  export type ConfigurationSource = 'environment' | 'file' | 'inline' | 'default';
15
+ /**
16
+ * Configuration entry with metadata
17
+ */
3
18
  export interface ConfigurationEntry {
19
+ /** Configuration value */
4
20
  readonly value: unknown;
21
+ /** Source of the configuration */
5
22
  readonly source: ConfigurationSource;
23
+ /** Environment this configuration applies to */
6
24
  readonly environment?: string;
25
+ /** Priority for configuration merging (higher wins) */
7
26
  readonly priority: number;
27
+ /** Validation schema for this configuration */
8
28
  readonly schema?: JSONSchema;
9
29
  }
30
+ /**
31
+ * Plugin configuration with validation
32
+ */
10
33
  export interface PluginConfiguration {
34
+ /** Plugin name */
11
35
  readonly pluginName: string;
36
+ /** Merged configuration value */
12
37
  readonly config: Record<string, unknown>;
38
+ /** Configuration entries that were merged */
13
39
  readonly entries: ConfigurationEntry[];
40
+ /** Whether configuration was validated */
14
41
  readonly validated: boolean;
42
+ /** Validation errors if any */
15
43
  readonly validationErrors?: string[];
16
44
  }
45
+ /**
46
+ * Environment-specific configuration
47
+ */
17
48
  export interface EnvironmentConfiguration {
49
+ /** Environment name (e.g., 'development', 'production') */
18
50
  readonly environment: string;
51
+ /** Plugin configurations for this environment */
19
52
  readonly plugins: Record<string, unknown>;
53
+ /** Global configuration options */
20
54
  readonly global?: Record<string, unknown>;
21
55
  }
56
+ /**
57
+ * Configuration loader for policy plugins
58
+ */
22
59
  export declare class ConfigurationLoader {
23
60
  private readonly options;
24
61
  private readonly logger;
25
62
  private readonly configurations;
26
63
  private readonly environmentConfigs;
27
64
  constructor(options?: ConfigurationLoaderOptions);
65
+ /**
66
+ * Loads configuration for a plugin
67
+ * @param plugin - Plugin to load configuration for
68
+ * @param environment - Target environment
69
+ * @param inlineConfig - Inline configuration to merge
70
+ * @returns Plugin configuration
71
+ */
28
72
  loadPluginConfiguration(plugin: PolicyPlugin, environment?: string, inlineConfig?: Record<string, unknown>): Promise<PluginConfiguration>;
73
+ /**
74
+ * Collects configuration entries from all sources
75
+ * @private
76
+ */
29
77
  private collectConfigurationEntries;
78
+ /**
79
+ * Validates plugin configuration
80
+ * @private
81
+ */
30
82
  private validatePluginConfiguration;
83
+ /**
84
+ * Loads environment-specific configuration
85
+ * @param environment - Environment name
86
+ * @returns Environment configuration
87
+ */
31
88
  loadEnvironmentConfiguration(environment: string): Promise<EnvironmentConfiguration | undefined>;
89
+ /**
90
+ * Validates configuration against a JSON schema
91
+ * @param config - Configuration to validate
92
+ * @param schema - JSON schema to validate against
93
+ * @param pluginName - Plugin name for error reporting
94
+ * @throws {PluginConfigurationError} When validation fails
95
+ */
32
96
  validateConfiguration(config: Record<string, unknown>, schema: JSONSchema, pluginName: string): void;
97
+ /**
98
+ * Adds a configuration entry for a plugin
99
+ * @param pluginName - Plugin name
100
+ * @param entry - Configuration entry to add
101
+ */
33
102
  addConfigurationEntry(pluginName: string, entry: ConfigurationEntry): void;
103
+ /**
104
+ * Gets all configuration entries for a plugin
105
+ * @param pluginName - Plugin name
106
+ * @returns Array of configuration entries
107
+ */
34
108
  getConfigurationEntries(pluginName: string): ConfigurationEntry[];
109
+ /**
110
+ * Clears all cached configurations
111
+ */
35
112
  clearCache(): void;
113
+ /**
114
+ * Loads file-based configuration for a plugin
115
+ * @param _pluginName - Plugin name (unused)
116
+ * @param _environment - Target environment (unused)
117
+ * @returns Configuration object or undefined
118
+ * @private
119
+ */
36
120
  private loadFileConfiguration;
121
+ /**
122
+ * Loads environment variable configuration for a plugin
123
+ * @param pluginName - Plugin name
124
+ * @returns Configuration object or undefined
125
+ * @private
126
+ */
37
127
  private loadEnvironmentVariableConfiguration;
128
+ /**
129
+ * Loads environment configuration from files
130
+ * @param _environment - Environment name (unused)
131
+ * @returns Environment configuration or undefined
132
+ * @private
133
+ */
38
134
  private loadEnvironmentFromFiles;
135
+ /**
136
+ * Merges configuration entries by priority
137
+ * @param entries - Configuration entries to merge
138
+ * @returns Merged configuration
139
+ * @private
140
+ */
39
141
  private mergeConfigurations;
142
+ /**
143
+ * Parses environment variable value to appropriate type
144
+ * @param value - Environment variable value
145
+ * @returns Parsed value
146
+ * @private
147
+ */
40
148
  private parseEnvironmentValue;
149
+ /**
150
+ * Validates a configuration object against a JSON schema
151
+ * @param config - Configuration to validate
152
+ * @param schema - JSON schema
153
+ * @param path - Current path for error reporting
154
+ * @throws {Error} When validation fails
155
+ * @private
156
+ */
41
157
  private validateAgainstSchema;
158
+ /**
159
+ * Validates basic type constraints
160
+ * @private
161
+ */
42
162
  private validateBasicType;
163
+ /**
164
+ * Validates object properties
165
+ * @private
166
+ */
43
167
  private validateObjectProperties;
168
+ /**
169
+ * Validates required properties
170
+ * @private
171
+ */
44
172
  private validateRequiredProperties;
173
+ /**
174
+ * Validates each property in the object
175
+ * @private
176
+ */
45
177
  private validateEachProperty;
46
178
  }