klasik 1.0.26 → 1.0.28

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 (74) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/README.md +35 -8
  3. package/USAGE_EXAMPLES.md +6 -6
  4. package/dist/cli.js +4 -2
  5. package/dist/crd/crd-generator.d.ts +22 -1
  6. package/dist/crd/crd-generator.js +73 -15
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +3 -3
  9. package/dist/jsonschema/jsonschema-generator.d.ts +1 -1
  10. package/dist/jsonschema/jsonschema-generator.js +5 -5
  11. package/dist/nestjs-type-fixer.d.ts +21 -5
  12. package/dist/nestjs-type-fixer.js +41 -7
  13. package/dist/{k8s-client-generator.d.ts → openapi-client-generator.d.ts} +3 -3
  14. package/dist/{k8s-client-generator.js → openapi-client-generator.js} +5 -5
  15. package/example.md +2 -2
  16. package/package.json +2 -2
  17. package/test-crd-fix/index.ts +2 -0
  18. package/test-crd-fix/v1alpha1/index.ts +2 -0
  19. package/test-crd-fix/v1alpha1/models/app-project-spec-cluster-resource-blacklist-item.ts +83 -0
  20. package/test-crd-fix/v1alpha1/models/app-project-spec-cluster-resource-whitelist-item.ts +83 -0
  21. package/test-crd-fix/v1alpha1/models/app-project-spec-destination-service-accounts-item.ts +83 -0
  22. package/test-crd-fix/v1alpha1/models/app-project-spec-destinations-item.ts +83 -0
  23. package/test-crd-fix/v1alpha1/models/app-project-spec-namespace-resource-blacklist-item.ts +65 -0
  24. package/test-crd-fix/v1alpha1/models/app-project-spec-namespace-resource-whitelist-item.ts +65 -0
  25. package/test-crd-fix/v1alpha1/models/app-project-spec-orphaned-resources-ignore-item.ts +83 -0
  26. package/test-crd-fix/v1alpha1/models/app-project-spec-orphaned-resources.ts +70 -0
  27. package/test-crd-fix/v1alpha1/models/app-project-spec-roles-item-jwt-tokens-item.ts +85 -0
  28. package/test-crd-fix/v1alpha1/models/app-project-spec-roles-item.ts +124 -0
  29. package/test-crd-fix/v1alpha1/models/app-project-spec-signature-keys-item.ts +47 -0
  30. package/test-crd-fix/v1alpha1/models/app-project-spec-sync-windows-item.ts +209 -0
  31. package/test-crd-fix/v1alpha1/models/app-project-spec.ts +331 -0
  32. package/test-crd-fix/v1alpha1/models/app-project.ts +119 -0
  33. package/test-crd-fix/v1alpha1/models/index.ts +16 -0
  34. package/test-crd-fix/v1alpha1/models/object-meta.ts +212 -0
  35. package/test-fix-verification/index.ts +27 -0
  36. package/test-fix-verification/models/config-map-args.ts +211 -0
  37. package/test-fix-verification/models/field-selector.ts +47 -0
  38. package/test-fix-verification/models/field-spec.ts +116 -0
  39. package/test-fix-verification/models/generator-options.ts +101 -0
  40. package/test-fix-verification/models/helm-chart.ts +241 -0
  41. package/test-fix-verification/models/image.ts +116 -0
  42. package/test-fix-verification/models/inventory.ts +70 -0
  43. package/test-fix-verification/models/kustomization-helm-globals.ts +65 -0
  44. package/test-fix-verification/models/kustomization-legacy-sort-options.ts +62 -0
  45. package/test-fix-verification/models/kustomization-sort-options-one-of.ts +75 -0
  46. package/test-fix-verification/models/kustomization-sort-options-one-of1.ts +52 -0
  47. package/test-fix-verification/models/kustomization-sort-options.ts +33 -0
  48. package/test-fix-verification/models/kustomization.ts +693 -0
  49. package/test-fix-verification/models/kvsource.ts +80 -0
  50. package/test-fix-verification/models/labels.ts +103 -0
  51. package/test-fix-verification/models/metadata.ts +98 -0
  52. package/test-fix-verification/models/name-args.ts +62 -0
  53. package/test-fix-verification/models/patch-json6902-one-of.ts +67 -0
  54. package/test-fix-verification/models/patch-json6902-one-of1.ts +67 -0
  55. package/test-fix-verification/models/patch-json6902-one-of2-value.ts +23 -0
  56. package/test-fix-verification/models/patch-json6902-one-of2.ts +116 -0
  57. package/test-fix-verification/models/patch-json6902.ts +38 -0
  58. package/test-fix-verification/models/patch-target.ts +116 -0
  59. package/test-fix-verification/models/patches-inline-patch.ts +90 -0
  60. package/test-fix-verification/models/patches-options.ts +62 -0
  61. package/test-fix-verification/models/patches-patch-path.ts +90 -0
  62. package/test-fix-verification/models/replacements-inline-one-of.ts +72 -0
  63. package/test-fix-verification/models/replacements-inline-one-of1.ts +67 -0
  64. package/test-fix-verification/models/replacements-inline.ts +35 -0
  65. package/test-fix-verification/models/replacements-path.ts +44 -0
  66. package/test-fix-verification/models/replacements-source-options.ts +80 -0
  67. package/test-fix-verification/models/replacements-source.ts +160 -0
  68. package/test-fix-verification/models/replacements-target-options.ts +80 -0
  69. package/test-fix-verification/models/replacements-target.ts +110 -0
  70. package/test-fix-verification/models/replicas.ts +62 -0
  71. package/test-fix-verification/models/secret-args.ts +229 -0
  72. package/test-fix-verification/models/selector.ts +155 -0
  73. package/test-fix-verification/models/target.ts +134 -0
  74. package/test-fix-verification/models/var.ts +93 -0
