@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.
@@ -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: "component.error",
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: "component.error"
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: "component.error",
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: "component.error",
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: "component.error",
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: "component.error"
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: "component.error"
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: "3.1.0",
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 schemas = await generateSchemas(typeRoots, types, workingDirectory);
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.1", // x-release-please-version
1066
+ version: "0.0.2-next.10", // x-release-please-version
1183
1067
  icon: "⚙️ ",
1184
1068
  supportsEnvFiles: false,
1185
1069
  overrideOutputWidth: options?.overrideOutputWidth