klasik 1.0.12 โ†’ 1.0.13

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 CHANGED
@@ -6,6 +6,8 @@ Download OpenAPI specifications from remote URLs and generate TypeScript clients
6
6
 
7
7
  - ๐Ÿ“ฅ **Download OpenAPI specs** from remote URLs or local files
8
8
  - ๐Ÿ“ **Multiple input formats** - HTTP/HTTPS URLs, file:// URLs, absolute/relative paths
9
+ - ๐Ÿ” **Authentication support** - Custom headers including Bearer tokens and API keys
10
+ - ๐Ÿ”— **External reference resolution** - Automatically download referenced schema files (`$ref`)
9
11
  - ๐Ÿ”„ **Automatic transformation** - Converts API responses to class instances using class-transformer
10
12
  - ๐ŸŽฏ **Type-safe** - Full TypeScript support with decorators
11
13
  - ๐Ÿ› ๏ธ **Configurable** - Custom headers, timeouts, and templates
@@ -81,6 +83,9 @@ Generate a TypeScript client from an OpenAPI spec (remote URL or local file).
81
83
  - Supports: `https://...`, `http://...`, `file://...`, `/absolute/path`, `./relative/path`
82
84
  - `-o, --output <dir>` - Output directory for generated client code (required)
83
85
  - `-H, --header <header...>` - Custom headers for HTTP requests (format: "Key: Value")
86
+ - Can be used multiple times for multiple headers
87
+ - Perfect for authorization: `--header "Authorization: Bearer token"`
88
+ - `-r, --resolve-refs` - Resolve and download external `$ref` references
84
89
  - `-t, --template <dir>` - Custom template directory
85
90
  - `-k, --keep-spec` - Keep the downloaded spec file after generation
86
91
  - `--timeout <ms>` - Request timeout in milliseconds for HTTP requests (default: 30000)
@@ -88,13 +93,27 @@ Generate a TypeScript client from an OpenAPI spec (remote URL or local file).
88
93
  **Examples:**
89
94
 
90
95
  ```bash
91
- # From remote URL
96
+ # From remote URL with authorization
92
97
  klasik generate \
93
98
  --url https://api.example.com/openapi.json \
94
99
  --output ./client \
95
100
  --header "Authorization: Bearer token123" \
96
101
  --timeout 60000
97
102
 
103
+ # With external reference resolution
104
+ klasik generate \
105
+ --url https://api.example.com/openapi.json \
106
+ --output ./client \
107
+ --header "Authorization: Bearer token123" \
108
+ --resolve-refs
109
+
110
+ # Multiple headers
111
+ klasik generate \
112
+ --url https://api.example.com/openapi.json \
113
+ --output ./client \
114
+ --header "Authorization: Bearer token" \
115
+ --header "X-API-Version: v1"
116
+
98
117
  # From local file
99
118
  klasik generate \
100
119
  --url ./my-spec.json \
@@ -114,15 +133,24 @@ Download an OpenAPI spec from a remote URL without generating code.
114
133
  - `-u, --url <url>` - Remote URL to download the OpenAPI spec from (required)
115
134
  - `-o, --output <file>` - Output file path for the downloaded spec (required)
116
135
  - `-H, --header <header...>` - Custom headers for the request
136
+ - `-r, --resolve-refs` - Resolve and download external `$ref` references
117
137
  - `--timeout <ms>` - Request timeout in milliseconds (default: 30000)
118
138
 
119
- **Example:**
139
+ **Examples:**
120
140
 
121
141
  ```bash
142
+ # Download spec only
122
143
  klasik download \
123
144
  --url https://api.example.com/openapi.json \
124
145
  --output ./specs/api-spec.json \
125
146
  --header "Authorization: Bearer token123"
