Skip to main content
Glama
interfaceGenerator.ts6.59 kB
/** * Generate inline TypeScript interfaces from Kubernetes types */ import { InterfaceInfo, extractInterfaceFromFile, findKubernetesTypeFile } from './typeExtractor.js'; /** * Types that should NOT be expanded (too complex or rarely used) */ const DONT_EXPAND_TYPES = new Set([ // Complex scheduling/affinity rules 'V1Affinity', 'V1NodeAffinity', 'V1PodAffinity', 'V1PodAntiAffinity', 'V1NodeSelector', 'V1NodeSelectorRequirement', 'V1LabelSelectorRequirement', // Complex security contexts 'V1SecurityContext', 'V1PodSecurityContext', 'V1SELinuxOptions', 'V1SeccompProfile', 'V1WindowsSecurityContextOptions', 'V1Capabilities', 'V1AppArmorProfile', // Probes (detailed structure not commonly needed) 'V1Probe', 'V1HTTPGetAction', 'V1TCPSocketAction', 'V1ExecAction', 'V1GRPCAction', 'V1SleepAction', 'V1HTTPHeader', // Lifecycle hooks 'V1Lifecycle', 'V1LifecycleHandler', // Volume types (too many variants) 'V1VolumeMount', 'V1PersistentVolumeClaimVolumeSource', 'V1ConfigMapVolumeSource', 'V1SecretVolumeSource', 'V1EmptyDirVolumeSource', 'V1HostPathVolumeSource', 'V1ProjectedVolumeSource', 'V1CSIVolumeSource', // Resource references 'V1ResourceClaim', 'V1TypedLocalObjectReference', 'V1LocalObjectReference', 'V1ObjectReference', // Field selectors 'V1EnvVarSource', 'V1ConfigMapKeySelector', 'V1SecretKeySelector', 'V1ObjectFieldSelector', 'V1ResourceFieldSelector', 'V1ConfigMapEnvSource', 'V1SecretEnvSource', // Tolerations and taints 'V1Taint', // Topology 'V1TopologySpreadConstraint', 'V1PodAffinityTerm', 'V1WeightedPodAffinityTerm', // Other complex types 'V1PodReadinessGate', 'V1PodResourceClaim', 'V1ContainerResizePolicy', 'V1EphemeralContainer', 'V1HostAlias', 'V1PodOS', ]); /** * Check if a type should not be expanded (return generic object instead) */ function shouldNotExpand(typeName: string): boolean { return DONT_EXPAND_TYPES.has(typeName); } /** * Check if a type is a primitive or should not be expanded */ function isPrimitiveType(typeName: string): boolean { const primitives = [ 'string', 'number', 'boolean', 'unknown', 'any', 'void', 'null', 'undefined', 'Date', 'object', 'never' ]; // Check if it's a primitive if (primitives.includes(typeName)) { return true; } // Check if it's a Record type if (typeName.startsWith('Record<') || typeName.startsWith('{')) { return true; } // Check if it's a union with primitives if (typeName.includes('|')) { return true; } return false; } /** * Extract the inner type from Array<T> */ function extractArrayType(typeName: string): string | null { const match = typeName.match(/^Array<(.+)>$/); return match && match[1] ? match[1] : null; } /** * Recursively expand a type into an inline interface definition */ function expandType(typeName: string, indent: string = ' ', depth: number = 0, visited: Set<string> = new Set()): string { // Prevent infinite recursion - limit to 3 levels for reasonable file sizes if (depth > 3) { return '{ [key: string]: unknown }'; } // Convert Date to string if (typeName === 'Date') { return 'string'; } // Check if this type should not be expanded (too complex) if (shouldNotExpand(typeName)) { return '{ [key: string]: unknown }'; } // Check if it's a primitive if (isPrimitiveType(typeName)) { return typeName === 'object' ? '{ [key: string]: unknown }' : typeName; } // Handle Array types const arrayInnerType = extractArrayType(typeName); if (arrayInnerType) { const expandedInner = expandType(arrayInnerType, indent, depth + 1, visited); // If the inner type is complex (multiline), format it properly if (expandedInner.includes('\n')) { return `Array<${expandedInner}>`; } return `Array<${expandedInner}>`; } // Check for circular reference if (visited.has(typeName)) { return '{ [key: string]: unknown }'; } // Try to find and expand Kubernetes types if (typeName.startsWith('V1') || typeName.startsWith('V2')) { const typeFile = findKubernetesTypeFile(typeName); if (typeFile) { const typeInfo = extractInterfaceFromFile(typeFile, typeName); if (typeInfo) { visited.add(typeName); const expanded = expandTypeInfo(typeInfo, indent + ' ', depth + 1, visited); visited.delete(typeName); return expanded; } } } // Fallback return '{ [key: string]: unknown }'; } /** * Expand a complete InterfaceInfo into inline type definition */ function expandTypeInfo(typeInfo: InterfaceInfo, indent: string, depth: number, visited: Set<string>): string { const lines: string[] = []; lines.push('{'); for (const field of typeInfo.fields) { // Skip static/internal fields if (['discriminator', 'mapping', 'attributeTypeMap'].includes(field.name)) { continue; } // Add field JSDoc if present if (field.jsDoc) { lines.push(`${indent}/** ${field.jsDoc} */`); } const optional = field.optional ? '?' : ''; const expandedType = expandType(field.type, indent, depth, visited); // Handle multiline expanded types if (expandedType.includes('\n')) { lines.push(`${indent}${field.name}${optional}: ${expandedType};`); } else { lines.push(`${indent}${field.name}${optional}: ${expandedType};`); } } lines.push(indent.slice(0, -2) + '}'); return lines.join('\n'); } /** * Generate expanded inline interface with detailed nested types */ export function generateExpandedInterface( resultTypeName: string, kubernetesType: string, interfaceInfo: InterfaceInfo ): string { const lines: string[] = []; // Add interface JSDoc if (interfaceInfo.jsDoc) { lines.push('/**'); lines.push(` * ${interfaceInfo.jsDoc}`); lines.push(' */'); } lines.push(`export interface ${resultTypeName} {`); const visited = new Set<string>(); visited.add(kubernetesType); // Generate fields with recursive expansion for (const field of interfaceInfo.fields) { // Skip static fields if (['discriminator', 'mapping', 'attributeTypeMap'].includes(field.name)) { continue; } // Add field JSDoc if (field.jsDoc) { lines.push(` /** ${field.jsDoc} */`); } const optional = field.optional ? '?' : ''; const expandedType = expandType(field.type, ' ', 0, visited); lines.push(` ${field.name}${optional}: ${expandedType};`); } lines.push('}'); return lines.join('\n'); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/harche/ProDisco'

If you have feedback or need assistance with the MCP directory API, please join our Discord server