@@ -17,7 +17,9 @@
17
17
  "Bash(node test-programmatic-api.js:*)",
18
18
  "WebFetch(domain:www.schemastore.org)",
19
19
  "WebFetch(domain:json.schemastore.org)",
20
- "Bash(node dist/cli.js:*)"
20
+ "Bash(node dist/cli.js:*)",
21
+ "Bash(curl:*)",
22
+ "Bash(node --input-type=module -e:*)"
21
23
  ]
22
24
  }
23
25
  }
package/README.md CHANGED
@@ -78,9 +78,9 @@ npx klasik download \
78
78
  ### Programmatic Usage
79
79
 
80
80
  ```typescript
81
- import { K8sClientGenerator } from 'klasik';
81
+ import { OpenAPIClientGenerator } from 'klasik';
82
82
 
83
- const generator = new K8sClientGenerator();
83
+ const generator = new OpenAPIClientGenerator();
84
84
 
85
85
  await generator.generate({
86
86
  specUrl: 'https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json',
@@ -502,6 +502,9 @@ Generate TypeScript models from Kubernetes CustomResourceDefinition (CRD) YAML f
502
502
  - `-H, --header <header...>` - Custom headers for HTTP requests (format: "Key: Value")
503
503
  - `-t, --template <dir>` - Custom template directory
504
504
  - `-k, --keep-spec` - Keep the generated OpenAPI spec files (for debugging)
505
+ - `--crd-kind-snake-case` - Convert CRD kind names to snake_case for folder names (default: false)
506
+ - When enabled: `AppProject` → `app_project`
507
+ - When disabled (default): `AppProject` → `AppProject`
505
508
  - `--timeout <ms>` - Request timeout in milliseconds (default: 30000)
506
509
 
507
510
  **Examples:**
@@ -550,6 +553,18 @@ klasik generate-crd \
550
553
  --output ./generated \
551
554
  --include-status \
552
555
  --nestjs-swagger
556
+
557
+ # Generate with snake_case folder names
558
+ klasik generate-crd \
559
+ --url https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/crds/appproject-crd.yaml \
560
+ --output ./generated \
561
+ --crd-kind-snake-case
562
+ # Output structure:
563
+ # generated/
564
+ # └── app_project/ # Instead of AppProject/
565
+ # └── v1alpha1/
566
+ # └── models/
567
+ # └── app-project.ts
553
568
  ```
554
569
 
555
570
  **Output Structure:**
@@ -566,7 +581,7 @@ output/
566
581
  └── index.ts
567
582
  ```
568
583
 
569
- For multiple CRDs:
584
+ For multiple CRDs (default):
570
585
  ```
571
586
  output/
572
587
  ├── AppProject/
@@ -578,6 +593,18 @@ output/
578
593
  └── index.ts
579
594
  ```
580
595
 
596
+ With `--crd-kind-snake-case`:
597
+ ```
598
+ output/
599
+ ├── app_project/
600
+ │ ├── v1alpha1/
601
+ │ └── index.ts
602
+ ├── application/
603
+ │ ├── v1alpha1/
604
+ │ └── index.ts
605
+ └── index.ts
606
+ ```
607
+
581
608
  ### `generate-jsonschema`
582
609
 
583
610
  Generate TypeScript models from JSON Schema files.
@@ -650,12 +677,12 @@ output/
650
677
 
651
678
  ## Programmatic API
652
679
 
653
- ### K8sClientGenerator
680
+ ### OpenAPIClientGenerator
654
681
 
655
682
  ```typescript
656
- import { K8sClientGenerator } from 'klasik';
683
+ import { OpenAPIClientGenerator } from 'klasik';
657
684
 
658
- const generator = new K8sClientGenerator();
685
+ const generator = new OpenAPIClientGenerator();
659
686
 
