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
@@ -0,0 +1,418 @@
1
+ import pino from 'pino';
2
+ import { SecurityUtils } from '../security.js';
3
+ export var LogLevel;
4
+ (function (LogLevel) {
5
+ LogLevel[LogLevel["FATAL"] = 60] = "FATAL";
6
+ LogLevel[LogLevel["ERROR"] = 50] = "ERROR";
7
+ LogLevel[LogLevel["WARN"] = 40] = "WARN";
8
+ LogLevel[LogLevel["INFO"] = 30] = "INFO";
9
+ LogLevel[LogLevel["DEBUG"] = 20] = "DEBUG";
10
+ LogLevel[LogLevel["TRACE"] = 10] = "TRACE";
11
+ })(LogLevel || (LogLevel = {}));
12
+ export class TimonelLogger {
13
+ constructor(config = {}) {
14
+ this.config = {
15
+ level: LogLevel.INFO,
16
+ silent: false,
17
+ service: 'timonel',
18
+ environment: process.env.NODE_ENV || 'development',
19
+ prettyPrint: process.env.NODE_ENV !== 'production',
20
+ ...config,
21
+ };
22
+ const pinoOptions = {
23
+ level: this.mapLogLevel(this.config.level),
24
+ enabled: !this.config.silent,
25
+ base: {
26
+ service: this.config.service,
27
+ environment: this.config.environment,
28
+ ...this.config.base,
29
+ },
30
+ timestamp: pino.stdTimeFunctions.isoTime,
31
+ redact: {
32
+ paths: [
33
+ 'password',
34
+ 'passphrase',
35
+ 'secret',
36
+ 'secrets',
37
+ 'token',
38
+ 'accessToken',
39
+ 'refreshToken',
40
+ 'idToken',
41
+ 'apiKey',
42
+ 'api_key',
43
+ 'clientSecret',
44
+ 'client_secret',
45
+ 'privateKey',
46
+ 'private_key',
47
+ 'sshKey',
48
+ 'ssh_key',
49
+ 'credentials',
50
+ 'authorization',
51
+ 'sessionId',
52
+ 'session_id',
53
+ 'sessionToken',
54
+ 'session_token',
55
+ 'bearer',
56
+ 'auth',
57
+ 'authToken',
58
+ 'auth_token',
59
+ 'jwt',
60
+ 'jwtToken',
61
+ 'jwt_token',
62
+ 'oauth',
63
+ 'oauthToken',
64
+ 'oauth_token',
65
+ 'key',
66
+ 'keys',
67
+ 'cert',
68
+ 'certificate',
69
+ 'certificates',
70
+ 'pem',
71
+ 'p12',
72
+ 'pfx',
73
+ '*.password',
74
+ '*.passphrase',
75
+ '*.secret',
76
+ '*.secrets',
77
+ '*.token',
78
+ '*.accessToken',
79
+ '*.refreshToken',
80
+ '*.idToken',
81
+ '*.apiKey',
82
+ '*.api_key',
83
+ '*.clientSecret',
84
+ '*.client_secret',
85
+ '*.privateKey',
86
+ '*.private_key',
87
+ '*.sshKey',
88
+ '*.ssh_key',
89
+ '*.credentials',
90
+ '*.authorization',
91
+ '*.sessionId',
92
+ '*.session_id',
93
+ '*.sessionToken',
94
+ '*.session_token',
95
+ '*.bearer',
96
+ '*.auth',
97
+ '*.authToken',
98
+ '*.auth_token',
99
+ '*.jwt',
100
+ '*.jwtToken',
101
+ '*.jwt_token',
102
+ '*.oauth',
103
+ '*.oauthToken',
104
+ '*.oauth_token',
105
+ '*.key',
106
+ '*.keys',
107
+ '*.cert',
108
+ '*.certificate',
109
+ '*.certificates',
110
+ '*.pem',
111
+ '*.p12',
112
+ '*.pfx',
113
+ 'req.headers.authorization',
114
+ 'req.headers.cookie',
115
+ 'req.headers.cookies',
116
+ 'req.headers["set-cookie"]',
117
+ 'req.headers["authorization"]',
118
+ 'req.headers["x-api-key"]',
119
+ 'req.headers["x-auth-token"]',
120
+ 'res.headers["set-cookie"]',
121
+ 'res.headers["authorization"]',
122
+ 'body.password',
123
+ 'body.token',
124
+ 'body.apiKey',
125
+ 'body.api_key',
126
+ 'body.secret',
127
+ 'query.password',
128
+ 'query.token',
129
+ 'query.apiKey',
130
+ 'query.secret',
131
+ 'config.password',
132
+ 'config.secret',
133
+ 'config.clientSecret',
134
+ 'config.privateKey',
135
+ 'config.apiKey',
136
+ ],
137
+ censor: '[REDACTED]',
138
+ },
139
+ serializers: {
140
+ err: pino.stdSerializers.err,
141
+ error: pino.stdSerializers.err,
142
+ req: this.requestSerializer,
143
+ res: this.responseSerializer,
144
+ },
145
+ };
146
+ if (this.config.prettyPrint && this.config.environment === 'development') {
147
+ this.logger = pino({
148
+ ...pinoOptions,
149
+ transport: {
150
+ target: 'pino-pretty',
151
+ options: {
152
+ colorize: true,
153
+ translateTime: 'HH:MM:ss.l',
154
+ ignore: 'pid,hostname',
155
+ messageFormat: '{service}[{component}]: {msg}',
156
+ },
157
+ },
158
+ });
159
+ }
160
+ else {
161
+ this.logger = pino(pinoOptions);
162
+ }
163
+ }
164
+ mapLogLevel(level) {
165
+ switch (level) {
166
+ case LogLevel.FATAL:
167
+ return 'fatal';
168
+ case LogLevel.ERROR:
169
+ return 'error';
170
+ case LogLevel.WARN:
171
+ return 'warn';
172
+ case LogLevel.INFO:
173
+ return 'info';
174
+ case LogLevel.DEBUG:
175
+ return 'debug';
176
+ case LogLevel.TRACE:
177
+ return 'trace';
178
+ default:
179
+ return 'info';
180
+ }
181
+ }
182
+ requestSerializer(req) {
183
+ if (!req || typeof req !== 'object')
184
+ return {};
185
+ const request = req;
186
+ return {
187
+ method: request.method,
188
+ url: SecurityUtils.sanitizeLogMessage(request.url || ''),
189
+ headers: {
190
+ 'user-agent': request.headers?.['user-agent'],
191
+ 'content-type': request.headers?.['content-type'],
192
+ },
193
+ remoteAddress: request.remoteAddress,
194
+ remotePort: request.remotePort,
195
+ };
196
+ }
197
+ responseSerializer(res) {
198
+ if (!res || typeof res !== 'object')
199
+ return {};
200
+ const response = res;
201
+ return {
202
+ statusCode: response.statusCode,
203
+ headers: {
204
+ 'content-type': response.headers?.['content-type'],
205
+ 'content-length': response.headers?.['content-length'],
206
+ },
207
+ };
208
+ }
209
+ fatal(message, context) {
210
+ this.log(LogLevel.FATAL, message, context);
211
+ }
212
+ error(message, context) {
213
+ this.log(LogLevel.ERROR, message, context);
214
+ }
215
+ warn(message, context) {
216
+ this.log(LogLevel.WARN, message, context);
217
+ }
218
+ info(message, context) {
219
+ this.log(LogLevel.INFO, message, context);
220
+ }
221
+ debug(message, context) {
222
+ this.log(LogLevel.DEBUG, message, context);
223
+ }
224
+ trace(message, context) {
225
+ this.log(LogLevel.TRACE, message, context);
226
+ }
227
+ log(level, message, context) {
228
+ if (this.config.silent || level < this.config.level) {
229
+ return;
230
+ }
231
+ const sanitizedMessage = SecurityUtils.sanitizeLogMessage(message);
232
+ const sanitizedContext = context ? this.sanitizeContext(context) : {};
233
+ const logEntry = {
234
+ ...sanitizedContext,
235
+ timestamp: new Date().toISOString(),
236
+ };
237
+ switch (level) {
238
+ case LogLevel.FATAL:
239
+ this.logger.fatal(logEntry, sanitizedMessage);
240
+ break;
241
+ case LogLevel.ERROR:
242
+ this.logger.error(logEntry, sanitizedMessage);
243
+ break;
244
+ case LogLevel.WARN:
245
+ this.logger.warn(logEntry, sanitizedMessage);
246
+ break;
247
+ case LogLevel.INFO:
248
+ this.logger.info(logEntry, sanitizedMessage);
249
+ break;
250
+ case LogLevel.DEBUG:
251
+ this.logger.debug(logEntry, sanitizedMessage);
252
+ break;
253
+ case LogLevel.TRACE:
254
+ this.logger.trace(logEntry, sanitizedMessage);
255
+ break;
256
+ }
257
+ }
258
+ sanitizeContext(context) {
259
+ const sanitized = {};
260
+ for (const [key, value] of Object.entries(context)) {
261
+ if (TimonelLogger.SENSITIVE_FIELDS.has(key.toLowerCase())) {
262
+ sanitized[key] = '[REDACTED]';
263
+ }
264
+ else if (typeof value === 'string') {
265
+ sanitized[key] = SecurityUtils.sanitizeLogMessage(value);
266
+ }
267
+ else if (value && typeof value === 'object') {
268
+ sanitized[key] = this.sanitizeNestedObject(value, 2);
269
+ }
270
+ else {
271
+ sanitized[key] = value;
272
+ }
273
+ }
274
+ return sanitized;
275
+ }
276
+ sanitizeNestedObject(obj, depth) {
277
+ if (depth <= 0 || !obj || typeof obj !== 'object') {
278
+ return obj;
279
+ }
280
+ const sanitized = {};
281
+ for (const [key, value] of Object.entries(obj)) {
282
+ if (typeof value === 'string') {
283
+ sanitized[key] = SecurityUtils.sanitizeLogMessage(value);
284
+ }
285
+ else if (value && typeof value === 'object') {
286
+ sanitized[key] = this.sanitizeNestedObject(value, depth - 1);
287
+ }
288
+ else {
289
+ sanitized[key] = value;
290
+ }
291
+ }
292
+ return sanitized;
293
+ }
294
+ child(context) {
295
+ const sanitizedContext = this.sanitizeContext(context);
296
+ const childLogger = new TimonelLogger(this.config);
297
+ Object.defineProperty(childLogger, 'logger', {
298
+ value: this.logger.child(sanitizedContext),
299
+ writable: false,
300
+ enumerable: false,
301
+ configurable: false,
302
+ });
303
+ return childLogger;
304
+ }
305
+ time(operation) {
306
+ const start = process.hrtime.bigint();
307
+ return () => {
308
+ const end = process.hrtime.bigint();
309
+ const duration = Number(end - start) / 1000000;
310
+ this.info('Operation completed', {
311
+ operation: SecurityUtils.sanitizeLogMessage(operation),
312
+ duration_ms: Math.round(duration * 100) / 100,
313
+ performance: 'timing',
314
+ });
315
+ };
316
+ }
317
+ setLevel(level) {
318
+ this.config.level = level;
319
+ this.logger.level = this.mapLogLevel(level);
320
+ }
321
+ setSilent(silent) {
322
+ this.config.silent = silent;
323
+ if (silent) {
324
+ this.logger.level = 'silent';
325
+ }
326
+ else {
327
+ this.logger.level = this.mapLogLevel(this.config.level);
328
+ }
329
+ }
330
+ async flush() {
331
+ return new Promise((resolve) => {
332
+ if (typeof this.logger.flush === 'function') {
333
+ this.logger.flush(() => resolve());
334
+ }
335
+ else {
336
+ resolve();
337
+ }
338
+ });
339
+ }
340
+ }
341
+ TimonelLogger.SENSITIVE_FIELDS = new Set([
342
+ 'password',
343
+ 'passphrase',
344
+ 'secret',
345
+ 'secrets',
346
+ 'token',
347
+ 'accessToken',
348
+ 'refreshToken',
349
+ 'idToken',
350
+ 'apiKey',
351
+ 'api_key',
352
+ 'clientSecret',
353
+ 'client_secret',
354
+ 'privateKey',
355
+ 'private_key',
356
+ 'sshKey',
357
+ 'ssh_key',
358
+ 'credentials',
359
+ 'authorization',
360
+ 'sessionId',
361
+ 'session_id',
362
+ 'sessionToken',
363
+ 'session_token',
364
+ 'bearer',
365
+ 'auth',
366
+ 'authToken',
367
+ 'auth_token',
368
+ 'jwt',
369
+ 'jwtToken',
370
+ 'jwt_token',
371
+ 'oauth',
372
+ 'oauthToken',
373
+ 'oauth_token',
374
+ 'key',
375
+ 'keys',
376
+ 'cert',
377
+ 'certificate',
378
+ 'certificates',
379
+ 'pem',
380
+ 'p12',
381
+ 'pfx',
382
+ ]);
383
+ export const logger = new TimonelLogger({
384
+ level: getLogLevelFromEnv(),
385
+ silent: process.env.TIMONEL_SILENT === 'true',
386
+ service: 'timonel',
387
+ environment: process.env.NODE_ENV || 'development',
388
+ });
389
+ export function createLogger(component, additionalConfig) {
390
+ return new TimonelLogger({
391
+ ...additionalConfig,
392
+ service: 'timonel',
393
+ base: {
394
+ component: SecurityUtils.sanitizeLogMessage(component),
395
+ ...additionalConfig?.base,
396
+ },
397
+ });
398
+ }
399
+ function getLogLevelFromEnv() {
400
+ const envLevel = process.env.LOG_LEVEL?.toUpperCase();
401
+ switch (envLevel) {
402
+ case 'FATAL':
403
+ return LogLevel.FATAL;
404
+ case 'ERROR':
405
+ return LogLevel.ERROR;
406
+ case 'WARN':
407
+ case 'WARNING':
408
+ return LogLevel.WARN;
409
+ case 'INFO':
410
+ return LogLevel.INFO;
411
+ case 'DEBUG':
412
+ return LogLevel.DEBUG;
413
+ case 'TRACE':
414
+ return LogLevel.TRACE;
415
+ default:
416
+ return process.env.NODE_ENV === 'production' ? LogLevel.INFO : LogLevel.DEBUG;
417
+ }
418
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "timonel",
3
3
  "type": "module",
4
- "version": "2.10.1",
4
+ "version": "2.11.0",
5
5
  "description": "Timonel: programmatic Helm chart generator using cdk8s (TypeScript)",
6
6
  "bin": {
7
7
  "timonel": "dist/cli.js",
@@ -17,6 +17,10 @@
17
17
  "./lib/helm": {
18
18
  "types": "./dist/lib/helm.d.ts",
19
19
  "default": "./dist/lib/helm.js"
20
+ },
21
+ "./lib/utils/logger": {
22
+ "types": "./dist/lib/utils/logger.d.ts",
23
+ "default": "./dist/lib/utils/logger.js"
20
24
  }
21
25
  },
22
26
  "scripts": {
@@ -37,7 +41,9 @@
37
41
  "deps:update": "pnpm update --latest",
38
42
  "md:lint": "markdownlint \"**/*.md\"",
39
43
  "md:fix": "markdownlint --fix \"**/*.md\"",
40
- "test:integration": "vitest run --config vitest.config.ts",
44
+ "test": "vitest run --config vitest.config.ts",
45
+ "test:unit": "vitest run --config vitest.config.ts",
46
+ "test:integration": "vitest run --config vitest.integration.config.ts",
41
47
  "test:watch": "vitest",
42
48
  "test:coverage": "vitest run --coverage",
43
49
  "ci:check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build",
@@ -81,26 +87,27 @@
81
87
  ]
82
88
  },
