@twin.org/tools-core 0.0.3-next.2 → 0.0.3-next.21

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.
Files changed (133) hide show
  1. package/README.md +2 -2
  2. package/dist/es/index.js +20 -11
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/es/models/ITypeScriptToSchemaContext.js +2 -0
  5. package/dist/es/models/ITypeScriptToSchemaContext.js.map +1 -0
  6. package/dist/es/models/ITypeScriptToSchemaDiagnostics.js +4 -0
  7. package/dist/es/models/ITypeScriptToSchemaDiagnostics.js.map +1 -0
  8. package/dist/es/models/ITypeScriptToSchemaOptions.js +4 -0
  9. package/dist/es/models/ITypeScriptToSchemaOptions.js.map +1 -0
  10. package/dist/es/utils/constants.js +43 -0
  11. package/dist/es/utils/constants.js.map +1 -0
  12. package/dist/es/utils/diagnosticReporter.js +32 -0
  13. package/dist/es/utils/diagnosticReporter.js.map +1 -0
  14. package/dist/es/utils/disallowedTypeGuard.js +151 -0
  15. package/dist/es/utils/disallowedTypeGuard.js.map +1 -0
  16. package/dist/es/utils/enum.js +152 -0
  17. package/dist/es/utils/enum.js.map +1 -0
  18. package/dist/es/utils/fileUtils.js +130 -0
  19. package/dist/es/utils/fileUtils.js.map +1 -0
  20. package/dist/es/utils/importTypeQuerySchemaResolver.js +328 -0
  21. package/dist/es/utils/importTypeQuerySchemaResolver.js.map +1 -0
  22. package/dist/es/utils/indexSignaturePatternResolver.js +94 -0
  23. package/dist/es/utils/indexSignaturePatternResolver.js.map +1 -0
  24. package/dist/es/utils/intersectionSchemaMerger.js +85 -0
  25. package/dist/es/utils/intersectionSchemaMerger.js.map +1 -0
  26. package/dist/es/utils/jsDoc.js +120 -0
  27. package/dist/es/utils/jsDoc.js.map +1 -0
  28. package/dist/es/utils/jsonSchemaBuilder.js +3373 -0
  29. package/dist/es/utils/jsonSchemaBuilder.js.map +1 -0
  30. package/dist/es/utils/mappedTypeSchemaResolver.js +231 -0
  31. package/dist/es/utils/mappedTypeSchemaResolver.js.map +1 -0
  32. package/dist/es/utils/objectTransformer.js +162 -0
  33. package/dist/es/utils/objectTransformer.js.map +1 -0
  34. package/dist/es/utils/regEx.js +128 -0
  35. package/dist/es/utils/regEx.js.map +1 -0
  36. package/dist/es/utils/resolver.js +164 -0
  37. package/dist/es/utils/resolver.js.map +1 -0
  38. package/dist/es/utils/templateLiteralPatternBuilder.js +94 -0
  39. package/dist/es/utils/templateLiteralPatternBuilder.js.map +1 -0
  40. package/dist/es/utils/typeScriptToSchema.js +112 -0
  41. package/dist/es/utils/typeScriptToSchema.js.map +1 -0
  42. package/dist/es/utils/utilityTypeSchemaMapper.js +412 -0
  43. package/dist/es/utils/utilityTypeSchemaMapper.js.map +1 -0
  44. package/dist/types/index.d.ts +20 -11
  45. package/dist/types/models/ITypeScriptToSchemaContext.d.ts +64 -0
  46. package/dist/types/models/ITypeScriptToSchemaDiagnostics.d.ts +31 -0
  47. package/dist/types/models/ITypeScriptToSchemaOptions.d.ts +22 -0
  48. package/dist/types/utils/constants.d.ts +13 -0
  49. package/dist/types/utils/diagnosticReporter.d.ts +17 -0
  50. package/dist/types/utils/disallowedTypeGuard.d.ts +16 -0
  51. package/dist/types/utils/enum.d.ts +42 -0
  52. package/dist/types/utils/fileUtils.d.ts +66 -0
  53. package/dist/types/utils/importTypeQuerySchemaResolver.d.ts +52 -0
  54. package/dist/types/utils/indexSignaturePatternResolver.d.ts +21 -0
  55. package/dist/types/utils/intersectionSchemaMerger.d.ts +16 -0
  56. package/dist/types/utils/jsDoc.d.ts +46 -0
  57. package/dist/types/utils/jsonSchemaBuilder.d.ts +747 -0
  58. package/dist/types/utils/mappedTypeSchemaResolver.d.ts +46 -0
  59. package/dist/types/utils/objectTransformer.d.ts +33 -0
  60. package/dist/types/utils/regEx.d.ts +24 -0
  61. package/dist/types/utils/resolver.d.ts +16 -0
  62. package/dist/types/utils/templateLiteralPatternBuilder.d.ts +12 -0
  63. package/dist/types/utils/typeScriptToSchema.d.ts +31 -0
  64. package/dist/types/utils/utilityTypeSchemaMapper.d.ts +92 -0
  65. package/docs/changelog.md +176 -1
  66. package/docs/examples.md +87 -1
  67. package/docs/reference/classes/Constants.md +29 -0
  68. package/docs/reference/classes/DiagnosticReporter.md +49 -0
  69. package/docs/reference/classes/DisallowedTypeGuard.md +35 -0
  70. package/docs/reference/classes/Enum.md +93 -0
  71. package/docs/reference/classes/FileUtils.md +237 -0
  72. package/docs/reference/classes/ImportTypeQuerySchemaResolver.md +87 -0
  73. package/docs/reference/classes/IndexSignaturePatternResolver.md +69 -0
  74. package/docs/reference/classes/IntersectionSchemaMerger.md +48 -0
  75. package/docs/reference/classes/JsDoc.md +141 -0
  76. package/docs/reference/classes/JsonSchemaBuilder.md +2870 -0
  77. package/docs/reference/classes/MappedTypeSchemaResolver.md +211 -0
  78. package/docs/reference/classes/ObjectTransformer.md +119 -0
  79. package/docs/reference/classes/RegEx.md +99 -0
  80. package/docs/reference/classes/Resolver.md +41 -0
  81. package/docs/reference/classes/TemplateLiteralPatternBuilder.md +35 -0
  82. package/docs/reference/classes/TypeScriptToSchema.md +91 -0
  83. package/docs/reference/classes/UtilityTypeSchemaMapper.md +341 -0
  84. package/docs/reference/index.md +20 -14
  85. package/docs/reference/interfaces/ITypeScriptToSchemaContext.md +113 -0
  86. package/docs/reference/interfaces/ITypeScriptToSchemaDiagnostics.md +55 -0
  87. package/docs/reference/interfaces/ITypeScriptToSchemaOptions.md +44 -0
  88. package/locales/en.json +32 -1
  89. package/package.json +4 -3
  90. package/dist/es/models/IJsonSchema.js +0 -2
  91. package/dist/es/models/IJsonSchema.js.map +0 -1
  92. package/dist/es/models/IOpenApi.js +0 -2
  93. package/dist/es/models/IOpenApi.js.map +0 -1
  94. package/dist/es/models/IOpenApiExample.js +0 -4
  95. package/dist/es/models/IOpenApiExample.js.map +0 -1
  96. package/dist/es/models/IOpenApiHeader.js +0 -4
  97. package/dist/es/models/IOpenApiHeader.js.map +0 -1
  98. package/dist/es/models/IOpenApiPathMethod.js +0 -2
  99. package/dist/es/models/IOpenApiPathMethod.js.map +0 -1
  100. package/dist/es/models/IOpenApiResponse.js +0 -2
  101. package/dist/es/models/IOpenApiResponse.js.map +0 -1
  102. package/dist/es/models/IOpenApiSecurityScheme.js +0 -4
  103. package/dist/es/models/IOpenApiSecurityScheme.js.map +0 -1
  104. package/dist/es/models/IPackageJson.js +0 -4
  105. package/dist/es/models/IPackageJson.js.map +0 -1
  106. package/dist/es/models/jsonTypeName.js +0 -2
  107. package/dist/es/models/jsonTypeName.js.map +0 -1
  108. package/dist/es/utils/jsonSchemaHelper.js +0 -258
  109. package/dist/es/utils/jsonSchemaHelper.js.map +0 -1
  110. package/dist/es/utils/openApiHelper.js +0 -12
  111. package/dist/es/utils/openApiHelper.js.map +0 -1
  112. package/dist/types/models/IJsonSchema.d.ts +0 -5
  113. package/dist/types/models/IOpenApi.d.ts +0 -54
  114. package/dist/types/models/IOpenApiExample.d.ts +0 -13
  115. package/dist/types/models/IOpenApiHeader.d.ts +0 -19
  116. package/dist/types/models/IOpenApiPathMethod.d.ts +0 -65
  117. package/dist/types/models/IOpenApiResponse.d.ts +0 -32
  118. package/dist/types/models/IOpenApiSecurityScheme.d.ts +0 -25
  119. package/dist/types/models/IPackageJson.d.ts +0 -15
  120. package/dist/types/models/jsonTypeName.d.ts +0 -5
  121. package/dist/types/utils/jsonSchemaHelper.d.ts +0 -78
  122. package/dist/types/utils/openApiHelper.d.ts +0 -9
  123. package/docs/reference/classes/JsonSchemaHelper.md +0 -233
  124. package/docs/reference/classes/OpenApiHelper.md +0 -21
  125. package/docs/reference/interfaces/IOpenApi.md +0 -103
  126. package/docs/reference/interfaces/IOpenApiExample.md +0 -19
  127. package/docs/reference/interfaces/IOpenApiHeader.md +0 -31
  128. package/docs/reference/interfaces/IOpenApiPathMethod.md +0 -119
  129. package/docs/reference/interfaces/IOpenApiResponse.md +0 -35
  130. package/docs/reference/interfaces/IOpenApiSecurityScheme.md +0 -43
  131. package/docs/reference/interfaces/IPackageJson.md +0 -23
  132. package/docs/reference/type-aliases/IJsonSchema.md +0 -5
  133. package/docs/reference/type-aliases/JsonTypeName.md +0 -5