147
+
148
+ # Download spec with all external references
149
+ klasik download \
150
+ --url https://api.example.com/openapi.json \
151
+ --output ./specs/api-spec.json \
152
+ --header "Authorization: Bearer token123" \
153
+ --resolve-refs
126
154
  ```
127
155
 
128
156
  ## Programmatic API
@@ -141,6 +169,7 @@ await generator.generate({
141
169
  'Authorization': 'Bearer token123',
142
170
  'X-Custom-Header': 'value'
143
171
  },
172
+ resolveReferences: true, // Download external $ref files
144
173
  templateDir: './custom-templates', // Optional
145
174
  keepSpec: true, // Keep downloaded spec file
146
175
  timeout: 60000 // Request timeout in ms
@@ -160,6 +189,7 @@ const specPath = await downloader.download({
160
189
  headers: {
161
190
  'Authorization': 'Bearer token123'
162
191
  },
192
+ resolveReferences: true, // Download external $ref files
163
193
  timeout: 30000
164
194
  });
165
195
 
@@ -235,6 +265,90 @@ const user = response.data; // user is instanceof User โœ…
235
265
  console.log(user.name); // Fully typed!
236
266
  ```
237
267
 
268
+ ## External Reference Resolution
269
+
270
+ Klasik can automatically resolve and download external `$ref` references in your OpenAPI specs. This is useful when your spec splits schemas across multiple files.
271
+
272
+ ### How It Works
273
+
274
+ When you enable `--resolve-refs` (CLI) or `resolveReferences: true` (programmatic), klasik will:
275
+
276
+ 1. **Parse the main spec** for external `$ref` references
277
+ 2. **Download all referenced files** preserving directory structure
278
+ 3. **Recursively resolve nested references** in downloaded files
279
+ 4. **Use the same authentication** for all downloads
280
+
281
+ ### Example OpenAPI Spec with External References
282
+
283
+ ```json
284
+ {
285
+ "openapi": "3.0.0",
286
+ "paths": {
287
+ "/users": {
288
+ "get": {
289
+ "responses": {
290
+ "200": {
291
+ "content": {
292
+ "application/json": {
293
+ "schema": {
294
+ "$ref": "./schemas/users-orgs.yaml#/components/schemas/Org"
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+ ```
305
+
306
+ With `--resolve-refs`, klasik will automatically download `./schemas/users-orgs.yaml` and any files it references.
307
+
308
+ ### Output Structure
309
+
310
+ Referenced files are downloaded preserving the directory structure:
311
+
312
+ ```
313
+ output/
314
+ โ”œโ”€โ”€ openapi-spec.json # Main spec
315
+ โ””โ”€โ”€ schemas/
316
+ โ”œโ”€โ”€ users-orgs.yaml # Referenced schema
317
+ โ””โ”€โ”€ common/
318
+ โ””โ”€โ”€ types.yaml # Nested reference
319
+ ```
320
+
321
+ ### Supported Reference Types
322
+
323
+ - **Relative paths**: `./schemas/user.yaml`, `../common/types.yaml`
324
+ - **Remote URLs**: `https://api.example.com/schemas/user.yaml`
325
+ - **Mixed**: Main spec from HTTPS, references can be relative or absolute
326
+
327
+ ### CLI Example
328
+
329
+ ```bash
330
+ # Download spec with all external references
331
+ npx klasik generate \
332
+ --url https://api.example.com/openapi.json \
333
+ --output ./client \
334
+ --header "Authorization: Bearer token" \
335
+ --resolve-refs
336
+ ```
337
+
338
+ ### Programmatic Example
339
+
340
+ ```typescript
341
+ import { K8sClientGenerator } from 'klasik';
342
+
343
+ await new K8sClientGenerator().generate({
344
+ specUrl: 'https://api.example.com/openapi.json',
345
+ outputDir: './client',
346
+ headers: { 'Authorization': 'Bearer token' },
347
+ resolveReferences: true,
348
+ keepSpec: true // Keep all downloaded files
349
+ });
350
+ ```
351
+
238
352
  ## Advanced Configuration
239
353
 
240
354
  ### Custom Error Handling
