openapi-sync 2.1.9 → 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 +348 -17
- package/db.json +1 -1
- package/dist/index.d.mts +172 -0
- package/dist/index.d.ts +170 -4
- package/dist/index.js +56 -88
- package/dist/index.mjs +56 -0
- package/package.json +5 -7
- package/dist/Openapi-sync/index.d.ts +0 -3
- package/dist/Openapi-sync/index.js +0 -776
- package/dist/Openapi-sync/state.d.ts +0 -4
- package/dist/Openapi-sync/state.js +0 -37
- package/dist/helpers.d.ts +0 -12
- package/dist/helpers.js +0 -161
- package/dist/openapi.sync.sample.d.ts +0 -1
- package/dist/openapi.sync.sample.js +0 -55
- package/dist/regex.d.ts +0 -2
- package/dist/regex.js +0 -5
- package/dist/types.d.ts +0 -113
- package/dist/types.js +0 -2
package/README.md
CHANGED
|
@@ -35,7 +35,11 @@
|
|
|
35
35
|
### 🔧 **Highly Configurable**
|
|
36
36
|
|
|
37
37
|
- Customizable naming conventions for types and endpoints
|
|
38
|
-
-
|
|
38
|
+
- Exclude/include endpoints by exact path or regex patterns
|
|
39
|
+
- Tag-based filtering, method-specific filtering, and pattern matching
|
|
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
|
|
39
43
|
- URL transformation and text replacement rules
|
|
40
44
|
- Configurable documentation generation
|
|
41
45
|
- Support for multiple API specifications
|
|
@@ -151,6 +155,18 @@ const config: IConfig = {
|
|
|
151
155
|
},
|
|
152
156
|
server: "https://api.example.com", // Override server URL
|
|
153
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
|
+
|
|
154
170
|
// Type generation configuration
|
|
155
171
|
types: {
|
|
156
172
|
name: {
|
|
@@ -201,6 +217,34 @@ const config: IConfig = {
|
|
|
201
217
|
disable: false,
|
|
202
218
|
showCurl: true, // Include cURL examples in documentation
|
|
203
219
|
},
|
|
220
|
+
exclude: {
|
|
221
|
+
// Exclude endpoints by tags
|
|
222
|
+
tags: ["deprecated", "internal"],
|
|
223
|
+
// Exclude individual endpoints by path and method
|
|
224
|
+
endpoints: [
|
|
225
|
+
// Exact path match
|
|
226
|
+
{ path: "/admin/users", method: "DELETE" },
|
|
227
|
+
// Regex pattern match
|
|
228
|
+
{ regex: "^/internal/.*", method: "GET" },
|
|
229
|
+
{ regex: ".*/debug$", method: "GET" },
|
|
230
|
+
// Don't specify method to exclude all methods
|
|
231
|
+
{ path: "/debug/logs" },
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
include: {
|
|
235
|
+
// Include endpoints by tags
|
|
236
|
+
tags: ["public"],
|
|
237
|
+
// Include individual endpoints by path and method
|
|
238
|
+
endpoints: [
|
|
239
|
+
// Exact path match
|
|
240
|
+
{ path: "/public/users", method: "GET" },
|
|
241
|
+
// Regex pattern match
|
|
242
|
+
{ regex: "^/public/.*", method: "GET" },
|
|
243
|
+
{ regex: ".*/logs$", method: "GET" },
|
|
244
|
+
// Don't specify method to include all methods
|
|
245
|
+
{ path: "/public/logs" },
|
|
246
|
+
],
|
|
247
|
+
},
|
|
204
248
|
},
|
|
205
249
|
};
|
|
206
250
|
|
|
@@ -217,27 +261,81 @@ export default config;
|
|
|
217
261
|
| `folder` | `string` | Output directory for generated files | `""` |
|
|
218
262
|
| `api` | `Record<string, string>` | Map of API names to OpenAPI spec URLs | Required |
|
|
219
263
|
| `server` | `number \| string` | Server index or custom server URL | `0` |
|
|
264
|
+
| `folderSplit` | `IConfigFolderSplit` | Configuration for folder splitting | - |
|
|
220
265
|
|
|
221
266
|
#### Type Configuration (`types`)
|
|
222
267
|
|
|
223
|
-
| Property
|
|
224
|
-
|
|
|
225
|
-
| `name.prefix`
|
|
226
|
-
| `name.
|
|
227
|
-
| `
|
|
268
|
+
| Property | Type | Description |
|
|
269
|
+
| --------------------- | ---------- | ------------------------------------------------------- |
|
|
270
|
+
| `name.prefix` | `string` | Prefix for generated type names |
|
|
271
|
+
| `name.useOperationId` | `boolean` | Use OpenAPI operationId for type naming when available |
|
|
272
|
+
| `name.format` | `function` | Custom naming function with source context and metadata |
|
|
273
|
+
| `doc.disable` | `boolean` | Disable JSDoc generation for types |
|
|
274
|
+
|
|
275
|
+
**OperationId-based Type Naming:**
|
|
276
|
+
|
|
277
|
+
When `useOperationId` is set to `true`, the system will use the OpenAPI `operationId` for type naming:
|
|
278
|
+
|
|
279
|
+
- **Query Types**: `{operationId}Query` (e.g., `getUserByIdQuery`)
|
|
280
|
+
- **DTO Types**: `{operationId}DTO` (e.g., `createUserDTO`)
|
|
281
|
+
- **Response Types**: `{operationId}{code}Response` (e.g., `getUserById200Response`)
|
|
282
|
+
|
|
283
|
+
If `operationId` is not available, the system falls back to the default path-based naming convention.
|
|
228
284
|
|
|
229
285
|
#### Endpoint Configuration (`endpoints`)
|
|
230
286
|
|
|
231
|
-
| Property | Type
|
|
232
|
-
| --------------------- |
|
|
233
|
-
| `value.replaceWords` | `IConfigReplaceWord[]`
|
|
234
|
-
| `value.includeServer` | `boolean`
|
|
235
|
-
| `value.type` | `"string" \| "object"`
|
|
236
|
-
| `name.prefix` | `string`
|
|
237
|
-
| `name.useOperationId` | `boolean`
|
|
238
|
-
| `name.format` | `function`
|
|
239
|
-
| `doc.disable` | `boolean`
|
|
240
|
-
| `doc.showCurl` | `boolean`
|
|
287
|
+
| Property | Type | Description |
|
|
288
|
+
| --------------------- | --------------------------------------------------------- | --------------------------------------------------------- |
|
|
289
|
+
| `value.replaceWords` | `IConfigReplaceWord[]` | URL transformation rules |
|
|
290
|
+
| `value.includeServer` | `boolean` | Include server URL in endpoints |
|
|
291
|
+
| `value.type` | `"string" \| "object"` | Output format for endpoints |
|
|
292
|
+
| `name.prefix` | `string` | Prefix for endpoint names |
|
|
293
|
+
| `name.useOperationId` | `boolean` | Use OpenAPI operationId for naming |
|
|
294
|
+
| `name.format` | `function` | Custom naming function for endpoints |
|
|
295
|
+
| `doc.disable` | `boolean` | Disable JSDoc generation for endpoints |
|
|
296
|
+
| `doc.showCurl` | `boolean` | Include cURL examples in documentation |
|
|
297
|
+
| `exclude.tags` | `string[]` | Exclude endpoints by tags |
|
|
298
|
+
| `exclude.endpoints` | `Array<{path?: string, regex?: string, method?: Method}>` | Exclude specific endpoints by exact path or regex pattern |
|
|
299
|
+
| `include.tags` | `string[]` | Include endpoints by tags |
|
|
300
|
+
| `include.endpoints` | `Array<{path?: string, regex?: string, method?: Method}>` | Include specific endpoints by exact path or regex pattern |
|
|
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
|
+
```
|
|
241
339
|
|
|
242
340
|
## Usage
|
|
243
341
|
|
|
@@ -333,6 +431,8 @@ export default (): IConfig => {
|
|
|
333
431
|
|
|
334
432
|
OpenAPI Sync generates a structured output in your specified folder:
|
|
335
433
|
|
|
434
|
+
### Default Structure
|
|
435
|
+
|
|
336
436
|
```
|
|
337
437
|
src/api/
|
|
338
438
|
├── petstore/
|
|
@@ -347,6 +447,37 @@ src/api/
|
|
|
347
447
|
└── shared.ts
|
|
348
448
|
```
|
|
349
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
|
+
|
|
350
481
|
### Generated Endpoints
|
|
351
482
|
|
|
352
483
|
When `endpoints.value.type` is set to `"string"`
|
|
@@ -512,11 +643,101 @@ import {
|
|
|
512
643
|
IOpenApiSpec,
|
|
513
644
|
IOpenApSchemaSpec,
|
|
514
645
|
IConfigReplaceWord,
|
|
646
|
+
IConfigExclude,
|
|
647
|
+
IConfigInclude,
|
|
648
|
+
IConfigDoc,
|
|
515
649
|
} from "openapi-sync/types";
|
|
516
650
|
```
|
|
517
651
|
|
|
518
652
|
## Advanced Examples
|
|
519
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
|
+
|
|
520
741
|
### Multi-Environment Configuration
|
|
521
742
|
|
|
522
743
|
```typescript
|
|
@@ -570,17 +791,33 @@ export default getConfig;
|
|
|
570
791
|
### Custom Type Formatting
|
|
571
792
|
|
|
572
793
|
```typescript
|
|
573
|
-
// Advanced type name formatting
|
|
794
|
+
// Advanced type name formatting with operationId support
|
|
574
795
|
const config: IConfig = {
|
|
575
796
|
// ... other config
|
|
576
797
|
types: {
|
|
577
798
|
name: {
|
|
578
799
|
prefix: "",
|
|
800
|
+
useOperationId: true, // Use operationId when available
|
|
579
801
|
format: (source, data, defaultName) => {
|
|
580
802
|
if (source === "shared") {
|
|
581
803
|
// Shared types: UserProfile, OrderStatus, etc.
|
|
582
804
|
return `${data.name}`;
|
|
583
805
|
} else if (source === "endpoint") {
|
|
806
|
+
// Use operationId if available and configured
|
|
807
|
+
if (data.operationId) {
|
|
808
|
+
switch (data.type) {
|
|
809
|
+
case "query":
|
|
810
|
+
return `${data.operationId}Query`;
|
|
811
|
+
case "dto":
|
|
812
|
+
return `${data.operationId}DTO`;
|
|
813
|
+
case "response":
|
|
814
|
+
return `${data.operationId}${data.code}Response`;
|
|
815
|
+
default:
|
|
816
|
+
return defaultName;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Fallback to path-based naming
|
|
584
821
|
const method = data.method?.toUpperCase();
|
|
585
822
|
const cleanPath = data.path
|
|
586
823
|
?.replace(/[{}\/]/g, "_")
|
|
@@ -604,6 +841,72 @@ const config: IConfig = {
|
|
|
604
841
|
};
|
|
605
842
|
```
|
|
606
843
|
|
|
844
|
+
### Endpoint Filtering and Selection
|
|
845
|
+
|
|
846
|
+
```typescript
|
|
847
|
+
// Advanced endpoint filtering configuration
|
|
848
|
+
const config: IConfig = {
|
|
849
|
+
// ... other config
|
|
850
|
+
endpoints: {
|
|
851
|
+
// ... other endpoint config
|
|
852
|
+
exclude: {
|
|
853
|
+
// Exclude endpoints by tags
|
|
854
|
+
tags: ["deprecated", "internal", "admin"],
|
|
855
|
+
// Exclude specific endpoints by exact path or regex pattern
|
|
856
|
+
endpoints: [
|
|
857
|
+
// Exact path matches
|
|
858
|
+
{ path: "/admin/users", method: "DELETE" },
|
|
859
|
+
{ path: "/admin/settings", method: "PUT" },
|
|
860
|
+
// Regex pattern matches
|
|
861
|
+
{ regex: "^/internal/.*", method: "GET" },
|
|
862
|
+
{ regex: ".*/debug$", method: "POST" },
|
|
863
|
+
// Exclude all methods for a specific path
|
|
864
|
+
{ path: "/debug/logs" },
|
|
865
|
+
],
|
|
866
|
+
},
|
|
867
|
+
include: {
|
|
868
|
+
// Include only public endpoints
|
|
869
|
+
tags: ["public", "user"],
|
|
870
|
+
// Include specific endpoints by exact path or regex pattern
|
|
871
|
+
endpoints: [
|
|
872
|
+
// Exact path matches
|
|
873
|
+
{ path: "/public/users", method: "GET" },
|
|
874
|
+
{ path: "/public/profile", method: "PUT" },
|
|
875
|
+
// Regex pattern matches
|
|
876
|
+
{ regex: "^/public/.*", method: "GET" },
|
|
877
|
+
{ regex: ".*/health$", method: "GET" },
|
|
878
|
+
],
|
|
879
|
+
},
|
|
880
|
+
},
|
|
881
|
+
};
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
### Path vs Regex Filtering
|
|
885
|
+
|
|
886
|
+
```typescript
|
|
887
|
+
// Demonstrating the difference between path and regex filtering
|
|
888
|
+
const config: IConfig = {
|
|
889
|
+
// ... other config
|
|
890
|
+
endpoints: {
|
|
891
|
+
exclude: {
|
|
892
|
+
endpoints: [
|
|
893
|
+
// Exact path match - only excludes exactly "/api/users"
|
|
894
|
+
{ path: "/api/users", method: "GET" },
|
|
895
|
+
|
|
896
|
+
// Regex match - excludes all paths starting with "/api/users"
|
|
897
|
+
{ regex: "^/api/users.*", method: "GET" },
|
|
898
|
+
|
|
899
|
+
// Regex match - excludes all paths ending with "/debug"
|
|
900
|
+
{ regex: ".*/debug$", method: "GET" },
|
|
901
|
+
|
|
902
|
+
// Regex match - excludes paths with specific pattern
|
|
903
|
+
{ regex: "^/internal/.*/admin$", method: "POST" },
|
|
904
|
+
],
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
};
|
|
908
|
+
```
|
|
909
|
+
|
|
607
910
|
### URL Transformation Rules
|
|
608
911
|
|
|
609
912
|
```typescript
|
|
@@ -859,6 +1162,34 @@ The tool maintains state in `db.json` to track changes:
|
|
|
859
1162
|
|
|
860
1163
|
---
|
|
861
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
|
+
|
|
862
1193
|
## License
|
|
863
1194
|
|
|
864
1195
|
This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
|