@twin.org/ts-to-openapi 0.0.1-next.26 → 0.0.1-next.28

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.
@@ -659,11 +659,13 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
659
659
  }
660
660
  }
661
661
  }
662
- if (finalSchemas.HttpStatusCode) {
663
- delete finalSchemas.HttpStatusCode;
662
+ // Remove standard types that we don't want in the final output
663
+ const removeTypes = ["HttpStatusCode", "Uint8Array", "ArrayBuffer"];
664
+ for (const type of removeTypes) {
665
+ delete finalSchemas[type];
664
666
  }
665
- if (finalSchemas.Uint8Array) {
666
- delete finalSchemas.Uint8Array;
667
+ for (const type in finalSchemas) {
668
+ processArrays(finalSchemas[type]);
667
669
  }
668
670
  const schemaKeys = Object.keys(finalSchemas);
669
671
  schemaKeys.sort();
@@ -696,12 +698,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
696
698
  // Remove the array [] from the type names
697
699
  // eslint-disable-next-line unicorn/better-regex
698
700
  json = json.replace(/#\/components\/schemas\/(.*)\[\]/g, "#/components/schemas/ListOf$1");
699
- // Remove the partial markers
700
- json = json.replace(/Partial%3CI(.*?)%3E/g, "$1");
701
- // Remove the omit markers
702
- json = json.replace(/Omit%3CI(.*?)%2C.*%3E/g, "$1");
703
- // Cleanup the generic markers
704
- json = json.replace(/%3Cunknown%3E/g, "");
701
+ json = normaliseTypeName(json);
705
702
  // Remove external references
706
703
  for (const finalExternal in finalExternals) {
707
704
  json = json.replace(new RegExp(`"#/components/schemas/${core.StringHelper.stripPrefix(finalExternal)}"`, "g"), `"${finalExternals[finalExternal]}"`);
@@ -855,12 +852,7 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
855
852
  const schema = generator.createSchema("*");
856
853
  if (schema.definitions) {
857
854
  for (const def in schema.definitions) {
858
- // Remove the partial markers
859
- let defSub = def.replace(/^Partial<(.*?)>/g, "$1");
860
- // Remove the omit markers
861
- defSub = defSub.replace(/^Omit<(.*?),.*>/g, "$1");
862
- // Cleanup the generic markers
863
- defSub = defSub.replace(/</g, "%3C").replace(/>/g, "%3E");
855
+ const defSub = normaliseTypeName(def);
864
856
  allSchemas[defSub] = schema.definitions[def];
865
857
  }
866
858
  }
@@ -902,10 +894,7 @@ function extractTypes(allSchemas, requiredTypes, referencedSchemas) {
902
894
  function extractTypesFromSchema(allTypes, schema, output) {
903
895
  const additionalTypes = [];
904
896
  if (core.Is.stringValue(schema.$ref)) {
905
- additionalTypes.push(schema.$ref
906
- .replace("#/definitions/", "")
907
- .replace(/^Partial%3C(.*?)%3E/g, "$1")
908
- .replace(/^Omit%3C(.*?)%2C.*%3E/g, "$1"));
897
+ additionalTypes.push(normaliseTypeName(schema.$ref).replace("#/definitions/", ""));
909
898
  }
910
899
  else if (core.Is.object(schema.items)) {
911
900
  if (core.Is.arrayValue(schema.items)) {
@@ -1098,6 +1087,79 @@ async function loadPackages(tsToOpenApiConfig, outputWorkingDir, typeRoots) {
1098
1087
  }
1099
1088
  return restRoutes;
1100
1089
  }
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
+ }
1101
1163
 
1102
1164
  // Copyright 2024 IOTA Stiftung.
1103
1165
  // SPDX-License-Identifier: Apache-2.0.
@@ -1117,7 +1179,7 @@ class CLI extends cliCore.CLIBase {
1117
1179
  return this.execute({
1118
1180
  title: "TWIN TypeScript To OpenAPI",
1119
1181
  appName: "ts-to-openapi",
1120
- version: "0.0.1-next.26", // x-release-please-version
1182
+ version: "0.0.1-next.28", // x-release-please-version
1121
1183
  icon: "⚙️ ",
1122
1184
  supportsEnvFiles: false,
1123
1185
  overrideOutputWidth: options?.overrideOutputWidth
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { CLIDisplay, CLIUtils, CLIBase } from '@twin.org/cli-core';
4
4
  import { mkdir, rm, writeFile } from 'node:fs/promises';
5
- import { I18n, GeneralError, Is, StringHelper, ObjectHelper } from '@twin.org/core';
5
+ import { I18n, GeneralError, Is, StringHelper, ObjectHelper, ArrayHelper } from '@twin.org/core';
6
6
  import { HttpStatusCode, MimeTypes } from '@twin.org/web';
7
7
  import { createGenerator } from 'ts-json-schema-generator';
8
8
 
@@ -656,11 +656,13 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
656
656
  }
657
657
  }
658
658
  }
659
- if (finalSchemas.HttpStatusCode) {
660
- delete finalSchemas.HttpStatusCode;
659
+ // Remove standard types that we don't want in the final output
660
+ const removeTypes = ["HttpStatusCode", "Uint8Array", "ArrayBuffer"];
661
+ for (const type of removeTypes) {
662
+ delete finalSchemas[type];
661
663
  }
662
- if (finalSchemas.Uint8Array) {
663
- delete finalSchemas.Uint8Array;
664
+ for (const type in finalSchemas) {
665
+ processArrays(finalSchemas[type]);
664
666
  }
665
667
  const schemaKeys = Object.keys(finalSchemas);
666
668
  schemaKeys.sort();
@@ -693,12 +695,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
693
695
  // Remove the array [] from the type names
694
696
  // eslint-disable-next-line unicorn/better-regex
695
697
  json = json.replace(/#\/components\/schemas\/(.*)\[\]/g, "#/components/schemas/ListOf$1");
696
- // Remove the partial markers
697
- json = json.replace(/Partial%3CI(.*?)%3E/g, "$1");
698
- // Remove the omit markers
699
- json = json.replace(/Omit%3CI(.*?)%2C.*%3E/g, "$1");
700
- // Cleanup the generic markers
701
- json = json.replace(/%3Cunknown%3E/g, "");
698
+ json = normaliseTypeName(json);
702
699
  // Remove external references
703
700
  for (const finalExternal in finalExternals) {
704
701
  json = json.replace(new RegExp(`"#/components/schemas/${StringHelper.stripPrefix(finalExternal)}"`, "g"), `"${finalExternals[finalExternal]}"`);
@@ -852,12 +849,7 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
852
849
  const schema = generator.createSchema("*");
853
850
  if (schema.definitions) {
854
851
  for (const def in schema.definitions) {
855
- // Remove the partial markers
856
- let defSub = def.replace(/^Partial<(.*?)>/g, "$1");
857
- // Remove the omit markers
858
- defSub = defSub.replace(/^Omit<(.*?),.*>/g, "$1");
859
- // Cleanup the generic markers
860
- defSub = defSub.replace(/</g, "%3C").replace(/>/g, "%3E");
852
+ const defSub = normaliseTypeName(def);
861
853
  allSchemas[defSub] = schema.definitions[def];
862
854
  }
863
855
  }
@@ -899,10 +891,7 @@ function extractTypes(allSchemas, requiredTypes, referencedSchemas) {
899
891
  function extractTypesFromSchema(allTypes, schema, output) {
900
892
  const additionalTypes = [];
901
893
  if (Is.stringValue(schema.$ref)) {
902
- additionalTypes.push(schema.$ref
903
- .replace("#/definitions/", "")
904
- .replace(/^Partial%3C(.*?)%3E/g, "$1")
905
- .replace(/^Omit%3C(.*?)%2C.*%3E/g, "$1"));
894
+ additionalTypes.push(normaliseTypeName(schema.$ref).replace("#/definitions/", ""));
906
895
  }
907
896
  else if (Is.object(schema.items)) {
908
897
  if (Is.arrayValue(schema.items)) {
@@ -1095,6 +1084,79 @@ async function loadPackages(tsToOpenApiConfig, outputWorkingDir, typeRoots) {
1095
1084
  }
1096
1085
  return restRoutes;
1097
1086
  }
1087
+ /**
1088
+ * Process arrays in the schema object.
1089
+ * @param schemaObject The schema object to process.
1090
+ */
1091
+ function processArrays(schemaObject) {
1092
+ if (Is.object(schemaObject)) {
1093
+ // latest specs have singular items in `items` property
1094
+ // and multiple items in prefixItems, so update the schema accordingly
1095
+ // https://www.learnjsonschema.com/2020-12/applicator/items/
1096
+ // https://www.learnjsonschema.com/2020-12/applicator/prefixitems/
1097
+ const schemaItems = schemaObject.items;
1098
+ if (Is.array(schemaItems) || Is.object(schemaItems)) {
1099
+ schemaObject.prefixItems = ArrayHelper.fromObjectOrArray(schemaItems);
1100
+ delete schemaObject.items;
1101
+ }
1102
+ const additionalItems = schemaObject.additionalItems;
1103
+ if (Is.array(additionalItems) || Is.object(additionalItems)) {
1104
+ schemaObject.items = ArrayHelper.fromObjectOrArray(additionalItems)[0];
1105
+ delete schemaObject.additionalItems;
1106
+ }
1107
+ processSchemaDictionary(schemaObject.properties);
1108
+ processArrays(schemaObject.additionalProperties);
1109
+ processSchemaArray(schemaObject.allOf);
1110
+ processSchemaArray(schemaObject.anyOf);
1111
+ processSchemaArray(schemaObject.oneOf);
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Process arrays in the schema object.
1116
+ * @param schemaDictionary The schema object to process.
1117
+ */
1118
+ function processSchemaDictionary(schemaDictionary) {
1119
+ if (Is.object(schemaDictionary)) {
1120
+ for (const item of Object.values(schemaDictionary)) {
1121
+ if (Is.object(item)) {
1122
+ processArrays(item);
1123
+ }
1124
+ }
1125
+ }
1126
+ }
1127
+ /**
1128
+ * Process arrays in the schema object.
1129
+ * @param schemaArray The schema object to process.
1130
+ */
1131
+ function processSchemaArray(schemaArray) {
1132
+ if (Is.arrayValue(schemaArray)) {
1133
+ for (const item of schemaArray) {
1134
+ if (Is.object(item)) {
1135
+ processArrays(item);
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ /**
1141
+ * Cleanup TypeScript markers from the type name.
1142
+ * @param typeName The definition string to clean up.
1143
+ * @returns The cleaned up definition string.
1144
+ */
1145
+ function normaliseTypeName(typeName) {
1146
+ // Remove the partial markers
1147
+ let sTypeName = typeName.replace(/^Partial<(.*?)>/g, "$1");
1148
+ sTypeName = sTypeName.replace(/Partial%3CI(.*?)%3E/g, "$1");
1149
+ // Remove the omit markers
1150
+ sTypeName = sTypeName.replace(/^Omit<(.*?),.*>/g, "$1");
1151
+ sTypeName = sTypeName.replace(/Omit%3CI(.*?)%2C.*%3E/g, "$1");
1152
+ // Remove the pick markers
1153
+ sTypeName = sTypeName.replace(/^Pick<(.*?),.*>/g, "$1");
1154
+ sTypeName = sTypeName.replace(/Pick%3CI(.*?)%2C.*%3E/g, "$1");
1155
+ // Cleanup the generic markers
1156
+ sTypeName = sTypeName.replace(/</g, "%3C").replace(/>/g, "%3E");
1157
+ sTypeName = sTypeName.replace(/%3Cunknown%3E/g, "");
1158
+ return sTypeName;
1159
+ }
1098
1160
 
1099
1161
  // Copyright 2024 IOTA Stiftung.
1100
1162
  // SPDX-License-Identifier: Apache-2.0.
@@ -1114,7 +1176,7 @@ class CLI extends CLIBase {
1114
1176
  return this.execute({
1115
1177
  title: "TWIN TypeScript To OpenAPI",
1116
1178
  appName: "ts-to-openapi",
1117
- version: "0.0.1-next.26", // x-release-please-version
1179
+ version: "0.0.1-next.28", // x-release-please-version
1118
1180
  icon: "⚙️ ",
1119
1181
  supportsEnvFiles: false,
1120
1182
  overrideOutputWidth: options?.overrideOutputWidth
package/docs/changelog.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @twin.org/ts-to-openapi - Changelog
2
2
 
3
+ ## [0.0.1-next.28](https://github.com/twinfoundation/tools/compare/ts-to-openapi-v0.0.1-next.27...ts-to-openapi-v0.0.1-next.28) (2025-06-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * improve schema type name normalisation ([1a18b26](https://github.com/twinfoundation/tools/commit/1a18b267d87e9179bda01b396b256c450ae2889e))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/nameof bumped from 0.0.1-next.27 to 0.0.1-next.28
16
+ * devDependencies
17
+ * @twin.org/merge-locales bumped from 0.0.1-next.27 to 0.0.1-next.28
18
+ * @twin.org/nameof-transformer bumped from 0.0.1-next.27 to 0.0.1-next.28
19
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.1-next.27 to 0.0.1-next.28
20
+
21
+ ## [0.0.1-next.27](https://github.com/twinfoundation/tools/compare/ts-to-openapi-v0.0.1-next.26...ts-to-openapi-v0.0.1-next.27) (2025-06-17)
22
+
23
+
24
+ ### Features
25
+
26
+ * add latest json schema features ([494293f](https://github.com/twinfoundation/tools/commit/494293f4252b9c7d4a20790ec157fc9d8c96c3d2))
27
+
28
+
29
+ ### Dependencies
30
+
31
+ * The following workspace dependencies were updated
32
+ * dependencies
33
+ * @twin.org/nameof bumped from 0.0.1-next.26 to 0.0.1-next.27
34
+ * devDependencies
35
+ * @twin.org/merge-locales bumped from 0.0.1-next.26 to 0.0.1-next.27
36
+ * @twin.org/nameof-transformer bumped from 0.0.1-next.26 to 0.0.1-next.27
37
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.1-next.26 to 0.0.1-next.27
38
+
3
39
  ## [0.0.1-next.26](https://github.com/twinfoundation/tools/compare/ts-to-openapi-v0.0.1-next.25...ts-to-openapi-v0.0.1-next.26) (2025-06-11)
4
40
 
5
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/ts-to-openapi",
3
- "version": "0.0.1-next.26",
3
+ "version": "0.0.1-next.28",
4
4
  "description": "Tool to convert TypeScript REST route definitions to OpenAPI Specifications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  "@twin.org/api-models": "next",
18
18
  "@twin.org/cli-core": "next",
19
19
  "@twin.org/core": "next",
20
- "@twin.org/nameof": "0.0.1-next.26",
20
+ "@twin.org/nameof": "0.0.1-next.28",
21
21
  "@twin.org/web": "next",
22
22
  "ajv": "8.17.1",
23
23
  "commander": "14.0.0",