@dotflash/openapi-semantic-generator 0.1.1 → 0.1.3
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 +48 -4
- package/dist/{chunk-IUOJNB6I.js → chunk-QEPXZXK5.js} +20 -4
- package/dist/cli.cjs +20 -4
- package/dist/cli.js +1 -1
- package/dist/index.cjs +20 -4
- package/dist/index.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -56,8 +56,30 @@ npx openapi-semantic-generator <spec-url-or-path> -o <generated-code-dir> [optio
|
|
|
56
56
|
|
|
57
57
|
#### Options
|
|
58
58
|
|
|
59
|
-
- `-o, --output <dir>`: Directory where the code is
|
|
60
|
-
-
|
|
59
|
+
- `-o, --output <dir>`: **Required.** Directory where the generated code is located. This tool will scan this directory to find API files.
|
|
60
|
+
- Example: `./src/api`, `./generated/client`
|
|
61
|
+
- **Important**: The generated TypeScript files must be inside this directory or its subdirectories.
|
|
62
|
+
|
|
63
|
+
- `--api-dir <path>`: Optional. Relative path from the output directory where API files are located.
|
|
64
|
+
- Example: `services`, `api`, `client/apis`
|
|
65
|
+
- This path is **relative to the `-o` directory**, not the current working directory.
|
|
66
|
+
- If not specified, the tool searches in `api/`, `apis/`, and the output directory itself (in that order).
|
|
67
|
+
|
|
68
|
+
#### Usage Examples
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Basic usage: API files are in ./src/api/
|
|
72
|
+
npx openapi-semantic-generator spec.yaml -o ./src/api
|
|
73
|
+
|
|
74
|
+
# API files are in ./generated/services/
|
|
75
|
+
npx openapi-semantic-generator spec.yaml -o ./generated --api-dir services
|
|
76
|
+
|
|
77
|
+
# Using a remote spec URL
|
|
78
|
+
npx openapi-semantic-generator https://api.example.com/openapi.json -o ./src/api
|
|
79
|
+
|
|
80
|
+
# API files are directly in the output directory
|
|
81
|
+
npx openapi-semantic-generator spec.yaml -o ./dist/client
|
|
82
|
+
```
|
|
61
83
|
|
|
62
84
|
### Generated Documents
|
|
63
85
|
|
|
@@ -106,8 +128,30 @@ npx openapi-semantic-generator <spec-url-or-path> -o <generated-code-dir> [optio
|
|
|
106
128
|
|
|
107
129
|
#### 옵션
|
|
108
130
|
|
|
109
|
-
- `-o, --output <dir>`:
|
|
110
|
-
-
|
|
131
|
+
- `-o, --output <dir>`: **필수.** 생성된 코드가 위치한 디렉토리입니다. 이 도구는 해당 디렉토리를 스캔하여 API 파일을 찾습니다.
|
|
132
|
+
- 예시: `./src/api`, `./generated/client`
|
|
133
|
+
- **중요**: 생성된 TypeScript 파일이 반드시 이 디렉토리 또는 하위 디렉토리에 있어야 합니다.
|
|
134
|
+
|
|
135
|
+
- `--api-dir <path>`: 선택사항. 출력 디렉토리 기준으로 API 파일이 위치한 상대 경로입니다.
|
|
136
|
+
- 예시: `services`, `api`, `client/apis`
|
|
137
|
+
- 이 경로는 **`-o` 디렉토리 기준의 상대 경로**이며, 현재 작업 디렉토리 기준이 아닙니다.
|
|
138
|
+
- 지정하지 않으면 `api/`, `apis/`, 출력 디렉토리 자체를 순서대로 검색합니다.
|
|
139
|
+
|
|
140
|
+
#### 사용 예시
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# 기본 사용: API 파일이 ./src/api/에 있는 경우
|
|
144
|
+
npx openapi-semantic-generator spec.yaml -o ./src/api
|
|
145
|
+
|
|
146
|
+
# API 파일이 ./generated/services/에 있는 경우
|
|
147
|
+
npx openapi-semantic-generator spec.yaml -o ./generated --api-dir services
|
|
148
|
+
|
|
149
|
+
# 원격 스펙 URL 사용
|
|
150
|
+
npx openapi-semantic-generator https://api.example.com/openapi.json -o ./src/api
|
|
151
|
+
|
|
152
|
+
# API 파일이 출력 디렉토리 바로 아래에 있는 경우
|
|
153
|
+
npx openapi-semantic-generator spec.yaml -o ./dist/client
|
|
154
|
+
```
|
|
111
155
|
|
|
112
156
|
### 생성되는 문서
|
|
113
157
|
|
|
@@ -37,7 +37,9 @@ async function extractMetadata(specPath) {
|
|
|
37
37
|
if (typedDetails.requestBody) {
|
|
38
38
|
}
|
|
39
39
|
operations.push({
|
|
40
|
-
operationId:
|
|
40
|
+
operationId: toCamelCase(
|
|
41
|
+
typedDetails.operationId || `${method}_${path4.replace(/\//g, "_")}`
|
|
42
|
+
),
|
|
41
43
|
summary: typedDetails.summary,
|
|
42
44
|
description: typedDetails.description,
|
|
43
45
|
httpMethod: method.toUpperCase(),
|
|
@@ -65,6 +67,9 @@ async function extractMetadata(specPath) {
|
|
|
65
67
|
}))
|
|
66
68
|
};
|
|
67
69
|
}
|
|
70
|
+
function toCamelCase(str) {
|
|
71
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
|
|
72
|
+
}
|
|
68
73
|
function cleanDescription(desc) {
|
|
69
74
|
if (!desc) return "";
|
|
70
75
|
return desc.split("\n").filter((line) => {
|
|
@@ -95,10 +100,21 @@ async function scanGeneratedFiles(outputDir, metadata, options = {}) {
|
|
|
95
100
|
}
|
|
96
101
|
if (metadata.operations) {
|
|
97
102
|
for (const op of metadata.operations) {
|
|
98
|
-
if (apis.length
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
if (apis.length === 0) continue;
|
|
104
|
+
let matchedApi = apis[0];
|
|
105
|
+
if (op.tags && op.tags.length > 0) {
|
|
106
|
+
const primaryTag = op.tags[0].toLowerCase();
|
|
107
|
+
const normalizedTag = primaryTag.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
108
|
+
const found = apis.find((api) => {
|
|
109
|
+
const fileName = api.className.toLowerCase();
|
|
110
|
+
return fileName.includes(normalizedTag) || fileName.includes(primaryTag.replace(/\s+/g, ""));
|
|
111
|
+
});
|
|
112
|
+
if (found) {
|
|
113
|
+
matchedApi = found;
|
|
114
|
+
}
|
|
101
115
|
}
|
|
116
|
+
op.className = matchedApi.className;
|
|
117
|
+
op.sourceFile = matchedApi.sourceFile;
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
return {
|
package/dist/cli.cjs
CHANGED
|
@@ -75,7 +75,9 @@ async function extractMetadata(specPath) {
|
|
|
75
75
|
if (typedDetails.requestBody) {
|
|
76
76
|
}
|
|
77
77
|
operations.push({
|
|
78
|
-
operationId:
|
|
78
|
+
operationId: toCamelCase(
|
|
79
|
+
typedDetails.operationId || `${method}_${path5.replace(/\//g, "_")}`
|
|
80
|
+
),
|
|
79
81
|
summary: typedDetails.summary,
|
|
80
82
|
description: typedDetails.description,
|
|
81
83
|
httpMethod: method.toUpperCase(),
|
|
@@ -103,6 +105,9 @@ async function extractMetadata(specPath) {
|
|
|
103
105
|
}))
|
|
104
106
|
};
|
|
105
107
|
}
|
|
108
|
+
function toCamelCase(str) {
|
|
109
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
|
|
110
|
+
}
|
|
106
111
|
function cleanDescription(desc) {
|
|
107
112
|
if (!desc) return "";
|
|
108
113
|
return desc.split("\n").filter((line) => {
|
|
@@ -133,10 +138,21 @@ async function scanGeneratedFiles(outputDir, metadata, options = {}) {
|
|
|
133
138
|
}
|
|
134
139
|
if (metadata.operations) {
|
|
135
140
|
for (const op of metadata.operations) {
|
|
136
|
-
if (apis.length
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
if (apis.length === 0) continue;
|
|
142
|
+
let matchedApi = apis[0];
|
|
143
|
+
if (op.tags && op.tags.length > 0) {
|
|
144
|
+
const primaryTag = op.tags[0].toLowerCase();
|
|
145
|
+
const normalizedTag = primaryTag.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
146
|
+
const found = apis.find((api) => {
|
|
147
|
+
const fileName = api.className.toLowerCase();
|
|
148
|
+
return fileName.includes(normalizedTag) || fileName.includes(primaryTag.replace(/\s+/g, ""));
|
|
149
|
+
});
|
|
150
|
+
if (found) {
|
|
151
|
+
matchedApi = found;
|
|
152
|
+
}
|
|
139
153
|
}
|
|
154
|
+
op.className = matchedApi.className;
|
|
155
|
+
op.sourceFile = matchedApi.sourceFile;
|
|
140
156
|
}
|
|
141
157
|
}
|
|
142
158
|
return {
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -84,7 +84,9 @@ async function extractMetadata(specPath) {
|
|
|
84
84
|
if (typedDetails.requestBody) {
|
|
85
85
|
}
|
|
86
86
|
operations.push({
|
|
87
|
-
operationId:
|
|
87
|
+
operationId: toCamelCase(
|
|
88
|
+
typedDetails.operationId || `${method}_${path4.replace(/\//g, "_")}`
|
|
89
|
+
),
|
|
88
90
|
summary: typedDetails.summary,
|
|
89
91
|
description: typedDetails.description,
|
|
90
92
|
httpMethod: method.toUpperCase(),
|
|
@@ -112,6 +114,9 @@ async function extractMetadata(specPath) {
|
|
|
112
114
|
}))
|
|
113
115
|
};
|
|
114
116
|
}
|
|
117
|
+
function toCamelCase(str) {
|
|
118
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
|
|
119
|
+
}
|
|
115
120
|
function cleanDescription(desc) {
|
|
116
121
|
if (!desc) return "";
|
|
117
122
|
return desc.split("\n").filter((line) => {
|
|
@@ -142,10 +147,21 @@ async function scanGeneratedFiles(outputDir, metadata, options = {}) {
|
|
|
142
147
|
}
|
|
143
148
|
if (metadata.operations) {
|
|
144
149
|
for (const op of metadata.operations) {
|
|
145
|
-
if (apis.length
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
if (apis.length === 0) continue;
|
|
151
|
+
let matchedApi = apis[0];
|
|
152
|
+
if (op.tags && op.tags.length > 0) {
|
|
153
|
+
const primaryTag = op.tags[0].toLowerCase();
|
|
154
|
+
const normalizedTag = primaryTag.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
155
|
+
const found = apis.find((api) => {
|
|
156
|
+
const fileName = api.className.toLowerCase();
|
|
157
|
+
return fileName.includes(normalizedTag) || fileName.includes(primaryTag.replace(/\s+/g, ""));
|
|
158
|
+
});
|
|
159
|
+
if (found) {
|
|
160
|
+
matchedApi = found;
|
|
161
|
+
}
|
|
148
162
|
}
|
|
163
|
+
op.className = matchedApi.className;
|
|
164
|
+
op.sourceFile = matchedApi.sourceFile;
|
|
149
165
|
}
|
|
150
166
|
}
|
|
151
167
|
return {
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotflash/openapi-semantic-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Semantic mapping automation tool for LLM agents and OpenAPI generated code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"dev": "tsup src/index.ts src/cli.ts --format cjs,esm --watch --shims",
|
|
20
20
|
"test": "vitest run",
|
|
21
21
|
"test:watch": "vitest",
|
|
22
|
-
"gen:example": "openapi-generator-cli generate -i
|
|
22
|
+
"gen:example": "openapi-generator-cli generate -i https://petstore3.swagger.io/api/v3/openapi.json -g typescript-axios -o test/generated/petstore && node dist/cli.js https://petstore3.swagger.io/api/v3/openapi.json -o test/generated/petstore",
|
|
23
|
+
"gen:example:separate": "openapi-generator-cli generate -i https://petstore3.swagger.io/api/v3/openapi.json -g typescript-axios -o test/generated/petstore-separate --additional-properties=withSeparateModelsAndApi=true,apiPackage=apis,modelPackage=models && node dist/cli.js https://petstore3.swagger.io/api/v3/openapi.json -o test/generated/petstore-separate --api-dir apis"
|
|
23
24
|
},
|
|
24
25
|
"keywords": [
|
|
25
26
|
"openapi",
|