openapi-sync 3.0.2 β 4.0.0
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 +46 -1343
- package/dist/index.d.mts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +72 -54
- package/dist/index.mjs +72 -54
- package/package.json +28 -13
- package/db.json +0 -1
package/README.md
CHANGED
|
@@ -2,93 +2,39 @@
|
|
|
2
2
|
[](https://github.com/akintomiwa-fisayo/openapi-sync/blob/main/LICENSE)
|
|
3
3
|
[](https://github.com/akintomiwa-fisayo/openapi-sync)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# OpenAPI Sync
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**OpenAPI Sync** is a powerful developer tool that automates the synchronization of your API documentation with your codebase using OpenAPI (formerly Swagger) specifications. It generates TypeScript types, endpoint definitions, runtime validation schemas (Zod, Yup, Joi), and comprehensive documentation from your OpenAPI schemaβensuring type safety from API specification to runtime validation.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- [Installation](#installation)
|
|
11
|
-
- [Quick Start](#quick-start)
|
|
12
|
-
- [Configuration](#configuration)
|
|
13
|
-
- [Usage](#usage)
|
|
14
|
-
- [Generated Output](#generated-output)
|
|
15
|
-
- [Custom Code Injection](#custom-code-injection)
|
|
16
|
-
- [API Reference](#api-reference)
|
|
17
|
-
- [Advanced Examples](#advanced-examples)
|
|
18
|
-
- [Troubleshooting](#troubleshooting)
|
|
9
|
+
> π **[Full documentation available at openapi-sync.com](https://openapi-sync.com)**
|
|
19
10
|
|
|
20
11
|
## Features
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
- π **Real-time API Synchronization** - Automatically syncs OpenAPI specs from remote URLs
|
|
14
|
+
- π **Automatic Type Generation** - Generates TypeScript interfaces for all endpoints
|
|
15
|
+
- π§ **Highly Configurable** - Customizable naming, filtering, and folder organization
|
|
16
|
+
- π‘οΈ **Enterprise Ready** - Error handling, validation, and state persistence
|
|
17
|
+
- π **Runtime Validation** - Generate Zod, Yup, or Joi schemas from OpenAPI specs
|
|
18
|
+
- π **Rich Documentation** - JSDoc comments with cURL examples
|
|
19
|
+
- π **Custom Code Injection** - Preserve your custom code between regenerations
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
- Supports both JSON and YAML formats
|
|
26
|
-
- Configurable refetch intervals for development environments
|
|
27
|
-
- Smart caching to avoid unnecessary regeneration
|
|
28
|
-
|
|
29
|
-
### π **Automatic Type Generation**
|
|
30
|
-
|
|
31
|
-
- Generates TypeScript interfaces for all API endpoints
|
|
32
|
-
- Creates type definitions for request/response bodies
|
|
33
|
-
- Supports complex nested objects, arrays, and unions
|
|
34
|
-
- Handles nullable types and optional properties
|
|
35
|
-
- Generates shared component types from OpenAPI components
|
|
36
|
-
|
|
37
|
-
### π§ **Highly Configurable**
|
|
38
|
-
|
|
39
|
-
- Customizable naming conventions for types and endpoints
|
|
40
|
-
- Exclude/include endpoints by exact path or regex patterns
|
|
41
|
-
- Tag-based filtering, method-specific filtering, and pattern matching
|
|
42
|
-
- Folder splitting configuration for organized code generation
|
|
43
|
-
- OperationId-based naming for better type and endpoint names
|
|
44
|
-
- Flexible output directory structure with custom folder organization
|
|
45
|
-
- URL transformation and text replacement rules
|
|
46
|
-
- Configurable documentation generation
|
|
47
|
-
- Support for multiple API specifications
|
|
48
|
-
- Custom code injection, preserve your custom code between regenerations
|
|
49
|
-
|
|
50
|
-
### π‘οΈ **Enterprise Ready**
|
|
51
|
-
|
|
52
|
-
- Network error handling with exponential backoff retry
|
|
53
|
-
- Schema validation using Redocly OpenAPI Core
|
|
54
|
-
- State persistence to track changes
|
|
55
|
-
- Environment-aware (disables auto-sync in production)
|
|
56
|
-
- TypeScript support with full type safety
|
|
57
|
-
|
|
58
|
-
### π **Rich Documentation**
|
|
59
|
-
|
|
60
|
-
- Generates comprehensive JSDoc comments
|
|
61
|
-
- Includes cURL examples for each endpoint
|
|
62
|
-
- Security scheme documentation
|
|
63
|
-
- Request/response type documentation
|
|
64
|
-
- Markdown-formatted type references
|
|
21
|
+
[View all features β](https://openapi-sync.com/docs#features)
|
|
65
22
|
|
|
66
23
|
## Installation
|
|
67
24
|
|
|
68
|
-
### NPM Package
|
|
69
|
-
|
|
70
25
|
```bash
|
|
71
26
|
npm install openapi-sync
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Global Installation
|
|
75
|
-
|
|
76
|
-
```bash
|
|
27
|
+
# or
|
|
77
28
|
npm install -g openapi-sync
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
### Direct Usage (No Installation)
|
|
81
|
-
|
|
82
|
-
```bash
|
|
29
|
+
# or use directly
|
|
83
30
|
npx openapi-sync
|
|
84
31
|
```
|
|
85
32
|
|
|
86
33
|
## Quick Start
|
|
87
34
|
|
|
88
|
-
1.
|
|
35
|
+
**1. Create `openapi.sync.json` in your project root:**
|
|
89
36
|
|
|
90
37
|
```json
|
|
91
|
-
// openapi.sync.json
|
|
92
38
|
{
|
|
93
39
|
"refetchInterval": 5000,
|
|
94
40
|
"folder": "./src/api",
|
|
@@ -98,1331 +44,88 @@ npx openapi-sync
|
|
|
98
44
|
}
|
|
99
45
|
```
|
|
100
46
|
|
|
101
|
-
2.
|
|
47
|
+
**2. Run the sync command:**
|
|
102
48
|
|
|
103
49
|
```bash
|
|
104
50
|
npx openapi-sync
|
|
105
51
|
```
|
|
106
52
|
|
|
107
|
-
3.
|
|
53
|
+
**3. Use generated types and endpoints:**
|
|
108
54
|
|
|
109
55
|
```typescript
|
|
110
56
|
import { getPetById } from "./src/api/petstore/endpoints";
|
|
111
|
-
import { IPet
|
|
57
|
+
import { IPet } from "./src/api/petstore/types";
|
|
112
58
|
|
|
113
|
-
// Use the endpoint URL
|
|
114
59
|
const petUrl = getPetById("123"); // Returns: "/pet/123"
|
|
115
|
-
|
|
116
|
-
// Use the generated types
|
|
117
|
-
const pet: IPet = {
|
|
118
|
-
id: 1,
|
|
119
|
-
name: "Fluffy",
|
|
120
|
-
status: "available",
|
|
121
|
-
};
|
|
122
60
|
```
|
|
123
61
|
|
|
124
|
-
|
|
62
|
+
[View detailed quick start guide β](https://openapi-sync.com/docs#quick-start)
|
|
125
63
|
|
|
126
|
-
|
|
64
|
+
## Configuration
|
|
127
65
|
|
|
128
|
-
|
|
129
|
-
- `openapi.sync.ts` (TypeScript format)
|
|
130
|
-
- `openapi.sync.js` (JavaScript format)
|
|
66
|
+
Supports multiple configuration formats: `openapi.sync.json`, `openapi.sync.ts`, or `openapi.sync.js`
|
|
131
67
|
|
|
132
|
-
|
|
68
|
+
**Basic Example:**
|
|
133
69
|
|
|
134
70
|
```json
|
|
135
71
|
{
|
|
136
72
|
"refetchInterval": 5000,
|
|
137
73
|
"folder": "./src/api",
|
|
138
74
|
"api": {
|
|
139
|
-
"petstore": "https://petstore3.swagger.io/api/v3/openapi.json"
|
|
140
|
-
"jsonplaceholder": "https://jsonplaceholder.typicode.com/openapi.yaml"
|
|
141
|
-
},
|
|
142
|
-
"server": 0
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Advanced Configuration
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
// openapi.sync.ts
|
|
150
|
-
import { IConfig } from "openapi-sync/types";
|
|
151
|
-
|
|
152
|
-
const config: IConfig = {
|
|
153
|
-
refetchInterval: 10000,
|
|
154
|
-
folder: "./src/generated/api",
|
|
155
|
-
api: {
|
|
156
|
-
"main-api": "https://api.example.com/openapi.json",
|
|
157
|
-
"auth-api": "https://auth.example.com/openapi.yaml",
|
|
158
|
-
},
|
|
159
|
-
server: "https://api.example.com", // Override server URL
|
|
160
|
-
|
|
161
|
-
// NEW: Folder splitting configuration
|
|
162
|
-
folderSplit: {
|
|
163
|
-
byTags: true, // Create folders based on endpoint tags
|
|
164
|
-
customFolder: ({ method, path, tags, operationId }) => {
|
|
165
|
-
// Custom logic to determine folder structure
|
|
166
|
-
if (tags?.includes("admin")) return "admin";
|
|
167
|
-
if (tags?.includes("public")) return "public";
|
|
168
|
-
if (path.startsWith("/api/v1/")) return "v1";
|
|
169
|
-
return null; // Use default folder structure
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
// Type generation configuration
|
|
174
|
-
types: {
|
|
175
|
-
name: {
|
|
176
|
-
prefix: "I", // Prefix for interface names
|
|
177
|
-
format: (source, data, defaultName) => {
|
|
178
|
-
if (source === "shared") {
|
|
179
|
-
return `${data.name}Type`;
|
|
180
|
-
} else if (source === "endpoint") {
|
|
181
|
-
return `${data.method?.toUpperCase()}${data.path?.replace(
|
|
182
|
-
/\//g,
|
|
183
|
-
"_"
|
|
184
|
-
)}${data.type}`;
|
|
185
|
-
}
|
|
186
|
-
return defaultName;
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
doc: {
|
|
190
|
-
disable: false, // Enable/disable JSDoc generation
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
// Endpoint generation configuration
|
|
195
|
-
endpoints: {
|
|
196
|
-
value: {
|
|
197
|
-
replaceWords: [
|
|
198
|
-
{
|
|
199
|
-
replace: "/api/v\\d+/", // Remove version from URLs
|
|
200
|
-
with: "/",
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
replace: "/internal/",
|
|
204
|
-
with: "/",
|
|
205
|
-
type: "endpoint",
|
|
206
|
-
},
|
|
207
|
-
],
|
|
208
|
-
includeServer: true, // Include server URL in endpoints
|
|
209
|
-
type: "object", // Generate as objects instead of strings
|
|
210
|
-
},
|
|
211
|
-
name: {
|
|
212
|
-
prefix: "API_",
|
|
213
|
-
useOperationId: true, // Use operationId from OpenAPI spec
|
|
214
|
-
format: ({ method, path, summary, operationId }, defaultName) => {
|
|
215
|
-
if (operationId) return operationId;
|
|
216
|
-
return path.replace(/\//g, "_").replace(/{|}/g, "");
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
doc: {
|
|
220
|
-
disable: false,
|
|
221
|
-
showCurl: true, // Include cURL examples in documentation
|
|
222
|
-
},
|
|
223
|
-
exclude: {
|
|
224
|
-
// Exclude endpoints by tags
|
|
225
|
-
tags: ["deprecated", "internal"],
|
|
226
|
-
// Exclude individual endpoints by path and method
|
|
227
|
-
endpoints: [
|
|
228
|
-
// Exact path match
|
|
229
|
-
{ path: "/admin/users", method: "DELETE" },
|
|
230
|
-
// Regex pattern match
|
|
231
|
-
{ regex: "^/internal/.*", method: "GET" },
|
|
232
|
-
{ regex: ".*/debug$", method: "GET" },
|
|
233
|
-
// Don't specify method to exclude all methods
|
|
234
|
-
{ path: "/debug/logs" },
|
|
235
|
-
],
|
|
236
|
-
},
|
|
237
|
-
include: {
|
|
238
|
-
// Include endpoints by tags
|
|
239
|
-
tags: ["public"],
|
|
240
|
-
// Include individual endpoints by path and method
|
|
241
|
-
endpoints: [
|
|
242
|
-
// Exact path match
|
|
243
|
-
{ path: "/public/users", method: "GET" },
|
|
244
|
-
// Regex pattern match
|
|
245
|
-
{ regex: "^/public/.*", method: "GET" },
|
|
246
|
-
{ regex: ".*/logs$", method: "GET" },
|
|
247
|
-
// Don't specify method to include all methods
|
|
248
|
-
{ path: "/public/logs" },
|
|
249
|
-
],
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
export default config;
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Configuration Options Reference
|
|
258
|
-
|
|
259
|
-
#### Root Configuration
|
|
260
|
-
|
|
261
|
-
| Property | Type | Description | Default |
|
|
262
|
-
| ----------------- | ------------------------ | --------------------------------------------- | -------- |
|
|
263
|
-
| `refetchInterval` | `number` | Milliseconds between API refetches (dev only) | - |
|
|
264
|
-
| `folder` | `string` | Output directory for generated files | `""` |
|
|
265
|
-
| `api` | `Record<string, string>` | Map of API names to OpenAPI spec URLs | Required |
|
|
266
|
-
| `server` | `number \| string` | Server index or custom server URL | `0` |
|
|
267
|
-
| `folderSplit` | `IConfigFolderSplit` | Configuration for folder splitting | - |
|
|
268
|
-
|
|
269
|
-
#### Type Configuration (`types`)
|
|
270
|
-
|
|
271
|
-
| Property | Type | Description |
|
|
272
|
-
| --------------------- | ---------- | ------------------------------------------------------- |
|
|
273
|
-
| `name.prefix` | `string` | Prefix for generated type names |
|
|
274
|
-
| `name.useOperationId` | `boolean` | Use OpenAPI operationId for type naming when available |
|
|
275
|
-
| `name.format` | `function` | Custom naming function with source context and metadata |
|
|
276
|
-
| `doc.disable` | `boolean` | Disable JSDoc generation for types |
|
|
277
|
-
|
|
278
|
-
**OperationId-based Type Naming:**
|
|
279
|
-
|
|
280
|
-
When `useOperationId` is set to `true`, the system will use the OpenAPI `operationId` for type naming:
|
|
281
|
-
|
|
282
|
-
- **Query Types**: `{operationId}Query` (e.g., `getUserByIdQuery`)
|
|
283
|
-
- **DTO Types**: `{operationId}DTO` (e.g., `createUserDTO`)
|
|
284
|
-
- **Response Types**: `{operationId}{code}Response` (e.g., `getUserById200Response`)
|
|
285
|
-
|
|
286
|
-
If `operationId` is not available, the system falls back to the default path-based naming convention.
|
|
287
|
-
|
|
288
|
-
#### Endpoint Configuration (`endpoints`)
|
|
289
|
-
|
|
290
|
-
| Property | Type | Description |
|
|
291
|
-
| --------------------- | --------------------------------------------------------- | --------------------------------------------------------- |
|
|
292
|
-
| `value.replaceWords` | `IConfigReplaceWord[]` | URL transformation rules |
|
|
293
|
-
| `value.includeServer` | `boolean` | Include server URL in endpoints |
|
|
294
|
-
| `value.type` | `"string" \| "object"` | Output format for endpoints |
|
|
295
|
-
| `name.prefix` | `string` | Prefix for endpoint names |
|
|
296
|
-
| `name.useOperationId` | `boolean` | Use OpenAPI operationId for naming |
|
|
297
|
-
| `name.format` | `function` | Custom naming function for endpoints |
|
|
298
|
-
| `doc.disable` | `boolean` | Disable JSDoc generation for endpoints |
|
|
299
|
-
| `doc.showCurl` | `boolean` | Include cURL examples in documentation |
|
|
300
|
-
| `exclude.tags` | `string[]` | Exclude endpoints by tags |
|
|
301
|
-
| `exclude.endpoints` | `Array<{path?: string, regex?: string, method?: Method}>` | Exclude specific endpoints by exact path or regex pattern |
|
|
302
|
-
| `include.tags` | `string[]` | Include endpoints by tags |
|
|
303
|
-
| `include.endpoints` | `Array<{path?: string, regex?: string, method?: Method}>` | Include specific endpoints by exact path or regex pattern |
|
|
304
|
-
|
|
305
|
-
#### Folder Splitting Configuration (`folderSplit`)
|
|
306
|
-
|
|
307
|
-
| Property | Type | Description |
|
|
308
|
-
| -------------- | ---------- | --------------------------------------------- |
|
|
309
|
-
| `byTags` | `boolean` | Create folders based on endpoint tags |
|
|
310
|
-
| `customFolder` | `function` | Custom function to determine folder structure |
|
|
311
|
-
|
|
312
|
-
**Folder Splitting Examples:**
|
|
313
|
-
|
|
314
|
-
```typescript
|
|
315
|
-
// Split by tags - creates folders like "admin/", "public/", "user/"
|
|
316
|
-
folderSplit: {
|
|
317
|
-
byTags: true,
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Custom folder logic
|
|
321
|
-
folderSplit: {
|
|
322
|
-
customFolder: ({ method, path, tags, operationId }) => {
|
|
323
|
-
// Admin endpoints go to admin folder
|
|
324
|
-
if (tags?.includes("admin")) return "admin";
|
|
325
|
-
|
|
326
|
-
// Public endpoints go to public folder
|
|
327
|
-
if (tags?.includes("public")) return "public";
|
|
328
|
-
|
|
329
|
-
// API versioning
|
|
330
|
-
if (path.startsWith("/api/v1/")) return "v1";
|
|
331
|
-
if (path.startsWith("/api/v2/")) return "v2";
|
|
332
|
-
|
|
333
|
-
// Method-based organization
|
|
334
|
-
const method = data.method.toLowerCase();
|
|
335
|
-
if (method === "get") return "read";
|
|
336
|
-
if (method === "post" || method === "PUT") return "write";
|
|
337
|
-
|
|
338
|
-
return null; // Use default structure
|
|
339
|
-
},
|
|
340
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
## Usage
|
|
344
|
-
|
|
345
|
-
### CLI Usage
|
|
346
|
-
|
|
347
|
-
#### Basic Commands
|
|
348
|
-
|
|
349
|
-
```bash
|
|
350
|
-
# Run with default configuration
|
|
351
|
-
npx openapi-sync
|
|
352
|
-
|
|
353
|
-
# Run with custom refetch interval
|
|
354
|
-
npx openapi-sync --refreshinterval 30000
|
|
355
|
-
npx openapi-sync -ri 30000
|
|
356
|
-
|
|
357
|
-
# Get help
|
|
358
|
-
npx openapi-sync --help
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
#### CLI Options
|
|
362
|
-
|
|
363
|
-
| Option | Alias | Type | Description |
|
|
364
|
-
| ------------------- | ----- | -------- | ------------------------------------- |
|
|
365
|
-
| `--refreshinterval` | `-ri` | `number` | Override refetch interval from config |
|
|
366
|
-
|
|
367
|
-
### Programmatic Usage
|
|
368
|
-
|
|
369
|
-
#### Basic Usage
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
import { Init } from "openapi-sync";
|
|
373
|
-
|
|
374
|
-
// Initialize with default config
|
|
375
|
-
await Init();
|
|
376
|
-
|
|
377
|
-
// Initialize with custom options
|
|
378
|
-
await Init({
|
|
379
|
-
refetchInterval: 30000,
|
|
380
|
-
});
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
#### Advanced Integration
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
import { Init } from "openapi-sync";
|
|
387
|
-
|
|
388
|
-
// In your application startup
|
|
389
|
-
export const initializeAPI = async () => {
|
|
390
|
-
try {
|
|
391
|
-
await Init({
|
|
392
|
-
refetchInterval: process.env.NODE_ENV === "development" ? 5000 : 0,
|
|
393
|
-
});
|
|
394
|
-
console.log("API types synchronized successfully");
|
|
395
|
-
} catch (error) {
|
|
396
|
-
console.error("Failed to sync API types:", error);
|
|
397
|
-
throw error;
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
// Call during app initialization
|
|
402
|
-
initializeAPI();
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
#### Function-based Configuration
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
// openapi.sync.ts
|
|
409
|
-
import { IConfig } from "openapi-sync/types";
|
|
410
|
-
|
|
411
|
-
export default (): IConfig => {
|
|
412
|
-
const baseConfig = {
|
|
413
|
-
refetchInterval: 5000,
|
|
414
|
-
folder: "./src/api",
|
|
415
|
-
api: {},
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
// Dynamic configuration based on environment
|
|
419
|
-
if (process.env.NODE_ENV === "development") {
|
|
420
|
-
baseConfig.api = {
|
|
421
|
-
"local-api": "http://localhost:3000/openapi.json",
|
|
422
|
-
};
|
|
423
|
-
} else {
|
|
424
|
-
baseConfig.api = {
|
|
425
|
-
"prod-api": "https://api.production.com/openapi.json",
|
|
426
|
-
};
|
|
75
|
+
"petstore": "https://petstore3.swagger.io/api/v3/openapi.json"
|
|
427
76
|
}
|
|
428
|
-
|
|
429
|
-
return baseConfig;
|
|
430
|
-
};
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
## Generated Output
|
|
434
|
-
|
|
435
|
-
OpenAPI Sync generates a structured output in your specified folder:
|
|
436
|
-
|
|
437
|
-
### Default Structure
|
|
438
|
-
|
|
439
|
-
```
|
|
440
|
-
src/api/
|
|
441
|
-
βββ petstore/
|
|
442
|
-
β βββ endpoints.ts # Endpoint URLs and metadata
|
|
443
|
-
β βββ types.ts # Endpoint-specific types
|
|
444
|
-
βββ auth-api/
|
|
445
|
-
β βββ endpoints.ts # Endpoint URLs and metadata
|
|
446
|
-
β βββ types.ts # Endpoint-specific types
|
|
447
|
-
βββ shared.ts # Shared component types
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### Folder Splitting Structure
|
|
451
|
-
|
|
452
|
-
When `folderSplit.byTags` is enabled or custom folder logic is used:
|
|
453
|
-
|
|
454
|
-
```
|
|
455
|
-
src/api/
|
|
456
|
-
βββ petstore/
|
|
457
|
-
β βββ admin/ # Endpoints with "admin" tag
|
|
458
|
-
β β βββ endpoints.ts
|
|
459
|
-
β β βββ types.ts
|
|
460
|
-
β βββ public/ # Endpoints with "public" tag
|
|
461
|
-
β β βββ endpoints.ts
|
|
462
|
-
β β βββ types.ts
|
|
463
|
-
β βββ user/ # Endpoints with "user" tag
|
|
464
|
-
β βββ endpoints.ts
|
|
465
|
-
β βββ types.ts
|
|
466
|
-
βββ auth-api/
|
|
467
|
-
β βββ v1/ # Custom folder logic
|
|
468
|
-
β β βββ endpoints.ts
|
|
469
|
-
β β βββ types.ts
|
|
470
|
-
β βββ v2/
|
|
471
|
-
β βββ endpoints.ts
|
|
472
|
-
β βββ types.ts
|
|
473
|
-
βββ shared.ts # Shared component types
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
### Generated Endpoints
|
|
477
|
-
|
|
478
|
-
When `endpoints.value.type` is set to `"string"`
|
|
479
|
-
|
|
480
|
-
#### String Format (Default)
|
|
481
|
-
|
|
482
|
-
````typescript
|
|
483
|
-
// endpoints.ts
|
|
484
|
-
/**
|
|
485
|
-
* **Method**: `GET`
|
|
486
|
-
* **Summary**: Find pet by ID
|
|
487
|
-
* **Tags**: [pet]
|
|
488
|
-
* **OperationId**: getPetById
|
|
489
|
-
* **Response**:
|
|
490
|
-
* - **200**:
|
|
491
|
-
* ```typescript
|
|
492
|
-
* {
|
|
493
|
-
* "id": number;
|
|
494
|
-
* "name": string;
|
|
495
|
-
* "status": ("available"|"pending"|"sold");
|
|
496
|
-
* }
|
|
497
|
-
* ```
|
|
498
|
-
*
|
|
499
|
-
* ```bash
|
|
500
|
-
* curl -X GET "https://petstore3.swagger.io/api/v3/pet/123" \
|
|
501
|
-
* -H "accept: application/json"
|
|
502
|
-
* ```
|
|
503
|
-
*/
|
|
504
|
-
export const getPetById = (petId: string) => `/pet/${petId}`;
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* **Method**: `POST`
|
|
508
|
-
* **Summary**: Add a new pet to the store
|
|
509
|
-
*/
|
|
510
|
-
export const addPet = "/pet";
|
|
511
|
-
````
|
|
512
|
-
|
|
513
|
-
#### Object Format
|
|
514
|
-
|
|
515
|
-
When `endpoints.value.type` is set to `"object"`, each endpoint is generated as an object containing metadata:
|
|
516
|
-
|
|
517
|
-
````typescript
|
|
518
|
-
// endpoints.ts (with type: "object")
|
|
519
|
-
/**
|
|
520
|
-
* **Method**: `POST`
|
|
521
|
-
* **Summary**: Add a new pet to the store
|
|
522
|
-
* **Tags**: [pet]
|
|
523
|
-
* **DTO**:
|
|
524
|
-
* ```typescript
|
|
525
|
-
* {
|
|
526
|
-
* "name": string;
|
|
527
|
-
* "photoUrls": string[];
|
|
528
|
-
* "status": ("available"|"pending"|"sold");
|
|
529
|
-
* }
|
|
530
|
-
* ```
|
|
531
|
-
*/
|
|
532
|
-
export const addPet = {
|
|
533
|
-
method: "POST",
|
|
534
|
-
operationId: "addPet",
|
|
535
|
-
url: "/pet",
|
|
536
|
-
tags: ["pet"],
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
export const getPetById = {
|
|
540
|
-
method: "GET",
|
|
541
|
-
operationId: "getPetById",
|
|
542
|
-
url: (petId: string) => `/pet/${petId}`,
|
|
543
|
-
tags: ["pet"],
|
|
544
|
-
};
|
|
545
|
-
````
|
|
546
|
-
|
|
547
|
-
### Generated Types
|
|
548
|
-
|
|
549
|
-
#### Endpoint Types
|
|
550
|
-
|
|
551
|
-
```typescript
|
|
552
|
-
// types/index.ts
|
|
553
|
-
import * as Shared from "./shared";
|
|
554
|
-
|
|
555
|
-
// Query parameter types
|
|
556
|
-
export type IFindPetsByStatusQuery = {
|
|
557
|
-
status?: "available" | "pending" | "sold";
|
|
558
|
-
};
|
|
559
|
-
|
|
560
|
-
// Request body types (DTO)
|
|
561
|
-
export type IAddPetDTO = {
|
|
562
|
-
id?: number;
|
|
563
|
-
name: string;
|
|
564
|
-
category?: Shared.ICategory;
|
|
565
|
-
photoUrls: string[];
|
|
566
|
-
tags?: Shared.ITag[];
|
|
567
|
-
status?: "available" | "pending" | "sold";
|
|
568
|
-
};
|
|
569
|
-
|
|
570
|
-
// Response types
|
|
571
|
-
export type IGetPetById200Response = {
|
|
572
|
-
id?: number;
|
|
573
|
-
name: string;
|
|
574
|
-
category?: Shared.ICategory;
|
|
575
|
-
photoUrls: string[];
|
|
576
|
-
tags?: Shared.ITag[];
|
|
577
|
-
status?: "available" | "pending" | "sold";
|
|
578
|
-
};
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
#### Shared Component Types
|
|
582
|
-
|
|
583
|
-
```typescript
|
|
584
|
-
// types/shared.ts
|
|
585
|
-
/**
|
|
586
|
-
* A category for a pet
|
|
587
|
-
*/
|
|
588
|
-
export type ICategory = {
|
|
589
|
-
id?: number;
|
|
590
|
-
name?: string;
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* A tag for a pet
|
|
595
|
-
*/
|
|
596
|
-
export type ITag = {
|
|
597
|
-
id?: number;
|
|
598
|
-
name?: string;
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
export type IPet = {
|
|
602
|
-
id?: number;
|
|
603
|
-
name: string;
|
|
604
|
-
category?: ICategory;
|
|
605
|
-
photoUrls: string[];
|
|
606
|
-
tags?: ITag[];
|
|
607
|
-
status?: "available" | "pending" | "sold";
|
|
608
|
-
};
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
## Custom Code Injection
|
|
612
|
-
|
|
613
|
-
OpenAPI Sync supports preserving custom code between regenerations using special comment markers. This allows you to add your own custom endpoints, types, or utility functions that will survive when the generated code is updated.
|
|
614
|
-
|
|
615
|
-
### How It Works
|
|
616
|
-
|
|
617
|
-
Custom code is preserved using special comment markers in the generated files. Any code you add between these markers will be preserved when the files are regenerated.
|
|
618
|
-
|
|
619
|
-
### Configuration
|
|
620
|
-
|
|
621
|
-
Add the `customCode` configuration to your `openapi.sync.ts` file:
|
|
622
|
-
|
|
623
|
-
```typescript
|
|
624
|
-
import { IConfig } from "openapi-sync/types";
|
|
625
|
-
|
|
626
|
-
export default {
|
|
627
|
-
refetchInterval: 5000,
|
|
628
|
-
folder: "./src/api",
|
|
629
|
-
api: {
|
|
630
|
-
petstore: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
631
|
-
},
|
|
632
|
-
customCode: {
|
|
633
|
-
enabled: true, // Enable custom code preservation (default: true)
|
|
634
|
-
position: "bottom", // Position of custom code: "top", "bottom", or "both"
|
|
635
|
-
markerText: "CUSTOM CODE", // Custom marker text (default: "CUSTOM CODE")
|
|
636
|
-
includeInstructions: true, // Include helpful instructions (default: true)
|
|
637
|
-
},
|
|
638
|
-
} as IConfig;
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### Configuration Options
|
|
642
|
-
|
|
643
|
-
| Option | Type | Default | Description |
|
|
644
|
-
| --------------------- | ----------------------------- | --------------- | ------------------------------------------ |
|
|
645
|
-
| `enabled` | `boolean` | `true` | Enable or disable custom code preservation |
|
|
646
|
-
| `position` | `"top" \| "bottom" \| "both"` | `"bottom"` | Where to place custom code markers |
|
|
647
|
-
| `markerText` | `string` | `"CUSTOM CODE"` | Custom text for markers |
|
|
648
|
-
| `includeInstructions` | `boolean` | `true` | Include helpful instructions in markers |
|
|
649
|
-
|
|
650
|
-
### Usage Example
|
|
651
|
-
|
|
652
|
-
After running OpenAPI Sync for the first time, your generated files will include custom code markers:
|
|
653
|
-
|
|
654
|
-
**endpoints.ts**
|
|
655
|
-
|
|
656
|
-
```typescript
|
|
657
|
-
// AUTO-GENERATED FILE - DO NOT EDIT OUTSIDE CUSTOM CODE MARKERS
|
|
658
|
-
export const getPet = (petId: string) => `/pet/${petId}`;
|
|
659
|
-
export const createPet = "/pet";
|
|
660
|
-
export const updatePet = (petId: string) => `/pet/${petId}`;
|
|
661
|
-
|
|
662
|
-
// ============================================================
|
|
663
|
-
// π CUSTOM CODE START
|
|
664
|
-
// Add your custom code above this line
|
|
665
|
-
// This section will be preserved during regeneration
|
|
666
|
-
// ============================================================
|
|
667
|
-
|
|
668
|
-
// ============================================================
|
|
669
|
-
// π CUSTOM CODE END
|
|
670
|
-
// ============================================================
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
### Adding Custom Code
|
|
674
|
-
|
|
675
|
-
Simply add your custom code between the markers:
|
|
676
|
-
|
|
677
|
-
**endpoints.ts**
|
|
678
|
-
|
|
679
|
-
```typescript
|
|
680
|
-
export const getPet = (petId: string) => `/pet/${petId}`;
|
|
681
|
-
export const createPet = "/pet";
|
|
682
|
-
|
|
683
|
-
// ============================================================
|
|
684
|
-
// π CUSTOM CODE START
|
|
685
|
-
// ============================================================
|
|
686
|
-
|
|
687
|
-
// Custom endpoints for legacy API
|
|
688
|
-
export const legacyGetPet = (petId: string) => `/api/v1/pet/${petId}`;
|
|
689
|
-
export const customSearch = "/api/search";
|
|
690
|
-
|
|
691
|
-
// Custom utility function
|
|
692
|
-
export const buildPetUrl = (petId: string, includePhotos: boolean) => {
|
|
693
|
-
const base = getPet(petId);
|
|
694
|
-
return includePhotos ? `${base}?include=photos` : base;
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
// ============================================================
|
|
698
|
-
// π CUSTOM CODE END
|
|
699
|
-
// ============================================================
|
|
700
|
-
|
|
701
|
-
export const updatePet = (petId: string) => `/pet/${petId}`;
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
### Custom Types
|
|
705
|
-
|
|
706
|
-
You can also add custom types in the `types.ts` and `shared.ts` files:
|
|
707
|
-
|
|
708
|
-
**types.ts**
|
|
709
|
-
|
|
710
|
-
```typescript
|
|
711
|
-
import * as Shared from "./shared";
|
|
712
|
-
|
|
713
|
-
export type IGetPetByIdResponse = Shared.IPet;
|
|
714
|
-
export type ICreatePetDTO = {
|
|
715
|
-
name: string;
|
|
716
|
-
status?: "available" | "pending" | "sold";
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
// ============================================================
|
|
720
|
-
// π CUSTOM CODE START
|
|
721
|
-
// ============================================================
|
|
722
|
-
|
|
723
|
-
// Custom type extending generated types
|
|
724
|
-
export interface IPetWithMetadata extends Shared.IPet {
|
|
725
|
-
fetchedAt: Date;
|
|
726
|
-
cached: boolean;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// Custom utility type
|
|
730
|
-
export type PartialPet = Partial<Shared.IPet>;
|
|
731
|
-
|
|
732
|
-
// Custom enum
|
|
733
|
-
export enum PetStatus {
|
|
734
|
-
Available = "available",
|
|
735
|
-
Pending = "pending",
|
|
736
|
-
Sold = "sold",
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
// ============================================================
|
|
740
|
-
// π CUSTOM CODE END
|
|
741
|
-
// ============================================================
|
|
742
|
-
```
|
|
743
|
-
|
|
744
|
-
### Position Options
|
|
745
|
-
|
|
746
|
-
#### Bottom Position (Default)
|
|
747
|
-
|
|
748
|
-
Custom code markers appear at the bottom of the file:
|
|
749
|
-
|
|
750
|
-
```typescript
|
|
751
|
-
// Generated code...
|
|
752
|
-
export const endpoint1 = "/api/v1";
|
|
753
|
-
|
|
754
|
-
// π CUSTOM CODE START
|
|
755
|
-
// Your custom code here
|
|
756
|
-
// π CUSTOM CODE END
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
#### Top Position
|
|
760
|
-
|
|
761
|
-
Custom code markers appear at the top of the file:
|
|
762
|
-
|
|
763
|
-
```typescript
|
|
764
|
-
// π CUSTOM CODE START
|
|
765
|
-
// Your custom code here
|
|
766
|
-
// π CUSTOM CODE END
|
|
767
|
-
|
|
768
|
-
// Generated code...
|
|
769
|
-
export const endpoint1 = "/api/v1";
|
|
770
|
-
```
|
|
771
|
-
|
|
772
|
-
#### Both Positions
|
|
773
|
-
|
|
774
|
-
Custom code markers appear at both top and bottom:
|
|
775
|
-
|
|
776
|
-
```typescript
|
|
777
|
-
// π CUSTOM CODE START (TOP)
|
|
778
|
-
// Top custom code
|
|
779
|
-
// π CUSTOM CODE END
|
|
780
|
-
|
|
781
|
-
// Generated code...
|
|
782
|
-
|
|
783
|
-
// π CUSTOM CODE START (BOTTOM)
|
|
784
|
-
// Bottom custom code
|
|
785
|
-
// π CUSTOM CODE END
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
### Best Practices
|
|
789
|
-
|
|
790
|
-
1. **Don't Edit Outside Markers**: Only add code between the custom code markers. Code outside these markers will be overwritten.
|
|
791
|
-
|
|
792
|
-
2. **Use Descriptive Names**: Use clear, descriptive names for your custom code to avoid conflicts with generated code.
|
|
793
|
-
|
|
794
|
-
3. **Keep It Organized**: Group related custom code together and add comments explaining its purpose.
|
|
795
|
-
|
|
796
|
-
4. **Test After Regeneration**: After regenerating, verify your custom code is still present and working correctly.
|
|
797
|
-
|
|
798
|
-
5. **Version Control**: Commit your custom code changes separately from regeneration to track what's custom vs generated.
|
|
799
|
-
|
|
800
|
-
### Use Cases
|
|
801
|
-
|
|
802
|
-
#### Legacy API Support
|
|
803
|
-
|
|
804
|
-
```typescript
|
|
805
|
-
// π CUSTOM CODE START
|
|
806
|
-
// Support for legacy v1 API that's not in OpenAPI spec
|
|
807
|
-
export const legacyLogin = "/api/v1/auth/login";
|
|
808
|
-
export const legacyLogout = "/api/v1/auth/logout";
|
|
809
|
-
// π CUSTOM CODE END
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
#### Custom Utilities
|
|
813
|
-
|
|
814
|
-
```typescript
|
|
815
|
-
// π CUSTOM CODE START
|
|
816
|
-
// Utility functions for working with generated endpoints
|
|
817
|
-
export const isPublicEndpoint = (endpoint: string): boolean => {
|
|
818
|
-
return endpoint.startsWith("/public/");
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
export const requiresAuth = (endpoint: string): boolean => {
|
|
822
|
-
return !isPublicEndpoint(endpoint);
|
|
823
|
-
};
|
|
824
|
-
// π CUSTOM CODE END
|
|
825
|
-
```
|
|
826
|
-
|
|
827
|
-
#### Type Extensions
|
|
828
|
-
|
|
829
|
-
```typescript
|
|
830
|
-
// π CUSTOM CODE START
|
|
831
|
-
// Extended types with additional client-side fields
|
|
832
|
-
export interface IUserWithUI extends Shared.IUser {
|
|
833
|
-
isLoading?: boolean;
|
|
834
|
-
hasError?: boolean;
|
|
835
|
-
lastFetched?: Date;
|
|
836
77
|
}
|
|
837
|
-
// π CUSTOM CODE END
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
### Disabling Custom Code Preservation
|
|
841
|
-
|
|
842
|
-
If you want to disable custom code preservation (not recommended for most use cases):
|
|
843
|
-
|
|
844
|
-
```typescript
|
|
845
|
-
export default {
|
|
846
|
-
// ... other config
|
|
847
|
-
customCode: {
|
|
848
|
-
enabled: false, // Disables custom code preservation
|
|
849
|
-
},
|
|
850
|
-
} as IConfig;
|
|
851
|
-
```
|
|
852
|
-
|
|
853
|
-
β οΈ **Warning**: When disabled, all files will be completely overwritten on each regeneration.
|
|
854
|
-
|
|
855
|
-
## API Reference
|
|
856
|
-
|
|
857
|
-
### Exported Functions
|
|
858
|
-
|
|
859
|
-
#### `Init(options?: { refetchInterval?: number }): Promise<void>`
|
|
860
|
-
|
|
861
|
-
Initializes OpenAPI sync with the specified configuration.
|
|
862
|
-
|
|
863
|
-
**Parameters:**
|
|
864
|
-
|
|
865
|
-
- `options.refetchInterval` - Override the refetch interval from config file
|
|
866
|
-
|
|
867
|
-
**Example:**
|
|
868
|
-
|
|
869
|
-
```typescript
|
|
870
|
-
import { Init } from "openapi-sync";
|
|
871
|
-
|
|
872
|
-
await Init({ refetchInterval: 10000 });
|
|
873
78
|
```
|
|
874
79
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
All types from the `types.ts` file are available for import:
|
|
80
|
+
**Advanced TypeScript Example:**
|
|
878
81
|
|
|
879
82
|
```typescript
|
|
880
|
-
import {
|
|
881
|
-
IConfig,
|
|
882
|
-
IOpenApiSpec,
|
|
883
|
-
IOpenApSchemaSpec,
|
|
884
|
-
IConfigReplaceWord,
|
|
885
|
-
IConfigExclude,
|
|
886
|
-
IConfigInclude,
|
|
887
|
-
IConfigDoc,
|
|
888
|
-
} from "openapi-sync/types";
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
## Advanced Examples
|
|
892
|
-
|
|
893
|
-
### Advanced Folder Splitting Configuration
|
|
894
|
-
|
|
895
|
-
```typescript
|
|
896
|
-
// openapi.sync.ts
|
|
897
83
|
import { IConfig } from "openapi-sync/types";
|
|
898
84
|
|
|
899
85
|
const config: IConfig = {
|
|
900
|
-
refetchInterval:
|
|
86
|
+
refetchInterval: 10000,
|
|
901
87
|
folder: "./src/api",
|
|
902
88
|
api: {
|
|
903
89
|
"main-api": "https://api.example.com/openapi.json",
|
|
904
90
|
},
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
folderSplit: {
|
|
908
|
-
byTags: true, // Enable tag-based splitting
|
|
909
|
-
customFolder: ({ method, path, tags, operationId }) => {
|
|
910
|
-
// Priority-based folder assignment
|
|
911
|
-
|
|
912
|
-
// 1. Admin endpoints always go to admin folder
|
|
913
|
-
if (tags?.includes("admin")) return "admin";
|
|
914
|
-
|
|
915
|
-
// 2. Public API endpoints
|
|
916
|
-
if (tags?.includes("public")) return "public";
|
|
917
|
-
|
|
918
|
-
// 3. Version-based splitting
|
|
919
|
-
if (path.startsWith("/api/v1/")) return "v1";
|
|
920
|
-
if (path.startsWith("/api/v2/")) return "v2";
|
|
921
|
-
|
|
922
|
-
// 4. Method-based organization for remaining endpoints
|
|
923
|
-
if (method === "GET") return "read";
|
|
924
|
-
if (method === "POST" || method === "PUT" || method === "PATCH")
|
|
925
|
-
return "write";
|
|
926
|
-
if (method === "DELETE") return "delete";
|
|
927
|
-
|
|
928
|
-
// 5. OperationId-based splitting for specific operations
|
|
929
|
-
if (operationId?.includes("Auth")) return "auth";
|
|
930
|
-
if (operationId?.includes("User")) return "user";
|
|
931
|
-
|
|
932
|
-
return null; // Use default structure
|
|
933
|
-
},
|
|
934
|
-
},
|
|
935
|
-
|
|
936
|
-
// Enhanced type naming with operationId support
|
|
937
|
-
types: {
|
|
938
|
-
name: {
|
|
939
|
-
prefix: "I",
|
|
940
|
-
useOperationId: true, // Use operationId when available
|
|
941
|
-
format: (source, data, defaultName) => {
|
|
942
|
-
if (source === "endpoint" && data.operationId) {
|
|
943
|
-
// Use operationId for better naming
|
|
944
|
-
switch (data.type) {
|
|
945
|
-
case "query":
|
|
946
|
-
return `${data.operationId}Query`;
|
|
947
|
-
case "dto":
|
|
948
|
-
return `${data.operationId}DTO`;
|
|
949
|
-
case "response":
|
|
950
|
-
return `${data.operationId}${data.code}Response`;
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
return defaultName;
|
|
954
|
-
},
|
|
955
|
-
},
|
|
956
|
-
},
|
|
957
|
-
|
|
958
|
-
// Enhanced endpoint configuration
|
|
91
|
+
folderSplit: { byTags: true },
|
|
92
|
+
types: { name: { prefix: "I", useOperationId: true } },
|
|
959
93
|
endpoints: {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
format: ({ operationId, method, path }, defaultName) => {
|
|
963
|
-
if (operationId) return operationId;
|
|
964
|
-
return defaultName;
|
|
965
|
-
},
|
|
966
|
-
},
|
|
967
|
-
exclude: {
|
|
968
|
-
tags: ["deprecated", "internal"],
|
|
969
|
-
endpoints: [
|
|
970
|
-
{ regex: "^/internal/.*" },
|
|
971
|
-
{ path: "/debug", method: "GET" },
|
|
972
|
-
],
|
|
973
|
-
},
|
|
94
|
+
exclude: { tags: ["deprecated"] },
|
|
95
|
+
doc: { showCurl: true },
|
|
974
96
|
},
|
|
97
|
+
validations: { library: "zod" },
|
|
975
98
|
};
|
|
976
99
|
|
|
977
100
|
export default config;
|
|
978
101
|
```
|
|
979
102
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
```typescript
|
|
983
|
-
// openapi.sync.ts
|
|
984
|
-
import { IConfig } from "openapi-sync/types";
|
|
985
|
-
|
|
986
|
-
const getConfig = (): IConfig => {
|
|
987
|
-
const env = process.env.NODE_ENV || "development";
|
|
988
|
-
|
|
989
|
-
const baseConfig: IConfig = {
|
|
990
|
-
refetchInterval: env === "development" ? 5000 : 0,
|
|
991
|
-
folder: "./src/api",
|
|
992
|
-
api: {},
|
|
993
|
-
types: {
|
|
994
|
-
name: {
|
|
995
|
-
prefix: "I",
|
|
996
|
-
format: (source, data, defaultName) => {
|
|
997
|
-
if (source === "endpoint" && data.type === "response") {
|
|
998
|
-
return `${defaultName.replace(/Response$/, "")}Data`;
|
|
999
|
-
}
|
|
1000
|
-
return defaultName;
|
|
1001
|
-
},
|
|
1002
|
-
},
|
|
1003
|
-
},
|
|
1004
|
-
};
|
|
1005
|
-
|
|
1006
|
-
switch (env) {
|
|
1007
|
-
case "development":
|
|
1008
|
-
baseConfig.api = {
|
|
1009
|
-
"local-api": "http://localhost:3000/api/openapi.json",
|
|
1010
|
-
};
|
|
1011
|
-
break;
|
|
1012
|
-
case "staging":
|
|
1013
|
-
baseConfig.api = {
|
|
1014
|
-
"staging-api": "https://staging-api.example.com/openapi.json",
|
|
1015
|
-
};
|
|
1016
|
-
break;
|
|
1017
|
-
case "production":
|
|
1018
|
-
baseConfig.api = {
|
|
1019
|
-
"prod-api": "https://api.example.com/openapi.json",
|
|
1020
|
-
};
|
|
1021
|
-
break;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
return baseConfig;
|
|
1025
|
-
};
|
|
1026
|
-
|
|
1027
|
-
export default getConfig;
|
|
1028
|
-
```
|
|
1029
|
-
|
|
1030
|
-
### Custom Type Formatting
|
|
1031
|
-
|
|
1032
|
-
```typescript
|
|
1033
|
-
// Advanced type name formatting with operationId support
|
|
1034
|
-
const config: IConfig = {
|
|
1035
|
-
// ... other config
|
|
1036
|
-
types: {
|
|
1037
|
-
name: {
|
|
1038
|
-
prefix: "",
|
|
1039
|
-
useOperationId: true, // Use operationId when available
|
|
1040
|
-
format: (source, data, defaultName) => {
|
|
1041
|
-
if (source === "shared") {
|
|
1042
|
-
// Shared types: UserProfile, OrderStatus, etc.
|
|
1043
|
-
return `${data.name}`;
|
|
1044
|
-
} else if (source === "endpoint") {
|
|
1045
|
-
// Use operationId if available and configured
|
|
1046
|
-
if (data.operationId) {
|
|
1047
|
-
switch (data.type) {
|
|
1048
|
-
case "query":
|
|
1049
|
-
return `${data.operationId}Query`;
|
|
1050
|
-
case "dto":
|
|
1051
|
-
return `${data.operationId}DTO`;
|
|
1052
|
-
case "response":
|
|
1053
|
-
return `${data.operationId}${data.code}Response`;
|
|
1054
|
-
default:
|
|
1055
|
-
return defaultName;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// Fallback to path-based naming
|
|
1060
|
-
const method = data.method?.toUpperCase();
|
|
1061
|
-
const cleanPath = data.path
|
|
1062
|
-
?.replace(/[{}\/]/g, "_")
|
|
1063
|
-
.replace(/_+/g, "_");
|
|
1064
|
-
|
|
1065
|
-
switch (data.type) {
|
|
1066
|
-
case "query":
|
|
1067
|
-
return `${method}${cleanPath}Query`;
|
|
1068
|
-
case "dto":
|
|
1069
|
-
return `${method}${cleanPath}Request`;
|
|
1070
|
-
case "response":
|
|
1071
|
-
return `${method}${cleanPath}${data.code}Response`;
|
|
1072
|
-
default:
|
|
1073
|
-
return defaultName;
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
return defaultName;
|
|
1077
|
-
},
|
|
1078
|
-
},
|
|
1079
|
-
},
|
|
1080
|
-
};
|
|
1081
|
-
```
|
|
1082
|
-
|
|
1083
|
-
### Endpoint Filtering and Selection
|
|
1084
|
-
|
|
1085
|
-
```typescript
|
|
1086
|
-
// Advanced endpoint filtering configuration
|
|
1087
|
-
const config: IConfig = {
|
|
1088
|
-
// ... other config
|
|
1089
|
-
endpoints: {
|
|
1090
|
-
// ... other endpoint config
|
|
1091
|
-
exclude: {
|
|
1092
|
-
// Exclude endpoints by tags
|
|
1093
|
-
tags: ["deprecated", "internal", "admin"],
|
|
1094
|
-
// Exclude specific endpoints by exact path or regex pattern
|
|
1095
|
-
endpoints: [
|
|
1096
|
-
// Exact path matches
|
|
1097
|
-
{ path: "/admin/users", method: "DELETE" },
|
|
1098
|
-
{ path: "/admin/settings", method: "PUT" },
|
|
1099
|
-
// Regex pattern matches
|
|
1100
|
-
{ regex: "^/internal/.*", method: "GET" },
|
|
1101
|
-
{ regex: ".*/debug$", method: "POST" },
|
|
1102
|
-
// Exclude all methods for a specific path
|
|
1103
|
-
{ path: "/debug/logs" },
|
|
1104
|
-
],
|
|
1105
|
-
},
|
|
1106
|
-
include: {
|
|
1107
|
-
// Include only public endpoints
|
|
1108
|
-
tags: ["public", "user"],
|
|
1109
|
-
// Include specific endpoints by exact path or regex pattern
|
|
1110
|
-
endpoints: [
|
|
1111
|
-
// Exact path matches
|
|
1112
|
-
{ path: "/public/users", method: "GET" },
|
|
1113
|
-
{ path: "/public/profile", method: "PUT" },
|
|
1114
|
-
// Regex pattern matches
|
|
1115
|
-
{ regex: "^/public/.*", method: "GET" },
|
|
1116
|
-
{ regex: ".*/health$", method: "GET" },
|
|
1117
|
-
],
|
|
1118
|
-
},
|
|
1119
|
-
},
|
|
1120
|
-
};
|
|
1121
|
-
```
|
|
1122
|
-
|
|
1123
|
-
### Path vs Regex Filtering
|
|
1124
|
-
|
|
1125
|
-
```typescript
|
|
1126
|
-
// Demonstrating the difference between path and regex filtering
|
|
1127
|
-
const config: IConfig = {
|
|
1128
|
-
// ... other config
|
|
1129
|
-
endpoints: {
|
|
1130
|
-
exclude: {
|
|
1131
|
-
endpoints: [
|
|
1132
|
-
// Exact path match - only excludes exactly "/api/users"
|
|
1133
|
-
{ path: "/api/users", method: "GET" },
|
|
1134
|
-
|
|
1135
|
-
// Regex match - excludes all paths starting with "/api/users"
|
|
1136
|
-
{ regex: "^/api/users.*", method: "GET" },
|
|
1137
|
-
|
|
1138
|
-
// Regex match - excludes all paths ending with "/debug"
|
|
1139
|
-
{ regex: ".*/debug$", method: "GET" },
|
|
1140
|
-
|
|
1141
|
-
// Regex match - excludes paths with specific pattern
|
|
1142
|
-
{ regex: "^/internal/.*/admin$", method: "POST" },
|
|
1143
|
-
],
|
|
1144
|
-
},
|
|
1145
|
-
},
|
|
1146
|
-
};
|
|
1147
|
-
```
|
|
1148
|
-
|
|
1149
|
-
### URL Transformation Rules
|
|
1150
|
-
|
|
1151
|
-
```typescript
|
|
1152
|
-
const config: IConfig = {
|
|
1153
|
-
// ... other config
|
|
1154
|
-
endpoints: {
|
|
1155
|
-
value: {
|
|
1156
|
-
replaceWords: [
|
|
1157
|
-
// Remove API versioning from URLs
|
|
1158
|
-
{
|
|
1159
|
-
replace: "/api/v[0-9]+/",
|
|
1160
|
-
with: "/",
|
|
1161
|
-
},
|
|
1162
|
-
// Remove internal prefixes
|
|
1163
|
-
{
|
|
1164
|
-
replace: "/internal/",
|
|
1165
|
-
with: "/",
|
|
1166
|
-
},
|
|
1167
|
-
// Transform specific paths
|
|
1168
|
-
{
|
|
1169
|
-
replace: "/users/profile",
|
|
1170
|
-
with: "/profile",
|
|
1171
|
-
},
|
|
1172
|
-
],
|
|
1173
|
-
includeServer: true,
|
|
1174
|
-
type: "object",
|
|
1175
|
-
},
|
|
1176
|
-
name: {
|
|
1177
|
-
format: ({ method, path, operationId }, defaultName) => {
|
|
1178
|
-
// Use operationId if available, otherwise generate from path
|
|
1179
|
-
if (operationId) {
|
|
1180
|
-
return operationId.replace(/[^a-zA-Z0-9]/g, "");
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
// Generate meaningful names from path and method
|
|
1184
|
-
const cleanPath = path
|
|
1185
|
-
.replace(/^\//, "")
|
|
1186
|
-
.replace(/\//g, "_")
|
|
1187
|
-
.replace(/{([^}]+)}/g, "By$1")
|
|
1188
|
-
.replace(/[^a-zA-Z0-9_]/g, "");
|
|
1189
|
-
|
|
1190
|
-
return `${method.toLowerCase()}${cleanPath}`;
|
|
1191
|
-
},
|
|
1192
|
-
},
|
|
1193
|
-
},
|
|
1194
|
-
};
|
|
1195
|
-
```
|
|
1196
|
-
|
|
1197
|
-
### Integration with Build Process
|
|
1198
|
-
|
|
1199
|
-
#### Package.json Scripts
|
|
1200
|
-
|
|
1201
|
-
```json
|
|
1202
|
-
{
|
|
1203
|
-
"scripts": {
|
|
1204
|
-
"api:sync": "openapi-sync",
|
|
1205
|
-
"api:sync:watch": "openapi-sync --refreshinterval 3000",
|
|
1206
|
-
"prebuild": "npm run api:sync",
|
|
1207
|
-
"build": "tsc",
|
|
1208
|
-
"dev": "concurrently \"npm run api:sync:watch\" \"npm run dev:server\""
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
```
|
|
1212
|
-
|
|
1213
|
-
#### Pre-commit Hook
|
|
1214
|
-
|
|
1215
|
-
```bash
|
|
1216
|
-
#!/bin/sh
|
|
1217
|
-
# .husky/pre-commit
|
|
1218
|
-
|
|
1219
|
-
# Sync API types before commit
|
|
1220
|
-
npm run api:sync
|
|
1221
|
-
|
|
1222
|
-
# Add generated files to commit
|
|
1223
|
-
git add src/api/
|
|
1224
|
-
```
|
|
1225
|
-
|
|
1226
|
-
#### CI/CD Integration
|
|
1227
|
-
|
|
1228
|
-
```yaml
|
|
1229
|
-
# .github/workflows/build.yml
|
|
1230
|
-
name: Build
|
|
1231
|
-
on: [push, pull_request]
|
|
103
|
+
[View full configuration options β](https://openapi-sync.com/docs#configuration)
|
|
1232
104
|
|
|
1233
|
-
|
|
1234
|
-
build:
|
|
1235
|
-
runs-on: ubuntu-latest
|
|
1236
|
-
steps:
|
|
1237
|
-
- uses: actions/checkout@v2
|
|
1238
|
-
- uses: actions/setup-node@v2
|
|
1239
|
-
with:
|
|
1240
|
-
node-version: "16"
|
|
105
|
+
## Documentation
|
|
1241
106
|
|
|
1242
|
-
|
|
1243
|
-
run: npm ci
|
|
1244
|
-
|
|
1245
|
-
- name: Sync API types
|
|
1246
|
-
run: npm run api:sync
|
|
1247
|
-
env:
|
|
1248
|
-
NODE_ENV: production
|
|
1249
|
-
|
|
1250
|
-
- name: Build
|
|
1251
|
-
run: npm run build
|
|
1252
|
-
```
|
|
1253
|
-
|
|
1254
|
-
### Error Handling and Monitoring
|
|
1255
|
-
|
|
1256
|
-
```typescript
|
|
1257
|
-
import { Init } from "openapi-sync";
|
|
1258
|
-
|
|
1259
|
-
const initializeAPIWithRetry = async (maxRetries = 3) => {
|
|
1260
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1261
|
-
try {
|
|
1262
|
-
await Init({
|
|
1263
|
-
refetchInterval: process.env.NODE_ENV === "development" ? 5000 : 0,
|
|
1264
|
-
});
|
|
1265
|
-
|
|
1266
|
-
console.log("β
API types synchronized successfully");
|
|
1267
|
-
return;
|
|
1268
|
-
} catch (error) {
|
|
1269
|
-
console.error(`β Attempt ${attempt} failed:`, error.message);
|
|
1270
|
-
|
|
1271
|
-
if (attempt === maxRetries) {
|
|
1272
|
-
console.error("π¨ Failed to sync API types after all retries");
|
|
1273
|
-
throw error;
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
// Wait before retry
|
|
1277
|
-
await new Promise((resolve) => setTimeout(resolve, 2000 * attempt));
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
};
|
|
1281
|
-
|
|
1282
|
-
// Usage in your app
|
|
1283
|
-
initializeAPIWithRetry().catch((error) => {
|
|
1284
|
-
// Handle critical error - maybe use cached types or exit
|
|
1285
|
-
console.error("Critical: Could not sync API types", error);
|
|
1286
|
-
process.exit(1);
|
|
1287
|
-
});
|
|
1288
|
-
```
|
|
1289
|
-
|
|
1290
|
-
## Troubleshooting
|
|
1291
|
-
|
|
1292
|
-
### Common Issues
|
|
1293
|
-
|
|
1294
|
-
#### 1. Configuration File Not Found
|
|
1295
|
-
|
|
1296
|
-
```
|
|
1297
|
-
Error: No config found
|
|
1298
|
-
```
|
|
1299
|
-
|
|
1300
|
-
**Solution:** Ensure you have one of these files in your project root:
|
|
1301
|
-
|
|
1302
|
-
- `openapi.sync.json`
|
|
1303
|
-
- `openapi.sync.ts`
|
|
1304
|
-
- `openapi.sync.js`
|
|
1305
|
-
|
|
1306
|
-
#### 2. Network Timeout Errors
|
|
1307
|
-
|
|
1308
|
-
```
|
|
1309
|
-
Error: timeout of 60000ms exceeded
|
|
1310
|
-
```
|
|
107
|
+
For complete documentation including:
|
|
1311
108
|
|
|
1312
|
-
**
|
|
109
|
+
- **Configuration Options** - All available settings and customization
|
|
110
|
+
- **Generated Output** - Understanding generated files and structure
|
|
111
|
+
- **Custom Code Injection** - Preserve your code between regenerations
|
|
112
|
+
- **Validation Schemas** - Runtime validation with Zod, Yup, or Joi
|
|
113
|
+
- **Advanced Examples** - Complex configurations and use cases
|
|
114
|
+
- **API Reference** - Programmatic usage and type definitions
|
|
115
|
+
- **Troubleshooting** - Common issues and solutions
|
|
1313
116
|
|
|
1314
|
-
-
|
|
1315
|
-
- Verify the OpenAPI spec URL is accessible
|
|
1316
|
-
- Consider increasing timeout in your configuration
|
|
1317
|
-
|
|
1318
|
-
#### 3. TypeScript Compilation Errors
|
|
1319
|
-
|
|
1320
|
-
```
|
|
1321
|
-
Error: Cannot find module './src/api/petstore/types'
|
|
1322
|
-
```
|
|
1323
|
-
|
|
1324
|
-
**Solution:**
|
|
1325
|
-
|
|
1326
|
-
- Ensure the sync process completed successfully
|
|
1327
|
-
- Check that the `folder` path in config is correct
|
|
1328
|
-
- Verify TypeScript can resolve the generated paths
|
|
1329
|
-
|
|
1330
|
-
#### 4. Invalid OpenAPI Specification
|
|
1331
|
-
|
|
1332
|
-
```
|
|
1333
|
-
Error: Schema validation failed
|
|
1334
|
-
```
|
|
1335
|
-
|
|
1336
|
-
**Solution:**
|
|
1337
|
-
|
|
1338
|
-
- Validate your OpenAPI spec using online validators
|
|
1339
|
-
- Check for syntax errors in YAML/JSON
|
|
1340
|
-
- Ensure the spec follows OpenAPI 3.0+ standards
|
|
1341
|
-
|
|
1342
|
-
### Performance Optimization
|
|
1343
|
-
|
|
1344
|
-
#### 1. Reduce Refetch Frequency
|
|
1345
|
-
|
|
1346
|
-
```json
|
|
1347
|
-
{
|
|
1348
|
-
"refetchInterval": 30000 // Increase to 30 seconds
|
|
1349
|
-
}
|
|
1350
|
-
```
|
|
1351
|
-
|
|
1352
|
-
#### 2. Disable Documentation Generation
|
|
1353
|
-
|
|
1354
|
-
```json
|
|
1355
|
-
{
|
|
1356
|
-
"types": { "doc": { "disable": true } },
|
|
1357
|
-
"endpoints": { "doc": { "disable": true } }
|
|
1358
|
-
}
|
|
1359
|
-
```
|
|
1360
|
-
|
|
1361
|
-
#### 3. Use Specific Output Folders
|
|
1362
|
-
|
|
1363
|
-
```json
|
|
1364
|
-
{
|
|
1365
|
-
"folder": "./src/api" // Specific path instead of root
|
|
1366
|
-
}
|
|
1367
|
-
```
|
|
1368
|
-
|
|
1369
|
-
### Debugging
|
|
1370
|
-
|
|
1371
|
-
#### Enable Verbose Logging
|
|
1372
|
-
|
|
1373
|
-
```typescript
|
|
1374
|
-
// Set environment variable for debugging
|
|
1375
|
-
process.env.DEBUG = "openapi-sync:*";
|
|
1376
|
-
|
|
1377
|
-
import { Init } from "openapi-sync";
|
|
1378
|
-
await Init();
|
|
1379
|
-
```
|
|
1380
|
-
|
|
1381
|
-
#### Check Generated State
|
|
1382
|
-
|
|
1383
|
-
The tool maintains state in `db.json` to track changes:
|
|
1384
|
-
|
|
1385
|
-
```json
|
|
1386
|
-
// db.json
|
|
1387
|
-
{
|
|
1388
|
-
"petstore": {
|
|
1389
|
-
"openapi": "3.0.0",
|
|
1390
|
-
"info": { "title": "Petstore API" }
|
|
1391
|
-
// ... full OpenAPI spec
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
```
|
|
1395
|
-
|
|
1396
|
-
### Getting Help
|
|
1397
|
-
|
|
1398
|
-
1. **Check the Issues**: [GitHub Issues](https://github.com/akintomiwa-fisayo/openapi-sync/issues)
|
|
1399
|
-
2. **Create a Bug Report**: Include your configuration and error logs
|
|
1400
|
-
3. **Feature Requests**: Describe your use case and expected behavior
|
|
117
|
+
**Visit [openapi-sync.com](https://openapi-sync.com)**
|
|
1401
118
|
|
|
1402
119
|
---
|
|
1403
120
|
|
|
1404
|
-
## Changelog
|
|
1405
|
-
|
|
1406
|
-
- v2.1.13: Fix dts type fixes and Clean up tsup build config and introduction of unit testing
|
|
1407
|
-
- v2.1.12: Add automatic sync support for function-based config, improved handling of missing OpenAPI urls
|
|
1408
|
-
- v2.1.11: Folder splitting configuration for organized code generation
|
|
1409
|
-
- v2.1.10: OperationId-based naming for types and endpoints, enhanced filtering and tag support
|
|
1410
|
-
- v2.1.9: Enhanced JSONStringify function improvements
|
|
1411
|
-
- v2.1.8: File extension corrections and path handling
|
|
1412
|
-
- v2.1.7: Endpoint tags support in API documentation
|
|
1413
|
-
- v2.1.6: Improved handling of nullable fields in generated types
|
|
1414
|
-
- v2.1.5: Fixed bug with recursive schema references
|
|
1415
|
-
- v2.1.4: Enhanced error messages on invalid config
|
|
1416
|
-
- v2.1.3: Add more informative debugging logs
|
|
1417
|
-
- v2.1.2: Support enum descriptions in output
|
|
1418
|
-
- v2.1.1: Update dependencies, minor TypeScript type fixes
|
|
1419
|
-
- v2.1.0: Initial v2 major release with new sync engine
|
|
1420
|
-
- v2.0.0: Major refactor and breaking changes for v2
|
|
1421
|
-
|
|
1422
121
|
## License
|
|
1423
122
|
|
|
1424
|
-
|
|
123
|
+
ISC License - see [LICENSE](LICENSE) file for details.
|
|
1425
124
|
|
|
1426
125
|
## Contributing
|
|
1427
126
|
|
|
1428
|
-
Contributions
|
|
127
|
+
Contributions welcome! Submit pull requests to our [GitHub repository](https://github.com/akintomiwa-fisayo/openapi-sync).
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
**[π Full Documentation](https://openapi-sync.com) | [GitHub](https://github.com/akintomiwa-fisayo/openapi-sync) | [npm](https://www.npmjs.com/package/openapi-sync)**
|