83
89
  "dependencies": {
84
- "cdk8s": "^2.70.15",
85
- "cdk8s-plus-33": "^2.3.6",
90
+ "cdk8s": "^2.70.16",
91
+ "cdk8s-plus-33": "^2.3.8",
86
92
  "constructs": "^10.4.2",
87
93
  "handlebars": "^4.7.8",
88
94
  "js-yaml": "^4.1.0",
89
- "timonel": "^2.10.0",
95
+ "pino": "^9.11.0",
96
+ "pino-pretty": "^13.1.1",
90
97
  "ts-node": "^10.9.2",
91
98
  "yaml": "^2.8.1"
92
99
  },
93
100
  "devDependencies": {
94
- "@commitlint/cli": "^19.8.1",
95
- "@commitlint/config-conventional": "^19.8.1",
101
+ "@commitlint/cli": "^20.0.0",
102
+ "@commitlint/config-conventional": "^20.0.0",
96
103
  "@eslint/js": "^9.36.0",
97
104
  "@semantic-release/changelog": "^6.0.3",
98
105
  "@semantic-release/exec": "^7.1.0",
99
106
  "@semantic-release/git": "^10.0.1",
100
107
  "@types/js-yaml": "^4.0.9",
101
108
  "@types/node": "^24.5.2",
102
- "@typescript-eslint/eslint-plugin": "^8.44.0",
103
- "@typescript-eslint/parser": "^8.44.0",
109
+ "@typescript-eslint/eslint-plugin": "^8.44.1",
110
+ "@typescript-eslint/parser": "^8.44.1",
104
111
  "@vitest/coverage-v8": "^3.2.4",
105
112
  "conventional-changelog-cli": "^5.0.0",
106
113
  "eslint": "^9.36.0",
@@ -111,12 +118,12 @@
111
118
  "eslint-plugin-sonarjs": "^3.0.5",
112
119
  "eslint-plugin-unused-imports": "^4.2.0",
113
120
  "husky": "^9.1.7",
114
- "lint-staged": "^16.1.6",
121
+ "lint-staged": "^16.2.1",
115
122
  "markdownlint": "^0.38.0",
116
123
  "markdownlint-cli": "^0.45.0",
117
124
  "prettier": "^3.6.2",
118
125
  "semantic-release": "^24.2.9",
119
- "tsx": "^4.20.5",
126
+ "tsx": "^4.20.6",
120
127
  "typescript": "^5.9.2",
121
128
  "vitest": "^3.2.4"
122
129
  },
@@ -131,5 +138,10 @@
131
138
  "access": "public",
132
139
  "provenance": true
133
140
  },
134
- "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
141
+ "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b",
142
+ "pnpm": {
143
+ "overrides": {
144
+ "@conventional-changelog/git-client": ">=2.0.0"
145
+ }
146
+ }
135
147
  }
@@ -1,19 +0,0 @@
1
- import { Chart } from 'cdk8s';
2
- import type { ChartProps } from 'cdk8s';
3
- import type { Construct } from 'constructs';
4
- export interface BasicChartProps extends ChartProps {
5
- appName?: string;
6
- image?: string;
7
- port?: number;
8
- replicas?: number;
9
- createNamespace?: boolean;
10
- }
11
- export declare class BasicChart extends Chart {
12
- private rutter;
13
- private props;
14
- constructor(scope: Construct, id: string, props?: BasicChartProps);
15
- private initializeRutter;
16
- writeHelmChart(outDir: string): void;
17
- private generateKubernetesManifests;
18
- }
19
- export declare function generateBasicChart(appName?: string): string;