@shepherdjerred/helm-types 1.1.0 → 1.2.0-dev.893
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.
- package/README.md +11 -2
- package/dist/cli.js +4570 -3203
- package/dist/index.js +8 -20585
- package/package.json +5 -1
- package/src/chart-fetcher.ts +37 -16
- package/src/chart-info-parser.ts +54 -34
- package/src/cli.ts +76 -58
- package/src/code-generator.ts +57 -22
- package/src/comment-parser.ts +79 -55
- package/src/config.ts +12 -5
- package/src/helm-types.ts +16 -46
- package/src/index.ts +14 -1
- package/src/interface-generator.ts +58 -23
- package/src/schemas.ts +3 -1
- package/src/type-converter-helpers.ts +180 -0
- package/src/type-converter.ts +273 -300
- package/src/type-inference.ts +302 -194
- package/src/utils.ts +2 -2
- package/src/yaml-comment-filters.ts +103 -0
- package/src/yaml-comment-regex-parser.ts +150 -0
- package/src/yaml-comments.ts +216 -508
- package/src/yaml-preprocess.ts +235 -0
package/src/type-inference.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
JSONSchemaProperty,
|
|
3
|
+
TypeScriptInterface,
|
|
4
|
+
TypeProperty,
|
|
5
|
+
} from "./types.ts";
|
|
6
|
+
import type { HelmValue } from "./schemas.ts";
|
|
3
7
|
import {
|
|
4
8
|
StringSchema,
|
|
5
9
|
ActualNumberSchema,
|
|
@@ -9,8 +13,12 @@ import {
|
|
|
9
13
|
ArraySchema,
|
|
10
14
|
HelmValueSchema,
|
|
11
15
|
StringBooleanSchema,
|
|
12
|
-
} from "./schemas.
|
|
13
|
-
import {
|
|
16
|
+
} from "./schemas.ts";
|
|
17
|
+
import {
|
|
18
|
+
capitalizeFirst,
|
|
19
|
+
sanitizePropertyName,
|
|
20
|
+
sanitizeTypeName,
|
|
21
|
+
} from "./utils.ts";
|
|
14
22
|
|
|
15
23
|
/**
|
|
16
24
|
* Convert JSON schema type to TypeScript type string
|
|
@@ -30,7 +38,11 @@ export function jsonSchemaToTypeScript(schema: JSONSchemaProperty): string {
|
|
|
30
38
|
|
|
31
39
|
// Handle enum
|
|
32
40
|
if (schema.enum) {
|
|
33
|
-
return schema.enum
|
|
41
|
+
return schema.enum
|
|
42
|
+
.map((v) =>
|
|
43
|
+
StringSchema.safeParse(v).success ? `"${String(v)}"` : String(v),
|
|
44
|
+
)
|
|
45
|
+
.join(" | ");
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
// Handle array type
|
|
@@ -40,8 +52,9 @@ export function jsonSchemaToTypeScript(schema: JSONSchemaProperty): string {
|
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
// Handle basic types
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
const stringTypeCheck = StringSchema.safeParse(schema.type);
|
|
56
|
+
if (stringTypeCheck.success) {
|
|
57
|
+
switch (stringTypeCheck.data) {
|
|
45
58
|
case "string":
|
|
46
59
|
return "string";
|
|
47
60
|
case "number":
|
|
@@ -65,7 +78,9 @@ export function jsonSchemaToTypeScript(schema: JSONSchemaProperty): string {
|
|
|
65
78
|
if (arrayTypeCheck.success) {
|
|
66
79
|
return arrayTypeCheck.data
|
|
67
80
|
.map((t: unknown) => {
|
|
68
|
-
if (!StringSchema.safeParse(t).success)
|
|
81
|
+
if (!StringSchema.safeParse(t).success) {
|
|
82
|
+
return "unknown";
|
|
83
|
+
}
|
|
69
84
|
const typeStr = String(t);
|
|
70
85
|
switch (typeStr) {
|
|
71
86
|
case "string":
|
|
@@ -96,7 +111,10 @@ export function jsonSchemaToTypeScript(schema: JSONSchemaProperty): string {
|
|
|
96
111
|
*/
|
|
97
112
|
export function inferTypeFromValue(value: unknown): string | null {
|
|
98
113
|
// Check null/undefined
|
|
99
|
-
if (
|
|
114
|
+
if (
|
|
115
|
+
NullSchema.safeParse(value).success ||
|
|
116
|
+
UndefinedSchema.safeParse(value).success
|
|
117
|
+
) {
|
|
100
118
|
return null;
|
|
101
119
|
}
|
|
102
120
|
|
|
@@ -119,7 +137,11 @@ export function inferTypeFromValue(value: unknown): string | null {
|
|
|
119
137
|
const stringCheck = StringSchema.safeParse(value);
|
|
120
138
|
if (stringCheck.success) {
|
|
121
139
|
const trimmed = stringCheck.data.trim();
|
|
122
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
trimmed !== "" &&
|
|
142
|
+
!Number.isNaN(Number(trimmed)) &&
|
|
143
|
+
Number.isFinite(Number(trimmed))
|
|
144
|
+
) {
|
|
123
145
|
return "number";
|
|
124
146
|
}
|
|
125
147
|
}
|
|
@@ -145,7 +167,10 @@ export function inferTypeFromValue(value: unknown): string | null {
|
|
|
145
167
|
/**
|
|
146
168
|
* Check if inferred type is compatible with schema type
|
|
147
169
|
*/
|
|
148
|
-
export function typesAreCompatible(
|
|
170
|
+
export function typesAreCompatible(
|
|
171
|
+
inferredType: string,
|
|
172
|
+
schemaType: string,
|
|
173
|
+
): boolean {
|
|
149
174
|
// Exact match
|
|
150
175
|
if (inferredType === schemaType) {
|
|
151
176
|
return true;
|
|
@@ -153,7 +178,9 @@ export function typesAreCompatible(inferredType: string, schemaType: string): bo
|
|
|
153
178
|
|
|
154
179
|
// Check if the inferred type is part of a union in the schema
|
|
155
180
|
// For example: schemaType might be "number | \"default\"" and inferredType is "string"
|
|
156
|
-
const schemaTypes = schemaType
|
|
181
|
+
const schemaTypes = schemaType
|
|
182
|
+
.split("|")
|
|
183
|
+
.map((t) => t.trim().replaceAll(/^["']|["']$/g, ""));
|
|
157
184
|
|
|
158
185
|
// If schema is a union, check if inferred type is compatible with any part
|
|
159
186
|
if (schemaTypes.length > 1) {
|
|
@@ -193,248 +220,329 @@ export function typesAreCompatible(inferredType: string, schemaType: string): bo
|
|
|
193
220
|
/**
|
|
194
221
|
* Convert Helm values to TypeScript interface
|
|
195
222
|
*/
|
|
196
|
-
export function convertToTypeScriptInterface(
|
|
197
|
-
values: HelmValue
|
|
198
|
-
interfaceName: string
|
|
199
|
-
schema?: JSONSchemaProperty | null
|
|
200
|
-
yamlComments?: Map<string, string
|
|
201
|
-
keyPrefix
|
|
202
|
-
): TypeScriptInterface {
|
|
223
|
+
export function convertToTypeScriptInterface(options: {
|
|
224
|
+
values: HelmValue;
|
|
225
|
+
interfaceName: string;
|
|
226
|
+
schema?: JSONSchemaProperty | null;
|
|
227
|
+
yamlComments?: Map<string, string>;
|
|
228
|
+
keyPrefix?: string;
|
|
229
|
+
}): TypeScriptInterface {
|
|
230
|
+
const keyPrefix = options.keyPrefix ?? "";
|
|
203
231
|
const properties: Record<string, TypeProperty> = {};
|
|
204
|
-
const schemaProps = schema?.properties;
|
|
232
|
+
const schemaProps = options.schema?.properties;
|
|
205
233
|
|
|
206
|
-
for (const [key, value] of Object.entries(values)) {
|
|
234
|
+
for (const [key, value] of Object.entries(options.values)) {
|
|
207
235
|
const sanitizedKey = sanitizePropertyName(key);
|
|
208
236
|
const typeNameSuffix = sanitizeTypeName(key);
|
|
209
237
|
const propertySchema = schemaProps?.[key];
|
|
210
238
|
const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key;
|
|
211
|
-
const yamlComment = yamlComments?.get(fullKey);
|
|
239
|
+
const yamlComment = options.yamlComments?.get(fullKey);
|
|
212
240
|
|
|
213
|
-
properties[sanitizedKey] = convertValueToProperty(
|
|
241
|
+
properties[sanitizedKey] = convertValueToProperty({
|
|
214
242
|
value,
|
|
215
|
-
`${interfaceName}${capitalizeFirst(typeNameSuffix)}`,
|
|
216
|
-
propertySchema,
|
|
217
|
-
key,
|
|
243
|
+
nestedTypeName: `${options.interfaceName}${capitalizeFirst(typeNameSuffix)}`,
|
|
244
|
+
schema: propertySchema,
|
|
245
|
+
propertyName: key,
|
|
218
246
|
yamlComment,
|
|
219
|
-
yamlComments,
|
|
247
|
+
yamlComments: options.yamlComments,
|
|
220
248
|
fullKey,
|
|
221
|
-
);
|
|
249
|
+
});
|
|
222
250
|
}
|
|
223
251
|
|
|
224
252
|
return {
|
|
225
|
-
name: interfaceName,
|
|
253
|
+
name: options.interfaceName,
|
|
226
254
|
properties,
|
|
227
255
|
};
|
|
228
256
|
}
|
|
229
257
|
|
|
230
|
-
|
|
231
|
-
value: unknown
|
|
232
|
-
nestedTypeName: string
|
|
233
|
-
schema?: JSONSchemaProperty
|
|
234
|
-
propertyName?: string
|
|
235
|
-
yamlComment?: string
|
|
236
|
-
yamlComments?: Map<string, string
|
|
237
|
-
fullKey?: string
|
|
258
|
+
type InferencePropertyContext = {
|
|
259
|
+
value: unknown;
|
|
260
|
+
nestedTypeName: string;
|
|
261
|
+
schema?: JSONSchemaProperty;
|
|
262
|
+
propertyName?: string;
|
|
263
|
+
yamlComment?: string;
|
|
264
|
+
yamlComments?: Map<string, string>;
|
|
265
|
+
fullKey?: string;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Merge description from schema and YAML comments
|
|
270
|
+
*/
|
|
271
|
+
function mergeDescriptions(
|
|
272
|
+
schemaDescription: string | undefined,
|
|
273
|
+
yamlComment: string | undefined,
|
|
274
|
+
): string | undefined {
|
|
275
|
+
if (yamlComment == null || yamlComment === "") {
|
|
276
|
+
return schemaDescription;
|
|
277
|
+
}
|
|
278
|
+
return schemaDescription != null && schemaDescription !== ""
|
|
279
|
+
? `${yamlComment}\n\n${schemaDescription}`
|
|
280
|
+
: yamlComment;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Convert a value to a TypeProperty using JSON schema information
|
|
285
|
+
*/
|
|
286
|
+
function convertWithSchema(
|
|
287
|
+
ctx: InferencePropertyContext & { schema: JSONSchemaProperty },
|
|
238
288
|
): TypeProperty {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
289
|
+
const {
|
|
290
|
+
value,
|
|
291
|
+
nestedTypeName,
|
|
292
|
+
schema,
|
|
293
|
+
propertyName,
|
|
294
|
+
yamlComment,
|
|
295
|
+
yamlComments,
|
|
296
|
+
fullKey,
|
|
297
|
+
} = ctx;
|
|
298
|
+
|
|
299
|
+
const inferredType = inferTypeFromValue(value);
|
|
300
|
+
const schemaType = jsonSchemaToTypeScript(schema);
|
|
301
|
+
|
|
302
|
+
if (
|
|
303
|
+
inferredType != null &&
|
|
304
|
+
inferredType !== "" &&
|
|
305
|
+
!typesAreCompatible(inferredType, schemaType)
|
|
306
|
+
) {
|
|
307
|
+
const propName =
|
|
308
|
+
propertyName != null && propertyName !== "" ? `'${propertyName}': ` : "";
|
|
309
|
+
console.warn(
|
|
310
|
+
` ⚠️ Type mismatch for ${propName}Schema says '${schemaType}' but value suggests '${inferredType}' (value: ${String(value).slice(0, 50)})`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
252
313
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (yamlComment) {
|
|
256
|
-
if (description) {
|
|
257
|
-
// If both exist, combine them
|
|
258
|
-
description = `${yamlComment}\n\n${description}`;
|
|
259
|
-
} else {
|
|
260
|
-
description = yamlComment;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const defaultValue = schema.default !== undefined ? schema.default : value;
|
|
264
|
-
|
|
265
|
-
// If schema defines it as an object with properties, recurse
|
|
266
|
-
const helmValueCheckForProps = HelmValueSchema.safeParse(value);
|
|
267
|
-
if (schema.properties && helmValueCheckForProps.success) {
|
|
268
|
-
const nestedInterface = convertToTypeScriptInterface(
|
|
269
|
-
helmValueCheckForProps.data,
|
|
270
|
-
nestedTypeName,
|
|
271
|
-
schema,
|
|
272
|
-
yamlComments,
|
|
273
|
-
fullKey,
|
|
274
|
-
);
|
|
275
|
-
return {
|
|
276
|
-
type: nestedTypeName,
|
|
277
|
-
optional: true,
|
|
278
|
-
nested: nestedInterface,
|
|
279
|
-
description,
|
|
280
|
-
default: defaultValue,
|
|
281
|
-
};
|
|
282
|
-
}
|
|
314
|
+
const description = mergeDescriptions(schema.description, yamlComment);
|
|
315
|
+
const defaultValue = schema.default === undefined ? value : schema.default;
|
|
283
316
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
default: defaultValue,
|
|
303
|
-
};
|
|
304
|
-
}
|
|
317
|
+
// If schema defines it as an object with properties, recurse
|
|
318
|
+
const helmValueCheckForProps = HelmValueSchema.safeParse(value);
|
|
319
|
+
if (schema.properties && helmValueCheckForProps.success) {
|
|
320
|
+
const nestedInterface = convertToTypeScriptInterface({
|
|
321
|
+
values: helmValueCheckForProps.data,
|
|
322
|
+
interfaceName: nestedTypeName,
|
|
323
|
+
schema,
|
|
324
|
+
yamlComments,
|
|
325
|
+
keyPrefix: fullKey,
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
type: nestedTypeName,
|
|
329
|
+
optional: true,
|
|
330
|
+
nested: nestedInterface,
|
|
331
|
+
description,
|
|
332
|
+
default: defaultValue,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
305
335
|
|
|
306
|
-
|
|
336
|
+
// Handle object types without explicit properties
|
|
337
|
+
const helmValueCheckForObject = HelmValueSchema.safeParse(value);
|
|
338
|
+
if (schemaType === "object" && helmValueCheckForObject.success) {
|
|
339
|
+
const nestedInterface = convertToTypeScriptInterface({
|
|
340
|
+
values: helmValueCheckForObject.data,
|
|
341
|
+
interfaceName: nestedTypeName,
|
|
342
|
+
yamlComments,
|
|
343
|
+
keyPrefix: fullKey,
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
type: nestedTypeName,
|
|
347
|
+
optional: true,
|
|
348
|
+
nested: nestedInterface,
|
|
349
|
+
description,
|
|
350
|
+
default: defaultValue,
|
|
351
|
+
};
|
|
307
352
|
}
|
|
308
353
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
354
|
+
return {
|
|
355
|
+
type: schemaType,
|
|
356
|
+
optional: true,
|
|
357
|
+
description,
|
|
358
|
+
default: defaultValue,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
312
361
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Infer array element type from sampled elements
|
|
364
|
+
*/
|
|
365
|
+
function inferArrayType(
|
|
366
|
+
nestedTypeName: string,
|
|
367
|
+
arrayValue: unknown[],
|
|
368
|
+
): TypeProperty {
|
|
369
|
+
if (arrayValue.length === 0) {
|
|
370
|
+
return { type: "unknown[]", optional: true };
|
|
316
371
|
}
|
|
317
372
|
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
const arrayValue = arrayResult.data;
|
|
322
|
-
if (arrayValue.length === 0) {
|
|
323
|
-
return { type: "unknown[]", optional: true };
|
|
324
|
-
}
|
|
373
|
+
const elementTypes = new Set<string>();
|
|
374
|
+
const elementTypeProps: TypeProperty[] = [];
|
|
375
|
+
const sampleSize = Math.min(arrayValue.length, 3);
|
|
325
376
|
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
377
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
378
|
+
const elementType = convertValueToProperty({
|
|
379
|
+
value: arrayValue[i],
|
|
380
|
+
nestedTypeName,
|
|
381
|
+
});
|
|
382
|
+
elementTypes.add(elementType.type);
|
|
383
|
+
elementTypeProps.push(elementType);
|
|
384
|
+
}
|
|
330
385
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
elementTypes
|
|
334
|
-
elementTypeProps
|
|
335
|
-
|
|
386
|
+
if (elementTypes.size === 1) {
|
|
387
|
+
return inferUniformArrayType(
|
|
388
|
+
elementTypes,
|
|
389
|
+
elementTypeProps,
|
|
390
|
+
nestedTypeName,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
336
393
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
// Create a new interface name for array elements
|
|
345
|
-
const arrayElementTypeName = `${nestedTypeName}Element`;
|
|
346
|
-
const arrayElementInterface: TypeScriptInterface = {
|
|
347
|
-
name: arrayElementTypeName,
|
|
348
|
-
properties: elementProp.nested.properties,
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
type: `${arrayElementTypeName}[]`,
|
|
353
|
-
optional: true,
|
|
354
|
-
nested: arrayElementInterface,
|
|
355
|
-
};
|
|
356
|
-
} else {
|
|
357
|
-
return {
|
|
358
|
-
type: `${elementType}[]`,
|
|
359
|
-
optional: true,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
394
|
+
const types = [...elementTypes].toSorted();
|
|
395
|
+
if (
|
|
396
|
+
types.length <= 3 &&
|
|
397
|
+
types.every((t) => ["string", "number", "boolean"].includes(t))
|
|
398
|
+
) {
|
|
399
|
+
return { type: `(${types.join(" | ")})[]`, optional: true };
|
|
400
|
+
}
|
|
364
401
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (types.length <= 3 && types.every((t) => ["string", "number", "boolean"].includes(t))) {
|
|
368
|
-
return {
|
|
369
|
-
type: `(${types.join(" | ")})[]`,
|
|
370
|
-
optional: true,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
402
|
+
return { type: "unknown[]", optional: true };
|
|
403
|
+
}
|
|
373
404
|
|
|
374
|
-
|
|
405
|
+
/**
|
|
406
|
+
* Build TypeProperty for a uniform-type array
|
|
407
|
+
*/
|
|
408
|
+
function inferUniformArrayType(
|
|
409
|
+
elementTypes: Set<string>,
|
|
410
|
+
elementTypeProps: TypeProperty[],
|
|
411
|
+
nestedTypeName: string,
|
|
412
|
+
): TypeProperty {
|
|
413
|
+
const elementType = [...elementTypes][0];
|
|
414
|
+
const elementProp = elementTypeProps[0];
|
|
415
|
+
if (elementType == null || elementType === "" || !elementProp) {
|
|
375
416
|
return { type: "unknown[]", optional: true };
|
|
376
417
|
}
|
|
377
418
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
undefined,
|
|
385
|
-
yamlComments,
|
|
386
|
-
fullKey,
|
|
387
|
-
);
|
|
419
|
+
if (elementProp.nested) {
|
|
420
|
+
const arrayElementTypeName = `${nestedTypeName}Element`;
|
|
421
|
+
const arrayElementInterface: TypeScriptInterface = {
|
|
422
|
+
name: arrayElementTypeName,
|
|
423
|
+
properties: elementProp.nested.properties,
|
|
424
|
+
};
|
|
388
425
|
return {
|
|
389
|
-
type:
|
|
426
|
+
type: `${arrayElementTypeName}[]`,
|
|
390
427
|
optional: true,
|
|
391
|
-
nested:
|
|
392
|
-
description: yamlComment,
|
|
393
|
-
default: value,
|
|
428
|
+
nested: arrayElementInterface,
|
|
394
429
|
};
|
|
395
430
|
}
|
|
396
431
|
|
|
397
|
-
|
|
398
|
-
|
|
432
|
+
return { type: `${elementType}[]`, optional: true };
|
|
433
|
+
}
|
|
399
434
|
|
|
400
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Infer a primitive TypeProperty from a runtime value (no schema)
|
|
437
|
+
*/
|
|
438
|
+
function inferPrimitiveType(
|
|
439
|
+
value: unknown,
|
|
440
|
+
yamlComment?: string,
|
|
441
|
+
): TypeProperty {
|
|
401
442
|
if (ActualBooleanSchema.safeParse(value).success) {
|
|
402
|
-
return {
|
|
443
|
+
return {
|
|
444
|
+
type: "boolean",
|
|
445
|
+
optional: true,
|
|
446
|
+
description: yamlComment,
|
|
447
|
+
default: value,
|
|
448
|
+
};
|
|
403
449
|
}
|
|
404
450
|
|
|
405
|
-
// Check for actual runtime number
|
|
406
451
|
if (ActualNumberSchema.safeParse(value).success) {
|
|
407
|
-
return {
|
|
452
|
+
return {
|
|
453
|
+
type: "number",
|
|
454
|
+
optional: true,
|
|
455
|
+
description: yamlComment,
|
|
456
|
+
default: value,
|
|
457
|
+
};
|
|
408
458
|
}
|
|
409
459
|
|
|
410
|
-
// Check if it's a string that represents a boolean ("true", "FALSE", etc.)
|
|
411
460
|
if (StringBooleanSchema.safeParse(value).success) {
|
|
412
|
-
return {
|
|
461
|
+
return {
|
|
462
|
+
type: "boolean",
|
|
463
|
+
optional: true,
|
|
464
|
+
description: yamlComment,
|
|
465
|
+
default: value,
|
|
466
|
+
};
|
|
413
467
|
}
|
|
414
468
|
|
|
415
|
-
// Check if it's a string that represents a number ("15", "0", etc.)
|
|
416
|
-
// Only treat non-empty strings that parse as numbers as numbers
|
|
417
469
|
const stringCheckForNumber = StringSchema.safeParse(value);
|
|
418
470
|
if (stringCheckForNumber.success) {
|
|
419
471
|
const trimmed = stringCheckForNumber.data.trim();
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
472
|
+
if (
|
|
473
|
+
trimmed !== "" &&
|
|
474
|
+
!Number.isNaN(Number(trimmed)) &&
|
|
475
|
+
Number.isFinite(Number(trimmed))
|
|
476
|
+
) {
|
|
477
|
+
return {
|
|
478
|
+
type: "number",
|
|
479
|
+
optional: true,
|
|
480
|
+
description: yamlComment,
|
|
481
|
+
default: value,
|
|
482
|
+
};
|
|
423
483
|
}
|
|
424
484
|
}
|
|
425
485
|
|
|
426
|
-
// Check for plain string (strings that don't look like numbers or booleans)
|
|
427
486
|
const stringCheckForPlain = StringSchema.safeParse(value);
|
|
428
487
|
if (stringCheckForPlain.success) {
|
|
429
|
-
// Special case: "default" is often used as a sentinel value in Helm charts
|
|
430
|
-
// that can be overridden with actual typed values (numbers, booleans, etc.)
|
|
431
488
|
if (stringCheckForPlain.data === "default") {
|
|
432
|
-
return {
|
|
489
|
+
return {
|
|
490
|
+
type: "string | number | boolean",
|
|
491
|
+
optional: true,
|
|
492
|
+
description: yamlComment,
|
|
493
|
+
default: value,
|
|
494
|
+
};
|
|
433
495
|
}
|
|
434
|
-
return {
|
|
496
|
+
return {
|
|
497
|
+
type: "string",
|
|
498
|
+
optional: true,
|
|
499
|
+
description: yamlComment,
|
|
500
|
+
default: value,
|
|
501
|
+
};
|
|
435
502
|
}
|
|
436
503
|
|
|
437
|
-
|
|
438
|
-
|
|
504
|
+
console.warn(
|
|
505
|
+
`Unrecognized value type for: ${String(value)}, using 'unknown'`,
|
|
506
|
+
);
|
|
439
507
|
return { type: "unknown", optional: true, description: yamlComment };
|
|
440
508
|
}
|
|
509
|
+
|
|
510
|
+
function convertValueToProperty(opts: InferencePropertyContext): TypeProperty {
|
|
511
|
+
const { value, nestedTypeName, schema, yamlComment, yamlComments, fullKey } =
|
|
512
|
+
opts;
|
|
513
|
+
|
|
514
|
+
if (schema) {
|
|
515
|
+
return convertWithSchema({ ...opts, schema });
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (
|
|
519
|
+
NullSchema.safeParse(value).success ||
|
|
520
|
+
UndefinedSchema.safeParse(value).success
|
|
521
|
+
) {
|
|
522
|
+
return { type: "unknown", optional: true };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const arrayResult = ArraySchema.safeParse(value);
|
|
526
|
+
if (arrayResult.success) {
|
|
527
|
+
return inferArrayType(nestedTypeName, arrayResult.data);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const objectResult = HelmValueSchema.safeParse(value);
|
|
531
|
+
if (objectResult.success) {
|
|
532
|
+
const nestedInterface = convertToTypeScriptInterface({
|
|
533
|
+
values: objectResult.data,
|
|
534
|
+
interfaceName: nestedTypeName,
|
|
535
|
+
yamlComments,
|
|
536
|
+
keyPrefix: fullKey,
|
|
537
|
+
});
|
|
538
|
+
return {
|
|
539
|
+
type: nestedTypeName,
|
|
540
|
+
optional: true,
|
|
541
|
+
nested: nestedInterface,
|
|
542
|
+
description: yamlComment,
|
|
543
|
+
default: value,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return inferPrimitiveType(value, yamlComment);
|
|
548
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -50,7 +50,7 @@ export function sanitizePropertyName(key: string): string {
|
|
|
50
50
|
// Check if key needs quoting
|
|
51
51
|
const needsQuoting =
|
|
52
52
|
reservedKeywords.has(key) ||
|
|
53
|
-
/[
|
|
53
|
+
/[^\w$]/.test(key) || // Contains special characters
|
|
54
54
|
/^\d/.test(key); // Starts with digit
|
|
55
55
|
|
|
56
56
|
return needsQuoting ? `"${key}"` : key;
|
|
@@ -62,7 +62,7 @@ export function sanitizePropertyName(key: string): string {
|
|
|
62
62
|
export function sanitizeTypeName(key: string): string {
|
|
63
63
|
return (
|
|
64
64
|
key
|
|
65
|
-
.
|
|
65
|
+
.replaceAll(/[^a-z0-9]/gi, "") // Remove all special characters
|
|
66
66
|
.replace(/^\d+/, "") || // Remove leading digits
|
|
67
67
|
"Property"
|
|
68
68
|
); // Fallback if empty
|