openapi-sync 2.1.10 → 2.1.11

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
@@ -37,7 +37,9 @@
37
37
  - Customizable naming conventions for types and endpoints
38
38
  - Exclude/include endpoints by exact path or regex patterns
39
39
  - Tag-based filtering, method-specific filtering, and pattern matching
40
- - Flexible output directory structure
40
+ - Folder splitting configuration for organized code generation
41
+ - OperationId-based naming for better type and endpoint names
42
+ - Flexible output directory structure with custom folder organization
41
43
  - URL transformation and text replacement rules
42
44
  - Configurable documentation generation
43
45
  - Support for multiple API specifications
@@ -153,6 +155,18 @@ const config: IConfig = {
153
155
  },
154
156
  server: "https://api.example.com", // Override server URL
155
157
 
158
+ // NEW: Folder splitting configuration
159
+ folderSplit: {
160
+ byTags: true, // Create folders based on endpoint tags
161
+ customFolder: ({ method, path, tags, operationId }) => {
162
+ // Custom logic to determine folder structure
163
+ if (tags?.includes("admin")) return "admin";
164
+ if (tags?.includes("public")) return "public";
165
+ if (path.startsWith("/api/v1/")) return "v1";
166
+ return null; // Use default folder structure
167
+ },
168
+ },
169
+
156
170
  // Type generation configuration