@@ -0,0 +1,160 @@
1
+ # Klasik Usage Examples
2
+
3
+ ## Using Custom Headers (Authorization)
4
+
5
+ ### CLI Example - Authorization Token
6
+
7
+ ```bash
8
+ # Using Bearer token
9
+ npx klasik generate \
10
+ --url https://api.example.com/openapi.json \
11
+ --output ./client \
12
+ --header "Authorization: Bearer your-token-here"
13
+
14
+ # Using API key
15
+ npx klasik generate \
16
+ --url https://api.example.com/openapi.json \
17
+ --output ./client \
18
+ --header "X-API-Key: your-api-key"
19
+
20
+ # Multiple headers
21
+ npx klasik generate \
22
+ --url https://api.example.com/openapi.json \
23
+ --output ./client \
24
+ --header "Authorization: Bearer your-token" \
25
+ --header "X-Custom-Header: custom-value"
26
+ ```
27
+
28
+ ### Programmatic Example - Authorization Token
29
+
30
+ ```typescript
31
+ import { K8sClientGenerator } from 'klasik';
32
+
33
+ const generator = new K8sClientGenerator();
34
+
35
+ await generator.generate({
36
+ specUrl: 'https://api.example.com/openapi.json',
37
+ outputDir: './client',
38
+ headers: {
39
+ 'Authorization': 'Bearer your-token-here',
40
+ 'X-API-Key': 'your-api-key'
41
+ }
42
+ });
43
+ ```
44
+
45
+ ## Resolving External References
46
+
47
+ ### CLI Example - With Reference Resolution
48
+
49
+ ```bash
50
+ # Download spec and all external $ref files
51
+ npx klasik generate \
52
+ --url https://api.example.com/openapi.json \
53
+ --output ./client \
54
+ --resolve-refs \
55
+ --header "Authorization: Bearer your-token"
56
+
57
+ # Download only (no code generation)
58
+ npx klasik download \
59
+ --url https://api.example.com/openapi.json \
60
+ --output ./specs/api-spec.json \
61
+ --resolve-refs \
62
+ --header "Authorization: Bearer your-token"
63
+ ```
64
+
65
+ ### Programmatic Example - With Reference Resolution
66
+
67
+ ```typescript
68
+ import { K8sClientGenerator } from 'klasik';
69
+
70
+ const generator = new K8sClientGenerator();
71
+
72
+ await generator.generate({
73
+ specUrl: 'https://api.example.com/openapi.json',
74
+ outputDir: './client',
75
+ resolveReferences: true,
76
+ headers: {
77
+ 'Authorization': 'Bearer your-token-here'
78
+ }
79
+ });
80
+ ```
81
+
82
+ ## How Reference Resolution Works
83
+
84
+ When you enable `--resolve-refs` (or `resolveReferences: true`), klasik will:
85
+
86
+ 1. **Parse the main OpenAPI spec** for external `$ref` references like:
87
+ ```yaml
88
+ schema:
89
+ $ref: './schemas/users-orgs.yaml#/components/schemas/Org'
90
+ ```
91
+
92
+ 2. **Download all referenced files** preserving the directory structure:
93
+ ```
94
+ output/
95
+ โ”œโ”€โ”€ openapi-spec.json (main spec)
96
+ โ””โ”€โ”€ schemas/
97
+ โ””โ”€โ”€ users-orgs.yaml (referenced schema)
98
+ ```
99
+
100
+ 3. **Recursively resolve nested references** - if `users-orgs.yaml` has its own `$ref` to other files, those will be downloaded too
101
+
102
+ 4. **Use the same headers** for all downloads (useful when all files require authentication)
103
+
104
+ ### Supported Reference Types
105
+
106
+ - **Relative paths**: `./schemas/user.yaml`, `../common/types.yaml`
107
+ - **Remote URLs**: `https://api.example.com/schemas/user.yaml`
108
+ - **Mixed**: Main spec from HTTPS, references can be relative or absolute
109
+
110
+ ### Example OpenAPI Spec with External References
111
+
112
+ ```json
113
+ {
114
+ "openapi": "3.0.0",
115
+ "info": {
116
+ "title": "My API",
117
+ "version": "1.0.0"
118
+ },
119
+ "paths": {
120
+ "/users": {
121
+ "get": {
122
+ "responses": {
123
+ "200": {
124
+ "content": {
125
+ "application/json": {
126
+ "schema": {
127
+ "$ref": "./schemas/users.yaml#/components/schemas/UserList"
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ When you run with `--resolve-refs`, klasik will automatically download `./schemas/users.yaml`.
140
+
141
+ ## Complete Example
142
+
143
+ ```bash
144
+ # Full workflow with private API
145
+ npx klasik generate \
146
+ --url https://private-api.example.com/v1/openapi.json \
147
+ --output ./generated-client \
148
+ --header "Authorization: Bearer eyJhbGc..." \
149
+ --header "X-API-Version: v1" \
150
+ --resolve-refs \
151
+ --keep-spec \
152
+ --timeout 60000
153
+ ```
154
+
155
+ This will:
156
+ - Download the OpenAPI spec with authorization headers
157
+ - Resolve and download all external schema references
158
+ - Generate the TypeScript client
159
+ - Keep all downloaded specs in the output directory
160
+ - Use a 60-second timeout for all HTTP requests
package/dist/cli.js CHANGED
@@ -51,6 +51,7 @@ program
51
51
  .option('-H, --header <header...>', 'Custom headers for the request (format: "Key: Value")')
52
52
  .option('-t, --template <dir>', 'Custom template directory')
53
53
  .option('-k, --keep-spec', 'Keep the downloaded spec file after generation', false)
54
+ .option('-r, --resolve-refs', 'Resolve and download external $ref references', false)
54
55
  .option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
55
56
  .action(async (options) => {
56
57
  try {
@@ -80,6 +81,7 @@ program
80
81
  headers: Object.keys(headers).length > 0 ? headers : undefined,
81
82
  templateDir: options.template,
82
83
  keepSpec: options.keepSpec,
84
+ resolveReferences: options.resolveRefs,
83
85
  timeout: parseInt(options.timeout, 10),
84
86
  });
85
87
  process.exit(0);
@@ -95,6 +97,7 @@ program
95
97
  .requiredOption('-u, --url <url>', 'Remote URL to download the OpenAPI spec from')
96
98
  .requiredOption('-o, --output <file>', 'Output file path for the downloaded spec')
97
99
  .option('-H, --header <header...>', 'Custom headers for the request (format: "Key: Value")')
100
+ .option('-r, --resolve-refs', 'Resolve and download external $ref references', false)
98
101
  .option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
99
102
  .action(async (options) => {
100
103
  try {
@@ -115,6 +118,7 @@ program
115
118
  url: options.url,
116
119
  outputPath: path.resolve(options.output),
117
120
  headers: Object.keys(headers).length > 0 ? headers : undefined,
121
+ resolveReferences: options.resolveRefs,
118
122
  timeout: parseInt(options.timeout, 10),
119
123
  });
120
124
  process.exit(0);
@@ -28,6 +28,11 @@ export interface K8sClientGeneratorOptions {
28
28
  * @default false
29
29
  */
30
30
  keepSpec?: boolean;
31
+ /**
32
+ * Whether to resolve and download external $ref references
33
+ * @default false
34
+ */
35
+ resolveReferences?: boolean;
31
36
  /**
32
37
  * Request timeout for downloading spec in milliseconds
33
38
  * @default 30000
@@ -46,7 +46,7 @@ class K8sClientGenerator {
46
46
  * Generate TypeScript client from a remote OpenAPI spec URL
47
47
  */
48
48
  async generate(options) {
49
- const { specUrl, outputDir, mode = 'full', headers, templateDir, keepSpec = false, timeout, } = options;
49
+ const { specUrl, outputDir, mode = 'full', headers, templateDir, keepSpec = false, resolveReferences = false, timeout, } = options;
50
50
  let specPath;
51
51
  try {
52
52
  // Step 1: Download the OpenAPI spec
@@ -55,6 +55,7 @@ class K8sClientGenerator {
55
55
  url: specUrl,
56
56
  headers,
57
57
  timeout,
58
+ resolveReferences,
58
59
  // If keepSpec is true, save the spec in the output directory
59
60
  outputPath: keepSpec ? path.join(outputDir, 'openapi-spec.json') : undefined,
60
61
  };
@@ -22,8 +22,19 @@ export interface DownloadOptions {
22
22
  * @default 30000
23
23
  */
24
24
  timeout?: number;
25
+ /**
26
+ * Whether to resolve and download external $ref references
27
+ * @default false
28
+ */
29
+ resolveReferences?: boolean;
30
+ /**
31
+ * Base URL for resolving relative references (used when main spec is from HTTP)
32
+ * If not provided, will be derived from the main spec URL
33
+ */
34
+ baseUrl?: string;
25
35
  }
26
36
  export declare class SpecDownloader {
37
+ private downloadedRefs;
27
38
  /**
28
39
  * Download an OpenAPI specification from a URL or load from a local file
29
40
  * @param options Download options
@@ -50,4 +61,24 @@ export declare class SpecDownloader {
50
61
  * Clean up temporary files
51
62
  */
52
63
  cleanupTemp(): void;
64
+ /**
65
+ * Get base URL from a full URL (removes the filename)
66
+ */
67
+ private getBaseUrl;
68
+ /**
69
+ * Resolve and download all external $ref references in the spec
70
+ */
71
+ private resolveExternalRefs;
72
+ /**
73
+ * Find all external $ref values in the spec
74
+ */
75
+ private findExternalRefs;
76
+ /**
77
+ * Check if a $ref is external (references another file)
78
+ */
79
+ private isExternalRef;
80
+ /**
81
+ * Download a single reference file
82
+ */
83
+ private downloadReference;
53
84
  }
@@ -41,16 +41,25 @@ const axios_1 = __importDefault(require("axios"));
41
41
  const fs = __importStar(require("fs"));
42
42
  const path = __importStar(require("path"));
43
43
  class SpecDownloader {
44
+ constructor() {
45
+ this.downloadedRefs = new Set();
46
+ }
44
47
  /**
45
48
  * Download an OpenAPI specification from a URL or load from a local file
46
49
  * @param options Download options
47
50
  * @returns Path to the spec file
48
51
  */
49
52
  async download(options) {
50
- const { url, outputPath, headers, timeout = 30000 } = options;
53
+ const { url, outputPath, headers, timeout = 30000, resolveReferences = false, baseUrl } = options;
54
+ // Reset downloaded refs tracking for each new download
55
+ this.downloadedRefs.clear();
51
56
  // Check if it's a local file path
52
57
  if (this.isLocalFile(url)) {
53
- return this.loadLocalFile(url, outputPath);
58
+ const specPath = await this.loadLocalFile(url, outputPath);
59
+ if (resolveReferences) {
60
+ await this.resolveExternalRefs(specPath, headers, timeout, baseUrl || path.dirname(specPath));
61
+ }
62
+ return specPath;
54
63
  }
55
64
  // Otherwise, download from HTTP/HTTPS
56
65
  console.log(`Downloading OpenAPI spec from ${url}...`);
@@ -88,6 +97,11 @@ class SpecDownloader {
88
97
  // Write to file
89
98
  fs.writeFileSync(specPath, specContent, 'utf-8');
90
99
  console.log(`OpenAPI spec downloaded successfully to ${specPath}`);
100
+ // Resolve references if requested
101
+ if (resolveReferences) {
102
+ const resolvedBaseUrl = baseUrl || this.getBaseUrl(url);
103
+ await this.resolveExternalRefs(specPath, headers, timeout, resolvedBaseUrl);
104
+ }
91
105
  return specPath;
92
106
  }
93
107
  catch (error) {
@@ -209,5 +223,161 @@ class SpecDownloader {
209
223
  console.log('Cleaned up temporary files');
210
224
  }
211
225
  }
226
+ /**
227
+ * Get base URL from a full URL (removes the filename)
228
+ */
229
+ getBaseUrl(url) {
230
+ try {
231
+ const urlObj = new URL(url);
232
+ const pathParts = urlObj.pathname.split('/');
233
+ pathParts.pop(); // Remove filename
234
+ urlObj.pathname = pathParts.join('/');
235
+ return urlObj.toString();
236
+ }
237
+ catch {
238
+ // If URL parsing fails, return the original URL
239
+ return url;
240
+ }
241
+ }
242
+ /**
243
+ * Resolve and download all external $ref references in the spec
244
+ */
245
+ async resolveExternalRefs(specPath, headers, timeout, baseUrl) {
246
+ console.log('Resolving external references...');
247
+ const content = fs.readFileSync(specPath, 'utf-8');
248
+ let spec;
249
+ try {
250
+ spec = JSON.parse(content);
251
+ }
252
+ catch {
253
+ console.log('Spec is not JSON, skipping reference resolution');
254
+ return;
255
+ }
256
+ const refs = this.findExternalRefs(spec);
257
+ if (refs.length === 0) {
258
+ console.log('No external references found');
259
+ return;
260
+ }
261
+ console.log(`Found ${refs.length} external reference(s)`);
262
+ const specDir = path.dirname(specPath);
263
+ const isRemoteSpec = !this.isLocalFile(baseUrl || '');
264
+ for (const ref of refs) {
265
+ await this.downloadReference(ref, specDir, baseUrl, isRemoteSpec, headers, timeout);
266
+ }
267
+ console.log('All external references resolved successfully');
268
+ }
269
+ /**
270
+ * Find all external $ref values in the spec
271
+ */
272
+ findExternalRefs(obj, refs = []) {
273
+ if (typeof obj !== 'object' || obj === null) {
274
+ return refs;
275
+ }
276
+ if (Array.isArray(obj)) {
277
+ for (const item of obj) {
278
+ this.findExternalRefs(item, refs);
279
+ }
280
+ return refs;
281
+ }
282
+ for (const [key, value] of Object.entries(obj)) {
283
+ if (key === '$ref' && typeof value === 'string') {
284
+ // Check if it's an external reference (contains a file path)
285
+ if (this.isExternalRef(value)) {
286
+ // Remove the fragment (#/...) part if present
287
+ const refPath = value.split('#')[0];
288
+ if (refPath && !refs.includes(refPath)) {
289
+ refs.push(refPath);
290
+ }
291
+ }
292
+ }
293
+ else {
294
+ this.findExternalRefs(value, refs);
295
+ }
296
+ }
297
+ return refs;
298
+ }
299
+ /**
300
+ * Check if a $ref is external (references another file)
301
+ */
302
+ isExternalRef(ref) {
303
+ // External refs start with ./ or ../ or / or http:// or https://
304
+ return (ref.startsWith('./') ||
305
+ ref.startsWith('../') ||
306
+ ref.startsWith('http://') ||
307
+ ref.startsWith('https://') ||
308
+ (ref.startsWith('/') && !ref.startsWith('/#')));
309
+ }
310
+ /**
311
+ * Download a single reference file
312
+ */
313
+ async downloadReference(ref, specDir, baseUrl, isRemoteSpec, headers, timeout) {
314
+ // Skip if already downloaded
315
+ if (this.downloadedRefs.has(ref)) {
316
+ return;
317
+ }
318
+ this.downloadedRefs.add(ref);
319
+ let refUrl;
320
+ let outputPath;
321
+ if (isRemoteSpec && baseUrl) {
322
+ // For remote specs, construct the full URL
323
+ refUrl = new URL(ref, baseUrl).toString();
324
+ // Preserve the directory structure in the output
325
+ const refPath = ref.split('#')[0];
326
+ outputPath = path.join(specDir, refPath);
327
+ }
328
+ else {
329
+ // For local specs, resolve relative to the spec directory
330
+ refUrl = ref;
331
+ outputPath = path.resolve(specDir, ref.split('#')[0]);
332
+ }
333
+ console.log(` - Downloading reference: ${ref}`);
334
+ try {
335
+ // Ensure output directory exists
336
+ const outputDir = path.dirname(outputPath);
337
+ if (!fs.existsSync(outputDir)) {
338
+ fs.mkdirSync(outputDir, { recursive: true });
339
+ }
340
+ if (this.isLocalFile(refUrl) && !isRemoteSpec) {
341
+ // Copy local file
342
+ const sourcePath = path.resolve(specDir, refUrl.split('#')[0]);
343
+ if (fs.existsSync(sourcePath)) {
344
+ const content = fs.readFileSync(sourcePath, 'utf-8');
345
+ fs.writeFileSync(outputPath, content, 'utf-8');
346
+ console.log(` โœ“ Copied: ${sourcePath} -> ${outputPath}`);
347
+ // Recursively resolve references in the downloaded file
348
+ await this.resolveExternalRefs(outputPath, headers, timeout, path.dirname(sourcePath));
349
+ }
350
+ else {
351
+ console.warn(` โš  Reference file not found: ${sourcePath}`);
352
+ }
353
+ }
354
+ else {
355
+ // Download from HTTP/HTTPS
356
+ const response = await axios_1.default.get(refUrl, {
357
+ headers,
358
+ timeout: timeout || 30000,
359
+ responseType: 'text',
360
+ });
361
+ let content = response.data;
362
+ if (typeof content !== 'string') {
363
+ content = JSON.stringify(content, null, 2);
364
+ }
365
+ fs.writeFileSync(outputPath, content, 'utf-8');
366
+ console.log(` โœ“ Downloaded: ${refUrl}`);
367
+ // Recursively resolve references in the downloaded file
368
+ const refBaseUrl = this.getBaseUrl(refUrl);
369
+ await this.resolveExternalRefs(outputPath, headers, timeout, refBaseUrl);
370
+ }
371
+ }
372
+ catch (error) {
373
+ if (axios_1.default.isAxiosError(error)) {
374
+ console.error(` โœ— Failed to download ${ref}: ${error.message}`);
375
+ }
376
+ else {
377
+ console.error(` โœ— Failed to process ${ref}:`, error);
378
+ }
379
+ // Continue with other references even if one fails
380
+ }
381
+ }
212
382
  }
213
383
  exports.SpecDownloader = SpecDownloader;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klasik",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "Download OpenAPI specs from remote URLs and generate TypeScript clients with class-transformer support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/test-runtime.js DELETED
@@ -1,72 +0,0 @@
1
- var _a, _b, _c;
2
- import 'reflect-metadata';
3
- import { plainToInstance } from 'class-transformer';
4
- import { Capability } from './test-discriminated-output/models/capability.js';
5
- import { HelmComponent } from './test-discriminated-output/models/helm-component.js';
6
- import { KustomizeComponent } from './test-discriminated-output/models/kustomize-component.js';
7
- import { ManifestComponent } from './test-discriminated-output/models/manifest-component.js';
8
- // Test data with discriminated union
9
- var plainData = {
10
- name: 'my-app',
11
- description: 'Application deployment',
12
- components: [
13
- {
14
- type: 'helm',
15
- chartName: 'nginx',
16
- version: '1.0.0',
17
- values: { replicas: 3 }
18
- },
19
- {
20
- type: 'kustomize',
21
- path: './overlays/production',
22
- namespace: 'prod'
23
- },
24
- {
25
- type: 'manifest',
26
- manifests: [
27
- 'apiVersion: v1\nkind: ConfigMap',
28
- 'apiVersion: v1\nkind: Service'
29
- ]
30
- }
31
- ]
32
- };
33
- console.log('๐Ÿงช Testing Discriminated Union Runtime Transformation\n');
34
- // Transform using plainToInstance
35
- var capability = plainToInstance(Capability, plainData);
36
- // Verify instance types
37
- console.log('โœ… Verification Results:');
38
- console.log(' capability instanceof Capability:', capability instanceof Capability);
39
- console.log(' capability.name:', capability.name);
40
- console.log(' capability.components.length:', capability.components.length);
41
- console.log('');
42
- console.log('โœ… Component Type Verification:');
43
- console.log(' components[0] instanceof HelmComponent:', capability.components[0] instanceof HelmComponent);
44
- console.log(' components[1] instanceof KustomizeComponent:', capability.components[1] instanceof KustomizeComponent);
45
- console.log(' components[2] instanceof ManifestComponent:', capability.components[2] instanceof ManifestComponent);
46
- console.log('');
47
- console.log('โœ… Property Access:');
48
- console.log(' HelmComponent.chartName:', capability.components[0].chartName);
49
- console.log(' KustomizeComponent.path:', capability.components[1].path);
50
- console.log(' ManifestComponent.manifests.length:', (_a = capability.components[2].manifests) === null || _a === void 0 ? void 0 : _a.length);
51
- console.log('');
52
- console.log('โœ… attributeTypeMap Check:');
53
- var componentsMetadata = Capability.attributeTypeMap.find(function (m) { return m.name === 'components'; });
54
- console.log(' type:', componentsMetadata === null || componentsMetadata === void 0 ? void 0 : componentsMetadata.type);
55
- console.log(' modelClasses:', (_b = componentsMetadata === null || componentsMetadata === void 0 ? void 0 : componentsMetadata.modelClasses) === null || _b === void 0 ? void 0 : _b.map(function (c) { return c.name; }));
56
- console.log(' discriminatorProperty:', componentsMetadata === null || componentsMetadata === void 0 ? void 0 : componentsMetadata.discriminatorProperty);
57
- console.log('');
58
- // Final verification
59
- var allCorrect = capability instanceof Capability &&
60
- capability.components[0] instanceof HelmComponent &&
61
- capability.components[1] instanceof KustomizeComponent &&
62
- capability.components[2] instanceof ManifestComponent &&
63
- capability.components[0].chartName === 'nginx' &&
64
- capability.components[1].path === './overlays/production' &&
65
- ((_c = capability.components[2].manifests) === null || _c === void 0 ? void 0 : _c.length) === 2;
66
- if (allCorrect) {
67
- console.log('๐ŸŽ‰ SUCCESS! All discriminated union transformations working correctly!');
68
- }
69
- else {
70
- console.log('โŒ FAILED! Some transformations did not work as expected.');
71
- process.exit(1);
72
- }
package/test-runtime.ts DELETED
@@ -1,80 +0,0 @@
1
- import 'reflect-metadata';
2
- import { plainToInstance } from 'class-transformer';
3
- import { Capability } from './test-discriminated-output/models/capability.js';
4
- import { HelmComponent } from './test-discriminated-output/models/helm-component.js';
5
- import { KustomizeComponent } from './test-discriminated-output/models/kustomize-component.js';
6
- import { ManifestComponent } from './test-discriminated-output/models/manifest-component.js';
7
-
8
- // Test data with discriminated union
9
- const plainData = {
10
- name: 'my-app',
11
- description: 'Application deployment',
12
- components: [
13
- {
14
- type: 'helm',
15
- chartName: 'nginx',
16
- version: '1.0.0',
17
- values: { replicas: 3 }
18
- },
19
- {
20
- type: 'kustomize',
21
- path: './overlays/production',
22
- namespace: 'prod'
23
- },
24
- {
25
- type: 'manifest',
26
- manifests: [
27
- 'apiVersion: v1\nkind: ConfigMap',
28
- 'apiVersion: v1\nkind: Service'
29
- ]
30
- }
31
- ]
32
- };
33
-
34
- console.log('๐Ÿงช Testing Discriminated Union Runtime Transformation\n');
35
-
36
- // Transform using plainToInstance
37
- const capability = plainToInstance(Capability, plainData);
38
-
39
- // Verify instance types
40
- console.log('โœ… Verification Results:');
41
- console.log(' capability instanceof Capability:', capability instanceof Capability);
42
- console.log(' capability.name:', capability.name);
43
- console.log(' capability.components.length:', capability.components.length);
44
- console.log('');
45
-
46
- console.log('โœ… Component Type Verification:');
47
- console.log(' components[0] instanceof HelmComponent:', capability.components[0] instanceof HelmComponent);
48
- console.log(' components[1] instanceof KustomizeComponent:', capability.components[1] instanceof KustomizeComponent);
49
- console.log(' components[2] instanceof ManifestComponent:', capability.components[2] instanceof ManifestComponent);
50
- console.log('');
51
-
52
- console.log('โœ… Property Access:');
53
- console.log(' HelmComponent.chartName:', (capability.components[0] as HelmComponent).chartName);
54
- console.log(' KustomizeComponent.path:', (capability.components[1] as KustomizeComponent).path);
55
- console.log(' ManifestComponent.manifests.length:', (capability.components[2] as ManifestComponent).manifests?.length);
56
- console.log('');
57
-
58
- console.log('โœ… attributeTypeMap Check:');
59
- const componentsMetadata = Capability.attributeTypeMap.find(m => m.name === 'components');
60
- console.log(' type:', componentsMetadata?.type);
61
- console.log(' modelClasses:', (componentsMetadata as any)?.modelClasses?.map((c: any) => c.name));
62
- console.log(' discriminatorProperty:', (componentsMetadata as any)?.discriminatorProperty);
63
- console.log('');
64
-
65
- // Final verification
66
- const allCorrect =
67
- capability instanceof Capability &&
68
- capability.components[0] instanceof HelmComponent &&
69
- capability.components[1] instanceof KustomizeComponent &&
70
- capability.components[2] instanceof ManifestComponent &&
71
- (capability.components[0] as HelmComponent).chartName === 'nginx' &&
72
- (capability.components[1] as KustomizeComponent).path === './overlays/production' &&
73
- (capability.components[2] as ManifestComponent).manifests?.length === 2;
74
-
75
- if (allCorrect) {
76
- console.log('๐ŸŽ‰ SUCCESS! All discriminated union transformations working correctly!');
77
- } else {
78
- console.log('โŒ FAILED! Some transformations did not work as expected.');
79
- process.exit(1);
80
- }