timonel 2.14.0-beta.1 → 3.0.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.
@@ -180,19 +180,24 @@ export class TimonelLogger {
180
180
  }
181
181
  }
182
182
  requestSerializer(req) {
183
- if (!req || typeof req !== 'object')
183
+ try {
184
+ if (!req || typeof req !== 'object')
185
+ return {};
186
+ const request = req;
187
+ return {
188
+ method: request.method,
189
+ url: SecurityUtils.sanitizeLogMessage(request.url || ''),
190
+ headers: {
191
+ 'user-agent': request.headers?.['user-agent'],
192
+ 'content-type': request.headers?.['content-type'],
193
+ },
194
+ remoteAddress: request.remoteAddress,
195
+ remotePort: request.remotePort,
196
+ };
197
+ }
198
+ catch {
184
199
  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
- };
200
+ }
196
201
  }
197
202
  responseSerializer(res) {
198
203
  if (!res || typeof res !== 'object')
@@ -256,40 +261,50 @@ export class TimonelLogger {
256
261
  }
257
262
  }
258
263
  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;
264
+ try {
265
+ const sanitized = {};
266
+ for (const [key, value] of Object.entries(context)) {
267
+ if (TimonelLogger.SENSITIVE_FIELDS.has(key.toLowerCase())) {
268
+ sanitized[key] = '[REDACTED]';
269
+ }
270
+ else if (typeof value === 'string') {
271
+ sanitized[key] = SecurityUtils.sanitizeLogMessage(value);
272
+ }
273
+ else if (value && typeof value === 'object') {
274
+ sanitized[key] = this.sanitizeNestedObject(value, 2);
275
+ }
276
+ else {
277
+ sanitized[key] = value;
278
+ }
272
279
  }
280
+ return sanitized;
281
+ }
282
+ catch {
283
+ return {};
273
284
  }
274
- return sanitized;
275
285
  }
276
286
  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);
287
+ try {
288
+ if (depth <= 0 || !obj || typeof obj !== 'object') {
289
+ return obj;
284
290
  }
285
- else if (value && typeof value === 'object') {
286
- sanitized[key] = this.sanitizeNestedObject(value, depth - 1);
287
- }
288
- else {
289
- sanitized[key] = value;
291
+ const sanitized = {};
292
+ for (const [key, value] of Object.entries(obj)) {
293
+ if (typeof value === 'string') {
294
+ sanitized[key] = SecurityUtils.sanitizeLogMessage(value);
295
+ }
296
+ else if (value && typeof value === 'object') {
297
+ sanitized[key] = this.sanitizeNestedObject(value, depth - 1);
298
+ }
299
+ else {
300
+ sanitized[key] = value;
301
+ }
290
302
  }
303
+ return sanitized;
304
+ }
305
+ catch {
306
+ return obj;
291
307
  }
292
- return sanitized;
293
308
  }
