klasik 1.0.13 → 1.0.14
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 +17 -4
- package/USAGE_EXAMPLES.md +61 -0
- package/dist/spec-downloader.d.ts +1 -1
- package/dist/spec-downloader.js +53 -39
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ 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
|
+
- 📄 **JSON and YAML support** - Automatically parse and handle both formats
|
|
9
10
|
- 🔐 **Authentication support** - Custom headers including Bearer tokens and API keys
|
|
10
11
|
- 🔗 **External reference resolution** - Automatically download referenced schema files (`$ref`)
|
|
11
12
|
- 🔄 **Automatic transformation** - Converts API responses to class instances using class-transformer
|
|
@@ -27,17 +28,23 @@ npm install klasik
|
|
|
27
28
|
Generate a TypeScript client from a remote OpenAPI spec:
|
|
28
29
|
|
|
29
30
|
```bash
|
|
31
|
+
# JSON spec
|
|
30
32
|
npx klasik generate \
|
|
31
33
|
--url https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json \
|
|
32
34
|
--output ./k8s-client
|
|
35
|
+
|
|
36
|
+
# YAML spec
|
|
37
|
+
npx klasik generate \
|
|
38
|
+
--url https://api.example.com/openapi.yaml \
|
|
39
|
+
--output ./client
|
|
33
40
|
```
|
|
34
41
|
|
|
35
|
-
Generate from a local OpenAPI spec file:
|
|
42
|
+
Generate from a local OpenAPI spec file (JSON or YAML):
|
|
36
43
|
|
|
37
44
|
```bash
|
|
38
45
|
# Absolute path
|
|
39
46
|
npx klasik generate \
|
|
40
|
-
--url /path/to/openapi.
|
|
47
|
+
--url /path/to/openapi.yaml \
|
|
41
48
|
--output ./client
|
|
42
49
|
|
|
43
50
|
# Relative path
|
|
@@ -47,16 +54,22 @@ npx klasik generate \
|
|
|
47
54
|
|
|
48
55
|
# file:// URL
|
|
49
56
|
npx klasik generate \
|
|
50
|
-
--url file:///path/to/openapi.
|
|
57
|
+
--url file:///path/to/openapi.yaml \
|
|
51
58
|
--output ./client
|
|
52
59
|
```
|
|
53
60
|
|
|
54
|
-
Download an OpenAPI spec without generating:
|
|
61
|
+
Download an OpenAPI spec without generating (supports JSON and YAML):
|
|
55
62
|
|
|
56
63
|
```bash
|
|
64
|
+
# Download JSON spec
|
|
57
65
|
npx klasik download \
|
|
58
66
|
--url https://api.example.com/openapi.json \
|
|
59
67
|
--output ./specs/api-spec.json
|
|
68
|
+
|
|
69
|
+
# Download YAML spec
|
|
70
|
+
npx klasik download \
|
|
71
|
+
--url https://api.example.com/openapi.yaml \
|
|
72
|
+
--output ./specs/api-spec.yaml
|
|
60
73
|
```
|
|
61
74
|
|
|
62
75
|
### Programmatic Usage
|
package/USAGE_EXAMPLES.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
# Klasik Usage Examples
|
|
2
2
|
|
|
3
|
+
## JSON and YAML Support
|
|
4
|
+
|
|
5
|
+
Klasik automatically detects and parses both JSON and YAML OpenAPI specifications. The original format is preserved when downloading.
|
|
6
|
+
|
|
7
|
+
### CLI Examples
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# JSON spec
|
|
11
|
+
npx klasik generate \
|
|
12
|
+
--url https://api.example.com/openapi.json \
|
|
13
|
+
--output ./client
|
|
14
|
+
|
|
15
|
+
# YAML spec
|
|
16
|
+
npx klasik generate \
|
|
17
|
+
--url https://api.example.com/openapi.yaml \
|
|
18
|
+
--output ./client
|
|
19
|
+
|
|
20
|
+
# Local YAML file
|
|
21
|
+
npx klasik generate \
|
|
22
|
+
--url ./specs/openapi.yaml \
|
|
23
|
+
--output ./client
|
|
24
|
+
|
|
25
|
+
# Download and keep YAML format
|
|
26
|
+
npx klasik download \
|
|
27
|
+
--url https://api.example.com/openapi.yaml \
|
|
28
|
+
--output ./specs/api-spec.yaml \
|
|
29
|
+
--resolve-refs
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Programmatic Examples
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { K8sClientGenerator } from 'klasik';
|
|
36
|
+
|
|
37
|
+
// Works with both JSON and YAML
|
|
38
|
+
await new K8sClientGenerator().generate({
|
|
39
|
+
specUrl: 'https://api.example.com/openapi.yaml',
|
|
40
|
+
outputDir: './client'
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Mixed Formats with References
|
|
45
|
+
|
|
46
|
+
Klasik handles specs where the main file is in one format and referenced files are in another:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
# openapi.yaml (main spec in YAML)
|
|
50
|
+
openapi: 3.0.0
|
|
51
|
+
paths:
|
|
52
|
+
/users:
|
|
53
|
+
get:
|
|
54
|
+
responses:
|
|
55
|
+
'200':
|
|
56
|
+
content:
|
|
57
|
+
application/json:
|
|
58
|
+
schema:
|
|
59
|
+
$ref: './schemas/users.json#/components/schemas/User' # JSON reference
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Both formats work seamlessly together!
|
|
63
|
+
|
|
3
64
|
## Using Custom Headers (Authorization)
|
|
4
65
|
|
|
5
66
|
### CLI Example - Authorization Token
|
package/dist/spec-downloader.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.SpecDownloader = void 0;
|
|
|
40
40
|
const axios_1 = __importDefault(require("axios"));
|
|
41
41
|
const fs = __importStar(require("fs"));
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
|
+
const yaml = __importStar(require("js-yaml"));
|
|
43
44
|
class SpecDownloader {
|
|
44
45
|
constructor() {
|
|
45
46
|
this.downloadedRefs = new Set();
|
|
@@ -78,22 +79,31 @@ class SpecDownloader {
|
|
|
78
79
|
}
|
|
79
80
|
// Parse and validate the spec
|
|
80
81
|
let specContent;
|
|
82
|
+
let parsedSpec;
|
|
81
83
|
if (typeof response.data === 'string') {
|
|
82
|
-
// Try to parse as JSON
|
|
84
|
+
// Try to parse as JSON first
|
|
83
85
|
try {
|
|
84
|
-
|
|
85
|
-
specContent = JSON.stringify(
|
|
86
|
+
parsedSpec = JSON.parse(response.data);
|
|
87
|
+
specContent = JSON.stringify(parsedSpec, null, 2);
|
|
86
88
|
}
|
|
87
89
|
catch {
|
|
88
|
-
// If not JSON,
|
|
89
|
-
|
|
90
|
+
// If not JSON, try YAML
|
|
91
|
+
try {
|
|
92
|
+
parsedSpec = yaml.load(response.data);
|
|
93
|
+
// Keep YAML format if input was YAML
|
|
94
|
+
specContent = response.data;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new Error('Failed to parse spec: not valid JSON or YAML');
|
|
98
|
+
}
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
else {
|
|
102
|
+
parsedSpec = response.data;
|
|
93
103
|
specContent = JSON.stringify(response.data, null, 2);
|
|
94
104
|
}
|
|
95
105
|
// Validate it's an OpenAPI spec
|
|
96
|
-
this.validateSpec(
|
|
106
|
+
this.validateSpec(parsedSpec);
|
|
97
107
|
// Write to file
|
|
98
108
|
fs.writeFileSync(specPath, specContent, 'utf-8');
|
|
99
109
|
console.log(`OpenAPI spec downloaded successfully to ${specPath}`);
|
|
@@ -154,24 +164,29 @@ class SpecDownloader {
|
|
|
154
164
|
}
|
|
155
165
|
// Read the file
|
|
156
166
|
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
167
|
+
// Parse the content (JSON or YAML)
|
|
168
|
+
let parsedSpec;
|
|
169
|
+
try {
|
|
170
|
+
parsedSpec = JSON.parse(content);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
try {
|
|
174
|
+
parsedSpec = yaml.load(content);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
throw new Error(`Failed to parse spec at ${resolvedPath}: not valid JSON or YAML`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
157
180
|
// Validate it's an OpenAPI spec
|
|
158
|
-
this.validateSpec(
|
|
181
|
+
this.validateSpec(parsedSpec);
|
|
159
182
|
// If outputPath is provided, copy to that location
|
|
160
183
|
if (outputPath) {
|
|
161
184
|
const dir = path.dirname(outputPath);
|
|
162
185
|
if (!fs.existsSync(dir)) {
|
|
163
186
|
fs.mkdirSync(dir, { recursive: true });
|
|
164
187
|
}
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const parsed = JSON.parse(content);
|
|
169
|
-
formattedContent = JSON.stringify(parsed, null, 2);
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
// Not JSON, keep original
|
|
173
|
-
}
|
|
174
|
-
fs.writeFileSync(outputPath, formattedContent, 'utf-8');
|
|
188
|
+
// Keep the original format (JSON or YAML)
|
|
189
|
+
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
175
190
|
console.log(`OpenAPI spec copied to ${outputPath}`);
|
|
176
191
|
return outputPath;
|
|
177
192
|
}
|
|
@@ -189,28 +204,22 @@ class SpecDownloader {
|
|
|
189
204
|
return path.join(process.cwd(), '.tmp', filename);
|
|
190
205
|
}
|
|
191
206
|
/**
|
|
192
|
-
* Validate that the
|
|
207
|
+
* Validate that the parsed content is an OpenAPI spec
|
|
193
208
|
*/
|
|
194
|
-
validateSpec(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// Check for OpenAPI version
|
|
198
|
-
if (!spec.openapi && !spec.swagger) {
|
|
199
|
-
throw new Error('Invalid OpenAPI spec: missing "openapi" or "swagger" field');
|
|
200
|
-
}
|
|
201
|
-
// Check for required fields
|
|
202
|
-
if (!spec.info) {
|
|
203
|
-
throw new Error('Invalid OpenAPI spec: missing "info" field');
|
|
204
|
-
}
|
|
205
|
-
if (!spec.paths && !spec.components) {
|
|
206
|
-
throw new Error('Invalid OpenAPI spec: must have either "paths" or "components"');
|
|
207
|
-
}
|
|
209
|
+
validateSpec(spec) {
|
|
210
|
+
if (!spec || typeof spec !== 'object') {
|
|
211
|
+
throw new Error('Invalid OpenAPI spec: not a valid object');
|
|
208
212
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
// Check for OpenAPI version
|
|
214
|
+
if (!spec.openapi && !spec.swagger) {
|
|
215
|
+
throw new Error('Invalid OpenAPI spec: missing "openapi" or "swagger" field');
|
|
216
|
+
}
|
|
217
|
+
// Check for required fields
|
|
218
|
+
if (!spec.info) {
|
|
219
|
+
throw new Error('Invalid OpenAPI spec: missing "info" field');
|
|
220
|
+
}
|
|
221
|
+
if (!spec.paths && !spec.components) {
|
|
222
|
+
throw new Error('Invalid OpenAPI spec: must have either "paths" or "components"');
|
|
214
223
|
}
|
|
215
224
|
}
|
|
216
225
|
/**
|
|
@@ -250,8 +259,13 @@ class SpecDownloader {
|
|
|
250
259
|
spec = JSON.parse(content);
|
|
251
260
|
}
|
|
252
261
|
catch {
|
|
253
|
-
|
|
254
|
-
|
|
262
|
+
try {
|
|
263
|
+
spec = yaml.load(content);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
console.log('Spec is not valid JSON or YAML, skipping reference resolution');
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
255
269
|
}
|
|
256
270
|
const refs = this.findExternalRefs(spec);
|
|
257
271
|
if (refs.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "klasik",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
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",
|
|
@@ -28,11 +28,13 @@
|
|
|
28
28
|
"axios": "^1.6.0",
|
|
29
29
|
"class-transformer": "^0.5.1",
|
|
30
30
|
"commander": "^11.0.0",
|
|
31
|
+
"js-yaml": "^4.1.0",
|
|
31
32
|
"openapi-class-transformer": "^1.0.11",
|
|
32
33
|
"reflect-metadata": "^0.2.2"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/jest": "^29.5.0",
|
|
37
|
+
"@types/js-yaml": "^4.0.9",
|
|
36
38
|
"@types/node": "^20.0.0",
|
|
37
39
|
"jest": "^29.5.0",
|
|
38
40
|
"ts-jest": "^29.1.0",
|