@@ -0,0 +1,231 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ /* eslint-disable jsdoc/require-param, jsdoc/require-returns */
4
+ import { Is, JsonHelper } from "@twin.org/core";
5
+ import * as ts from "typescript";
6
+ import { JsonSchemaBuilder } from "./jsonSchemaBuilder.js";
7
+ /**
8
+ * Static mapped-type schema transformation helpers.
9
+ */
10
+ export class MappedTypeSchemaResolver {
11
+ /**
12
+ * Resolve mapped type output keys, including remapped key names via `as`.
13
+ */
14
+ static resolveMappedTypePropertyEntries(context, typeNode, mappedKeys, mappedTypeParameterName) {
15
+ if (!typeNode.nameType) {
16
+ return mappedKeys.map(mappedKey => ({ sourceKey: mappedKey, mappedKey }));
17
+ }
18
+ const mappedEntries = [];
19
+ for (const sourceKey of mappedKeys) {
20
+ const mappedKey = MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(context, typeNode.nameType, sourceKey, mappedTypeParameterName);
21
+ if (mappedKey !== null && !Is.stringValue(mappedKey)) {
22
+ return undefined;
23
+ }
24
+ if (Is.stringValue(mappedKey)) {
25
+ mappedEntries.push({ sourceKey, mappedKey });
26
+ }
27
+ }
28
+ return mappedEntries;
29
+ }
30
+ /**
31
+ * Resolve a remapped mapped-type key expression for a concrete source key.
32
+ */
33
+ static resolveMappedTypeRemappedKey(context, nameTypeNode, sourceKey, mappedTypeParameterName) {
34
+ if (ts.isParenthesizedTypeNode(nameTypeNode)) {
35
+ return MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(context, nameTypeNode.type, sourceKey, mappedTypeParameterName);
36
+ }
37
+ if (nameTypeNode.kind === ts.SyntaxKind.NeverKeyword) {
38
+ return null;
39
+ }
40
+ if (JsonSchemaBuilder.isMappedTypeParameterReference(nameTypeNode, mappedTypeParameterName)) {
41
+ return sourceKey;
42
+ }
43
+ if (ts.isLiteralTypeNode(nameTypeNode)) {
44
+ if (ts.isStringLiteral(nameTypeNode.literal) || ts.isNumericLiteral(nameTypeNode.literal)) {
45
+ return nameTypeNode.literal.text;
46
+ }
47
+ if (nameTypeNode.literal.kind === ts.SyntaxKind.TrueKeyword) {
48
+ return "true";
49
+ }
50
+ if (nameTypeNode.literal.kind === ts.SyntaxKind.FalseKeyword) {
51
+ return "false";
52
+ }
53
+ }
54
+ if (ts.isIntersectionTypeNode(nameTypeNode)) {
55
+ const hasMappedKeyReference = nameTypeNode.types.some(type => JsonSchemaBuilder.isMappedTypeParameterReference(type, mappedTypeParameterName));
56
+ const hasStringKeyword = nameTypeNode.types.some(type => type.kind === ts.SyntaxKind.StringKeyword);
57
+ if (hasMappedKeyReference && hasStringKeyword) {
58
+ return sourceKey;
59
+ }
60
+ return undefined;
61
+ }
62
+ if (ts.isTemplateLiteralTypeNode(nameTypeNode)) {
63
+ let remappedKey = nameTypeNode.head.text;
64
+ for (const span of nameTypeNode.templateSpans) {
65
+ const spanValue = MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(context, span.type, sourceKey, mappedTypeParameterName);
66
+ if (!Is.stringValue(spanValue)) {
67
+ return undefined;
68
+ }
69
+ remappedKey += `${spanValue}${span.literal.text}`;
70
+ }
71
+ return remappedKey;
72
+ }
73
+ if (ts.isTypeReferenceNode(nameTypeNode) && ts.isIdentifier(nameTypeNode.typeName)) {
74
+ const intrinsicName = nameTypeNode.typeName.text;
75
+ if (nameTypeNode.typeArguments?.length === 1) {
76
+ const intrinsicInput = MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(context, nameTypeNode.typeArguments[0], sourceKey, mappedTypeParameterName);
77
+ if (!Is.stringValue(intrinsicInput)) {
78
+ return undefined;
79
+ }
80
+ return MappedTypeSchemaResolver.applyIntrinsicMappedTypeKeyRemap(intrinsicName, intrinsicInput);
81
+ }
82
+ }
83
+ if (ts.isConditionalTypeNode(nameTypeNode)) {
84
+ if (!JsonSchemaBuilder.isMappedTypeParameterReference(nameTypeNode.checkType, mappedTypeParameterName)) {
85
+ return undefined;
86
+ }
87
+ const satisfies = MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(context, sourceKey, nameTypeNode.extendsType);
88
+ if (satisfies === undefined) {
89
+ return undefined;
90
+ }
91
+ const branchType = satisfies ? nameTypeNode.trueType : nameTypeNode.falseType;
92
+ return MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(context, branchType, sourceKey, mappedTypeParameterName);
93
+ }
94
+ return undefined;
95
+ }
96
+ /**
97
+ * Evaluate whether a concrete mapped key satisfies an `extends` condition.
98
+ */
99
+ static evaluateMappedKeyExtendsCondition(context, sourceKey, extendsTypeNode) {
100
+ if (ts.isParenthesizedTypeNode(extendsTypeNode)) {
101
+ return MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(context, sourceKey, extendsTypeNode.type);
102
+ }
103
+ if (extendsTypeNode.kind === ts.SyntaxKind.StringKeyword ||
104
+ extendsTypeNode.kind === ts.SyntaxKind.AnyKeyword ||
105
+ extendsTypeNode.kind === ts.SyntaxKind.UnknownKeyword) {
106
+ return true;
107
+ }
108
+ if (extendsTypeNode.kind === ts.SyntaxKind.NeverKeyword ||
109
+ extendsTypeNode.kind === ts.SyntaxKind.NumberKeyword ||
110
+ extendsTypeNode.kind === ts.SyntaxKind.BooleanKeyword ||
111
+ extendsTypeNode.kind === ts.SyntaxKind.ObjectKeyword) {
112
+ return false;
113
+ }
114
+ if (ts.isLiteralTypeNode(extendsTypeNode)) {
115
+ if (ts.isStringLiteral(extendsTypeNode.literal) ||
116
+ ts.isNumericLiteral(extendsTypeNode.literal)) {
117
+ return sourceKey === extendsTypeNode.literal.text;
118
+ }
119
+ return false;
120
+ }
121
+ if (ts.isUnionTypeNode(extendsTypeNode)) {
122
+ let hasUndetermined = false;
123
+ for (const memberType of extendsTypeNode.types) {
124
+ const result = MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(context, sourceKey, memberType);
125
+ if (result === true) {
126
+ return true;
127
+ }
128
+ if (result === undefined) {
129
+ hasUndetermined = true;
130
+ }
131
+ }
132
+ return hasUndetermined ? undefined : false;
133
+ }
134
+ if (ts.isTypeOperatorNode(extendsTypeNode) &&
135
+ extendsTypeNode.operator === ts.SyntaxKind.KeyOfKeyword) {
136
+ const keys = JsonSchemaBuilder.extractKeyofTypeKeys(context, extendsTypeNode.type);
137
+ if (keys.length > 0) {
138
+ return keys.includes(sourceKey);
139
+ }
140
+ return undefined;
141
+ }
142
+ if (ts.isTypeReferenceNode(extendsTypeNode)) {
143
+ const resolved = JsonSchemaBuilder.resolveReferencedTypeNodeFromLocalDeclaration(context, extendsTypeNode);
144
+ if (resolved) {
145
+ return MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(context, sourceKey, resolved);
146
+ }
147
+ }
148
+ return undefined;
149
+ }
150
+ /**
151
+ * Build a conservative fallback schema for mapped types whose key remapping cannot be resolved.
152
+ */
153
+ static buildMappedTypeFallbackSchema(context, typeNode, mappedKeys, mappedTypeParameterName, sourceObjectSchema) {
154
+ const additionalProperties = MappedTypeSchemaResolver.buildMappedTypeFallbackAdditionalProperties(context, typeNode, mappedKeys, mappedTypeParameterName, sourceObjectSchema);
155
+ return {
156
+ type: "object",
157
+ additionalProperties: additionalProperties ?? {}
158
+ };
159
+ }
160
+ /**
161
+ * Build fallback additionalProperties for unresolved mapped key remapping.
162
+ */
163
+ static buildMappedTypeFallbackAdditionalProperties(context, typeNode, mappedKeys, mappedTypeParameterName, sourceObjectSchema) {
164
+ const resolvedPropertySchemas = mappedKeys
165
+ .map(mappedKey => JsonSchemaBuilder.mapMappedTypePropertySchema(context, typeNode, mappedKey, mappedTypeParameterName, sourceObjectSchema))
166
+ .filter((mappedType) => mappedType !== undefined);
167
+ if (resolvedPropertySchemas.length === 0) {
168
+ return undefined;
169
+ }
170
+ const uniqueSchemas = resolvedPropertySchemas.filter((schema, index, allSchemas) => {
171
+ const schemaKey = JsonHelper.canonicalize(schema);
172
+ return allSchemas.findIndex(s => JsonHelper.canonicalize(s) === schemaKey) === index;
173
+ });
174
+ if (uniqueSchemas.length === 1) {
175
+ return uniqueSchemas[0];
176
+ }
177
+ return {
178
+ anyOf: uniqueSchemas
179
+ };
180
+ }
181
+ /**
182
+ * Merge mapped property schemas when multiple source keys remap to the same output key.
183
+ */
184
+ static mergeMappedTypePropertySchemas(existingSchema, nextSchema) {
185
+ const schemaVariants = [
186
+ ...(existingSchema.anyOf ?? [existingSchema]),
187
+ ...(nextSchema.anyOf ?? [nextSchema])
188
+ ];
189
+ const uniqueSchemas = schemaVariants.filter((schema, index, allSchemas) => {
190
+ const schemaKey = JsonHelper.canonicalize(schema);
191
+ return allSchemas.findIndex(s => JsonHelper.canonicalize(s) === schemaKey) === index;
192
+ });
193
+ if (uniqueSchemas.length === 1) {
194
+ return uniqueSchemas[0];
195
+ }
196
+ return {
197
+ anyOf: uniqueSchemas
198
+ };
199
+ }
200
+ /**
201
+ * Resolve required remapped keys from the source object's required key set.
202
+ */
203
+ static resolveMappedTypeSourceRequiredPropertyKeys(mappedEntries, sourceObjectSchema) {
204
+ if (!Is.array(sourceObjectSchema.required)) {
205
+ return undefined;
206
+ }
207
+ const sourceRequiredKeys = new Set(sourceObjectSchema.required);
208
+ const remappedRequiredKeys = mappedEntries
209
+ .filter(mappedEntry => sourceRequiredKeys.has(mappedEntry.sourceKey))
210
+ .map(mappedEntry => mappedEntry.mappedKey);
211
+ return [...new Set(remappedRequiredKeys)];
212
+ }
213
+ /**
214
+ * Apply TypeScript intrinsic string remapping helpers to a key.
215
+ */
216
+ static applyIntrinsicMappedTypeKeyRemap(intrinsicName, value) {
217
+ switch (intrinsicName) {
218
+ case "Uppercase":
219
+ return value.toUpperCase();
220
+ case "Lowercase":
221
+ return value.toLowerCase();
222
+ case "Capitalize":
223
+ return value.length > 0 ? `${value[0].toUpperCase()}${value.slice(1)}` : value;
224
+ case "Uncapitalize":
225
+ return value.length > 0 ? `${value[0].toLowerCase()}${value.slice(1)}` : value;
226
+ default:
227
+ return undefined;
228
+ }
229
+ }
230
+ }
231
+ //# sourceMappingURL=mappedTypeSchemaResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mappedTypeSchemaResolver.js","sourceRoot":"","sources":["../../../src/utils/mappedTypeSchemaResolver.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n/* eslint-disable jsdoc/require-param, jsdoc/require-returns */\nimport { Is, JsonHelper } from \"@twin.org/core\";\nimport type { IJsonSchema } from \"@twin.org/tools-models\";\nimport * as ts from \"typescript\";\nimport { JsonSchemaBuilder } from \"./jsonSchemaBuilder.js\";\nimport type { ITypeScriptToSchemaContext } from \"../models/ITypeScriptToSchemaContext.js\";\n\n/**\n * Static mapped-type schema transformation helpers.\n */\nexport class MappedTypeSchemaResolver {\n\t/**\n\t * Resolve mapped type output keys, including remapped key names via `as`.\n\t */\n\tpublic static resolveMappedTypePropertyEntries(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\ttypeNode: ts.MappedTypeNode,\n\t\tmappedKeys: string[],\n\t\tmappedTypeParameterName: string\n\t): { sourceKey: string; mappedKey: string }[] | undefined {\n\t\tif (!typeNode.nameType) {\n\t\t\treturn mappedKeys.map(mappedKey => ({ sourceKey: mappedKey, mappedKey }));\n\t\t}\n\n\t\tconst mappedEntries: { sourceKey: string; mappedKey: string }[] = [];\n\n\t\tfor (const sourceKey of mappedKeys) {\n\t\t\tconst mappedKey = MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(\n\t\t\t\tcontext,\n\t\t\t\ttypeNode.nameType,\n\t\t\t\tsourceKey,\n\t\t\t\tmappedTypeParameterName\n\t\t\t);\n\t\t\tif (mappedKey !== null && !Is.stringValue(mappedKey)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (Is.stringValue(mappedKey)) {\n\t\t\t\tmappedEntries.push({ sourceKey, mappedKey });\n\t\t\t}\n\t\t}\n\n\t\treturn mappedEntries;\n\t}\n\n\t/**\n\t * Resolve a remapped mapped-type key expression for a concrete source key.\n\t */\n\tpublic static resolveMappedTypeRemappedKey(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\tnameTypeNode: ts.TypeNode,\n\t\tsourceKey: string,\n\t\tmappedTypeParameterName: string\n\t): string | null | undefined {\n\t\tif (ts.isParenthesizedTypeNode(nameTypeNode)) {\n\t\t\treturn MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(\n\t\t\t\tcontext,\n\t\t\t\tnameTypeNode.type,\n\t\t\t\tsourceKey,\n\t\t\t\tmappedTypeParameterName\n\t\t\t);\n\t\t}\n\n\t\tif (nameTypeNode.kind === ts.SyntaxKind.NeverKeyword) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (JsonSchemaBuilder.isMappedTypeParameterReference(nameTypeNode, mappedTypeParameterName)) {\n\t\t\treturn sourceKey;\n\t\t}\n\n\t\tif (ts.isLiteralTypeNode(nameTypeNode)) {\n\t\t\tif (ts.isStringLiteral(nameTypeNode.literal) || ts.isNumericLiteral(nameTypeNode.literal)) {\n\t\t\t\treturn nameTypeNode.literal.text;\n\t\t\t}\n\t\t\tif (nameTypeNode.literal.kind === ts.SyntaxKind.TrueKeyword) {\n\t\t\t\treturn \"true\";\n\t\t\t}\n\t\t\tif (nameTypeNode.literal.kind === ts.SyntaxKind.FalseKeyword) {\n\t\t\t\treturn \"false\";\n\t\t\t}\n\t\t}\n\n\t\tif (ts.isIntersectionTypeNode(nameTypeNode)) {\n\t\t\tconst hasMappedKeyReference = nameTypeNode.types.some(type =>\n\t\t\t\tJsonSchemaBuilder.isMappedTypeParameterReference(type, mappedTypeParameterName)\n\t\t\t);\n\t\t\tconst hasStringKeyword = nameTypeNode.types.some(\n\t\t\t\ttype => type.kind === ts.SyntaxKind.StringKeyword\n\t\t\t);\n\t\t\tif (hasMappedKeyReference && hasStringKeyword) {\n\t\t\t\treturn sourceKey;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (ts.isTemplateLiteralTypeNode(nameTypeNode)) {\n\t\t\tlet remappedKey = nameTypeNode.head.text;\n\t\t\tfor (const span of nameTypeNode.templateSpans) {\n\t\t\t\tconst spanValue = MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(\n\t\t\t\t\tcontext,\n\t\t\t\t\tspan.type,\n\t\t\t\t\tsourceKey,\n\t\t\t\t\tmappedTypeParameterName\n\t\t\t\t);\n\t\t\t\tif (!Is.stringValue(spanValue)) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\tremappedKey += `${spanValue}${span.literal.text}`;\n\t\t\t}\n\n\t\t\treturn remappedKey;\n\t\t}\n\n\t\tif (ts.isTypeReferenceNode(nameTypeNode) && ts.isIdentifier(nameTypeNode.typeName)) {\n\t\t\tconst intrinsicName = nameTypeNode.typeName.text;\n\t\t\tif (nameTypeNode.typeArguments?.length === 1) {\n\t\t\t\tconst intrinsicInput = MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(\n\t\t\t\t\tcontext,\n\t\t\t\t\tnameTypeNode.typeArguments[0],\n\t\t\t\t\tsourceKey,\n\t\t\t\t\tmappedTypeParameterName\n\t\t\t\t);\n\t\t\t\tif (!Is.stringValue(intrinsicInput)) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\treturn MappedTypeSchemaResolver.applyIntrinsicMappedTypeKeyRemap(\n\t\t\t\t\tintrinsicName,\n\t\t\t\t\tintrinsicInput\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (ts.isConditionalTypeNode(nameTypeNode)) {\n\t\t\tif (\n\t\t\t\t!JsonSchemaBuilder.isMappedTypeParameterReference(\n\t\t\t\t\tnameTypeNode.checkType,\n\t\t\t\t\tmappedTypeParameterName\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst satisfies = MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(\n\t\t\t\tcontext,\n\t\t\t\tsourceKey,\n\t\t\t\tnameTypeNode.extendsType\n\t\t\t);\n\t\t\tif (satisfies === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst branchType = satisfies ? nameTypeNode.trueType : nameTypeNode.falseType;\n\t\t\treturn MappedTypeSchemaResolver.resolveMappedTypeRemappedKey(\n\t\t\t\tcontext,\n\t\t\t\tbranchType,\n\t\t\t\tsourceKey,\n\t\t\t\tmappedTypeParameterName\n\t\t\t);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Evaluate whether a concrete mapped key satisfies an `extends` condition.\n\t */\n\tpublic static evaluateMappedKeyExtendsCondition(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\tsourceKey: string,\n\t\textendsTypeNode: ts.TypeNode\n\t): boolean | undefined {\n\t\tif (ts.isParenthesizedTypeNode(extendsTypeNode)) {\n\t\t\treturn MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(\n\t\t\t\tcontext,\n\t\t\t\tsourceKey,\n\t\t\t\textendsTypeNode.type\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.StringKeyword ||\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.AnyKeyword ||\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.UnknownKeyword\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.NeverKeyword ||\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.NumberKeyword ||\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.BooleanKeyword ||\n\t\t\textendsTypeNode.kind === ts.SyntaxKind.ObjectKeyword\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (ts.isLiteralTypeNode(extendsTypeNode)) {\n\t\t\tif (\n\t\t\t\tts.isStringLiteral(extendsTypeNode.literal) ||\n\t\t\t\tts.isNumericLiteral(extendsTypeNode.literal)\n\t\t\t) {\n\t\t\t\treturn sourceKey === extendsTypeNode.literal.text;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tif (ts.isUnionTypeNode(extendsTypeNode)) {\n\t\t\tlet hasUndetermined = false;\n\t\t\tfor (const memberType of extendsTypeNode.types) {\n\t\t\t\tconst result = MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(\n\t\t\t\t\tcontext,\n\t\t\t\t\tsourceKey,\n\t\t\t\t\tmemberType\n\t\t\t\t);\n\t\t\t\tif (result === true) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (result === undefined) {\n\t\t\t\t\thasUndetermined = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hasUndetermined ? undefined : false;\n\t\t}\n\n\t\tif (\n\t\t\tts.isTypeOperatorNode(extendsTypeNode) &&\n\t\t\textendsTypeNode.operator === ts.SyntaxKind.KeyOfKeyword\n\t\t) {\n\t\t\tconst keys = JsonSchemaBuilder.extractKeyofTypeKeys(context, extendsTypeNode.type);\n\t\t\tif (keys.length > 0) {\n\t\t\t\treturn keys.includes(sourceKey);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (ts.isTypeReferenceNode(extendsTypeNode)) {\n\t\t\tconst resolved = JsonSchemaBuilder.resolveReferencedTypeNodeFromLocalDeclaration(\n\t\t\t\tcontext,\n\t\t\t\textendsTypeNode\n\t\t\t);\n\t\t\tif (resolved) {\n\t\t\t\treturn MappedTypeSchemaResolver.evaluateMappedKeyExtendsCondition(\n\t\t\t\t\tcontext,\n\t\t\t\t\tsourceKey,\n\t\t\t\t\tresolved\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Build a conservative fallback schema for mapped types whose key remapping cannot be resolved.\n\t */\n\tpublic static buildMappedTypeFallbackSchema(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\ttypeNode: ts.MappedTypeNode,\n\t\tmappedKeys: string[],\n\t\tmappedTypeParameterName: string,\n\t\tsourceObjectSchema?: IJsonSchema\n\t): IJsonSchema {\n\t\tconst additionalProperties =\n\t\t\tMappedTypeSchemaResolver.buildMappedTypeFallbackAdditionalProperties(\n\t\t\t\tcontext,\n\t\t\t\ttypeNode,\n\t\t\t\tmappedKeys,\n\t\t\t\tmappedTypeParameterName,\n\t\t\t\tsourceObjectSchema\n\t\t\t);\n\n\t\treturn {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: additionalProperties ?? {}\n\t\t};\n\t}\n\n\t/**\n\t * Build fallback additionalProperties for unresolved mapped key remapping.\n\t */\n\tpublic static buildMappedTypeFallbackAdditionalProperties(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\ttypeNode: ts.MappedTypeNode,\n\t\tmappedKeys: string[],\n\t\tmappedTypeParameterName: string,\n\t\tsourceObjectSchema?: IJsonSchema\n\t): IJsonSchema | undefined {\n\t\tconst resolvedPropertySchemas = mappedKeys\n\t\t\t.map(mappedKey =>\n\t\t\t\tJsonSchemaBuilder.mapMappedTypePropertySchema(\n\t\t\t\t\tcontext,\n\t\t\t\t\ttypeNode,\n\t\t\t\t\tmappedKey,\n\t\t\t\t\tmappedTypeParameterName,\n\t\t\t\t\tsourceObjectSchema\n\t\t\t\t)\n\t\t\t)\n\t\t\t.filter((mappedType): mappedType is IJsonSchema => mappedType !== undefined);\n\n\t\tif (resolvedPropertySchemas.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst uniqueSchemas = resolvedPropertySchemas.filter((schema, index, allSchemas) => {\n\t\t\tconst schemaKey = JsonHelper.canonicalize(schema);\n\t\t\treturn allSchemas.findIndex(s => JsonHelper.canonicalize(s) === schemaKey) === index;\n\t\t});\n\n\t\tif (uniqueSchemas.length === 1) {\n\t\t\treturn uniqueSchemas[0];\n\t\t}\n\n\t\treturn {\n\t\t\tanyOf: uniqueSchemas\n\t\t};\n\t}\n\n\t/**\n\t * Merge mapped property schemas when multiple source keys remap to the same output key.\n\t */\n\tpublic static mergeMappedTypePropertySchemas(\n\t\texistingSchema: IJsonSchema,\n\t\tnextSchema: IJsonSchema\n\t): IJsonSchema {\n\t\tconst schemaVariants = [\n\t\t\t...(existingSchema.anyOf ?? [existingSchema]),\n\t\t\t...(nextSchema.anyOf ?? [nextSchema])\n\t\t];\n\n\t\tconst uniqueSchemas = schemaVariants.filter((schema, index, allSchemas) => {\n\t\t\tconst schemaKey = JsonHelper.canonicalize(schema);\n\t\t\treturn allSchemas.findIndex(s => JsonHelper.canonicalize(s) === schemaKey) === index;\n\t\t});\n\n\t\tif (uniqueSchemas.length === 1) {\n\t\t\treturn uniqueSchemas[0];\n\t\t}\n\n\t\treturn {\n\t\t\tanyOf: uniqueSchemas\n\t\t};\n\t}\n\n\t/**\n\t * Resolve required remapped keys from the source object's required key set.\n\t */\n\tpublic static resolveMappedTypeSourceRequiredPropertyKeys(\n\t\tmappedEntries: { sourceKey: string; mappedKey: string }[],\n\t\tsourceObjectSchema: IJsonSchema\n\t): string[] | undefined {\n\t\tif (!Is.array<string>(sourceObjectSchema.required)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst sourceRequiredKeys = new Set(sourceObjectSchema.required);\n\t\tconst remappedRequiredKeys = mappedEntries\n\t\t\t.filter(mappedEntry => sourceRequiredKeys.has(mappedEntry.sourceKey))\n\t\t\t.map(mappedEntry => mappedEntry.mappedKey);\n\n\t\treturn [...new Set(remappedRequiredKeys)];\n\t}\n\n\t/**\n\t * Apply TypeScript intrinsic string remapping helpers to a key.\n\t */\n\tprivate static applyIntrinsicMappedTypeKeyRemap(\n\t\tintrinsicName: string,\n\t\tvalue: string\n\t): string | undefined {\n\t\tswitch (intrinsicName) {\n\t\t\tcase \"Uppercase\":\n\t\t\t\treturn value.toUpperCase();\n\t\t\tcase \"Lowercase\":\n\t\t\t\treturn value.toLowerCase();\n\t\t\tcase \"Capitalize\":\n\t\t\t\treturn value.length > 0 ? `${value[0].toUpperCase()}${value.slice(1)}` : value;\n\t\t\tcase \"Uncapitalize\":\n\t\t\t\treturn value.length > 0 ? `${value[0].toLowerCase()}${value.slice(1)}` : value;\n\t\t\tdefault:\n\t\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,162 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { Is, ObjectHelper } from "@twin.org/core";
4
+ /**
5
+ * Applies common object-schema transformations used by the builder.
6
+ */
7
+ export class ObjectTransformer {
8
+ /**
9
+ * Resolve a property schema from an object schema by property key.
10
+ * @param baseSchema The source object schema.
11
+ * @param propertyKey The property key to resolve.
12
+ * @returns The resolved property schema.
13
+ */
14
+ static resolvePropertySchemaFromObjectSchema(baseSchema, propertyKey) {
15
+ if (!baseSchema.properties || !Is.object(baseSchema.properties)) {
16
+ return undefined;
17
+ }
18
+ const propertySchema = baseSchema.properties[propertyKey];
19
+ return propertySchema ? ObjectHelper.clone(propertySchema) : undefined;
20
+ }
21
+ /**
22
+ * Remove keys from an object schema.
23
+ * @param baseSchema The source object schema.
24
+ * @param omittedKeys The keys to remove.
25
+ * @returns The transformed object schema.
26
+ */
27
+ static omitKeysFromObjectSchema(baseSchema, omittedKeys) {
28
+ const omittedKeySet = new Set(omittedKeys);
29
+ const mappedSchema = ObjectTransformer.toInlineUtilityObjectSchema(baseSchema);
30
+ if (Is.object(mappedSchema.properties)) {
31
+ for (const key of Object.keys(mappedSchema.properties)) {
32
+ if (omittedKeySet.has(key)) {
33
+ delete mappedSchema.properties[key];
34
+ }
35
+ }
36
+ if (Object.keys(mappedSchema.properties).length === 0) {
37
+ delete mappedSchema.properties;
38
+ }
39
+ }
40
+ if (Is.array(mappedSchema.required)) {
41
+ mappedSchema.required = mappedSchema.required.filter(key => !omittedKeySet.has(key));
42
+ if (mappedSchema.required.length === 0) {
43
+ delete mappedSchema.required;
44
+ }
45
+ }
46
+ return mappedSchema;
47
+ }
48
+ /**
49
+ * Keep only keys from an object schema.
50
+ * @param baseSchema The source object schema.
51
+ * @param pickedKeys The keys to keep.
52
+ * @returns The transformed object schema.
53
+ */
54
+ static pickKeysFromObjectSchema(baseSchema, pickedKeys) {
55
+ const pickedKeySet = new Set(pickedKeys);
56
+ const mappedSchema = ObjectTransformer.toInlineUtilityObjectSchema(baseSchema);
57
+ if (Is.object(mappedSchema.properties)) {
58
+ for (const key of Object.keys(mappedSchema.properties)) {
59
+ if (!pickedKeySet.has(key)) {
60
+ delete mappedSchema.properties[key];
61
+ }
62
+ }
63
+ if (Object.keys(mappedSchema.properties).length === 0) {
64
+ delete mappedSchema.properties;
65
+ }
66
+ }
67
+ if (Is.array(mappedSchema.required)) {
68
+ mappedSchema.required = mappedSchema.required.filter(key => pickedKeySet.has(key));
69
+ if (mappedSchema.required.length === 0) {
70
+ delete mappedSchema.required;
71
+ }
72
+ }
73
+ return mappedSchema;
74
+ }
75
+ /**
76
+ * Normalize utility-derived object schemas for inline property usage.
77
+ * When the schema contains an allOf, flatten it by merging properties from all branches.
78
+ * @param schema The source object schema.
79
+ * @returns The normalized inline schema.
80
+ * @internal
81
+ */
82
+ static toInlineUtilityObjectSchema(schema) {
83
+ const mappedSchema = ObjectHelper.clone(schema);
84
+ delete mappedSchema.$schema;
85
+ delete mappedSchema.$id;
86
+ delete mappedSchema.title;
87
+ // Flatten allOf by merging properties and required arrays from all branches
88
+ // This handles inheritance cases where a type extends another type
89
+ if (Is.array(mappedSchema.allOf) && mappedSchema.allOf.length > 0) {
90
+ const mergedProperties = {};
91
+ const mergedRequired = new Set();
92
+ // Collect properties and required fields from all allOf branches
93
+ for (const branch of mappedSchema.allOf) {
94
+ if (Is.object(branch)) {
95
+ const branchRecord = branch;
96
+ // Merge properties
97
+ if (Is.object(branchRecord.properties)) {
98
+ Object.assign(mergedProperties, branchRecord.properties);
99
+ }
100
+ // Collect required fields
101
+ if (Is.array(branchRecord.required)) {
102
+ for (const field of branchRecord.required) {
103
+ if (Is.stringValue(field)) {
104
+ mergedRequired.add(field);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ // Remove allOf and set merged properties at the top level
111
+ delete mappedSchema.allOf;
112
+ if (Object.keys(mergedProperties).length > 0) {
113
+ mappedSchema.properties = Object.assign(mappedSchema.properties ?? {}, mergedProperties);
114
+ }
115
+ if (mergedRequired.size > 0) {
116
+ const existingRequired = Is.array(mappedSchema.required) ? mappedSchema.required : [];
117
+ mappedSchema.required = [...new Set([...existingRequired, ...mergedRequired])];
118
+ }
119
+ }
120
+ return mappedSchema;
121
+ }
122
+ /**
123
+ * Normalize schema description whitespace while preserving intentional line breaks.
124
+ * @param schema The schema to normalize.
125
+ * @returns The normalized schema.
126
+ */
127
+ static normalizeSchemaDescriptions(schema) {
128
+ const normalizedSchema = ObjectHelper.clone(schema);
129
+ const normalizeObject = (value) => {
130
+ if (Array.isArray(value)) {
131
+ for (const item of value) {
132
+ normalizeObject(item);
133
+ }
134
+ return;
135
+ }
136
+ if (Is.object(value)) {
137
+ const valueRecord = value;
138
+ if (Is.stringValue(valueRecord.description)) {
139
+ valueRecord.description = ObjectTransformer.normalizeSchemaDescriptionText(valueRecord.description);
140
+ }
141
+ for (const nestedValue of Object.values(valueRecord)) {
142
+ normalizeObject(nestedValue);
143
+ }
144
+ }
145
+ };
146
+ normalizeObject(normalizedSchema);
147
+ return normalizedSchema;
148
+ }
149
+ /**
150
+ * Normalize a description string while preserving explicit line breaks from source comments.
151
+ * @param description The description to normalize.
152
+ * @returns The normalized description.
153
+ * @internal
154
+ */
155
+ static normalizeSchemaDescriptionText(description) {
156
+ return description
157
+ .split(/\r?\n/)
158
+ .map(line => line.replace(/\s{2,}/g, " ").trim())
159
+ .join("\n");
160
+ }
161
+ }
162
+ //# sourceMappingURL=objectTransformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objectTransformer.js","sourceRoot":"","sources":["../../../src/utils/objectTransformer.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is, ObjectHelper } from \"@twin.org/core\";\nimport type { IJsonSchema } from \"@twin.org/tools-models\";\n\n/**\n * Applies common object-schema transformations used by the builder.\n */\nexport class ObjectTransformer {\n\t/**\n\t * Resolve a property schema from an object schema by property key.\n\t * @param baseSchema The source object schema.\n\t * @param propertyKey The property key to resolve.\n\t * @returns The resolved property schema.\n\t */\n\tpublic static resolvePropertySchemaFromObjectSchema(\n\t\tbaseSchema: IJsonSchema,\n\t\tpropertyKey: string\n\t): IJsonSchema | undefined {\n\t\tif (!baseSchema.properties || !Is.object(baseSchema.properties)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst propertySchema = baseSchema.properties[propertyKey];\n\t\treturn propertySchema ? ObjectHelper.clone(propertySchema) : undefined;\n\t}\n\n\t/**\n\t * Remove keys from an object schema.\n\t * @param baseSchema The source object schema.\n\t * @param omittedKeys The keys to remove.\n\t * @returns The transformed object schema.\n\t */\n\tpublic static omitKeysFromObjectSchema(\n\t\tbaseSchema: IJsonSchema,\n\t\tomittedKeys: string[]\n\t): IJsonSchema {\n\t\tconst omittedKeySet = new Set(omittedKeys);\n\t\tconst mappedSchema = ObjectTransformer.toInlineUtilityObjectSchema(baseSchema);\n\n\t\tif (Is.object(mappedSchema.properties)) {\n\t\t\tfor (const key of Object.keys(mappedSchema.properties)) {\n\t\t\t\tif (omittedKeySet.has(key)) {\n\t\t\t\t\tdelete mappedSchema.properties[key];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Object.keys(mappedSchema.properties).length === 0) {\n\t\t\t\tdelete mappedSchema.properties;\n\t\t\t}\n\t\t}\n\n\t\tif (Is.array(mappedSchema.required)) {\n\t\t\tmappedSchema.required = mappedSchema.required.filter(key => !omittedKeySet.has(key));\n\t\t\tif (mappedSchema.required.length === 0) {\n\t\t\t\tdelete mappedSchema.required;\n\t\t\t}\n\t\t}\n\n\t\treturn mappedSchema;\n\t}\n\n\t/**\n\t * Keep only keys from an object schema.\n\t * @param baseSchema The source object schema.\n\t * @param pickedKeys The keys to keep.\n\t * @returns The transformed object schema.\n\t */\n\tpublic static pickKeysFromObjectSchema(\n\t\tbaseSchema: IJsonSchema,\n\t\tpickedKeys: string[]\n\t): IJsonSchema {\n\t\tconst pickedKeySet = new Set(pickedKeys);\n\t\tconst mappedSchema = ObjectTransformer.toInlineUtilityObjectSchema(baseSchema);\n\n\t\tif (Is.object(mappedSchema.properties)) {\n\t\t\tfor (const key of Object.keys(mappedSchema.properties)) {\n\t\t\t\tif (!pickedKeySet.has(key)) {\n\t\t\t\t\tdelete mappedSchema.properties[key];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Object.keys(mappedSchema.properties).length === 0) {\n\t\t\t\tdelete mappedSchema.properties;\n\t\t\t}\n\t\t}\n\n\t\tif (Is.array(mappedSchema.required)) {\n\t\t\tmappedSchema.required = mappedSchema.required.filter(key => pickedKeySet.has(key));\n\t\t\tif (mappedSchema.required.length === 0) {\n\t\t\t\tdelete mappedSchema.required;\n\t\t\t}\n\t\t}\n\n\t\treturn mappedSchema;\n\t}\n\n\t/**\n\t * Normalize utility-derived object schemas for inline property usage.\n\t * When the schema contains an allOf, flatten it by merging properties from all branches.\n\t * @param schema The source object schema.\n\t * @returns The normalized inline schema.\n\t * @internal\n\t */\n\tpublic static toInlineUtilityObjectSchema(schema: IJsonSchema): IJsonSchema {\n\t\tconst mappedSchema = ObjectHelper.clone(schema);\n\t\tdelete mappedSchema.$schema;\n\t\tdelete mappedSchema.$id;\n\t\tdelete mappedSchema.title;\n\n\t\t// Flatten allOf by merging properties and required arrays from all branches\n\t\t// This handles inheritance cases where a type extends another type\n\t\tif (Is.array(mappedSchema.allOf) && mappedSchema.allOf.length > 0) {\n\t\t\tconst mergedProperties: { [key: string]: unknown } = {};\n\t\t\tconst mergedRequired = new Set<string>();\n\n\t\t\t// Collect properties and required fields from all allOf branches\n\t\t\tfor (const branch of mappedSchema.allOf) {\n\t\t\t\tif (Is.object(branch)) {\n\t\t\t\t\tconst branchRecord = branch as { [key: string]: unknown };\n\t\t\t\t\t// Merge properties\n\t\t\t\t\tif (Is.object(branchRecord.properties)) {\n\t\t\t\t\t\tObject.assign(mergedProperties, branchRecord.properties);\n\t\t\t\t\t}\n\t\t\t\t\t// Collect required fields\n\t\t\t\t\tif (Is.array(branchRecord.required)) {\n\t\t\t\t\t\tfor (const field of branchRecord.required) {\n\t\t\t\t\t\t\tif (Is.stringValue(field)) {\n\t\t\t\t\t\t\t\tmergedRequired.add(field);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove allOf and set merged properties at the top level\n\t\t\tdelete mappedSchema.allOf;\n\n\t\t\tif (Object.keys(mergedProperties).length > 0) {\n\t\t\t\tmappedSchema.properties = Object.assign(mappedSchema.properties ?? {}, mergedProperties);\n\t\t\t}\n\n\t\t\tif (mergedRequired.size > 0) {\n\t\t\t\tconst existingRequired = Is.array(mappedSchema.required) ? mappedSchema.required : [];\n\t\t\t\tmappedSchema.required = [...new Set([...existingRequired, ...mergedRequired])];\n\t\t\t}\n\t\t}\n\n\t\treturn mappedSchema;\n\t}\n\n\t/**\n\t * Normalize schema description whitespace while preserving intentional line breaks.\n\t * @param schema The schema to normalize.\n\t * @returns The normalized schema.\n\t */\n\tpublic static normalizeSchemaDescriptions(schema: IJsonSchema): IJsonSchema {\n\t\tconst normalizedSchema = ObjectHelper.clone(schema);\n\t\tconst normalizeObject = (value: unknown): void => {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const item of value) {\n\t\t\t\t\tnormalizeObject(item);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (Is.object(value)) {\n\t\t\t\tconst valueRecord = value as { [key: string]: unknown };\n\t\t\t\tif (Is.stringValue(valueRecord.description)) {\n\t\t\t\t\tvalueRecord.description = ObjectTransformer.normalizeSchemaDescriptionText(\n\t\t\t\t\t\tvalueRecord.description\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tfor (const nestedValue of Object.values(valueRecord)) {\n\t\t\t\t\tnormalizeObject(nestedValue);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tnormalizeObject(normalizedSchema);\n\t\treturn normalizedSchema;\n\t}\n\n\t/**\n\t * Normalize a description string while preserving explicit line breaks from source comments.\n\t * @param description The description to normalize.\n\t * @returns The normalized description.\n\t * @internal\n\t */\n\tprivate static normalizeSchemaDescriptionText(description: string): string {\n\t\treturn description\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map(line => line.replace(/\\s{2,}/g, \" \").trim())\n\t\t\t.join(\"\\n\");\n\t}\n}\n"]}
@@ -0,0 +1,128 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ /**
4
+ * Utility methods for pattern matching and regex construction.
5
+ */
6
+ export class RegEx {
7
+ /**
8
+ * Determine whether a reference mapping pattern matches an external reference.
9
+ * @param pattern The mapping pattern.
10
+ * @param packageName The external package name.
11
+ * @param typeName The imported type name.
12
+ * @param schemaTitle The derived schema title.
13
+ * @returns True if matched.
14
+ */
15
+ static isReferencePatternMatch(pattern, packageName, typeName, schemaTitle) {
16
+ const regex = RegEx.toRegexFromReferencePattern(pattern);
17
+ return regex
18
+ ? regex.test(packageName) || regex.test(typeName) || regex.test(schemaTitle)
19
+ : false;
20
+ }
21
+ /**
22
+ * Apply a reference mapping replacement to the first matching candidate.
23
+ * @param pattern The mapping pattern.
24
+ * @param replacement The replacement template.
25
+ * @param packageName The external package name.
26
+ * @param typeName The imported type name.
27
+ * @param schemaTitle The derived schema title.
28
+ * @returns The replaced value if a candidate matches.
29
+ */
30
+ static applyReferencePatternReplacement(pattern, replacement, packageName, typeName, schemaTitle) {
31
+ const regex = RegEx.toRegexFromReferencePattern(pattern);
32
+ if (!regex) {
33
+ return undefined;
34
+ }
35
+ for (const candidate of [packageName, typeName, schemaTitle]) {
36
+ regex.lastIndex = 0;
37
+ if (regex.test(candidate)) {
38
+ regex.lastIndex = 0;
39
+ return candidate.replace(regex, replacement);
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ /**
45
+ * Determine if a mapping pattern is a regex literal surrounded by forward slashes.
46
+ * Example pattern: /^foo.*bar$/
47
+ * @param pattern The mapping pattern.
48
+ * @returns True if the pattern looks like a regex literal.
49
+ * @internal
50
+ */
51
+ static isRegexLiteralPattern(pattern) {
52
+ return pattern.length > 2 && pattern.startsWith("/") && pattern.endsWith("/");
53
+ }
54
+ /**
55
+ * Determine if a mapping pattern should be treated as a raw regex expression.
56
+ * Any pattern that contains at least one regex meta character is treated as a raw expression.
57
+ * @param pattern The mapping pattern.
58
+ * @returns True if the pattern contains regex syntax.
59
+ * @internal
60
+ */
61
+ static isRegexExpressionPattern(pattern) {
62
+ return /[()[\]{}+?|^$\\]/u.test(pattern);
63
+ }
64
+ /**
65
+ * Convert a regex literal mapping pattern to a RegExp.
66
+ * The surrounding forward slashes are stripped before compiling.
67
+ * @param pattern The regex literal pattern, e.g. /^foo.*bar$/
68
+ * @returns The RegExp instance, or undefined when the inner expression is invalid.
69
+ * @internal
70
+ */
71
+ static toRegexFromLiteralPattern(pattern) {
72
+ try {
73
+ return new RegExp(pattern.slice(1, -1));
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ /**
80
+ * Convert a raw regex expression mapping pattern to a RegExp anchored to full-string match.
81
+ * The expression is wrapped with ^ and $ so that partial matches are not accepted.
82
+ * @param pattern The raw regex expression.
83
+ * @returns The RegExp instance, or undefined when the expression is invalid.
84
+ * @internal
85
+ */
86
+ static toRegexFromExpressionPattern(pattern) {
87
+ try {
88
+ return new RegExp(`^${pattern}$`);
89
+ }
90
+ catch {
91
+ return undefined;
92
+ }
93
+ }
94
+ /**
95
+ * Select and compile the appropriate RegExp for a reference mapping pattern.
96
+ * Evaluation order: regex literal -> raw regex expression -> wildcard glob.
97
+ * Plain strings with none of these characteristics return undefined.
98
+ * @param pattern The mapping pattern.
99
+ * @returns The RegExp instance, or undefined when the pattern is not pattern-based.
100
+ * @internal
101
+ */
102
+ static toRegexFromReferencePattern(pattern) {
103
+ if (RegEx.isRegexLiteralPattern(pattern)) {
104
+ return RegEx.toRegexFromLiteralPattern(pattern);
105
+ }
106
+ if (RegEx.isRegexExpressionPattern(pattern)) {
107
+ return RegEx.toRegexFromExpressionPattern(pattern);
108
+ }
109
+ if (pattern.includes("*")) {
110
+ return RegEx.toRegexFromWildcardPattern(pattern);
111
+ }
112
+ return undefined;
113
+ }
114
+ /**
115
+ * Convert a glob wildcard pattern to a RegExp.
116
+ * All regex metacharacters in the pattern are escaped first, then each * is replaced with .*.
117
+ * The resulting expression is anchored with ^ and $ for full-string matching.
118
+ * @param pattern The wildcard pattern, e.g. *Operation or @twin.org/*.
119
+ * @returns The compiled RegExp.
120
+ * @internal
121
+ */
122
+ static toRegexFromWildcardPattern(pattern) {
123
+ const escapedPattern = pattern.replace(/[$()+./?[\\\]^{|}-]/g, "\\$&");
124
+ const regexPattern = `^${escapedPattern.replace(/\*/g, ".*")}$`;
125
+ return new RegExp(regexPattern);
126
+ }
127
+ }
128
+ //# sourceMappingURL=regEx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regEx.js","sourceRoot":"","sources":["../../../src/utils/regEx.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Utility methods for pattern matching and regex construction.\n */\nexport class RegEx {\n\t/**\n\t * Determine whether a reference mapping pattern matches an external reference.\n\t * @param pattern The mapping pattern.\n\t * @param packageName The external package name.\n\t * @param typeName The imported type name.\n\t * @param schemaTitle The derived schema title.\n\t * @returns True if matched.\n\t */\n\tpublic static isReferencePatternMatch(\n\t\tpattern: string,\n\t\tpackageName: string,\n\t\ttypeName: string,\n\t\tschemaTitle: string\n\t): boolean {\n\t\tconst regex = RegEx.toRegexFromReferencePattern(pattern);\n\t\treturn regex\n\t\t\t? regex.test(packageName) || regex.test(typeName) || regex.test(schemaTitle)\n\t\t\t: false;\n\t}\n\n\t/**\n\t * Apply a reference mapping replacement to the first matching candidate.\n\t * @param pattern The mapping pattern.\n\t * @param replacement The replacement template.\n\t * @param packageName The external package name.\n\t * @param typeName The imported type name.\n\t * @param schemaTitle The derived schema title.\n\t * @returns The replaced value if a candidate matches.\n\t */\n\tpublic static applyReferencePatternReplacement(\n\t\tpattern: string,\n\t\treplacement: string,\n\t\tpackageName: string,\n\t\ttypeName: string,\n\t\tschemaTitle: string\n\t): string | undefined {\n\t\tconst regex = RegEx.toRegexFromReferencePattern(pattern);\n\t\tif (!regex) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tfor (const candidate of [packageName, typeName, schemaTitle]) {\n\t\t\tregex.lastIndex = 0;\n\t\t\tif (regex.test(candidate)) {\n\t\t\t\tregex.lastIndex = 0;\n\t\t\t\treturn candidate.replace(regex, replacement);\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Determine if a mapping pattern is a regex literal surrounded by forward slashes.\n\t * Example pattern: /^foo.*bar$/\n\t * @param pattern The mapping pattern.\n\t * @returns True if the pattern looks like a regex literal.\n\t * @internal\n\t */\n\tprivate static isRegexLiteralPattern(pattern: string): boolean {\n\t\treturn pattern.length > 2 && pattern.startsWith(\"/\") && pattern.endsWith(\"/\");\n\t}\n\n\t/**\n\t * Determine if a mapping pattern should be treated as a raw regex expression.\n\t * Any pattern that contains at least one regex meta character is treated as a raw expression.\n\t * @param pattern The mapping pattern.\n\t * @returns True if the pattern contains regex syntax.\n\t * @internal\n\t */\n\tprivate static isRegexExpressionPattern(pattern: string): boolean {\n\t\treturn /[()[\\]{}+?|^$\\\\]/u.test(pattern);\n\t}\n\n\t/**\n\t * Convert a regex literal mapping pattern to a RegExp.\n\t * The surrounding forward slashes are stripped before compiling.\n\t * @param pattern The regex literal pattern, e.g. /^foo.*bar$/\n\t * @returns The RegExp instance, or undefined when the inner expression is invalid.\n\t * @internal\n\t */\n\tprivate static toRegexFromLiteralPattern(pattern: string): RegExp | undefined {\n\t\ttry {\n\t\t\treturn new RegExp(pattern.slice(1, -1));\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Convert a raw regex expression mapping pattern to a RegExp anchored to full-string match.\n\t * The expression is wrapped with ^ and $ so that partial matches are not accepted.\n\t * @param pattern The raw regex expression.\n\t * @returns The RegExp instance, or undefined when the expression is invalid.\n\t * @internal\n\t */\n\tprivate static toRegexFromExpressionPattern(pattern: string): RegExp | undefined {\n\t\ttry {\n\t\t\treturn new RegExp(`^${pattern}$`);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Select and compile the appropriate RegExp for a reference mapping pattern.\n\t * Evaluation order: regex literal -> raw regex expression -> wildcard glob.\n\t * Plain strings with none of these characteristics return undefined.\n\t * @param pattern The mapping pattern.\n\t * @returns The RegExp instance, or undefined when the pattern is not pattern-based.\n\t * @internal\n\t */\n\tprivate static toRegexFromReferencePattern(pattern: string): RegExp | undefined {\n\t\tif (RegEx.isRegexLiteralPattern(pattern)) {\n\t\t\treturn RegEx.toRegexFromLiteralPattern(pattern);\n\t\t}\n\n\t\tif (RegEx.isRegexExpressionPattern(pattern)) {\n\t\t\treturn RegEx.toRegexFromExpressionPattern(pattern);\n\t\t}\n\n\t\tif (pattern.includes(\"*\")) {\n\t\t\treturn RegEx.toRegexFromWildcardPattern(pattern);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Convert a glob wildcard pattern to a RegExp.\n\t * All regex metacharacters in the pattern are escaped first, then each * is replaced with .*.\n\t * The resulting expression is anchored with ^ and $ for full-string matching.\n\t * @param pattern The wildcard pattern, e.g. *Operation or @twin.org/*.\n\t * @returns The compiled RegExp.\n\t * @internal\n\t */\n\tprivate static toRegexFromWildcardPattern(pattern: string): RegExp {\n\t\tconst escapedPattern = pattern.replace(/[$()+./?[\\\\\\]^{|}-]/g, \"\\\\$&\");\n\t\tconst regexPattern = `^${escapedPattern.replace(/\\*/g, \".*\")}$`;\n\t\treturn new RegExp(regexPattern);\n\t}\n}\n"]}