@twin.org/ts-to-openapi 0.0.1 → 0.0.2-next.10
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/dist/cjs/index.cjs +44 -160
- package/dist/esm/index.mjs +45 -161
- package/dist/locales/en.json +20 -32
- package/dist/types/models/ITsToOpenApiConfig.d.ts +11 -0
- package/docs/changelog.md +153 -0
- package/docs/examples.md +1 -1
- package/docs/reference/interfaces/ITsToOpenApiConfig.md +20 -0
- package/locales/.validate-ignore +1 -0
- package/locales/en.json +1 -0
- package/package.json +22 -10
- package/dist/types/models/IJsonSchema.d.ts +0 -5
- package/dist/types/models/IOpenApi.d.ts +0 -54
- package/dist/types/models/IOpenApiExample.d.ts +0 -13
- package/dist/types/models/IOpenApiHeader.d.ts +0 -19
- package/dist/types/models/IOpenApiPathMethod.d.ts +0 -65
- package/dist/types/models/IOpenApiResponse.d.ts +0 -32
- package/dist/types/models/IOpenApiSecurityScheme.d.ts +0 -25
- package/dist/types/models/IPackageJson.d.ts +0 -15
- package/dist/types/models/jsonTypeName.d.ts +0 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var node_url = require('node:url');
|
|
|
5
5
|
var cliCore = require('@twin.org/cli-core');
|
|
6
6
|
var promises = require('node:fs/promises');
|
|
7
7
|
var core = require('@twin.org/core');
|
|
8
|
+
var toolsCore = require('@twin.org/tools-core');
|
|
8
9
|
var web = require('@twin.org/web');
|
|
9
10
|
var tsJsonSchemaGenerator = require('ts-json-schema-generator');
|
|
10
11
|
|
|
@@ -31,7 +32,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
31
32
|
responseType: "IBadRequestResponse",
|
|
32
33
|
example: {
|
|
33
34
|
name: "GeneralError",
|
|
34
|
-
message: "
|
|
35
|
+
message: "errorMessage",
|
|
35
36
|
properties: {
|
|
36
37
|
foo: "bar"
|
|
37
38
|
}
|
|
@@ -42,7 +43,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
42
43
|
responseType: "IUnauthorizedResponse",
|
|
43
44
|
example: {
|
|
44
45
|
name: "UnauthorizedError",
|
|
45
|
-
message: "
|
|
46
|
+
message: "errorMessage"
|
|
46
47
|
}
|
|
47
48
|
},
|
|
48
49
|
forbidden: {
|
|
@@ -50,7 +51,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
50
51
|
responseType: "IForbiddenResponse",
|
|
51
52
|
example: {
|
|
52
53
|
name: "NotImplementedError",
|
|
53
|
-
message: "
|
|
54
|
+
message: "errorMessage",
|
|
54
55
|
properties: {
|
|
55
56
|
method: "aMethod"
|
|
56
57
|
}
|
|
@@ -61,7 +62,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
61
62
|
responseType: "INotFoundResponse",
|
|
62
63
|
example: {
|
|
63
64
|
name: "NotFoundError",
|
|
64
|
-
message: "
|
|
65
|
+
message: "errorMessage",
|
|
65
66
|
properties: {
|
|
66
67
|
notFoundId: "1"
|
|
67
68
|
}
|
|
@@ -72,7 +73,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
72
73
|
responseType: "IConflictResponse",
|
|
73
74
|
example: {
|
|
74
75
|
name: "ConflictError",
|
|
75
|
-
message: "
|
|
76
|
+
message: "errorMessage",
|
|
76
77
|
properties: {
|
|
77
78
|
conflicts: ["1"]
|
|
78
79
|
}
|
|
@@ -83,7 +84,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
83
84
|
responseType: "IInternalServerErrorResponse",
|
|
84
85
|
example: {
|
|
85
86
|
name: "InternalServerError",
|
|
86
|
-
message: "
|
|
87
|
+
message: "errorMessage"
|
|
87
88
|
}
|
|
88
89
|
},
|
|
89
90
|
unprocessableEntity: {
|
|
@@ -91,7 +92,7 @@ const HTTP_STATUS_CODE_MAP = {
|
|
|
91
92
|
responseType: "IUnprocessableEntityResponse",
|
|
92
93
|
example: {
|
|
93
94
|
name: "UnprocessableError",
|
|
94
|
-
message: "
|
|
95
|
+
message: "errorMessage"
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
};
|
|
@@ -195,7 +196,7 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
|
|
|
195
196
|
compilerOptions: {}
|
|
196
197
|
}, undefined, "\t"));
|
|
197
198
|
const openApi = {
|
|
198
|
-
openapi:
|
|
199
|
+
openapi: toolsCore.OpenApiHelper.API_VERSION,
|
|
199
200
|
info: {
|
|
200
201
|
title: config.title,
|
|
201
202
|
description: config.description,
|
|
@@ -239,7 +240,20 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
|
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-openapi.progress.generatingSchemas"));
|
|
242
|
-
const
|
|
243
|
+
const autoExpandTypes = config.autoExpandTypes ?? [];
|
|
244
|
+
const defaultExpandTypes = ["/ObjectOrArray<.*>/"];
|
|
245
|
+
for (const defaultType of defaultExpandTypes) {
|
|
246
|
+
if (!autoExpandTypes.includes(defaultType)) {
|
|
247
|
+
autoExpandTypes.push(defaultType);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const schemas = await generateSchemas(typeRoots, types, autoExpandTypes, workingDirectory);
|
|
251
|
+
for (const type in schemas) {
|
|
252
|
+
if (core.Is.object(config.overrides?.[type])) {
|
|
253
|
+
cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-openapi.progress.overridingSchema"));
|
|
254
|
+
schemas[type] = config.overrides?.[type];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
243
257
|
const usedCommonResponseTypes = [];
|
|
244
258
|
for (let i = 0; i < inputResults.length; i++) {
|
|
245
259
|
const result = inputResults[i];
|
|
@@ -424,12 +438,13 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
|
|
|
424
438
|
if (requestObject?.properties) {
|
|
425
439
|
// If there are any properties other than body, query, pathParams and headers
|
|
426
440
|
// we should throw an error as we don't know what to do with them
|
|
427
|
-
const otherKeys = Object.keys(requestObject.properties).filter(k => !["body", "query", "pathParams", "headers"].includes(k));
|
|
441
|
+
const otherKeys = Object.keys(requestObject.properties).filter(k => !["body", "query", "pathParams", "headers", "authentication"].includes(k));
|
|
428
442
|
if (otherKeys.length > 0) {
|
|
429
443
|
throw new core.GeneralError("commands", "commands.ts-to-openapi.unsupportedProperties", {
|
|
430
444
|
keys: otherKeys.join(", ")
|
|
431
445
|
});
|
|
432
446
|
}
|
|
447
|
+
delete requestObject.properties.authentication;
|
|
433
448
|
// If there is a path params object convert these to params
|
|
434
449
|
if (core.Is.object(requestObject.properties.pathParams)) {
|
|
435
450
|
for (const pathParam of pathQueryHeaderParams) {
|
|
@@ -586,7 +601,7 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
|
|
|
586
601
|
}
|
|
587
602
|
}
|
|
588
603
|
}
|
|
589
|
-
await finaliseOutput(usedCommonResponseTypes, schemas, openApi, securitySchemes, config.externalReferences, outputFile);
|
|
604
|
+
await finaliseOutput(usedCommonResponseTypes, schemas, openApi, securitySchemes, config.externalReferences, autoExpandTypes, outputFile);
|
|
590
605
|
}
|
|
591
606
|
/**
|
|
592
607
|
* Finalise the schemas and output the spec.
|
|
@@ -595,9 +610,10 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
|
|
|
595
610
|
* @param openApi The OpenAPI spec.
|
|
596
611
|
* @param securitySchemes The security schemes.
|
|
597
612
|
* @param externalReferences The external references.
|
|
613
|
+
* @param autoExpandTypes The auto expand types.
|
|
598
614
|
* @param outputFile The output file.
|
|
599
615
|
*/
|
|
600
|
-
async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securitySchemes, externalReferences, outputFile) {
|
|
616
|
+
async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securitySchemes, externalReferences, autoExpandTypes, outputFile) {
|
|
601
617
|
cliCore.CLIDisplay.break();
|
|
602
618
|
cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-openapi.progress.finalisingSchemas"));
|
|
603
619
|
// Remove the response codes that we haven't used
|
|
@@ -638,6 +654,15 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
|
|
|
638
654
|
}
|
|
639
655
|
}
|
|
640
656
|
}
|
|
657
|
+
// We can remove any auto expand types from the final schema as they
|
|
658
|
+
// will have been expanded inline so no need to keep them
|
|
659
|
+
for (const autoExpandType of autoExpandTypes) {
|
|
660
|
+
const regExp = toolsCore.JsonSchemaHelper.stringToRegEx(autoExpandType);
|
|
661
|
+
if (regExp.test(schema)) {
|
|
662
|
+
skipSchema = true;
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
641
666
|
if (!skipSchema) {
|
|
642
667
|
// If the final schema has no properties and is just a ref to another object type
|
|
643
668
|
// then replace the references with that of the referenced type
|
|
@@ -651,7 +676,6 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
|
|
|
651
676
|
if (/I[A-Z]/.test(finalName)) {
|
|
652
677
|
finalName = finalName.slice(1);
|
|
653
678
|
}
|
|
654
|
-
finalName = finalName.replace("<", "_").replace(">", "_");
|
|
655
679
|
if (finalName.endsWith("[]")) {
|
|
656
680
|
finalName = `ListOf${finalName.slice(0, -2)}`;
|
|
657
681
|
}
|
|
@@ -665,7 +689,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
|
|
|
665
689
|
delete finalSchemas[type];
|
|
666
690
|
}
|
|
667
691
|
for (const type in finalSchemas) {
|
|
668
|
-
processArrays(finalSchemas[type]);
|
|
692
|
+
toolsCore.JsonSchemaHelper.processArrays(finalSchemas[type]);
|
|
669
693
|
}
|
|
670
694
|
const schemaKeys = Object.keys(finalSchemas);
|
|
671
695
|
schemaKeys.sort();
|
|
@@ -698,7 +722,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
|
|
|
698
722
|
// Remove the array [] from the type names
|
|
699
723
|
// eslint-disable-next-line unicorn/better-regex
|
|
700
724
|
json = json.replace(/#\/components\/schemas\/(.*)\[\]/g, "#/components/schemas/ListOf$1");
|
|
701
|
-
json = normaliseTypeName(json);
|
|
725
|
+
json = toolsCore.JsonSchemaHelper.normaliseTypeName(json);
|
|
702
726
|
// Remove external references
|
|
703
727
|
for (const finalExternal in finalExternals) {
|
|
704
728
|
json = json.replace(new RegExp(`"#/components/schemas/${core.StringHelper.stripPrefix(finalExternal)}"`, "g"), `"${finalExternals[finalExternal]}"`);
|
|
@@ -828,7 +852,7 @@ async function processPackageRestDetails(restRoutes) {
|
|
|
828
852
|
* @returns Nothing.
|
|
829
853
|
* @internal
|
|
830
854
|
*/
|
|
831
|
-
async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
|
|
855
|
+
async function generateSchemas(modelDirWildcards, types, autoExpandTypes, outputWorkingDir) {
|
|
832
856
|
const allSchemas = {};
|
|
833
857
|
const arraySingularTypes = [];
|
|
834
858
|
for (const type of types) {
|
|
@@ -852,13 +876,14 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
|
|
|
852
876
|
const schema = generator.createSchema("*");
|
|
853
877
|
if (schema.definitions) {
|
|
854
878
|
for (const def in schema.definitions) {
|
|
855
|
-
const defSub = normaliseTypeName(def);
|
|
879
|
+
const defSub = toolsCore.JsonSchemaHelper.normaliseTypeName(def);
|
|
856
880
|
allSchemas[defSub] = schema.definitions[def];
|
|
857
881
|
}
|
|
858
882
|
}
|
|
859
883
|
}
|
|
860
884
|
const referencedSchemas = {};
|
|
861
|
-
extractTypes(allSchemas, types, referencedSchemas);
|
|
885
|
+
toolsCore.JsonSchemaHelper.extractTypes(allSchemas, [...types, ...autoExpandTypes], referencedSchemas);
|
|
886
|
+
toolsCore.JsonSchemaHelper.expandTypes(referencedSchemas, autoExpandTypes);
|
|
862
887
|
for (const arraySingularType of arraySingularTypes) {
|
|
863
888
|
referencedSchemas[`${arraySingularType}[]`] = {
|
|
864
889
|
type: "array",
|
|
@@ -869,74 +894,6 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
|
|
|
869
894
|
}
|
|
870
895
|
return referencedSchemas;
|
|
871
896
|
}
|
|
872
|
-
/**
|
|
873
|
-
* Extract the required types from all the known schemas.
|
|
874
|
-
* @param allSchemas All the known schemas.
|
|
875
|
-
* @param requiredTypes The required types.
|
|
876
|
-
* @param referencedSchemas The references schemas.
|
|
877
|
-
* @internal
|
|
878
|
-
*/
|
|
879
|
-
function extractTypes(allSchemas, requiredTypes, referencedSchemas) {
|
|
880
|
-
for (const type of requiredTypes) {
|
|
881
|
-
if (allSchemas[type] && !referencedSchemas[type]) {
|
|
882
|
-
referencedSchemas[type] = allSchemas[type];
|
|
883
|
-
extractTypesFromSchema(allSchemas, allSchemas[type], referencedSchemas);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Extract type from properties definition.
|
|
889
|
-
* @param allTypes All the known types.
|
|
890
|
-
* @param schema The schema to extract from.
|
|
891
|
-
* @param output The output types.
|
|
892
|
-
* @internal
|
|
893
|
-
*/
|
|
894
|
-
function extractTypesFromSchema(allTypes, schema, output) {
|
|
895
|
-
const additionalTypes = [];
|
|
896
|
-
if (core.Is.stringValue(schema.$ref)) {
|
|
897
|
-
additionalTypes.push(normaliseTypeName(schema.$ref).replace("#/definitions/", ""));
|
|
898
|
-
}
|
|
899
|
-
else if (core.Is.object(schema.items)) {
|
|
900
|
-
if (core.Is.arrayValue(schema.items)) {
|
|
901
|
-
for (const itemSchema of schema.items) {
|
|
902
|
-
extractTypesFromSchema(allTypes, itemSchema, output);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
extractTypesFromSchema(allTypes, schema.items, output);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
else if (core.Is.object(schema.properties) || core.Is.object(schema.additionalProperties)) {
|
|
910
|
-
if (core.Is.object(schema.properties)) {
|
|
911
|
-
for (const prop in schema.properties) {
|
|
912
|
-
const p = schema.properties[prop];
|
|
913
|
-
if (core.Is.object(p)) {
|
|
914
|
-
extractTypesFromSchema(allTypes, p, output);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
if (core.Is.object(schema.additionalProperties)) {
|
|
919
|
-
extractTypesFromSchema(allTypes, schema.additionalProperties, output);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
else if (core.Is.arrayValue(schema.anyOf)) {
|
|
923
|
-
for (const prop of schema.anyOf) {
|
|
924
|
-
if (core.Is.object(prop)) {
|
|
925
|
-
extractTypesFromSchema(allTypes, prop, output);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
else if (core.Is.arrayValue(schema.oneOf)) {
|
|
930
|
-
for (const prop of schema.oneOf) {
|
|
931
|
-
if (core.Is.object(prop)) {
|
|
932
|
-
extractTypesFromSchema(allTypes, prop, output);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
if (additionalTypes.length > 0) {
|
|
937
|
-
extractTypes(allTypes, additionalTypes, output);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
897
|
/**
|
|
941
898
|
* Tidy up the schemas for use in OpenAPI context.
|
|
942
899
|
* @param props The properties to tidy up.
|
|
@@ -1087,79 +1044,6 @@ async function loadPackages(tsToOpenApiConfig, outputWorkingDir, typeRoots) {
|
|
|
1087
1044
|
}
|
|
1088
1045
|
return restRoutes;
|
|
1089
1046
|
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Process arrays in the schema object.
|
|
1092
|
-
* @param schemaObject The schema object to process.
|
|
1093
|
-
*/
|
|
1094
|
-
function processArrays(schemaObject) {
|
|
1095
|
-
if (core.Is.object(schemaObject)) {
|
|
1096
|
-
// latest specs have singular items in `items` property
|
|
1097
|
-
// and multiple items in prefixItems, so update the schema accordingly
|
|
1098
|
-
// https://www.learnjsonschema.com/2020-12/applicator/items/
|
|
1099
|
-
// https://www.learnjsonschema.com/2020-12/applicator/prefixitems/
|
|
1100
|
-
const schemaItems = schemaObject.items;
|
|
1101
|
-
if (core.Is.array(schemaItems) || core.Is.object(schemaItems)) {
|
|
1102
|
-
schemaObject.prefixItems = core.ArrayHelper.fromObjectOrArray(schemaItems);
|
|
1103
|
-
delete schemaObject.items;
|
|
1104
|
-
}
|
|
1105
|
-
const additionalItems = schemaObject.additionalItems;
|
|
1106
|
-
if (core.Is.array(additionalItems) || core.Is.object(additionalItems)) {
|
|
1107
|
-
schemaObject.items = core.ArrayHelper.fromObjectOrArray(additionalItems)[0];
|
|
1108
|
-
delete schemaObject.additionalItems;
|
|
1109
|
-
}
|
|
1110
|
-
processSchemaDictionary(schemaObject.properties);
|
|
1111
|
-
processArrays(schemaObject.additionalProperties);
|
|
1112
|
-
processSchemaArray(schemaObject.allOf);
|
|
1113
|
-
processSchemaArray(schemaObject.anyOf);
|
|
1114
|
-
processSchemaArray(schemaObject.oneOf);
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Process arrays in the schema object.
|
|
1119
|
-
* @param schemaDictionary The schema object to process.
|
|
1120
|
-
*/
|
|
1121
|
-
function processSchemaDictionary(schemaDictionary) {
|
|
1122
|
-
if (core.Is.object(schemaDictionary)) {
|
|
1123
|
-
for (const item of Object.values(schemaDictionary)) {
|
|
1124
|
-
if (core.Is.object(item)) {
|
|
1125
|
-
processArrays(item);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
/**
|
|
1131
|
-
* Process arrays in the schema object.
|
|
1132
|
-
* @param schemaArray The schema object to process.
|
|
1133
|
-
*/
|
|
1134
|
-
function processSchemaArray(schemaArray) {
|
|
1135
|
-
if (core.Is.arrayValue(schemaArray)) {
|
|
1136
|
-
for (const item of schemaArray) {
|
|
1137
|
-
if (core.Is.object(item)) {
|
|
1138
|
-
processArrays(item);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Cleanup TypeScript markers from the type name.
|
|
1145
|
-
* @param typeName The definition string to clean up.
|
|
1146
|
-
* @returns The cleaned up definition string.
|
|
1147
|
-
*/
|
|
1148
|
-
function normaliseTypeName(typeName) {
|
|
1149
|
-
// Remove the partial markers
|
|
1150
|
-
let sTypeName = typeName.replace(/^Partial<(.*?)>/g, "$1");
|
|
1151
|
-
sTypeName = sTypeName.replace(/Partial%3CI(.*?)%3E/g, "$1");
|
|
1152
|
-
// Remove the omit markers
|
|
1153
|
-
sTypeName = sTypeName.replace(/^Omit<(.*?),.*>/g, "$1");
|
|
1154
|
-
sTypeName = sTypeName.replace(/Omit%3CI(.*?)%2C.*%3E/g, "$1");
|
|
1155
|
-
// Remove the pick markers
|
|
1156
|
-
sTypeName = sTypeName.replace(/^Pick<(.*?),.*>/g, "$1");
|
|
1157
|
-
sTypeName = sTypeName.replace(/Pick%3CI(.*?)%2C.*%3E/g, "$1");
|
|
1158
|
-
// Cleanup the generic markers
|
|
1159
|
-
sTypeName = sTypeName.replace(/</g, "%3C").replace(/>/g, "%3E");
|
|
1160
|
-
sTypeName = sTypeName.replace(/%3Cunknown%3E/g, "");
|
|
1161
|
-
return sTypeName;
|
|
1162
|
-
}
|
|
1163
1047
|
|
|
1164
1048
|
// Copyright 2024 IOTA Stiftung.
|
|
1165
1049
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -1179,7 +1063,7 @@ class CLI extends cliCore.CLIBase {
|
|
|
1179
1063
|
return this.execute({
|
|
1180
1064
|
title: "TWIN TypeScript To OpenAPI",
|
|
1181
1065
|
appName: "ts-to-openapi",
|
|
1182
|
-
version: "0.0.
|
|
1066
|
+
version: "0.0.2-next.10", // x-release-please-version
|
|
1183
1067
|
icon: "⚙️ ",
|
|
1184
1068
|
supportsEnvFiles: false,
|
|
1185
1069
|
overrideOutputWidth: options?.overrideOutputWidth
|