157
171
  types: {
158
172
  name: {
@@ -247,6 +261,7 @@ export default config;
247
261
  | `folder` | `string` | Output directory for generated files | `""` |
248
262
  | `api` | `Record<string, string>` | Map of API names to OpenAPI spec URLs | Required |
249
263
  | `server` | `number \| string` | Server index or custom server URL | `0` |
264
+ | `folderSplit` | `IConfigFolderSplit` | Configuration for folder splitting | - |
250
265
 
251
266
  #### Type Configuration (`types`)
252
267
 
@@ -284,6 +299,44 @@ If `operationId` is not available, the system falls back to the default path-bas
284
299
  | `include.tags` | `string[]` | Include endpoints by tags |
285
300
  | `include.endpoints` | `Array<{path?: string, regex?: string, method?: Method}>` | Include specific endpoints by exact path or regex pattern |
286
301
 
302
+ #### Folder Splitting Configuration (`folderSplit`)
303
+
304
+ | Property | Type | Description |
305
+ | -------------- | ---------- | --------------------------------------------- |
306
+ | `byTags` | `boolean` | Create folders based on endpoint tags |
307
+ | `customFolder` | `function` | Custom function to determine folder structure |
308
+
309
+ **Folder Splitting Examples:**
310
+
311
+ ```typescript
312
+ // Split by tags - creates folders like "admin/", "public/", "user/"
313
+ folderSplit: {
314
+ byTags: true,
315
+ }
316
+
317
+ // Custom folder logic
318
+ folderSplit: {
319
+ customFolder: ({ method, path, tags, operationId }) => {
320
+ // Admin endpoints go to admin folder
321
+ if (tags?.includes("admin")) return "admin";
322
+
323
+ // Public endpoints go to public folder
324
+ if (tags?.includes("public")) return "public";
325
+
326
+ // API versioning
327
+ if (path.startsWith("/api/v1/")) return "v1";
328
+ if (path.startsWith("/api/v2/")) return "v2";
329
+
330
+ // Method-based organization
331
+ const method = data.method.toLowerCase();
332
+ if (method === "get") return "read";
333
+ if (method === "post" || method === "PUT") return "write";
334
+
335
+ return null; // Use default structure
336
+ },
337
+ }
338
+ ```
339
+
287
340
  ## Usage
288
341
 
289
342
  ### CLI Usage
@@ -378,6 +431,8 @@ export default (): IConfig => {
378
431
 
379
432
  OpenAPI Sync generates a structured output in your specified folder:
380
433
 
434
+ ### Default Structure
435
+
381
436
  ```
382
437
  src/api/
383
438
  ├── petstore/
@@ -392,6 +447,37 @@ src/api/
392
447
  └── shared.ts
393
448
  ```
394
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/
460
+ │ │ ├── index.ts
461
+ │ │ └── shared.ts
462
+ │ ├── public/ # Endpoints with "public" tag
463
+ │ │ ├── endpoints.ts
464
+ │ │ └── types/
465
+ │ │ ├── index.ts
466
+ │ │ └── shared.ts
467
+ │ └── user/ # Endpoints with "user" tag
468
+ │ ├── endpoints.ts
469
+ │ └── types/
470
+ │ ├── index.ts
471
+ │ └── shared.ts
472
+ └── auth-api/
473
+ ├── v1/ # Custom folder logic
474
+ │ ├── endpoints.ts
475
+ │ └── types/
476
+ └── v2/
477
+ ├── endpoints.ts
478
+ └── types/
479
+ ```
480
+
395
481
  ### Generated Endpoints
396
482
 
397
483
  When `endpoints.value.type` is set to `"string"`
@@ -565,6 +651,93 @@ import {
565
651
 
566
652
  ## Advanced Examples
567
653
 
654
+ ### Advanced Folder Splitting Configuration
655
+
656
+ ```typescript
657
+ // openapi.sync.ts
658
+ import { IConfig } from "openapi-sync/types";
659
+
660
+ const config: IConfig = {
661
+ refetchInterval: 5000,
662
+ folder: "./src/api",
663
+ api: {
664
+ "main-api": "https://api.example.com/openapi.json",
665
+ },
666
+
667
+ // Advanced folder splitting with multiple strategies
668
+ folderSplit: {
669
+ byTags: true, // Enable tag-based splitting
670
+ customFolder: ({ method, path, tags, operationId }) => {
671
+ // Priority-based folder assignment
672
+
673
+ // 1. Admin endpoints always go to admin folder
674
+ if (tags?.includes("admin")) return "admin";
675
+
676
+ // 2. Public API endpoints
677
+ if (tags?.includes("public")) return "public";
678
+
679
+ // 3. Version-based splitting
680
+ if (path.startsWith("/api/v1/")) return "v1";
681
+ if (path.startsWith("/api/v2/")) return "v2";
682
+
683
+ // 4. Method-based organization for remaining endpoints
684
+ if (method === "GET") return "read";
685
+ if (method === "POST" || method === "PUT" || method === "PATCH")
686
+ return "write";
687
+ if (method === "DELETE") return "delete";
688
+
689
+ // 5. OperationId-based splitting for specific operations
690
+ if (operationId?.includes("Auth")) return "auth";
691
+ if (operationId?.includes("User")) return "user";
692
+
693
+ return null; // Use default structure
694
+ },
695
+ },
696
+
697
+ // Enhanced type naming with operationId support
698
+ types: {
699
+ name: {
700
+ prefix: "I",
701
+ useOperationId: true, // Use operationId when available
702
+ format: (source, data, defaultName) => {
703
+ if (source === "endpoint" && data.operationId) {
704
+ // Use operationId for better naming
705
+ switch (data.type) {
706
+ case "query":
707
+ return `${data.operationId}Query`;
708
+ case "dto":
709
+ return `${data.operationId}DTO`;
710
+ case "response":
711
+ return `${data.operationId}${data.code}Response`;
712
+ }
713
+ }
714
+ return defaultName;
715
+ },
716
+ },
717
+ },
718
+
719
+ // Enhanced endpoint configuration
720
+ endpoints: {
721
+ name: {
722
+ useOperationId: true, // Use operationId for endpoint names
723
+ format: ({ operationId, method, path }, defaultName) => {
724
+ if (operationId) return operationId;
725
+ return defaultName;
726
+ },
727
+ },
728
+ exclude: {
729
+ tags: ["deprecated", "internal"],
730
+ endpoints: [
731
+ { regex: "^/internal/.*" },
732
+ { path: "/debug", method: "GET" },
733
+ ],
734
+ },
735
+ },
736
+ };
737
+
738
+ export default config;
739
+ ```
740
+
568
741
  ### Multi-Environment Configuration
569
742
 
570
743
  ```typescript
@@ -989,6 +1162,34 @@ The tool maintains state in `db.json` to track changes:
989
1162
 
990
1163
  ---
991
1164
 
1165
+ ## Changelog
1166
+
1167
+ ### v2.1.11 (Latest)
1168
+
1169
+ - **NEW**: Folder splitting configuration for organized code generation
1170
+ - `folderSplit.byTags` - Create folders based on endpoint tags
1171
+ - `folderSplit.customFolder` - Custom function for folder structure logic
1172
+ - Enhanced folder organization with priority-based assignment
1173
+ - Improved code organization for large APIs
1174
+
1175
+ ### v2.1.10
1176
+
1177
+ - OperationId-based naming for types and endpoints
1178
+ - `types.name.useOperationId` - Use OpenAPI operationId for type naming
1179
+ - `endpoints.name.useOperationId` - Use operationId for endpoint naming
1180
+ - Enhanced endpoint filtering capabilities
1181
+ - Improved tag-based filtering
1182
+ - Better regex pattern matching
1183
+ - More flexible include/exclude rules
1184
+ - Enhanced JSONStringify function with array serialization support
1185
+ - Improved endpoint tags support in generated documentation
1186
+
1187
+ ### Previous Versions
1188
+
1189
+ - v2.1.9: Enhanced JSONStringify function improvements
1190
+ - v2.1.8: File extension corrections and path handling
1191
+ - v2.1.7: Endpoint tags support in API documentation
1192
+
992
1193
  ## License
993
1194
 
994
1195
  This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
@@ -1,6 +1,7 @@
1
- import { Method } from "axios";
2
- export type IOpenApiSpec = Record<"openapi", string> & Record<string, any>;
3
- export type IOpenApSchemaSpec = {
1
+ import { Method } from 'axios';
2
+
3
+ type IOpenApiSpec = Record<"openapi", string> & Record<string, any>;
4
+ type IOpenApSchemaSpec = {
4
5
  nullable?: boolean;
5
6
  type: "string" | "integer" | "number" | "array" | "object" | "boolean" | "null" | any[];
6
7
  example?: any;
@@ -16,7 +17,7 @@ export type IOpenApSchemaSpec = {
16
17
  oneOf?: IOpenApSchemaSpec[];
17
18
  allOf?: IOpenApSchemaSpec[];
18
19
  };
19
- export type IOpenApiParameterSpec = {
20
+ type IOpenApiParameterSpec = {
20
21
  $ref?: string;
21
22
  name: string;
22
23
  in: string;
@@ -32,28 +33,28 @@ export type IOpenApiParameterSpec = {
32
33
  example?: any;
33
34
  examples?: any[];
34
35
  };
35
- export type IOpenApiMediaTypeSpec = {
36
+ type IOpenApiMediaTypeSpec = {
36
37
  schema?: IOpenApSchemaSpec;
37
38
  example?: any;
38
39
  examples?: any[];
39
40
  encoding?: any;
40
41
  };
41
- export type IOpenApiRequestBodySpec = {
42
+ type IOpenApiRequestBodySpec = {
42
43
  description?: string;
43
44
  required?: boolean;
44
45
  content: Record<string, IOpenApiMediaTypeSpec>;
45
46
  };
46
- export type IOpenApiResponseSpec = Record<string, IOpenApiRequestBodySpec>;
47
- export type IConfigReplaceWord = {
47
+ type IOpenApiResponseSpec = Record<string, IOpenApiRequestBodySpec>;
48
+ type IConfigReplaceWord = {
48
49
  /** string and regular expression as a string*/
49
50
  replace: string;
50
51
  with: string;
51
52
  };
52
- export type IConfigDoc = {
53
+ type IConfigDoc = {
53
54
  disable?: boolean;
54
55
  showCurl?: boolean;
55
56
  };
56
- export type IConfigExclude = {
57
+ type IConfigExclude = {
57
58
  /** Exclude/Include endpoints by tags */
58
59
  tags?: string[];
59
60
  /** Exclude/Include individual endpoints by path and method */
@@ -66,13 +67,30 @@ export type IConfigExclude = {
66
67
  method?: Method;
67
68
  }>;
68
69
  };
69
- export interface IConfigInclude extends IConfigExclude {
70
+ interface IConfigInclude extends IConfigExclude {
70
71
  }
71
- export type IConfig = {
72
+ type IConfigFolderSplit = {
73
+ /** Split folders by tags - creates folders named after each tag */
74
+ byTags?: boolean;
75
+ /** Custom function to determine folder name for each endpoint */
76
+ customFolder?: (data: {
77
+ method: Method;
78
+ path: string;
79
+ summary?: string;
80
+ operationId?: string;
81
+ tags?: string[];
82
+ parameters?: IOpenApiParameterSpec[];
83
+ requestBody?: IOpenApiRequestBodySpec;
84
+ responses?: IOpenApiResponseSpec;
85
+ }) => string | null;
86
+ };
87
+ type IConfig = {
72
88
  refetchInterval?: number;
73
89
  folder?: string;
74
90
  api: Record<string, string>;
75
91
  server?: number | string;
92
+ /** Configuration for splitting generated code into folders */
93
+ folderSplit?: IConfigFolderSplit;
76
94
  /** Configuration for excluding endpoints from code generation */
77
95
  types?: {
78
96
  name?: {
@@ -111,7 +129,7 @@ export type IConfig = {
111
129
  include?: IConfigInclude;
112
130
  };
113
131
  };
114
- export type IOpenApiSecuritySchemes = {
132
+ type IOpenApiSecuritySchemes = {
115
133
  [key: string]: {
116
134
  type: "http" | "apiKey" | "oauth2" | "openIdConnect" | "mutualTLS";
117
135
  scheme?: "bearer" | "basic";
@@ -130,3 +148,25 @@ export type IOpenApiSecuritySchemes = {
130
148
  name?: string;
131
149
  };
132
150
  };
151
+
152
+ declare const isJson: (value: any) => boolean;
153
+ declare const isYamlString: (fileContent: string) => boolean;
154
+ declare const yamlStringToJson: (fileContent: string) => any;
155
+ declare const capitalize: (text: string) => string;
156
+ declare const getEndpointDetails: (path: string, method: string) => {
157
+ name: string;
158
+ variables: string[];
159
+ pathParts: string[];
160
+ };
161
+ declare const JSONStringify: (obj: Record<string, any>, indent?: number) => string;
162
+ declare const renderTypeRefMD: (typeRef: string, indent?: number) => string;
163
+ declare function getNestedValue<T>(obj: object, path: string): T | undefined;
164
+
165
+ declare const variableName: RegExp;
166
+ declare const variableNameChar: RegExp;
167
+
168
+ declare const Init: (options?: {
169
+ refetchInterval?: number;
170
+ }) => Promise<void>;
171
+
172
+ export { type IConfig, type IConfigDoc, type IConfigExclude, type IConfigFolderSplit, type IConfigInclude, type IConfigReplaceWord, type IOpenApSchemaSpec, type IOpenApiMediaTypeSpec, type IOpenApiParameterSpec, type IOpenApiRequestBodySpec, type IOpenApiResponseSpec, type IOpenApiSecuritySchemes, type IOpenApiSpec, Init, JSONStringify, capitalize, getEndpointDetails, getNestedValue, isJson, isYamlString, renderTypeRefMD, variableName, variableNameChar, yamlStringToJson };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,172 @@
1
- export * from "./types";
2
- export * from "./helpers";
3
- export * from "./regex";
4
- export declare const Init: (options?: {
1
+ import { Method } from 'axios';
2
+
3
+ type IOpenApiSpec = Record<"openapi", string> & Record<string, any>;
4
+ type IOpenApSchemaSpec = {
5
+ nullable?: boolean;
6
+ type: "string" | "integer" | "number" | "array" | "object" | "boolean" | "null" | any[];
7
+ example?: any;
8
+ enum?: string[];
9
+ format?: string;
10
+ items?: IOpenApSchemaSpec;
11
+ required?: string[];
12
+ description?: string;
13
+ $ref?: string;
14
+ properties?: Record<string, IOpenApSchemaSpec>;
15
+ additionalProperties?: IOpenApSchemaSpec;
16
+ anyOf?: IOpenApSchemaSpec[];
17
+ oneOf?: IOpenApSchemaSpec[];
18
+ allOf?: IOpenApSchemaSpec[];
19
+ };
20
+ type IOpenApiParameterSpec = {
21
+ $ref?: string;
22
+ name: string;
23
+ in: string;
24
+ enum?: string[];
25
+ description?: string;
26
+ required?: boolean;
27
+ deprecated?: boolean;
28
+ allowEmptyValue?: boolean;
29
+ style?: string;
30
+ explode?: boolean;
31
+ allowReserved?: boolean;
32
+ schema?: IOpenApSchemaSpec;
33
+ example?: any;
34
+ examples?: any[];
35
+ };
36
+ type IOpenApiMediaTypeSpec = {
37
+ schema?: IOpenApSchemaSpec;
38
+ example?: any;
39
+ examples?: any[];
40
+ encoding?: any;
41
+ };
42
+ type IOpenApiRequestBodySpec = {
43
+ description?: string;
44
+ required?: boolean;
45
+ content: Record<string, IOpenApiMediaTypeSpec>;
46
+ };
47
+ type IOpenApiResponseSpec = Record<string, IOpenApiRequestBodySpec>;
48
+ type IConfigReplaceWord = {
49
+ /** string and regular expression as a string*/
50
+ replace: string;
51
+ with: string;
52
+ };
53
+ type IConfigDoc = {
54
+ disable?: boolean;
55
+ showCurl?: boolean;
56
+ };
57
+ type IConfigExclude = {
58
+ /** Exclude/Include endpoints by tags */
59
+ tags?: string[];
60
+ /** Exclude/Include individual endpoints by path and method */
61
+ endpoints?: Array<{
62
+ /** Exact path match (regex will be ignore when provided)*/
63
+ path?: string;
64
+ /** Regular expression pattern for path matching */
65
+ regex?: string;
66
+ /** Don't specify method to exclude all methods */
67
+ method?: Method;
68
+ }>;
69
+ };
70
+ interface IConfigInclude extends IConfigExclude {
71
+ }
72
+ type IConfigFolderSplit = {
73
+ /** Split folders by tags - creates folders named after each tag */
74
+ byTags?: boolean;
75
+ /** Custom function to determine folder name for each endpoint */
76
+ customFolder?: (data: {
77
+ method: Method;
78
+ path: string;
79
+ summary?: string;
80
+ operationId?: string;
81
+ tags?: string[];
82
+ parameters?: IOpenApiParameterSpec[];
83
+ requestBody?: IOpenApiRequestBodySpec;
84
+ responses?: IOpenApiResponseSpec;
85
+ }) => string | null;
86
+ };
87
+ type IConfig = {
88
+ refetchInterval?: number;
89
+ folder?: string;
90
+ api: Record<string, string>;
91
+ server?: number | string;
92
+ /** Configuration for splitting generated code into folders */
93
+ folderSplit?: IConfigFolderSplit;
94
+ /** Configuration for excluding endpoints from code generation */
95
+ types?: {
96
+ name?: {
97
+ prefix?: string;
98
+ useOperationId?: boolean;
99
+ format?: (source: "shared" | "endpoint", data: {
100
+ name?: string;
101
+ type?: "response" | "dto" | "query";
102
+ code?: string;
103
+ method?: Method;
104
+ path?: string;
105
+ summary?: string;
106
+ operationId?: string;
107
+ }, defaultName: string) => string | null | undefined;
108
+ };
109
+ doc?: IConfigDoc;
110
+ };
111
+ endpoints?: {
112
+ value?: {
113
+ replaceWords?: IConfigReplaceWord[];
114
+ includeServer?: boolean;
115
+ type?: "string" | "object";
116
+ };
117
+ name?: {
118
+ format?: (data: {
119
+ method: Method;
120
+ path: string;
121
+ summary: string;
122
+ operationId: string;
123
+ }, defaultName: string) => string | null;
124
+ prefix?: string;
125
+ useOperationId?: boolean;
126
+ };
127
+ doc?: IConfigDoc;
128
+ exclude?: IConfigExclude;
129
+ include?: IConfigInclude;
130
+ };
131
+ };
132
+ type IOpenApiSecuritySchemes = {
133
+ [key: string]: {
134
+ type: "http" | "apiKey" | "oauth2" | "openIdConnect" | "mutualTLS";
135
+ scheme?: "bearer" | "basic";
136
+ in?: "query" | "header" | "cookie";
137
+ flows?: {
138
+ authorizationCode: {
139
+ authorizationUrl: "https://example.com/auth";
140
+ tokenUrl: "https://example.com/token";
141
+ scopes: {
142
+ "read:data": "Grants read access";
143
+ };
144
+ };
145
+ };
146
+ bearerFormat?: "JWT";
147
+ openIdConnectUrl?: string;
148
+ name?: string;
149
+ };
150
+ };
151
+
152
+ declare const isJson: (value: any) => boolean;
153
+ declare const isYamlString: (fileContent: string) => boolean;
154
+ declare const yamlStringToJson: (fileContent: string) => any;
155
+ declare const capitalize: (text: string) => string;
156
+ declare const getEndpointDetails: (path: string, method: string) => {
157
+ name: string;
158
+ variables: string[];
159
+ pathParts: string[];
160
+ };
161
+ declare const JSONStringify: (obj: Record<string, any>, indent?: number) => string;
162
+ declare const renderTypeRefMD: (typeRef: string, indent?: number) => string;
163
+ declare function getNestedValue<T>(obj: object, path: string): T | undefined;
164
+
165
+ declare const variableName: RegExp;
166
+ declare const variableNameChar: RegExp;
167
+
168
+ declare const Init: (options?: {
5
169
  refetchInterval?: number;
6
170
  }) => Promise<void>;
171
+
172
+ export { type IConfig, type IConfigDoc, type IConfigExclude, type IConfigFolderSplit, type IConfigInclude, type IConfigReplaceWord, type IOpenApSchemaSpec, type IOpenApiMediaTypeSpec, type IOpenApiParameterSpec, type IOpenApiRequestBodySpec, type IOpenApiResponseSpec, type IOpenApiSecuritySchemes, type IOpenApiSpec, Init, JSONStringify, capitalize, getEndpointDetails, getNestedValue, isJson, isYamlString, renderTypeRefMD, variableName, variableNameChar, yamlStringToJson };