@tzurp/openapi-skills 1.2.5
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/LICENSE +201 -0
- package/README.md +230 -0
- package/bin/openapi-skills +4 -0
- package/dist/cli.d.ts +37 -0
- package/dist/cli.js +1646 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-schema-builder.d.ts +35 -0
- package/dist/client-schema-builder.js +268 -0
- package/dist/client-schema-builder.js.map +1 -0
- package/dist/helper/base64url-json.d.ts +3 -0
- package/dist/helper/base64url-json.js +28 -0
- package/dist/helper/base64url-json.js.map +1 -0
- package/dist/helper/dotNotation.d.ts +3 -0
- package/dist/helper/dotNotation.js +34 -0
- package/dist/helper/dotNotation.js.map +1 -0
- package/dist/helper/endpoint-filter.d.ts +16 -0
- package/dist/helper/endpoint-filter.js +165 -0
- package/dist/helper/endpoint-filter.js.map +1 -0
- package/dist/helper/endpoint-utils.d.ts +2 -0
- package/dist/helper/endpoint-utils.js +10 -0
- package/dist/helper/endpoint-utils.js.map +1 -0
- package/dist/helper/error-codes.d.ts +27 -0
- package/dist/helper/error-codes.js +116 -0
- package/dist/helper/error-codes.js.map +1 -0
- package/dist/helper/error-formatter.d.ts +46 -0
- package/dist/helper/error-formatter.js +72 -0
- package/dist/helper/error-formatter.js.map +1 -0
- package/dist/helper/get-operation-filter.d.ts +17 -0
- package/dist/helper/get-operation-filter.js +26 -0
- package/dist/helper/get-operation-filter.js.map +1 -0
- package/dist/helper/graphql.d.ts +40 -0
- package/dist/helper/graphql.js +967 -0
- package/dist/helper/graphql.js.map +1 -0
- package/dist/helper/http-client.d.ts +28 -0
- package/dist/helper/http-client.js +90 -0
- package/dist/helper/http-client.js.map +1 -0
- package/dist/helper/json-updater.d.ts +17 -0
- package/dist/helper/json-updater.js +195 -0
- package/dist/helper/json-updater.js.map +1 -0
- package/dist/helper/logger.d.ts +13 -0
- package/dist/helper/logger.js +56 -0
- package/dist/helper/logger.js.map +1 -0
- package/dist/helper/output-size.d.ts +5 -0
- package/dist/helper/output-size.js +7 -0
- package/dist/helper/output-size.js.map +1 -0
- package/dist/helper/parameter-schema.d.ts +14 -0
- package/dist/helper/parameter-schema.js +36 -0
- package/dist/helper/parameter-schema.js.map +1 -0
- package/dist/helper/paths.d.ts +11 -0
- package/dist/helper/paths.js +47 -0
- package/dist/helper/paths.js.map +1 -0
- package/dist/helper/prompt-delete.d.ts +1 -0
- package/dist/helper/prompt-delete.js +13 -0
- package/dist/helper/prompt-delete.js.map +1 -0
- package/dist/helper/request-artifacts.d.ts +14 -0
- package/dist/helper/request-artifacts.js +60 -0
- package/dist/helper/request-artifacts.js.map +1 -0
- package/dist/helper/request-preparation.d.ts +23 -0
- package/dist/helper/request-preparation.js +85 -0
- package/dist/helper/request-preparation.js.map +1 -0
- package/dist/helper/sanitizer.d.ts +1 -0
- package/dist/helper/sanitizer.js +17 -0
- package/dist/helper/sanitizer.js.map +1 -0
- package/dist/helper/update-check.d.ts +1 -0
- package/dist/helper/update-check.js +36 -0
- package/dist/helper/update-check.js.map +1 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +163 -0
- package/dist/index.js.map +1 -0
- package/dist/install-skill.d.ts +5 -0
- package/dist/install-skill.js +139 -0
- package/dist/install-skill.js.map +1 -0
- package/dist/parser.d.ts +12 -0
- package/dist/parser.js +393 -0
- package/dist/parser.js.map +1 -0
- package/dist/validate-response.d.ts +47 -0
- package/dist/validate-response.js +1017 -0
- package/dist/validate-response.js.map +1 -0
- package/package.json +63 -0
- package/skill-templates/SKILL.md +204 -0
- package/skill-templates/create-endpoint-test.md +413 -0
- package/skill-templates/http-client.md +141 -0
- package/skill-templates/write-client-code.md +219 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Scenario: Write Client Code for an Operation
|
|
2
|
+
|
|
3
|
+
**Goal:** Generate TypeScript client code for a specific operation using schema metadata.
|
|
4
|
+
|
|
5
|
+
## Workflow
|
|
6
|
+
|
|
7
|
+
1. **Ensure API is parsed:** Run `openapi-skills get-api-names` to verify API exists, or run `openapi-skills generate <spec-source> --base-url <url> --no-progress --rename<newApiName>` if needed.
|
|
8
|
+
2. **Find the operation identifier:** Run `openapi-skills list --api <apiName>` with at least one schema-appropriate filter. Use `--method`/`--path`/`--tag` when the schema exposes OpenAPI operations, or `--root-type` when it exposes GraphQL root fields. Use `--filter`, `--tag`, and `--index` as needed.
|
|
9
|
+
3. **Get schema:** Run `openapi-skills generate-client-schema <operationId> --api <apiName>` to retrieve structured metadata for code generation.
|
|
10
|
+
4. **Generate client code:** Using the schema output, write the TypeScript client class following the rules in this document.
|
|
11
|
+
|
|
12
|
+
**Example:**
|
|
13
|
+
```bash
|
|
14
|
+
openapi-skills generate-client-schema addPet --api petstore
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Scope
|
|
20
|
+
|
|
21
|
+
**Generate:** TypeScript class with typed methods, interfaces for parameters and responses, JSDoc comments describing each method.
|
|
22
|
+
|
|
23
|
+
**Do NOT generate:** Usage examples showing how to instantiate or call the client. Users will write their own usage code.
|
|
24
|
+
|
|
25
|
+
## Generated Code Structure
|
|
26
|
+
|
|
27
|
+
### File Header
|
|
28
|
+
|
|
29
|
+
All generated client code must begin with:
|
|
30
|
+
```typescript
|
|
31
|
+
/**
|
|
32
|
+
* <API Name> Client
|
|
33
|
+
* Generated from <apiName> API schema by openapi-skills.
|
|
34
|
+
*/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Substitute placeholders:
|
|
38
|
+
- `<API Name>`: Human-readable API name (e.g., "Petstore" or "GitHub")
|
|
39
|
+
- `<operationId>`: The operation identifier from the schema (for example, an OpenAPI `operationId` or a GraphQL `name`)
|
|
40
|
+
- `<apiName>`: The parsed API name from config (e.g., "petstore")
|
|
41
|
+
|
|
42
|
+
### Class Structure Rules
|
|
43
|
+
|
|
44
|
+
**Constructor:**
|
|
45
|
+
- Must accept a single `httpClient` parameter implementing the `HttpClient` interface (see `http-client.md` for the HttpClient interface definition).
|
|
46
|
+
- Must NOT accept or define `baseUrl`, `authToken`, `apiKey`, or any authentication-related properties.
|
|
47
|
+
- Must include this comment above the constructor:
|
|
48
|
+
```typescript
|
|
49
|
+
// Pass your own http client that implements the HttpClient interface.
|
|
50
|
+
```
|
|
51
|
+
- All base URL and authentication handling is delegated to the `httpClient` implementation.
|
|
52
|
+
|
|
53
|
+
**Methods:**
|
|
54
|
+
- Each operation becomes an instance method named after the operation identifier from the schema.
|
|
55
|
+
- All methods must use `this.httpClient` to make requests.
|
|
56
|
+
- All methods are instance methods (not static).
|
|
57
|
+
- If the operation request body is multipart or contains binary fields, preserve that distinction in the request-body type metadata instead of collapsing everything into a plain string/object payload.
|
|
58
|
+
|
|
59
|
+
**Class Naming:**
|
|
60
|
+
- Format: `<ApiName><FirstPathSegment>Client`
|
|
61
|
+
- `<ApiName>`: Capitalized API name (e.g., `Petstore`)
|
|
62
|
+
- `<FirstPathSegment>`: Capitalized first path segment (e.g., `User` for `/user/login`, `Pet` for `/pet/{id}`)
|
|
63
|
+
- Example: `PetstorePetClient` for API `petstore` and path `/pet/{petId}`
|
|
64
|
+
|
|
65
|
+
**Required HttpClient Interface:**
|
|
66
|
+
```typescript
|
|
67
|
+
interface HttpClient {
|
|
68
|
+
get(url: string, headers?: Record<string, string>): Promise<any>;
|
|
69
|
+
post(url: string, body?: any, headers?: Record<string, string>): Promise<any>;
|
|
70
|
+
put(url: string, body?: any, headers?: Record<string, string>): Promise<any>;
|
|
71
|
+
delete(url: string, headers?: Record<string, string>): Promise<any>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Method Generation Rules
|
|
76
|
+
|
|
77
|
+
### Parameter Ordering in Method Signatures
|
|
78
|
+
|
|
79
|
+
Parameters must appear in this order:
|
|
80
|
+
1. **Path parameters** (required, positional)
|
|
81
|
+
2. **Query parameters** (optional, single object with all query params as optional properties)
|
|
82
|
+
3. **Request body** (required if the operation has a requestBody, named `body`)
|
|
83
|
+
4. **Headers** (optional, named `headers`)
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
- GET with path + query: `async getPet(petId: number, query?: { details?: boolean }, headers?: Record<string, string>): Promise<GetPetResponse>`
|
|
87
|
+
- POST with body: `async createPet(body: CreatePetRequest, headers?: Record<string, string>): Promise<CreatePetResponse>`
|
|
88
|
+
- DELETE with path: `async deletePet(petId: number, headers?: Record<string, string>): Promise<void>`
|
|
89
|
+
|
|
90
|
+
### Query Parameter Serialization
|
|
91
|
+
|
|
92
|
+
- **Flat objects only**: Query parameters are always a single flat object (no nested objects).
|
|
93
|
+
- **Arrays**: If a query param is an array (e.g., `tags: string[]`), serialize using `URLSearchParams`, which handles array values via comma separation or bracket notation per API convention.
|
|
94
|
+
- **Encoding**: Use `URLSearchParams` for automatic URL encoding of special characters.
|
|
95
|
+
|
|
96
|
+
Example implementation:
|
|
97
|
+
```typescript
|
|
98
|
+
if (query) {
|
|
99
|
+
const params = new URLSearchParams();
|
|
100
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
value.forEach(v => params.append(key, v));
|
|
103
|
+
} else if (value !== undefined) {
|
|
104
|
+
params.append(key, String(value));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
const queryString = params.toString();
|
|
108
|
+
if (queryString) url += `?${queryString}`;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Response Handling
|
|
113
|
+
|
|
114
|
+
- **Single success response**: Return type is `Promise<ResponseType>`. Use `<operationId>Response` naming for the interface.
|
|
115
|
+
- **Multiple success/error responses (different status codes)**: Include all in a union type. Example: `Promise<FindPetByIdResponse | FindPetByIdError>` for 200/404 responses. Each status code gets its own interface derived from the response schema.
|
|
116
|
+
- **Void/No response**: If `generate-client-schema` returns `response: null`, generate `Promise<void>` as return type.
|
|
117
|
+
- **Response type naming**: Use `<operationId>Response`, `<operationId>Error`, `<operationId>NotFound`, etc. to reflect the complete response schema for each status code, not just extracted data properties.
|
|
118
|
+
|
|
119
|
+
### URL Construction
|
|
120
|
+
|
|
121
|
+
- Path parameters are substituted using template literals: `/pet/${petId}`
|
|
122
|
+
- Query string is appended only if query parameters exist and are not empty
|
|
123
|
+
- Do not include the base URL; the `httpClient` implementation handles it
|
|
124
|
+
|
|
125
|
+
### Multipart and File Uploads
|
|
126
|
+
|
|
127
|
+
- When `generate-client-schema` indicates multipart content, represent file fields explicitly in the generated request-body shape.
|
|
128
|
+
- Prefer a file descriptor style for binary fields, for example `{ kind: "file", path: string, fileName?: string, mimeType?: string }`, and derive `fileName` and `mimeType` from the file context when the path is known.
|
|
129
|
+
- Keep ordinary text fields as strings or normal JSON values.
|
|
130
|
+
- Use any `encoding.<field>.contentType` hint from the schema when choosing the file MIME type.
|
|
131
|
+
- If the operation is a raw binary upload, the client should accept a file-like body instead of a JSON object.
|
|
132
|
+
|
|
133
|
+
## Complete Example
|
|
134
|
+
|
|
135
|
+
### Sample Input: generate-client-schema Output
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"operationId": "findPetById",
|
|
140
|
+
"method": "GET",
|
|
141
|
+
"path": "/pet/{petId}",
|
|
142
|
+
"parameters": [
|
|
143
|
+
{ "name": "petId", "in": "path", "required": true, "type": "integer" },
|
|
144
|
+
{ "name": "details", "in": "query", "required": false, "type": "boolean" }
|
|
145
|
+
],
|
|
146
|
+
"requestBody": null,
|
|
147
|
+
"responses": {
|
|
148
|
+
"200": { "type": "object", "properties": { "id": { "type": "number" }, "name": { "type": "string" } } },
|
|
149
|
+
"404": { "type": "object", "properties": { "error": { "type": "string" } } }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Sample Output: Generated TypeScript Client
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
/**
|
|
158
|
+
* Petstore Client - findPetById
|
|
159
|
+
* Generated from petstore API schema by openapi-skills.
|
|
160
|
+
*/
|
|
161
|
+
|
|
162
|
+
interface HttpClient {
|
|
163
|
+
get(url: string, headers?: Record<string, string>): Promise<any>;
|
|
164
|
+
post(url: string, body?: any, headers?: Record<string, string>): Promise<any>;
|
|
165
|
+
put(url: string, body?: any, headers?: Record<string, string>): Promise<any>;
|
|
166
|
+
delete(url: string, headers?: Record<string, string>): Promise<any>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface FindPetByIdResponse {
|
|
170
|
+
id: number;
|
|
171
|
+
name: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface FindPetByIdError {
|
|
175
|
+
error: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export class PetstorePetClient {
|
|
179
|
+
// Pass your own http client that implements the HttpClient interface.
|
|
180
|
+
constructor(private httpClient: HttpClient) {}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Find pet by ID.
|
|
184
|
+
* @param petId - ID of pet to return
|
|
185
|
+
* @param query - Optional query parameters
|
|
186
|
+
* @param headers - Optional HTTP headers
|
|
187
|
+
* @returns Promise resolving to FindPetByIdResponse or FindPetByIdError
|
|
188
|
+
*/
|
|
189
|
+
async findPetById(
|
|
190
|
+
petId: number,
|
|
191
|
+
query?: { details?: boolean },
|
|
192
|
+
headers?: Record<string, string>
|
|
193
|
+
): Promise<FindPetByIdResponse | FindPetByIdError> {
|
|
194
|
+
let url = `/pet/${petId}`;
|
|
195
|
+
|
|
196
|
+
if (query) {
|
|
197
|
+
const params = new URLSearchParams();
|
|
198
|
+
if (query.details !== undefined) {
|
|
199
|
+
params.append('details', String(query.details));
|
|
200
|
+
}
|
|
201
|
+
const queryString = params.toString();
|
|
202
|
+
if (queryString) url += `?${queryString}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return this.httpClient.get(url, headers);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Key Implementation Points
|
|
211
|
+
|
|
212
|
+
1. **Type definitions** for request/response are placed above the class
|
|
213
|
+
2. **Constructor** accepts only `httpClient`; no auth or baseUrl handling
|
|
214
|
+
3. **Method signature** follows parameter order: path → query → headers
|
|
215
|
+
4. **URL construction** is relative (no base URL; httpClient adds it)
|
|
216
|
+
5. **Query serialization** uses URLSearchParams for proper encoding
|
|
217
|
+
6. **Return type** is a union of all possible response types (`FindPetByIdResponse | FindPetByIdError`)
|
|
218
|
+
7. **Response type naming** reflects the complete response schema (not extracted properties). Use `<operationId>Response` and `<operationId>Error` format
|
|
219
|
+
8. **JSDoc** documents parameters and return type
|