294
309
  child(context) {
295
310
  const sanitizedContext = this.sanitizeContext(context);
@@ -0,0 +1,102 @@
1
+ import { type HelmExpression } from './helmControlStructures.js';
2
+ declare const HELM_VALUE_SYMBOL: unique symbol;
3
+ export interface HelmValue<T = unknown> {
4
+ [HELM_VALUE_SYMBOL]: true;
5
+ __path: string;
6
+ __type?: T;
7
+ eq(value: HelmValue | string | number | boolean): HelmCondition;
8
+ ne(value: HelmValue | string | number | boolean): HelmCondition;
9
+ gt(value: HelmValue | number): HelmCondition;
10
+ ge(value: HelmValue | number): HelmCondition;
11
+ lt(value: HelmValue | number): HelmCondition;
12
+ le(value: HelmValue | number): HelmCondition;
13
+ not(): HelmCondition;
14
+ and(other: HelmCondition): HelmCondition;
15
+ or(other: HelmCondition): HelmCondition;
16
+ default(defaultValue: HelmValue | string | number | boolean): HelmValue<T>;
17
+ quote(): HelmValue<string>;
18
+ upper(): HelmValue<string>;
19
+ lower(): HelmValue<string>;
20
+ title(): HelmValue<string>;
21
+ trim(): HelmValue<string>;
22
+ trimPrefix(prefix: string): HelmValue<string>;
23
+ trimSuffix(suffix: string): HelmValue<string>;
24
+ replace(old: string, newStr: string): HelmValue<string>;
25
+ contains(substr: string): HelmCondition;
26
+ hasPrefix(prefix: string): HelmCondition;
27
+ hasSuffix(suffix: string): HelmCondition;
28
+ trunc(length: number): HelmValue<string>;
29
+ kindIs(kind: 'string' | 'slice' | 'map' | 'bool' | 'int' | 'float'): HelmCondition;
30
+ hasKey(key: string): HelmCondition;
31
+ toYaml(): HelmValue<string>;
32
+ toJson(): HelmValue<string>;
33
+ nindent(spaces: number): HelmValue<string>;
34
+ indent(spaces: number): HelmValue<string>;
35
+ toExpression(): HelmExpression;
36
+ if<V>(condition: HelmCondition, thenValue: V): HelmFieldConditional<V>;
37
+ ifElse<V>(condition: HelmCondition, thenValue: V, elseValue: V): HelmValue<V>;
38
+ range<V>(callback: (item: HelmValue<T extends (infer U)[] ? U : unknown>, index: HelmValue<number>) => V): HelmRange<V>;
39
+ with<V>(callback: (ctx: HelmValue<T>) => V): HelmWith<V>;
40
+ }
41
+ export interface HelmCondition {
42
+ [HELM_VALUE_SYMBOL]: true;
43
+ __condition: string;
44
+ not(): HelmCondition;
45
+ and(other: HelmCondition): HelmCondition;
46
+ or(other: HelmCondition): HelmCondition;
47
+ toString(): string;
48
+ }
49
+ export interface HelmFieldConditional<T> {
50
+ __helmFieldConditional: true;
51
+ condition: HelmCondition;
52
+ thenValue: T;
53
+ elseValue?: T;
54
+ }
55
+ export interface HelmRange<T> {
56
+ __helmRange: true;
57
+ source: HelmValue;
58
+ callback: (item: HelmValue, index: HelmValue<number>) => T;
59
+ }
60
+ export interface HelmWith<T> {
61
+ __helmWith: true;
62
+ source: HelmValue;
63
+ callback: (ctx: HelmValue) => T;
64
+ }
65
+ export interface HelmHelpers {
66
+ include(templateName: string, context?: '.' | HelmValue): HelmValue<string>;
67
+ printf(format: string, ...args: (HelmValue | string | number)[]): HelmValue<string>;
68
+ release: {
69
+ name: HelmValue<string>;
70
+ namespace: HelmValue<string>;
71
+ service: HelmValue<string>;
72
+ isUpgrade: HelmValue<boolean>;
73
+ isInstall: HelmValue<boolean>;
74
+ revision: HelmValue<number>;
75
+ };
76
+ chart: {
77
+ name: HelmValue<string>;
78
+ version: HelmValue<string>;
79
+ appVersion: HelmValue<string>;
80
+ type: HelmValue<string>;
81
+ };
82
+ capabilities: {
83
+ kubeVersion: {
84
+ version: HelmValue<string>;
85
+ major: HelmValue<string>;
86
+ minor: HelmValue<string>;
87
+ };
88
+ apiVersions: {
89
+ has(apiVersion: string): HelmCondition;
90
+ };
91
+ };
92
+ rawCondition(condition: string): HelmCondition;
93
+ }
94
+ export declare function valuesRef<T extends Record<string, unknown>>(): HelmValue<T> & HelmHelpers;
95
+ export declare function isHelmValue(value: unknown): value is HelmValue;
96
+ export declare function isHelmCondition(value: unknown): value is HelmCondition;
97
+ export declare function isHelmFieldConditional(value: unknown): value is HelmFieldConditional<unknown>;
98
+ export declare function isHelmRange(value: unknown): value is HelmRange<unknown>;
99
+ export declare function isHelmWith(value: unknown): value is HelmWith<unknown>;
100
+ export declare function serializeHelmValue(value: HelmValue): string;
101
+ export declare function serializeHelmCondition(condition: HelmCondition): string;
102
+ export {};
@@ -0,0 +1,273 @@
1
+ import { createHelmExpression } from './helmControlStructures.js';
2
+ const HELM_VALUE_SYMBOL = Symbol('HelmValue');
3
+ function serializeValue(value) {
4
+ if (typeof value === 'object' && value !== null && HELM_VALUE_SYMBOL in value) {
5
+ return value.__path;
6
+ }
7
+ if (typeof value === 'string') {
8
+ return `"${value}"`;
9
+ }
10
+ return String(value);
11
+ }
12
+ function createCondition(condition) {
13
+ if (!condition || typeof condition !== 'string') {
14
+ throw new Error('Condition must be a non-empty string');
15
+ }
16
+ return {
17
+ [HELM_VALUE_SYMBOL]: true,
18
+ __condition: condition,
19
+ not() {
20
+ return createCondition(`not (${this.__condition})`);
21
+ },
22
+ and(other) {
23
+ if (!other || typeof other.__condition !== 'string') {
24
+ throw new Error('Invalid HelmCondition provided to and()');
25
+ }
26
+ return createCondition(`and (${this.__condition}) (${other.__condition})`);
27
+ },
28
+ or(other) {
29
+ if (!other || typeof other.__condition !== 'string') {
30
+ throw new Error('Invalid HelmCondition provided to or()');
31
+ }
32
+ return createCondition(`or (${this.__condition}) (${other.__condition})`);
33
+ },
34
+ toString() {
35
+ return this.__condition;
36
+ },
37
+ };
38
+ }
39
+ function createValueProxy(path) {
40
+ const baseObject = Object.create(null);
41
+ baseObject.__helmExpression = true;
42
+ baseObject.value = `{{ ${path} }}`;
43
+ const handler = {
44
+ ownKeys() {
45
+ return ['__helmExpression', 'value'];
46
+ },
47
+ getOwnPropertyDescriptor(target, prop) {
48
+ if (prop === '__helmExpression') {
49
+ return { value: true, writable: false, enumerable: true, configurable: true };
50
+ }
51
+ if (prop === 'value') {
52
+ return { value: `{{ ${path} }}`, writable: false, enumerable: true, configurable: true };
53
+ }
54
+ return undefined;
55
+ },
56
+ has(target, prop) {
57
+ if (prop === HELM_VALUE_SYMBOL)
58
+ return true;
59
+ if (prop === '__helmExpression')
60
+ return true;
61
+ if (prop === 'value')
62
+ return true;
63
+ return false;
64
+ },
65
+ get(target, prop) {
66
+ if (prop === HELM_VALUE_SYMBOL)
67
+ return true;
68
+ if (prop === '__path')
69
+ return path;
70
+ if (prop === '__type')
71
+ return undefined;
72
+ if (prop === '__helmExpression')
73
+ return true;
74
+ if (prop === 'value')
75
+ return `{{ ${path} }}`;
76
+ if (prop === 'toJSON') {
77
+ return () => ({ __helmExpression: true, value: `{{ ${path} }}` });
78
+ }
79
+ if (typeof prop === 'string') {
80
+ switch (prop) {
81
+ case 'eq':
82
+ return (value) => createCondition(`eq ${path} ${serializeValue(value)}`);
83
+ case 'ne':
84
+ return (value) => createCondition(`ne ${path} ${serializeValue(value)}`);
85
+ case 'gt':
86
+ return (value) => createCondition(`gt ${path} ${serializeValue(value)}`);
87
+ case 'ge':
88
+ return (value) => createCondition(`ge ${path} ${serializeValue(value)}`);
89
+ case 'lt':
90
+ return (value) => createCondition(`lt ${path} ${serializeValue(value)}`);
91
+ case 'le':
92
+ return (value) => createCondition(`le ${path} ${serializeValue(value)}`);
93
+ case 'not':
94
+ return () => createCondition(`not ${path}`);
95
+ case 'and':
96
+ return (other) => createCondition(`and ${path} (${other.__condition})`);
97
+ case 'or':
98
+ return (other) => createCondition(`or ${path} (${other.__condition})`);
99
+ case 'default':
100
+ return (defaultValue) => createValueProxy(`${path} | default ${serializeValue(defaultValue)}`);
101
+ case 'quote':
102
+ return () => createValueProxy(`(${path} | quote)`);
103
+ case 'upper':
104
+ return () => createValueProxy(`(${path} | upper)`);
105
+ case 'lower':
106
+ return () => createValueProxy(`(${path} | lower)`);
107
+ case 'title':
108
+ return () => createValueProxy(`(${path} | title)`);
109
+ case 'trim':
110
+ return () => createValueProxy(`(${path} | trim)`);
111
+ case 'trimPrefix':
112
+ return (prefix) => createValueProxy(`(${path} | trimPrefix "${prefix}")`);
113
+ case 'trimSuffix':
114
+ return (suffix) => createValueProxy(`(${path} | trimSuffix "${suffix}")`);
115
+ case 'replace':
116
+ return (old, newStr) => createValueProxy(`(${path} | replace "${old}" "${newStr}")`);
117
+ case 'contains':
118
+ return (substr) => createCondition(`contains "${substr}" ${path}`);
119
+ case 'hasPrefix':
120
+ return (prefix) => createCondition(`hasPrefix ${path} "${prefix}"`);
121
+ case 'hasSuffix':
122
+ return (suffix) => createCondition(`hasSuffix ${path} "${suffix}"`);
123
+ case 'trunc':
124
+ return (length) => createValueProxy(`(${path} | trunc ${length})`);
125
+ case 'kindIs':
126
+ return (kind) => createCondition(`kindIs "${kind}" ${path}`);
127
+ case 'hasKey':
128
+ return (key) => createCondition(`hasKey ${path} "${key}"`);
129
+ case 'toYaml':
130
+ return () => createValueProxy(`${path} | toYaml`);
131
+ case 'toJson':
132
+ return () => createValueProxy(`${path} | toJson`);
133
+ case 'nindent':
134
+ return (spaces) => createValueProxy(`${path} | nindent ${spaces}`);
135
+ case 'indent':
136
+ return (spaces) => createValueProxy(`${path} | indent ${spaces}`);
137
+ case 'toExpression':
138
+ return () => createHelmExpression(`{{ ${path} }}`);
139
+ case 'if':
140
+ return (condition, thenValue) => {
141
+ let helmCondition;
142
+ if (isHelmCondition(condition)) {
143
+ helmCondition = condition;
144
+ }
145
+ else if (isHelmValue(condition)) {
146
+ helmCondition = createCondition(condition.__path);
147
+ }
148
+ else {
149
+ throw new Error('v.if() requires a HelmCondition or HelmValue as the first argument');
150
+ }
151
+ return {
152
+ __helmFieldConditional: true,
153
+ condition: helmCondition,
154
+ thenValue,
155
+ };
156
+ };
157
+ case 'ifElse':
158
+ return (condition, thenValue, elseValue) => {
159
+ const condStr = condition.__condition;
160
+ return createValueProxy(`(ternary ${serializeValue(thenValue)} ${serializeValue(elseValue)} (${condStr}))`);
161
+ };
162
+ case 'range':
163
+ return (callback) => ({
164
+ __helmRange: true,
165
+ source: createValueProxy(path),
166
+ callback,
167
+ });
168
+ case 'with':
169
+ return (callback) => {
170
+ const ctxProxy = { __path: '.', [HELM_VALUE_SYMBOL]: true };
171
+ const content = callback(ctxProxy);
172
+ let contentStr;
173
+ if (typeof content === 'object' &&
174
+ content !== null &&
175
+ '__helmExpression' in content) {
176
+ const helmExpr = content;
177
+ contentStr = helmExpr.value;
178
+ }
179
+ else {
180
+ contentStr = String(content);
181
+ }
182
+ const marker = `__FIELD_WITH_MARKER__:${path}:${contentStr}`;
183
+ return createHelmExpression(marker);
184
+ };
185
+ case 'toString':
186
+ return () => `{{ ${path} }}`;
187
+ case 'valueOf':
188
+ return () => path;
189
+ default:
190
+ return createValueProxy(`${path}.${prop}`);
191
+ }
192
+ }
193
+ return undefined;
194
+ },
195
+ };
196
+ return new Proxy(baseObject, handler);
197
+ }
198
+ export function valuesRef() {
199
+ const values = createValueProxy('.Values');
200
+ const helpers = {
201
+ include(templateName, context = '.') {
202
+ const ctx = context === '.' ? '.' : context.__path;
203
+ return createValueProxy(`(include "${templateName}" ${ctx})`);
204
+ },
205
+ printf(format, ...args) {
206
+ const argsStr = args
207
+ .map((arg) => serializeValue(arg))
208
+ .join(' ');
209
+ return createValueProxy(`(printf "${format}" ${argsStr})`);
210
+ },
211
+ release: {
212
+ name: createValueProxy('.Release.Name'),
213
+ namespace: createValueProxy('.Release.Namespace'),
214
+ service: createValueProxy('.Release.Service'),
215
+ isUpgrade: createValueProxy('.Release.IsUpgrade'),
216
+ isInstall: createValueProxy('.Release.IsInstall'),
217
+ revision: createValueProxy('.Release.Revision'),
218
+ },
219
+ chart: {
220
+ name: createValueProxy('.Chart.Name'),
221
+ version: createValueProxy('.Chart.Version'),
222
+ appVersion: createValueProxy('.Chart.AppVersion'),
223
+ type: createValueProxy('.Chart.Type'),
224
+ },
225
+ capabilities: {
226
+ kubeVersion: {
227
+ version: createValueProxy('.Capabilities.KubeVersion.Version'),
228
+ major: createValueProxy('.Capabilities.KubeVersion.Major'),
229
+ minor: createValueProxy('.Capabilities.KubeVersion.Minor'),
230
+ },
231
+ apiVersions: {
232
+ has(apiVersion) {
233
+ return createCondition(`.Capabilities.APIVersions.Has "${apiVersion}"`);
234
+ },
235
+ },
236
+ },
237
+ rawCondition(condition) {
238
+ return createCondition(condition);
239
+ },
240
+ };
241
+ return new Proxy(values, {
242
+ get(target, prop) {
243
+ if (prop in helpers) {
244
+ return helpers[prop];
245
+ }
246
+ return target[prop];
247
+ },
248
+ });
249
+ }
250
+ export function isHelmValue(value) {
251
+ return typeof value === 'object' && value !== null && HELM_VALUE_SYMBOL in value;
252
+ }
253
+ export function isHelmCondition(value) {
254
+ return (typeof value === 'object' &&
255
+ value !== null &&
256
+ HELM_VALUE_SYMBOL in value &&
257
+ '__condition' in value);
258
+ }
259
+ export function isHelmFieldConditional(value) {
260
+ return typeof value === 'object' && value !== null && '__helmFieldConditional' in value;
261
+ }
262
+ export function isHelmRange(value) {
263
+ return typeof value === 'object' && value !== null && '__helmRange' in value;
264
+ }
265
+ export function isHelmWith(value) {
266
+ return typeof value === 'object' && value !== null && '__helmWith' in value;
267
+ }
268
+ export function serializeHelmValue(value) {
269
+ return `{{ ${value.__path} }}`;
270
+ }
271
+ export function serializeHelmCondition(condition) {
272
+ return condition.__condition;
273
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "timonel",
3
3
  "type": "module",
4
- "version": "2.14.0-beta.1",
4
+ "version": "3.0.0",
5
5
  "description": "Timonel: programmatic Helm chart generator using cdk8s (TypeScript)",
6
6
  "bin": {
7
7
  "timonel": "dist/cli.js",