timonel 2.10.2 → 2.12.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 +17 -0
  2. package/README.md +8 -54
  3. package/dist/cli.js +65 -52
  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 +21 -11
  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.2",
4
+ "version": "2.12.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": {
@@ -83,26 +87,27 @@
83
87
  ]
84
88
  },
85
89
  "dependencies": {
86
- "cdk8s": "^2.70.15",
87
- "cdk8s-plus-33": "^2.3.6",
90
+ "cdk8s": "^2.70.16",
91
+ "cdk8s-plus-33": "^2.3.8",
88
92
  "constructs": "^10.4.2",
89
93
  "handlebars": "^4.7.8",
90
94
  "js-yaml": "^4.1.0",
91
- "timonel": "^2.10.0",
95
+ "pino": "^9.11.0",
96
+ "pino-pretty": "^13.1.1",
92
97
  "ts-node": "^10.9.2",
93
98
  "yaml": "^2.8.1"
94
99
  },
95
100
  "devDependencies": {
96
- "@commitlint/cli": "^19.8.1",
97
- "@commitlint/config-conventional": "^19.8.1",
101
+ "@commitlint/cli": "^20.0.0",
102
+ "@commitlint/config-conventional": "^20.0.0",
98
103
  "@eslint/js": "^9.36.0",
99
104
  "@semantic-release/changelog": "^6.0.3",
100
105
  "@semantic-release/exec": "^7.1.0",
101
106
  "@semantic-release/git": "^10.0.1",
102
107
  "@types/js-yaml": "^4.0.9",
103
108
  "@types/node": "^24.5.2",
104
- "@typescript-eslint/eslint-plugin": "^8.44.0",
105
- "@typescript-eslint/parser": "^8.44.0",
109
+ "@typescript-eslint/eslint-plugin": "^8.44.1",
110
+ "@typescript-eslint/parser": "^8.44.1",
106
111
  "@vitest/coverage-v8": "^3.2.4",
107
112
  "conventional-changelog-cli": "^5.0.0",
108
113
  "eslint": "^9.36.0",
@@ -113,12 +118,12 @@
113
118
  "eslint-plugin-sonarjs": "^3.0.5",
114
119
  "eslint-plugin-unused-imports": "^4.2.0",
115
120
  "husky": "^9.1.7",
116
- "lint-staged": "^16.1.6",
121
+ "lint-staged": "^16.2.1",
117
122
  "markdownlint": "^0.38.0",
118
123
  "markdownlint-cli": "^0.45.0",
119
124
  "prettier": "^3.6.2",
120
125
  "semantic-release": "^24.2.9",
121
- "tsx": "^4.20.5",
126
+ "tsx": "^4.20.6",
122
127
  "typescript": "^5.9.2",
123
128
  "vitest": "^3.2.4"
124
129
  },
@@ -133,5 +138,10 @@
133
138
  "access": "public",
134
139
  "provenance": true
135
140
  },
136
- "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
+ }
137
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;