660
687
  await generator.generate({
661
688
  specUrl: 'https://api.example.com/openapi.json',
@@ -946,9 +973,9 @@ npx klasik generate \
946
973
  ### Programmatic Example
947
974
 
948
975
  ```typescript
949
- import { K8sClientGenerator } from 'klasik';
976
+ import { OpenAPIClientGenerator } from 'klasik';
950
977
 
951
- await new K8sClientGenerator().generate({
978
+ await new OpenAPIClientGenerator().generate({
952
979
  specUrl: 'https://api.example.com/openapi.json',
953
980
  outputDir: './client',
954
981
  headers: { 'Authorization': 'Bearer token' },
package/USAGE_EXAMPLES.md CHANGED
@@ -32,10 +32,10 @@ npx klasik download \
32
32
  ### Programmatic Examples
33
33
 
34
34
  ```typescript
35
- import { K8sClientGenerator } from 'klasik';
35
+ import { OpenAPIClientGenerator } from 'klasik';
36
36
 
37
37
  // Works with both JSON and YAML
38
- await new K8sClientGenerator().generate({
38
+ await new OpenAPIClientGenerator().generate({
39
39
  specUrl: 'https://api.example.com/openapi.yaml',
40
40
  outputDir: './client'
41
41
  });
@@ -89,9 +89,9 @@ npx klasik generate \
89
89
  ### Programmatic Example - Authorization Token
90
90
 
91
91
  ```typescript
92
- import { K8sClientGenerator } from 'klasik';
92
+ import { OpenAPIClientGenerator } from 'klasik';
93
93
 
94
- const generator = new K8sClientGenerator();
94
+ const generator = new OpenAPIClientGenerator();
95
95
 
96
96
  await generator.generate({
97
97
  specUrl: 'https://api.example.com/openapi.json',
@@ -126,9 +126,9 @@ npx klasik download \
126
126
  ### Programmatic Example - With Reference Resolution
127
127
 
128
128
  ```typescript
129
- import { K8sClientGenerator } from 'klasik';
129
+ import { OpenAPIClientGenerator } from 'klasik';
130
130
 
131
- const generator = new K8sClientGenerator();
131
+ const generator = new OpenAPIClientGenerator();
132
132
 
133
133
  await generator.generate({
134
134
  specUrl: 'https://api.example.com/openapi.json',
package/dist/cli.js CHANGED
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  const commander_1 = require("commander");
38
- const k8s_client_generator_1 = require("./k8s-client-generator");
38
+ const openapi_client_generator_1 = require("./openapi-client-generator");
39
39
  const path = __importStar(require("path"));
40
40
  const program = new commander_1.Command();
41
41
  program
@@ -76,7 +76,7 @@ program
76
76
  }
77
77
  // Resolve output directory to absolute path
78
78
  const outputDir = path.resolve(options.output);
79
- const generator = new k8s_client_generator_1.K8sClientGenerator();
79
+ const generator = new openapi_client_generator_1.OpenAPIClientGenerator();
80
80
  await generator.generate({
81
81
  specUrl: options.url,
82
82
  outputDir,
@@ -147,6 +147,7 @@ program
147
147
  .option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
148
148
  .option('-t, --template <dir>', 'Custom template directory')
149
149
  .option('-k, --keep-spec', 'Keep the generated OpenAPI spec files', false)
150
+ .option('--crd-kind-snake-case', 'Convert CRD kind names to snake_case for folder names', false)
150
151
  .action(async (options) => {
151
152
  try {
152
153
  // Parse headers if provided
@@ -177,6 +178,7 @@ program
177
178
  classValidator: options.classValidator,
178
179
  templateDir: options.template,
179
180
  keepSpec: options.keepSpec,
181
+ crdKindSnakeCase: options.crdKindSnakeCase,
180
182
  });
181
183
  process.exit(0);
182
184
  }
@@ -54,6 +54,11 @@ export interface CRDGeneratorOptions {
54
54
  * @default false
55
55
  */
56
56
  keepSpec?: boolean;
57
+ /**
58
+ * Convert CRD kind names to snake_case for folder names
59
+ * @default false
60
+ */
61
+ crdKindSnakeCase?: boolean;
57
62
  }
58
63
  /**
59
64
  * Main orchestrator for generating TypeScript models from Kubernetes CRDs
@@ -61,7 +66,7 @@ export interface CRDGeneratorOptions {
61
66
  export declare class CRDGenerator {
62
67
  private parser;
63
68
  private converter;
64
- private k8sGenerator;
69
+ private openapiGenerator;
65
70
  constructor();
66
71
  /**
67
72
  * Main generation workflow
@@ -76,22 +81,38 @@ export declare class CRDGenerator {
76
81
  * @returns Array of version directory names
77
82
  */
78
83
  private processCRD;
84
+ /**
85
+ * Format CRD kind name for use as folder name
86
+ * @param kind The CRD kind name (e.g., "AppProject")
87
+ * @param useSnakeCase Whether to convert to snake_case
88
+ * @returns Formatted name (e.g., "app_project" if useSnakeCase, otherwise "AppProject")
89
+ */
90
+ private formatKindName;
79
91
  /**
80
92
  * Generate index file for a CRD (when multiple CRDs in one file)
81
93
  * @param crdDir CRD directory
82
94
  * @param versions Array of version names
95
+ * @param skipJsExtensions Whether to skip .js extensions
83
96
  */
84
97
  private generateCRDIndex;
98
+ /**
99
+ * Generate index file for a version directory (re-exports from models)
100
+ * @param versionDir Version directory path
101
+ * @param skipJsExtensions Whether to skip .js extensions in exports
102
+ */
103
+ private generateVersionIndexFile;
85
104
  /**
86
105
  * Generate main index file (when multiple CRDs in one file)
87
106
  * @param outputDir Output directory
88
107
  * @param crdNames Array of CRD kind names
108
+ * @param skipJsExtensions Whether to skip .js extensions
89
109
  */
90
110
  private generateMainIndex;
91
111
  /**
92
112
  * Generate version index file (for single CRD)
93
113
  * @param outputDir Output directory
94
114
  * @param versions Array of version names
115
+ * @param skipJsExtensions Whether to skip .js extensions
95
116
  */
96
117
  private generateVersionIndex;
97
118
  /**
@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.CRDGenerator = void 0;
40
40
  const crd_parser_1 = require("./crd-parser");
41
41
  const crd_to_openapi_1 = require("./crd-to-openapi");
42
- const k8s_client_generator_1 = require("../k8s-client-generator");
42
+ const openapi_client_generator_1 = require("../openapi-client-generator");
43
43
  const fs = __importStar(require("fs"));
44
44
  const path = __importStar(require("path"));
45
45
  const axios_1 = __importDefault(require("axios"));
@@ -50,14 +50,14 @@ class CRDGenerator {
50
50
  constructor() {
51
51
  this.parser = new crd_parser_1.CRDParser();
52
52
  this.converter = new crd_to_openapi_1.CRDToOpenAPIConverter();
53
- this.k8sGenerator = new k8s_client_generator_1.K8sClientGenerator();
53
+ this.openapiGenerator = new openapi_client_generator_1.OpenAPIClientGenerator();
54
54
  }
55
55
  /**
56
56
  * Main generation workflow
57
57
  * @param options Generation options
58
58
  */
59
59
  async generate(options) {
60
- const { urls: urlsInput, outputDir, includeStatus = false, headers, timeout, fixEsmImports = false, nestJsSwagger = false, classValidator = false, skipJsExtensions = false, templateDir, keepSpec = false, } = options;
60
+ const { urls: urlsInput, outputDir, includeStatus = false, headers, timeout, fixEsmImports = false, nestJsSwagger = false, classValidator = false, skipJsExtensions = false, templateDir, keepSpec = false, crdKindSnakeCase = false, } = options;
61
61
  // Normalize to array
62
62
  const urls = Array.isArray(urlsInput) ? urlsInput : [urlsInput];
63
63
  try {
@@ -115,8 +115,9 @@ class CRDGenerator {
115
115
  for (const crd of allCRDs) {
116
116
  console.log(`\nProcessing CRD: ${crd.spec.names.kind}`);
117
117
  // Determine output directory structure
118
+ const formattedKind = this.formatKindName(crd.spec.names.kind, crdKindSnakeCase);
118
119
  const crdOutputDir = allCRDs.length > 1
119
- ? path.join(outputDir, crd.spec.names.kind)
120
+ ? path.join(outputDir, formattedKind)
120
121
  : outputDir;
121
122
  const versionDirs = await this.processCRD(crd, crdOutputDir, {
122
123
  includeStatus,
@@ -131,18 +132,19 @@ class CRDGenerator {
131
132
  });
132
133
  // Generate index file for this CRD if multiple CRDs
133
134
  if (allCRDs.length > 1) {
134
- this.generateCRDIndex(crdOutputDir, versionDirs);
135
+ this.generateCRDIndex(crdOutputDir, versionDirs, skipJsExtensions);
135
136
  }
136
137
  }
137
138
  // Step 4: Generate main index file
138
139
  console.log('\nGenerating index files...');
139
140
  if (allCRDs.length > 1) {
140
- this.generateMainIndex(outputDir, allCRDs.map(c => c.spec.names.kind));
141
+ const formattedKindNames = allCRDs.map(c => this.formatKindName(c.spec.names.kind, crdKindSnakeCase));
142
+ this.generateMainIndex(outputDir, formattedKindNames, skipJsExtensions);
141
143
  }
142
144
  else {
143
145
  // For single CRD, generate version index
144
146
  const versionNames = allCRDs[0].spec.versions.map(v => v.name);
145
- this.generateVersionIndex(outputDir, versionNames);
147
+ this.generateVersionIndex(outputDir, versionNames, skipJsExtensions);
146
148
  }
147
149
  console.log('\n✅ CRD client generation completed successfully!');
148
150
  console.log(`📁 Generated files location: ${outputDir}`);
@@ -183,8 +185,8 @@ class CRDGenerator {
183
185
  const specPath = path.resolve(versionDir, '.openapi-spec.json');
184
186
  fs.writeFileSync(specPath, JSON.stringify(openApiSpec, null, 2), 'utf-8');
185
187
  try {
186
- // Generate TypeScript models using existing K8sClientGenerator
187
- await this.k8sGenerator.generate({
188
+ // Generate TypeScript models using OpenAPIClientGenerator
189
+ await this.openapiGenerator.generate({
188
190
  specUrl: specPath,
189
191
  outputDir: versionDir,
190
192
  mode: 'models-only',
@@ -195,6 +197,8 @@ class CRDGenerator {
195
197
  templateDir: options.templateDir,
196
198
  keepSpec: false, // Don't keep the OpenAPI spec in the final output
197
199
  });
200
+ // Generate version-level index file
201
+ this.generateVersionIndexFile(versionDir, options.skipJsExtensions);
198
202
  // Clean up temp spec file if not keeping it
199
203
  if (!options.keepSpec) {
200
204
  if (fs.existsSync(specPath)) {
@@ -209,27 +213,79 @@ class CRDGenerator {
209
213
  }
210
214
  return versionDirs;
211
215
  }
216
+ /**
217
+ * Format CRD kind name for use as folder name
218
+ * @param kind The CRD kind name (e.g., "AppProject")
219
+ * @param useSnakeCase Whether to convert to snake_case
220
+ * @returns Formatted name (e.g., "app_project" if useSnakeCase, otherwise "AppProject")
221
+ */
222
+ formatKindName(kind, useSnakeCase) {
223
+ if (!useSnakeCase) {
224
+ return kind;
225
+ }
226
+ // Convert PascalCase to snake_case
227
+ // AppProject -> app_project
228
+ // HTTPServer -> http_server
229
+ return kind
230
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2') // Add underscore between lowercase and uppercase
231
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1_$2') // Handle consecutive capitals (HTTPServer -> HTTP_Server)
232
+ .toLowerCase();
233
+ }
212
234
  /**
213
235
  * Generate index file for a CRD (when multiple CRDs in one file)
214
236
  * @param crdDir CRD directory
215
237
  * @param versions Array of version names
238
+ * @param skipJsExtensions Whether to skip .js extensions
216
239
  */
217
- generateCRDIndex(crdDir, versions) {
240
+ generateCRDIndex(crdDir, versions, skipJsExtensions = false) {
241
+ const extension = skipJsExtensions ? '' : '.js';
218
242
  const exports = versions
219
- .map(version => `export * as ${version.replace(/\./g, '_')} from './${version}';`)
243
+ .map(version => `export * as ${version.replace(/\./g, '_')} from './${version}/index${extension}';`)
220
244
  .join('\n');
221
245
  const indexContent = `// Auto-generated index for CRD\n${exports}\n`;
222
246
  const indexPath = path.join(crdDir, 'index.ts');
223
247
  fs.writeFileSync(indexPath, indexContent, 'utf-8');
224
248
  }
249
+ /**
250
+ * Generate index file for a version directory (re-exports from models)
251
+ * @param versionDir Version directory path
252
+ * @param skipJsExtensions Whether to skip .js extensions in exports
253
+ */
254
+ generateVersionIndexFile(versionDir, skipJsExtensions = false) {
255
+ const extension = skipJsExtensions ? '' : '.js';
256
+ // First, create models/index.ts if it doesn't exist
257
+ // OpenAPI Generator in models-only mode doesn't create this file
258
+ const modelsDir = path.join(versionDir, 'models');
259
+ const modelsIndexPath = path.join(modelsDir, 'index.ts');
260
+ if (!fs.existsSync(modelsIndexPath)) {
261
+ // Get all .ts files in models directory (excluding index.ts itself)
262
+ const modelFiles = fs.readdirSync(modelsDir)
263
+ .filter(file => file.endsWith('.ts') && file !== 'index.ts')
264
+ .map(file => file.replace('.ts', ''));
265
+ // Generate exports for each model file
266
+ const exports = modelFiles
267
+ .map(file => `export * from './${file}${extension}';`)
268
+ .join('\n');
269
+ const modelsIndexContent = `// Auto-generated models index\n${exports}\n`;
270
+ fs.writeFileSync(modelsIndexPath, modelsIndexContent, 'utf-8');
271
+ console.log(` Generated models index: ${modelsIndexPath}`);
272
+ }
273
+ // Now create version index that exports from models
274
+ const indexContent = `// Auto-generated version index\nexport * from './models/index${extension}';\n`;
275
+ const indexPath = path.join(versionDir, 'index.ts');
276
+ fs.writeFileSync(indexPath, indexContent, 'utf-8');
277
+ console.log(` Generated version index: ${indexPath}`);
278
+ }
225
279
  /**
226
280
  * Generate main index file (when multiple CRDs in one file)
227
281
  * @param outputDir Output directory
228
282
  * @param crdNames Array of CRD kind names
283
+ * @param skipJsExtensions Whether to skip .js extensions
229
284
  */
230
- generateMainIndex(outputDir, crdNames) {
285
+ generateMainIndex(outputDir, crdNames, skipJsExtensions = false) {
286
+ const extension = skipJsExtensions ? '' : '.js';
231
287
  const exports = crdNames
232
- .map(name => `export * as ${name} from './${name}';`)
288
+ .map(name => `export * as ${name} from './${name}/index${extension}';`)
233
289
  .join('\n');
234
290
  const indexContent = `// Auto-generated main index\n${exports}\n`;
235
291
  const indexPath = path.join(outputDir, 'index.ts');
@@ -239,10 +295,12 @@ class CRDGenerator {
239
295
  * Generate version index file (for single CRD)
240
296
  * @param outputDir Output directory
241
297
  * @param versions Array of version names
298
+ * @param skipJsExtensions Whether to skip .js extensions
242
299
  */
243
- generateVersionIndex(outputDir, versions) {
300
+ generateVersionIndex(outputDir, versions, skipJsExtensions = false) {
301
+ const extension = skipJsExtensions ? '' : '.js';
244
302
  const exports = versions
245
- .map(version => `export * as ${version.replace(/\./g, '_')} from './${version}';`)
303
+ .map(version => `export * as ${version.replace(/\./g, '_')} from './${version}/index${extension}';`)
246
304
  .join('\n');
247
305
  const indexContent = `// Auto-generated version index\n${exports}\n`;
248
306
  const indexPath = path.join(outputDir, 'index.ts');
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { K8sClientGenerator, K8sClientGeneratorOptions, GenerationMode } from './k8s-client-generator';
1
+ export { OpenAPIClientGenerator, OpenAPIClientGeneratorOptions, GenerationMode } from './openapi-client-generator';
2
2
  export { SpecDownloader, DownloadOptions } from './spec-downloader';
3
3
  export { EsmFixer } from './esm-fixer';
4
4
  export { CRDGenerator, CRDGeneratorOptions } from './crd/crd-generator';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JSONSchemaToOpenAPIConverter = exports.JSONSchemaParser = exports.JSONSchemaGenerator = exports.CRDToOpenAPIConverter = exports.CRDParser = exports.CRDGenerator = exports.EsmFixer = exports.SpecDownloader = exports.K8sClientGenerator = void 0;
4
- var k8s_client_generator_1 = require("./k8s-client-generator");
5
- Object.defineProperty(exports, "K8sClientGenerator", { enumerable: true, get: function () { return k8s_client_generator_1.K8sClientGenerator; } });
3
+ exports.JSONSchemaToOpenAPIConverter = exports.JSONSchemaParser = exports.JSONSchemaGenerator = exports.CRDToOpenAPIConverter = exports.CRDParser = exports.CRDGenerator = exports.EsmFixer = exports.SpecDownloader = exports.OpenAPIClientGenerator = void 0;
4
+ var openapi_client_generator_1 = require("./openapi-client-generator");
5
+ Object.defineProperty(exports, "OpenAPIClientGenerator", { enumerable: true, get: function () { return openapi_client_generator_1.OpenAPIClientGenerator; } });
6
6
  var spec_downloader_1 = require("./spec-downloader");
7
7
  Object.defineProperty(exports, "SpecDownloader", { enumerable: true, get: function () { return spec_downloader_1.SpecDownloader; } });
8
8
  var esm_fixer_1 = require("./esm-fixer");
@@ -56,7 +56,7 @@ export interface JSONSchemaGeneratorOptions {
56
56
  export declare class JSONSchemaGenerator {
57
57
  private parser;
58
58
  private converter;
59
- private k8sGenerator;
59
+ private openapiGenerator;
60
60
  constructor();
61
61
  /**
62
62
  * Main generation workflow
@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.JSONSchemaGenerator = void 0;
40
40
  const jsonschema_parser_1 = require("./jsonschema-parser");
41
41
  const jsonschema_to_openapi_1 = require("./jsonschema-to-openapi");
42
- const k8s_client_generator_1 = require("../k8s-client-generator");
42
+ const openapi_client_generator_1 = require("../openapi-client-generator");
43
43
  const fs = __importStar(require("fs"));
44
44
  const path = __importStar(require("path"));
45
45
  const axios_1 = __importDefault(require("axios"));
@@ -50,7 +50,7 @@ class JSONSchemaGenerator {
50
50
  constructor() {
51
51
  this.parser = new jsonschema_parser_1.JSONSchemaParser();
52
52
  this.converter = new jsonschema_to_openapi_1.JSONSchemaToOpenAPIConverter();
53
- this.k8sGenerator = new k8s_client_generator_1.K8sClientGenerator();
53
+ this.openapiGenerator = new openapi_client_generator_1.OpenAPIClientGenerator();
54
54
  }
55
55
  /**
56
56
  * Main generation workflow
@@ -122,9 +122,9 @@ class JSONSchemaGenerator {
122
122
  const specPath = path.resolve(outputDir, '.openapi-spec.json');
123
123
  fs.writeFileSync(specPath, JSON.stringify(openApiSpec, null, 2), 'utf-8');
124
124
  try {
125
- // Generate TypeScript models using K8sClientGenerator
125
+ // Generate TypeScript models using OpenAPIClientGenerator
126
126
  console.log(' Generating TypeScript models...');
127
- await this.k8sGenerator.generate({
127
+ await this.openapiGenerator.generate({
128
128
  specUrl: specPath,
129
129
  outputDir,
130
130
  mode: 'models-only',
@@ -133,7 +133,7 @@ class JSONSchemaGenerator {
133
133
  fixEsmImports,
134
134
  skipJsExtensions,
135
135
  templateDir,
136
- keepSpec: false, // Don't keep the OpenAPI spec in K8sClientGenerator
136
+ keepSpec: false, // Don't keep the OpenAPI spec in OpenAPIClientGenerator
137
137
  });
138
138
  // Track definition names for index generation
139
139
  allDefinitions.push(...Object.keys(schema.definitions));
@@ -1,9 +1,15 @@
1
1
  /**
2
- * Fixes primitive type casing in NestJS @ApiProperty decorators
2
+ * Fixes multiple issues in NestJS @ApiProperty decorators
3
3
  *
4
4
  * When openapi-class-transformer generates files with @ApiProperty decorators,
5
- * it uses lowercase string literals for primitive types (type: 'string'), but
6
- * NestJS Swagger expects capitalized constructor references (type: String).
5
+ * several issues can occur:
6
+ *
7
+ * 1. Primitive type casing: Uses lowercase string literals (type: 'string')
8
+ * instead of constructor references (type: String)
9
+ * 2. Unescaped backticks: Descriptions wrapped in backticks don't escape
10
+ * backticks within the text
11
+ * 3. Undefined descriptions: Some decorators have description: undefined
12
+ * instead of empty strings
7
13
  *
8
14
  * This utility transforms:
9
15
  * - type: 'string' -> type: String
@@ -12,9 +18,11 @@
12
18
  * - type: () => [string] -> type: [String]
13
19
  * - type: () => [number] -> type: [Number]
14
20
  * - type: () => [boolean] -> type: [Boolean]
21
+ * - description: `text `code` here` -> description: `text \`code\` here`
22
+ * - description: undefined -> description: ''
15
23
  *
16
24
  * IMPORTANT: Does NOT modify attributeTypeMap at the bottom of files - those
17
- * metadata strings must remain lowercase.
25
+ * metadata strings must remain unchanged.
18
26
  */
19
27
  export declare class NestJsTypeFixer {
20
28
  /**
@@ -32,9 +40,17 @@ export declare class NestJsTypeFixer {
32
40
  */
33
41
  private fixPrimitiveTypes;
34
42
  /**
35
- * Apply type transformations to content
43
+ * Apply all transformations to content
36
44
  */
37
45
  private applyTransformations;
46
+ /**
47
+ * Escape backticks in description template literals
48
+ */
49
+ private escapeBackticksInDescriptions;
50
+ /**
51
+ * Replace undefined descriptions with empty strings
52
+ */
53
+ private fixUndefinedDescriptions;
38
54
  /**
39
55
  * Get all TypeScript files in a directory recursively
40
56
  */
@@ -37,11 +37,17 @@ exports.NestJsTypeFixer = void 0;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  /**
40
- * Fixes primitive type casing in NestJS @ApiProperty decorators
40
+ * Fixes multiple issues in NestJS @ApiProperty decorators
41
41
  *
42
42
  * When openapi-class-transformer generates files with @ApiProperty decorators,
43
- * it uses lowercase string literals for primitive types (type: 'string'), but
44
- * NestJS Swagger expects capitalized constructor references (type: String).
43
+ * several issues can occur:
44
+ *
45
+ * 1. Primitive type casing: Uses lowercase string literals (type: 'string')
46
+ * instead of constructor references (type: String)
47
+ * 2. Unescaped backticks: Descriptions wrapped in backticks don't escape
48
+ * backticks within the text
49
+ * 3. Undefined descriptions: Some decorators have description: undefined
50
+ * instead of empty strings
45
51
  *
46
52
  * This utility transforms:
47
53
  * - type: 'string' -> type: String
@@ -50,16 +56,18 @@ const path = __importStar(require("path"));
50
56
  * - type: () => [string] -> type: [String]
51
57
  * - type: () => [number] -> type: [Number]
52
58
  * - type: () => [boolean] -> type: [Boolean]
59
+ * - description: `text `code` here` -> description: `text \`code\` here`
60
+ * - description: undefined -> description: ''
53
61
  *
54
62
  * IMPORTANT: Does NOT modify attributeTypeMap at the bottom of files - those
55
- * metadata strings must remain lowercase.
63
+ * metadata strings must remain unchanged.
56
64
  */
57
65
  class NestJsTypeFixer {
58
66
  /**
59
67
  * Fix all TypeScript files in a directory recursively
60
68
  */
61
69
  async fixDirectory(directory) {
62
- console.log('Fixing primitive type casing in @ApiProperty decorators...');
70
+ console.log('Fixing @ApiProperty decorator issues...');
63
71
  const files = this.getAllTsFiles(directory);
64
72
  let fixedCount = 0;
65
73
  for (const file of files) {
@@ -68,7 +76,7 @@ class NestJsTypeFixer {
68
76
  fixedCount++;
69
77
  }
70
78
  }
71
- console.log(`✅ Fixed primitive types in ${fixedCount} file(s)`);
79
+ console.log(`✅ Fixed @ApiProperty issues in ${fixedCount} file(s)`);
72
80
  }
73
81
  /**
74
82
  * Fix primitive types in a single file
@@ -116,7 +124,7 @@ class NestJsTypeFixer {
116
124
  return fixedSafeZone + protectedZone;
117
125
  }
118
126
  /**
119
- * Apply type transformations to content
127
+ * Apply all transformations to content
120
128
  */
121
129
  applyTransformations(content) {
122
130
  // Fix simple primitive types: type: 'string' -> type: String
@@ -129,8 +137,34 @@ class NestJsTypeFixer {
129
137
  const capitalized = primitive.charAt(0).toUpperCase() + primitive.slice(1);
130
138
  return `type: [${capitalized}]`;
131
139
  });
140
+ // Fix unescaped backticks in description strings
141
+ content = this.escapeBackticksInDescriptions(content);
142
+ // Fix undefined descriptions
143
+ content = this.fixUndefinedDescriptions(content);
132
144
  return content;
133
145
  }
146
+ /**
147
+ * Escape backticks in description template literals
148
+ */
149
+ escapeBackticksInDescriptions(content) {
150
+ // Match "description: `" followed by anything until "`,"
151
+ // Uses non-greedy match (.*?) with dotall flag (s) to handle multiline
152
+ // This works even if there are unescaped backticks inside
153
+ return content.replace(/(description:\s*`)(.*?)(`,)/gs, (fullMatch, prefix, description, suffix) => {
154
+ // Escape any unescaped backticks in the description
155
+ // Uses negative lookbehind to avoid double-escaping
156
+ const escaped = description.replace(/(?<!\\)`/g, '\\`');
157
+ return prefix + escaped + suffix;
158
+ });
159
+ }
160
+ /**
161
+ * Replace undefined descriptions with empty strings
162
+ */
163
+ fixUndefinedDescriptions(content) {
164
+ // Replace description: undefined with description: ''
165
+ // Only affects @ApiProperty decorators (split-point protects attributeTypeMap)
166
+ return content.replace(/description:\s*undefined/g, "description: ''");
167
+ }
134
168
  /**
135
169
  * Get all TypeScript files in a directory recursively
136
170
  */
@@ -1,5 +1,5 @@
1
1
  export type GenerationMode = 'full' | 'models-only';
2
- export interface K8sClientGeneratorOptions {
2
+ export interface OpenAPIClientGeneratorOptions {
3
3
  /**
4
4
  * Remote URL to download the OpenAPI spec from
5
5
  */
@@ -65,13 +65,13 @@ export interface K8sClientGeneratorOptions {
65
65
  */
66
66
  timeout?: number;
67
67
  }
68
- export declare class K8sClientGenerator {
68
+ export declare class OpenAPIClientGenerator {
69
69
  private downloader;
70
70
  constructor();
71
71
  /**
72
72
  * Generate TypeScript client from a remote OpenAPI spec URL
73
73
  */
74
- generate(options: K8sClientGeneratorOptions): Promise<void>;
74
+ generate(options: OpenAPIClientGeneratorOptions): Promise<void>;
75
75
  /**
76
76
  * Prepare the output directory
77
77
  */